diff options
25 files changed, 475 insertions, 163 deletions
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index 03039b1..dfea17c 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -174,18 +174,16 @@ void NavigationNotificationObserver::Observe( // occur while authentication is ongoing. navigation_started_ = true; } else if (type == NotificationType::AUTH_NEEDED) { - if (navigation_started_) { - // Remember the login handler that wants authentication. - LoginHandler* handler = - Details<LoginNotificationDetails>(details)->handler(); - automation_->AddLoginHandler(controller_, handler); - - // Respond that authentication is needed. - navigation_started_ = false; - ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED); - } else { - NOTREACHED(); - } + // Remember the login handler that wants authentication. + // We do this in all cases (not just when navigation_started_ == true) so + // tests can still wait for auth dialogs outside of navigation. + LoginHandler* handler = + Details<LoginNotificationDetails>(details)->handler(); + automation_->AddLoginHandler(controller_, handler); + + // Respond that authentication is needed. + navigation_started_ = false; + ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED); } else { NOTREACHED(); } diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index 65182c2..2907d7f 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -159,7 +159,11 @@ class RVHDelegateNotificationTask : public Task { if (!ResourceDispatcherHost::RenderViewForRequest(request, &render_process_host_id_, &render_view_host_id_)) { - NOTREACHED(); + // Issue a warning here - this can happen during normal operation (for + // example, if a worker exits while a network operation is pending), but + // it should be fairly rare. + DLOG(WARNING) << "Trying to deliver a message to a RenderViewHost" << + " that has already exited or has never existed."; } } @@ -1451,8 +1455,13 @@ bool ResourceDispatcherHost::RenderViewForRequest(const URLRequest* request, *render_view_host_id = -1; return false; } - *render_process_host_id = worker_instance->renderer_id(); - *render_view_host_id = worker_instance->render_view_route_id(); + DCHECK(!worker_instance->worker_document_set()->IsEmpty()); + const WorkerDocumentSet::DocumentInfoSet& parents = + worker_instance->worker_document_set()->documents(); + // Need to display some related UI for this network request - pick an + // arbitrary parent to do so. + *render_process_host_id = parents.begin()->renderer_id(); + *render_view_host_id = parents.begin()->render_view_route_id(); } else { *render_process_host_id = info->child_id(); *render_view_host_id = info->route_id(); diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 11d7091..4313dbd 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -636,26 +636,24 @@ void ResourceMessageFilter::OnLaunchNaCl( host->Launch(this, channel_descriptor, reply_msg); } -void ResourceMessageFilter::OnCreateWorker(const GURL& url, - bool is_shared, - const string16& name, - int render_view_route_id, - int* route_id) { +void ResourceMessageFilter::OnCreateWorker( + const ViewHostMsg_CreateWorker_Params& params, int* route_id) { *route_id = render_widget_helper_->GetNextRoutingID(); WorkerService::GetInstance()->CreateWorker( - url, is_shared, off_the_record(), name, id(), render_view_route_id, this, - *route_id); + params.url, params.is_shared, off_the_record(), params.name, + params.document_id, id(), params.render_view_route_id, this, *route_id); } void ResourceMessageFilter::OnLookupSharedWorker(const GURL& url, const string16& name, unsigned long long document_id, + int render_view_route_id, int* route_id, bool* url_mismatch) { int new_route_id = render_widget_helper_->GetNextRoutingID(); bool worker_found = WorkerService::GetInstance()->LookupSharedWorker( - url, name, off_the_record(), document_id, this, new_route_id, - url_mismatch); + url, name, off_the_record(), document_id, id(), render_view_route_id, + this, new_route_id, url_mismatch); *route_id = worker_found ? new_route_id : MSG_ROUTING_NONE; } diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 585995f..7cd1251 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -44,6 +44,7 @@ class Profile; class RenderWidgetHelper; class URLRequestContextGetter; struct ViewHostMsg_Audio_CreateStream; +struct ViewHostMsg_CreateWorker_Params; struct WebPluginInfo; namespace printing { @@ -166,14 +167,12 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, void OnLaunchNaCl(const std::wstring& url, int channel_descriptor, IPC::Message* reply_msg); - void OnCreateWorker(const GURL& url, - bool is_shared, - const string16& name, - int render_view_route_id, + void OnCreateWorker(const ViewHostMsg_CreateWorker_Params& params, int* route_id); void OnLookupSharedWorker(const GURL& url, const string16& name, unsigned long long document_id, + int render_view_route_id, int* route_id, bool* url_error); void OnDocumentDetached(unsigned long long document_id); diff --git a/chrome/browser/renderer_host/resource_request_details.h b/chrome/browser/renderer_host/resource_request_details.h index c34af0f..ad0df97d 100644 --- a/chrome/browser/renderer_host/resource_request_details.h +++ b/chrome/browser/renderer_host/resource_request_details.h @@ -46,8 +46,18 @@ class ResourceRequestDetails { // such as ssl state etc. const WorkerProcessHost::WorkerInstance* worker_instance = WorkerService::GetInstance()->FindWorkerInstance(info->child_id()); - origin_child_id_ = - worker_instance ? worker_instance->renderer_id() : info->child_id(); + if (worker_instance) { + DCHECK(!worker_instance->worker_document_set()->IsEmpty()); + const WorkerDocumentSet::DocumentInfoSet& parents = + worker_instance->worker_document_set()->documents(); + // TODO(atwilson): need to notify all associated renderers in the case + // of ssl state change (http://crbug.com/25357). For now, just notify + // the first one (works for dedicated workers and shared workers with + // a single process). + origin_child_id_ = parents.begin()->renderer_id(); + } else { + origin_child_id_ = info->child_id(); + } } virtual ~ResourceRequestDetails() {} diff --git a/chrome/browser/worker_host/worker_document_set.cc b/chrome/browser/worker_host/worker_document_set.cc new file mode 100644 index 0000000..2e4d9aa --- /dev/null +++ b/chrome/browser/worker_host/worker_document_set.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/worker_host/worker_document_set.h" + +WorkerDocumentSet::WorkerDocumentSet() { +} + +void WorkerDocumentSet::Add(IPC::Message::Sender* parent, + unsigned long long document_id, + int renderer_id, + int render_view_route_id) { + DocumentInfo info(parent, document_id, renderer_id, render_view_route_id); + document_set_.insert(info); +} + +bool WorkerDocumentSet::Contains(IPC::Message::Sender* parent, + unsigned long long document_id) const { + for (DocumentInfoSet::const_iterator i = document_set_.begin(); + i != document_set_.end(); ++i) { + if (i->sender() == parent && i->document_id() == document_id) + return true; + } + return false; +} + +void WorkerDocumentSet::Remove(IPC::Message::Sender* parent, + unsigned long long document_id) { + for (DocumentInfoSet::iterator i = document_set_.begin(); + i != document_set_.end(); i++) { + if (i->sender() == parent && i->document_id() == document_id) { + document_set_.erase(i); + break; + } + } + // Should not be duplicate copies in the document set. + DCHECK(!Contains(parent, document_id)); +} + +void WorkerDocumentSet::RemoveAll(IPC::Message::Sender* parent) { + for (DocumentInfoSet::iterator i = document_set_.begin(); + i != document_set_.end();) { + + // Note this idiom is somewhat tricky - calling document_set_.erase(iter) + // invalidates any iterators that point to the element being removed, so + // bump the iterator beyond the item being removed before calling erase. + if (i->sender() == parent) { + DocumentInfoSet::iterator item_to_delete = i++; + document_set_.erase(item_to_delete); + } else { + ++i; + } + } +} + +WorkerDocumentSet::DocumentInfo::DocumentInfo( + IPC::Message::Sender* sender, unsigned long long document_id, + int renderer_id, int render_view_route_id) + : sender_(sender), + document_id_(document_id), + renderer_id_(renderer_id), + render_view_route_id_(render_view_route_id) { +} diff --git a/chrome/browser/worker_host/worker_document_set.h b/chrome/browser/worker_host/worker_document_set.h new file mode 100644 index 0000000..57c1669 --- /dev/null +++ b/chrome/browser/worker_host/worker_document_set.h @@ -0,0 +1,85 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_WORKER_HOST_WORKER_DOCUMENT_SET_H_ +#define CHROME_BROWSER_WORKER_HOST_WORKER_DOCUMENT_SET_H_ + +#include <set> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "ipc/ipc_message.h" + +// The WorkerDocumentSet tracks all of the DOM documents associated with a +// set of workers. With nested workers, multiple workers can share the same +// WorkerDocumentSet (meaning that they all share the same lifetime, and will +// all exit when the last document in that set exits, per the WebWorkers spec). +class WorkerDocumentSet : public base::RefCounted<WorkerDocumentSet> { + public: + WorkerDocumentSet(); + + // The information we track for each document + class DocumentInfo { + public: + DocumentInfo(IPC::Message::Sender* sender, unsigned long long document_id, + int renderer_id, int render_view_route_id); + IPC::Message::Sender* sender() const { return sender_; } + unsigned long long document_id() const { return document_id_; } + int renderer_id() const { return renderer_id_; } + int render_view_route_id() const { return render_view_route_id_; } + + // Define operator "<", which is used to determine uniqueness within + // the set. + bool operator <(DocumentInfo other) const { + // Items are identical if the sender and document_id are identical, + // otherwise create an arbitrary stable ordering based on the document + // id/sender. + if (sender() == other.sender()) { + return document_id() < other.document_id(); + } else { + return reinterpret_cast<unsigned long long>(sender()) < + reinterpret_cast<unsigned long long>(other.sender()); + } + } + + private: + IPC::Message::Sender* sender_; + unsigned long long document_id_; + int renderer_id_; + int render_view_route_id_; + }; + + // Adds a document to a shared worker's document set. Also includes the + // associated renderer_id the document is associated with, to enable + // communication with the parent tab for things like http auth dialogs. + void Add(IPC::Message::Sender* parent, + unsigned long long document_id, + int renderer_id, + int render_view_route_id); + + // Checks to see if a document is in a shared worker's document set. + bool Contains(IPC::Message::Sender* parent, + unsigned long long document_id) const; + + // Removes a specific document from a worker's document set when that document + // is detached. + void Remove(IPC::Message::Sender* parent, unsigned long long document_id); + + // Invoked when a render process exits, to remove all associated documents + // from a worker's document set. + void RemoveAll(IPC::Message::Sender* parent); + + bool IsEmpty() const { return document_set_.empty(); } + + // Define a typedef for convenience here when declaring iterators, etc. + typedef std::set<DocumentInfo> DocumentInfoSet; + + // Returns the set of documents associated with this worker. + const DocumentInfoSet& documents() { return document_set_; } + + private: + DocumentInfoSet document_set_; +}; + +#endif // CHROME_BROWSER_WORKER_HOST_WORKER_DOCUMENT_SET_H_ diff --git a/chrome/browser/worker_host/worker_process_host.cc b/chrome/browser/worker_host/worker_process_host.cc index 562daea..4f6d5ae 100644 --- a/chrome/browser/worker_host/worker_process_host.cc +++ b/chrome/browser/worker_host/worker_process_host.cc @@ -65,11 +65,17 @@ WorkerProcessHost::~WorkerProcessHost() { Source<WorkerProcessHost>(this), NotificationService::NoDetails()); - // If we crashed, tell the RenderViewHost. + // If we crashed, tell the RenderViewHosts. for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - new WorkerCrashTask(i->renderer_id(), i->render_view_route_id())); + const WorkerDocumentSet::DocumentInfoSet& parents = + i->worker_document_set()->documents(); + for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = + parents.begin(); parent_iter != parents.end(); ++parent_iter) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + new WorkerCrashTask(parent_iter->renderer_id(), + parent_iter->render_view_route_id())); + } } ChildProcessSecurityPolicy::GetInstance()->Remove(id()); @@ -331,8 +337,8 @@ void WorkerProcessHost::SenderShutdown(IPC::Message::Sender* sender) { bool shutdown = false; i->RemoveSenders(sender); if (i->shared()) { - i->RemoveAllAssociatedDocuments(sender); - if (i->IsDocumentSetEmpty()) { + i->worker_document_set()->RemoveAll(sender); + if (i->worker_document_set()->IsEmpty()) { shutdown = true; } } else if (i->NumSenders() == 0) { @@ -375,28 +381,37 @@ void WorkerProcessHost::UpdateTitle() { void WorkerProcessHost::OnLookupSharedWorker(const GURL& url, const string16& name, unsigned long long document_id, + int render_view_route_id, int* route_id, bool* url_mismatch) { int new_route_id = WorkerService::GetInstance()->next_worker_route_id(); - // TODO(atwilson): Add code to merge document sets for nested shared workers. + // TODO(atwilson): Add code to pass in the current worker's document set for + // these nested workers. Code below will not work for SharedWorkers as it + // only looks at a single parent. + DCHECK(instances_.front().worker_document_set()->documents().size() == 1); + WorkerDocumentSet::DocumentInfoSet::const_iterator first_parent = + instances_.front().worker_document_set()->documents().begin(); bool worker_found = WorkerService::GetInstance()->LookupSharedWorker( - url, name, instances_.front().off_the_record(), document_id, this, + url, name, instances_.front().off_the_record(), document_id, + first_parent->renderer_id(), first_parent->render_view_route_id(), this, new_route_id, url_mismatch); *route_id = worker_found ? new_route_id : MSG_ROUTING_NONE; } -void WorkerProcessHost::OnCreateWorker(const GURL& url, - bool shared, - const string16& name, - int render_view_route_id, - int* route_id) { +void WorkerProcessHost::OnCreateWorker( + const ViewHostMsg_CreateWorker_Params& params, int* route_id) { DCHECK(instances_.size() == 1); // Only called when one process per worker. + // TODO(atwilson): Add code to pass in the current worker's document set for + // these nested workers. Code below will not work for SharedWorkers as it + // only looks at a single parent. + DCHECK(instances_.front().worker_document_set()->documents().size() == 1); + WorkerDocumentSet::DocumentInfoSet::const_iterator first_parent = + instances_.front().worker_document_set()->documents().begin(); *route_id = WorkerService::GetInstance()->next_worker_route_id(); WorkerService::GetInstance()->CreateWorker( - url, shared, instances_.front().off_the_record(), name, - instances_.front().renderer_id(), - instances_.front().render_view_route_id(), this, *route_id); - // TODO(atwilson): Add code to merge document sets for nested shared workers. + params.url, params.is_shared, instances_.front().off_the_record(), + params.name, params.document_id, first_parent->renderer_id(), + first_parent->render_view_route_id(), this, *route_id); } void WorkerProcessHost::OnCancelCreateDedicatedWorker(int route_id) { @@ -415,8 +430,8 @@ void WorkerProcessHost::DocumentDetached(IPC::Message::Sender* parent, if (!i->shared()) { ++i; } else { - i->RemoveFromDocumentSet(parent, document_id); - if (i->IsDocumentSetEmpty()) { + i->worker_document_set()->Remove(parent, document_id); + if (i->worker_document_set()->IsEmpty()) { // This worker has no more associated documents - shut it down. Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id())); i = instances_.erase(i); @@ -431,17 +446,14 @@ WorkerProcessHost::WorkerInstance::WorkerInstance(const GURL& url, bool shared, bool off_the_record, const string16& name, - int renderer_id, - int render_view_route_id, int worker_route_id) : url_(url), shared_(shared), off_the_record_(off_the_record), closed_(false), name_(name), - renderer_id_(renderer_id), - render_view_route_id_(render_view_route_id), - worker_route_id_(worker_route_id) { + worker_route_id_(worker_route_id), + worker_document_set_(new WorkerDocumentSet()) { } // Compares an instance based on the algorithm in the WebWorkers spec - an @@ -469,48 +481,6 @@ bool WorkerProcessHost::WorkerInstance::Matches( return name_ == match_name; } -void WorkerProcessHost::WorkerInstance::AddToDocumentSet( - IPC::Message::Sender* parent, unsigned long long document_id) { - if (!IsInDocumentSet(parent, document_id)) { - DocumentInfo info(parent, document_id); - document_set_.push_back(info); - } -} - -bool WorkerProcessHost::WorkerInstance::IsInDocumentSet( - IPC::Message::Sender* parent, unsigned long long document_id) const { - for (DocumentSet::const_iterator i = document_set_.begin(); - i != document_set_.end(); ++i) { - if (i->first == parent && i->second == document_id) - return true; - } - return false; -} - -void WorkerProcessHost::WorkerInstance::RemoveFromDocumentSet( - IPC::Message::Sender* parent, unsigned long long document_id) { - for (DocumentSet::iterator i = document_set_.begin(); - i != document_set_.end(); i++) { - if (i->first == parent && i->second == document_id) { - document_set_.erase(i); - break; - } - } - // Should not be duplicate copies in the document set. - DCHECK(!IsInDocumentSet(parent, document_id)); -} - -void WorkerProcessHost::WorkerInstance::RemoveAllAssociatedDocuments( - IPC::Message::Sender* parent) { - for (DocumentSet::iterator i = document_set_.begin(); - i != document_set_.end();) { - if (i->first == parent) - i = document_set_.erase(i); - else - ++i; - } -} - void WorkerProcessHost::WorkerInstance::AddSender(IPC::Message::Sender* sender, int sender_route_id) { if (!HasSender(sender, sender_route_id)) { @@ -553,6 +523,21 @@ bool WorkerProcessHost::WorkerInstance::HasSender( return false; } +bool WorkerProcessHost::WorkerInstance::RendererIsParent( + int renderer_id, int render_view_route_id) const { + const WorkerDocumentSet::DocumentInfoSet& parents = + worker_document_set()->documents(); + for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = + parents.begin(); + parent_iter != parents.end(); ++parent_iter) { + if (parent_iter->renderer_id() == renderer_id && + parent_iter->render_view_route_id() == render_view_route_id) { + return true; + } + } + return false; +} + WorkerProcessHost::WorkerInstance::SenderInfo WorkerProcessHost::WorkerInstance::GetSender() const { DCHECK(NumSenders() == 1); diff --git a/chrome/browser/worker_host/worker_process_host.h b/chrome/browser/worker_host/worker_process_host.h index 1834725..b5a8d74f 100644 --- a/chrome/browser/worker_host/worker_process_host.h +++ b/chrome/browser/worker_host/worker_process_host.h @@ -9,12 +9,16 @@ #include "base/basictypes.h" #include "base/task.h" +#include "chrome/browser/worker_host/worker_document_set.h" #include "chrome/common/child_process_host.h" #include "googleurl/src/gurl.h" #include "ipc/ipc_channel.h" +struct ViewHostMsg_CreateWorker_Params; + class WorkerProcessHost : public ChildProcessHost { public: + // Contains information about each worker instance, needed to forward messages // between the renderer and worker processes. class WorkerInstance { @@ -23,8 +27,6 @@ class WorkerProcessHost : public ChildProcessHost { bool shared, bool off_the_record, const string16& name, - int renderer_id, - int render_view_route_id, int worker_route_id); // Unique identifier for a worker client. @@ -35,6 +37,7 @@ class WorkerProcessHost : public ChildProcessHost { void RemoveSender(IPC::Message::Sender* sender, int sender_route_id); void RemoveSenders(IPC::Message::Sender* sender); bool HasSender(IPC::Message::Sender* sender, int sender_route_id) const; + bool RendererIsParent(int renderer_id, int render_view_route_id) const; int NumSenders() const { return senders_.size(); } // Returns the single sender (must only be one). SenderInfo GetSender() const; @@ -45,31 +48,13 @@ class WorkerProcessHost : public ChildProcessHost { bool Matches( const GURL& url, const string16& name, bool off_the_record) const; - // Adds a document to a shared worker's document set. - void AddToDocumentSet(IPC::Message::Sender* parent, - unsigned long long document_id); - - // Checks to see if a document is in a shared worker's document set. - bool IsInDocumentSet(IPC::Message::Sender* parent, - unsigned long long document_id) const; - - // Removes a specific document from a shared worker's document set when - // that document is detached. - void RemoveFromDocumentSet(IPC::Message::Sender* parent, - unsigned long long document_id); - - // Copies the document set from one instance to another - void CopyDocumentSet(const WorkerInstance& instance) { - document_set_ = instance.document_set_; + // Shares the passed instance's WorkerDocumentSet with this instance. This + // instance's current WorkerDocumentSet is dereferenced (and freed if this + // is the only reference) as a result. + void ShareDocumentSet(const WorkerInstance& instance) { + worker_document_set_ = instance.worker_document_set_; }; - // Invoked when a render process exits, to remove all associated documents - // from a shared worker's document set. - void RemoveAllAssociatedDocuments(IPC::Message::Sender* parent); - - bool IsDocumentSetEmpty() const { return document_set_.empty(); } - - // Accessors bool shared() const { return shared_; } bool off_the_record() const { return off_the_record_; } @@ -77,14 +62,12 @@ class WorkerProcessHost : public ChildProcessHost { void set_closed(bool closed) { closed_ = closed; } const GURL& url() const { return url_; } const string16 name() const { return name_; } - int renderer_id() const { return renderer_id_; } - int render_view_route_id() const { return render_view_route_id_; } int worker_route_id() const { return worker_route_id_; } + WorkerDocumentSet* worker_document_set() const { + return worker_document_set_; + } private: - // Unique identifier for an associated document. - typedef std::pair<IPC::Message::Sender*, unsigned long long> DocumentInfo; - typedef std::list<DocumentInfo> DocumentSet; // Set of all senders (clients) associated with this worker. typedef std::list<SenderInfo> SenderList; GURL url_; @@ -92,11 +75,9 @@ class WorkerProcessHost : public ChildProcessHost { bool off_the_record_; bool closed_; string16 name_; - int renderer_id_; - int render_view_route_id_; int worker_route_id_; SenderList senders_; - DocumentSet document_set_; + scoped_refptr<WorkerDocumentSet> worker_document_set_; }; explicit WorkerProcessHost(ResourceDispatcherHost* resource_dispatcher_host); @@ -142,6 +123,7 @@ class WorkerProcessHost : public ChildProcessHost { void OnLookupSharedWorker(const GURL& url, const string16& name, unsigned long long document_id, + int render_view_route_id, int* route_id, bool* url_error); @@ -161,10 +143,7 @@ class WorkerProcessHost : public ChildProcessHost { // Updates the title shown in the task manager. void UpdateTitle(); - void OnCreateWorker(const GURL& url, - bool shared, - const string16& name, - int render_view_route_id, + void OnCreateWorker(const ViewHostMsg_CreateWorker_Params& params, int* route_id); void OnCancelCreateDedicatedWorker(int route_id); void OnForwardToWorker(const IPC::Message& message); diff --git a/chrome/browser/worker_host/worker_service.cc b/chrome/browser/worker_host/worker_service.cc index fe768c3..ffdbb1d 100644 --- a/chrome/browser/worker_host/worker_service.cc +++ b/chrome/browser/worker_host/worker_service.cc @@ -49,6 +49,7 @@ bool WorkerService::CreateWorker(const GURL &url, bool is_shared, bool off_the_record, const string16& name, + unsigned long long document_id, int renderer_id, int render_view_route_id, IPC::Message::Sender* sender, @@ -61,10 +62,10 @@ bool WorkerService::CreateWorker(const GURL &url, is_shared, off_the_record, name, - renderer_id, - render_view_route_id, next_worker_route_id()); instance.AddSender(sender, sender_route_id); + instance.worker_document_set()->Add( + sender, document_id, renderer_id, render_view_route_id); WorkerProcessHost* worker = NULL; if (CommandLine::ForCurrentProcess()->HasSwitch( @@ -89,6 +90,7 @@ bool WorkerService::CreateWorker(const GURL &url, // If this worker is already running, no need to create a new copy. Just // inform the caller that the worker has been created. if (existing_instance) { + // TODO(atwilson): Change this to scan the sender list (crbug.com/29243). existing_instance->AddSender(sender, sender_route_id); sender->Send(new ViewMsg_WorkerCreated(sender_route_id)); return true; @@ -108,8 +110,8 @@ bool WorkerService::CreateWorker(const GURL &url, // Assign the accumulated document set and sender list for this pending // worker to the new instance. - DCHECK(!pending->IsDocumentSetEmpty()); - instance.CopyDocumentSet(*pending); + DCHECK(!pending->worker_document_set()->IsEmpty()); + instance.ShareDocumentSet(*pending); RemovePendingInstance(url, name, off_the_record); } @@ -129,6 +131,8 @@ bool WorkerService::LookupSharedWorker(const GURL &url, const string16& name, bool off_the_record, unsigned long long document_id, + int renderer_id, + int render_view_route_id, IPC::Message::Sender* sender, int sender_route_id, bool* url_mismatch) { @@ -160,7 +164,8 @@ bool WorkerService::LookupSharedWorker(const GURL &url, instance->AddSender(sender, sender_route_id); // Add the passed sender/document_id to the worker instance. - instance->AddToDocumentSet(sender, document_id); + instance->worker_document_set()->Add( + sender, document_id, renderer_id, render_view_route_id); return found_instance; } @@ -176,8 +181,8 @@ void WorkerService::DocumentDetached(IPC::Message::Sender* sender, for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin(); iter != queued_workers_.end();) { if (iter->shared()) { - iter->RemoveFromDocumentSet(sender, document_id); - if (iter->IsDocumentSetEmpty()) { + iter->worker_document_set()->Remove(sender, document_id); + if (iter->worker_document_set()->IsEmpty()) { iter = queued_workers_.erase(iter); continue; } @@ -189,8 +194,8 @@ void WorkerService::DocumentDetached(IPC::Message::Sender* sender, for (WorkerProcessHost::Instances::iterator iter = pending_shared_workers_.begin(); iter != pending_shared_workers_.end(); ) { - iter->RemoveFromDocumentSet(sender, document_id); - if (iter->IsDocumentSetEmpty()) { + iter->worker_document_set()->Remove(sender, document_id); + if (iter->worker_document_set()->IsEmpty()) { iter = pending_shared_workers_.erase(iter); } else { ++iter; @@ -294,8 +299,35 @@ WorkerProcessHost* WorkerService::GetLeastLoadedWorker() { bool WorkerService::CanCreateWorkerProcess( const WorkerProcessHost::WorkerInstance& instance) { + // Worker can be fired off if *any* parent has room. + const WorkerDocumentSet::DocumentInfoSet& parents = + instance.worker_document_set()->documents(); + + for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter = + parents.begin(); + parent_iter != parents.end(); ++parent_iter) { + bool hit_total_worker_limit = false; + if (TabCanCreateWorkerProcess(parent_iter->renderer_id(), + parent_iter->render_view_route_id(), + &hit_total_worker_limit)) { + return true; + } + // Return false if already at the global worker limit (no need to continue + // checking parent tabs). + if (hit_total_worker_limit) + return false; + } + // If we've reached here, none of the parent tabs is allowed to create an + // instance. + return false; +} + +bool WorkerService::TabCanCreateWorkerProcess(int renderer_id, + int render_view_route_id, + bool* hit_total_worker_limit) { int total_workers = 0; int workers_per_tab = 0; + *hit_total_worker_limit = false; for (ChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS); !iter.Done(); ++iter) { WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter); @@ -303,11 +335,11 @@ bool WorkerService::CanCreateWorkerProcess( worker->instances().begin(); cur_instance != worker->instances().end(); ++cur_instance) { total_workers++; - if (total_workers >= kMaxWorkersWhenSeparate) + if (total_workers >= kMaxWorkersWhenSeparate) { + *hit_total_worker_limit = true; return false; - if (cur_instance->renderer_id() == instance.renderer_id() && - cur_instance->render_view_route_id() == - instance.render_view_route_id()) { + } + if (cur_instance->RendererIsParent(renderer_id, render_view_route_id)) { workers_per_tab++; if (workers_per_tab >= kMaxWorkersPerTabWhenSeparate) return false; @@ -355,8 +387,8 @@ void WorkerService::SenderShutdown(IPC::Message::Sender* sender) { for (WorkerProcessHost::Instances::iterator iter = pending_shared_workers_.begin(); iter != pending_shared_workers_.end(); ) { - iter->RemoveAllAssociatedDocuments(sender); - if (iter->IsDocumentSetEmpty()) { + iter->worker_document_set()->RemoveAll(sender); + if (iter->worker_document_set()->IsEmpty()) { iter = pending_shared_workers_.erase(iter); } else { ++iter; @@ -461,7 +493,7 @@ WorkerService::CreatePendingInstance(const GURL& url, // No existing pending worker - create a new one. WorkerProcessHost::WorkerInstance pending( - url, true, off_the_record, name, 0, MSG_ROUTING_NONE, MSG_ROUTING_NONE); + url, true, off_the_record, name, MSG_ROUTING_NONE); pending_shared_workers_.push_back(pending); return &pending_shared_workers_.back(); } diff --git a/chrome/browser/worker_host/worker_service.h b/chrome/browser/worker_host/worker_service.h index 5db6eee..eec45a0 100644 --- a/chrome/browser/worker_host/worker_service.h +++ b/chrome/browser/worker_host/worker_service.h @@ -30,6 +30,7 @@ class WorkerService : public NotificationObserver { bool is_shared, bool is_off_the_record, const string16& name, + unsigned long long document_id, int renderer_pid, int render_view_route_id, IPC::Message::Sender* sender, @@ -43,6 +44,8 @@ class WorkerService : public NotificationObserver { const string16& name, bool off_the_record, unsigned long long document_id, + int renderer_pid, + int render_view_route_id, IPC::Message::Sender* sender, int sender_route_id, bool* url_mismatch); @@ -101,6 +104,12 @@ class WorkerService : public NotificationObserver { bool CanCreateWorkerProcess( const WorkerProcessHost::WorkerInstance& instance); + // Checks if the tab associated with the passed RenderView can create a + // worker process based on the process limit when we're using a strategy of + // one worker per process. + bool TabCanCreateWorkerProcess( + int renderer_id, int render_view_route_id, bool* hit_total_worker_limit); + // NotificationObserver interface. void Observe(NotificationType type, const NotificationSource& source, diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 4c52ea0..7676b1c 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1951,6 +1951,8 @@ 'browser/window_sizer_win.cc', 'browser/worker_host/message_port_dispatcher.cc', 'browser/worker_host/message_port_dispatcher.h', + 'browser/worker_host/worker_document_set.cc', + 'browser/worker_host/worker_document_set.h', 'browser/worker_host/worker_process_host.cc', 'browser/worker_host/worker_process_host.h', 'browser/worker_host/worker_service.cc', diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 24e1e72..ebc7af4 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -534,6 +534,24 @@ struct ViewMsg_ExecuteCode_Params { bool all_frames; }; +// Parameters for the message that creates a worker thread. +struct ViewHostMsg_CreateWorker_Params { + // URL for the worker script. + GURL url; + + // True if this is a SharedWorker, false if it is a dedicated Worker. + bool is_shared; + + // Name for a SharedWorker, otherwise empty string. + string16 name; + + // The ID of the parent document (unique within parent renderer). + unsigned long long document_id; + + // RenderView routing id used to send messages back to the parent. + int render_view_route_id; +}; + // Creates a new view via a control message since the view doesn't yet exist. struct ViewMsg_New_Params { // The parent window's id. @@ -2310,6 +2328,40 @@ struct ParamTraits<ViewMsg_DOMStorageEvent_Params> { } }; +// Traits for ViewHostMsg_CreateWorker_Params +template <> +struct ParamTraits<ViewHostMsg_CreateWorker_Params> { + typedef ViewHostMsg_CreateWorker_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.url); + WriteParam(m, p.is_shared); + WriteParam(m, p.name); + WriteParam(m, p.document_id); + WriteParam(m, p.render_view_route_id); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->is_shared) && + ReadParam(m, iter, &p->name) && + ReadParam(m, iter, &p->document_id) && + ReadParam(m, iter, &p->render_view_route_id); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.is_shared, l); + l->append(L", "); + LogParam(p.name, l); + l->append(L", "); + LogParam(p.document_id, l); + l->append(L", "); + LogParam(p.render_view_route_id, l); + l->append(L")"); + } +}; + // Traits for WebCookie template <> struct ParamTraits<webkit_glue::WebCookie> { diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 19d6fae..22f4a0f 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -1754,11 +1754,8 @@ IPC_BEGIN_MESSAGES(ViewHost) // A renderer sends this to the browser process when it wants to create a // worker. The browser will create the worker process if necessary, and // will return the route id on success. On error returns MSG_ROUTING_NONE. - IPC_SYNC_MESSAGE_CONTROL4_1(ViewHostMsg_CreateWorker, - GURL /* url */, - bool /* is_shared */, - string16 /* name */, - int /* render_view_route_id */, + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_CreateWorker, + ViewHostMsg_CreateWorker_Params, int /* route_id */) // This message is sent to the browser to see if an instance of this shared @@ -1768,10 +1765,11 @@ IPC_BEGIN_MESSAGES(ViewHost) // the existing worker. If a matching worker is found, the passed-in // document_id is associated with that worker, to ensure that the worker // stays alive until the document is detached. - IPC_SYNC_MESSAGE_CONTROL3_2(ViewHostMsg_LookupSharedWorker, + IPC_SYNC_MESSAGE_CONTROL4_2(ViewHostMsg_LookupSharedWorker, GURL /* url */, string16 /* name */, unsigned long long /* document_id */, + int /* render_view_route_id */, int /* route_id */, bool /* url_mismatch */) diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 8a718db..4c74a61 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -1925,11 +1925,12 @@ WebSharedWorker* RenderView::createSharedWorker( int route_id = MSG_ROUTING_NONE; bool url_mismatch = false; Send(new ViewHostMsg_LookupSharedWorker( - url, name, document_id, &route_id, &url_mismatch)); + url, name, document_id, routing_id_, &route_id, &url_mismatch)); if (url_mismatch) { return NULL; } else { return new WebSharedWorkerProxy(RenderThread::current(), + document_id, route_id, routing_id_); } diff --git a/chrome/renderer/websharedworker_proxy.cc b/chrome/renderer/websharedworker_proxy.cc index 1d5425f..c6741c1 100644 --- a/chrome/renderer/websharedworker_proxy.cc +++ b/chrome/renderer/websharedworker_proxy.cc @@ -10,9 +10,10 @@ #include "third_party/WebKit/WebKit/chromium/public/WebURL.h" WebSharedWorkerProxy::WebSharedWorkerProxy(ChildThread* child_thread, + unsigned long long document_id, int route_id, int render_view_route_id) - : WebWorkerBase(child_thread, route_id, render_view_route_id), + : WebWorkerBase(child_thread, document_id, route_id, render_view_route_id), connect_listener_(NULL) { } diff --git a/chrome/renderer/websharedworker_proxy.h b/chrome/renderer/websharedworker_proxy.h index c3143db..4a93ff6 100644 --- a/chrome/renderer/websharedworker_proxy.h +++ b/chrome/renderer/websharedworker_proxy.h @@ -22,6 +22,7 @@ class WebSharedWorkerProxy : public WebKit::WebSharedWorker, public: // If the worker not loaded yet, route_id == MSG_ROUTING_NONE WebSharedWorkerProxy(ChildThread* child_thread, + unsigned long long document_id, int route_id, int render_view_route_id); diff --git a/chrome/renderer/webworker_base.cc b/chrome/renderer/webworker_base.cc index e32026c..e78ef64 100644 --- a/chrome/renderer/webworker_base.cc +++ b/chrome/renderer/webworker_base.cc @@ -19,11 +19,13 @@ using WebKit::WebWorkerClient; WebWorkerBase::WebWorkerBase( ChildThread* child_thread, + unsigned long long document_id, int route_id, int render_view_route_id) : route_id_(route_id), render_view_route_id_(render_view_route_id), - child_thread_(child_thread) { + child_thread_(child_thread), + document_id_(document_id) { if (route_id_ != MSG_ROUTING_NONE) child_thread_->AddRoute(route_id_, this); } @@ -54,8 +56,14 @@ void WebWorkerBase::CreateWorkerContext(const GURL& script_url, const string16& user_agent, const string16& source_code) { DCHECK(route_id_ == MSG_ROUTING_NONE); + ViewHostMsg_CreateWorker_Params params; + params.url = script_url; + params.is_shared = is_shared; + params.name = name; + params.document_id = document_id_; + params.render_view_route_id = render_view_route_id_; IPC::Message* create_message = new ViewHostMsg_CreateWorker( - script_url, is_shared, name, render_view_route_id_, &route_id_); + params, &route_id_); child_thread_->Send(create_message); if (route_id_ == MSG_ROUTING_NONE) return; diff --git a/chrome/renderer/webworker_base.h b/chrome/renderer/webworker_base.h index fe5becc..6c031c8 100644 --- a/chrome/renderer/webworker_base.h +++ b/chrome/renderer/webworker_base.h @@ -20,6 +20,7 @@ class GURL; class WebWorkerBase : public IPC::Channel::Listener { public: WebWorkerBase(ChildThread* child_thread, + unsigned long long document_id, int route_id, int render_view_route_id); @@ -61,6 +62,10 @@ class WebWorkerBase : public IPC::Channel::Listener { ChildThread* child_thread_; private: + // ID of our parent document (used to shutdown workers when the parent + // document is detached). + unsigned long long document_id_; + // Stores messages that were sent before the StartWorkerContext message. std::vector<IPC::Message*> queued_messages_; }; diff --git a/chrome/renderer/webworker_proxy.cc b/chrome/renderer/webworker_proxy.cc index fdcc9f1..553a3ac 100644 --- a/chrome/renderer/webworker_proxy.cc +++ b/chrome/renderer/webworker_proxy.cc @@ -22,8 +22,10 @@ WebWorkerProxy::WebWorkerProxy( WebWorkerClient* client, ChildThread* child_thread, int render_view_route_id) - : WebWorkerBase(child_thread, MSG_ROUTING_NONE, render_view_route_id), + : WebWorkerBase(child_thread, 0, MSG_ROUTING_NONE, render_view_route_id), client_(client) { + // TODO(atwilson): Change to pass in a real document_id when we support nested + // workers. } WebWorkerProxy::~WebWorkerProxy() { diff --git a/chrome/test/data/workers/shared_worker_auth.html b/chrome/test/data/workers/shared_worker_auth.html new file mode 100644 index 0000000..fbe4ad2 --- /dev/null +++ b/chrome/test/data/workers/shared_worker_auth.html @@ -0,0 +1,18 @@ +<html> + +<head> +<title>SharedWorker HTTP Auth Test</title> + +<script src="worker_utils.js"></script> + +<script> + +var worker = new SharedWorker("worker_common.js"); +worker.port.postMessage("auth"); +</script> +</head> + +<body> +<div id=statusPanel></div> +</body> +</html> diff --git a/chrome/test/data/workers/worker_auth.html b/chrome/test/data/workers/worker_auth.html new file mode 100644 index 0000000..e452155 --- /dev/null +++ b/chrome/test/data/workers/worker_auth.html @@ -0,0 +1,18 @@ +<html> + +<head> +<title>Worker HTTP Auth Test</title> + +<script src="worker_utils.js"></script> + +<script> + +var worker = getWorker("worker_common.js"); +worker.postMessage("auth"); +</script> +</head> + +<body> +<div id=statusPanel></div> +</body> +</html> diff --git a/chrome/test/data/workers/worker_common.js b/chrome/test/data/workers/worker_common.js index 8caa622..664da58 100644 --- a/chrome/test/data/workers/worker_common.js +++ b/chrome/test/data/workers/worker_common.js @@ -12,6 +12,8 @@ if (!self.postMessage) { onmessage = function(evt) { if (evt.data == "ping") postMessage("pong"); + else if (evt.data == "auth") + importScripts("/auth-basic"); else if (/eval.+/.test(evt.data)) { try { postMessage(eval(evt.data.substr(5))); diff --git a/chrome/worker/worker_uitest.cc b/chrome/worker/worker_uitest.cc index aafad3c..ced60bf 100644 --- a/chrome/worker/worker_uitest.cc +++ b/chrome/worker/worker_uitest.cc @@ -9,6 +9,7 @@ #include "chrome/test/automation/browser_proxy.h" #include "chrome/test/automation/tab_proxy.h" #include "chrome/test/ui/ui_layout_test.h" +#include "net/url_request/url_request_unittest.h" static const char kTestCompleteCookie[] = "status"; static const char kTestCompleteSuccess[] = "OK"; @@ -136,6 +137,15 @@ class WorkerTest : public UILayoutTest { GURL about_url(std::string("file://localhost/")); EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(about_url)); } + + bool NavigateAndWaitForAuth(TabProxy* tab, const GURL& url) { + // Pass a large number of navigations to tell the tab to block until an auth + // dialog pops up. + bool timeout = false; + tab->NavigateToURLWithTimeout(url, 100, kTestWaitTimeoutMs, &timeout); + EXPECT_FALSE(timeout); + return tab->NeedsAuth(); + } }; @@ -173,6 +183,32 @@ TEST_F(WorkerTest, IncognitoSharedWorkers) { RunIncognitoTest(L"incognito_worker.html"); } +const wchar_t kDocRoot[] = L"chrome/test/data/workers"; + +// Make sure that auth dialog is displayed from worker context. +TEST_F(WorkerTest, WorkerHttpAuth) { + scoped_refptr<HTTPTestServer> server = + HTTPTestServer::CreateServer(kDocRoot, NULL); + + ASSERT_TRUE(NULL != server.get()); + scoped_refptr<TabProxy> tab(GetActiveTab()); + GURL url = server->TestServerPage("files/worker_auth.html"); + EXPECT_TRUE(NavigateAndWaitForAuth(tab, url)); +} + +// Make sure that auth dialog is displayed from shared worker context. +TEST_F(WorkerTest, SharedWorkerHttpAuth) { + scoped_refptr<HTTPTestServer> server = + HTTPTestServer::CreateServer(kDocRoot, NULL); + ASSERT_TRUE(NULL != server.get()); + scoped_refptr<TabProxy> tab(GetActiveTab()); + EXPECT_EQ(1, GetTabCount()); + GURL url = server->TestServerPage("files/shared_worker_auth.html"); + EXPECT_TRUE(NavigateAndWaitForAuth(tab, url)); + // TODO(atwilson): Add support to automation framework to test for auth + // dialogs displayed by non-navigating tabs. +} + #if defined(OS_LINUX) // Crashes on Linux - http://crbug.com/22898 #define WorkerFastLayoutTests0 DISABLED_WorkerFastLayoutTests0 diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt index da9c45b..f916499 100644 --- a/tools/valgrind/memcheck/suppressions.txt +++ b/tools/valgrind/memcheck/suppressions.txt @@ -1046,9 +1046,9 @@ Memcheck:Leak fun:_Znw* ... - fun:_ZN13WorkerService12CreateWorkerERK4GURLbbRKSbItN4base20string16_char_traitsESaItEEiiPN3IPC7Message6SenderEi - fun:_ZN21ResourceMessageFilter14OnCreateWorkerERK4GURLbRKSbItN4base20string16_char_traitsESaItEEiPi - fun:_Z16DispatchToMethodI21ResourceMessageFilterMS0_FvRK4GURLbRKSbItN4base20string16_char_traitsESaItEEiPiES1_bS7_iiEvPT_T0_RK6Tuple4IT1_T2_T3_T4_EP6Tuple1IT5_E + fun:_ZN13WorkerService12CreateWorker* + fun:_ZN21ResourceMessageFilter14OnCreateWorker* + fun:_Z16DispatchToMethodI21ResourceMessageFilter* } { bug_22932 |