diff options
Diffstat (limited to 'chrome/browser')
6 files changed, 217 insertions, 42 deletions
diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc index 29d94af..7d8a6c7 100644 --- a/chrome/browser/extensions/extension_event_router.cc +++ b/chrome/browser/extensions/extension_event_router.cc @@ -382,7 +382,11 @@ void ExtensionEventRouter::LoadLazyBackgroundPagesForEvent( if (extension && !CanDispatchEventNow(extension)) { AppendEvent(extension->id(), event); - pm->CreateBackgroundHost(extension, extension->GetBackgroundURL()); + if (!pm->GetBackgroundHostForExtension(extension->id())) { + // Balanced in DispatchPendingEvents, after the page has loaded. + pm->IncrementLazyKeepaliveCount(extension); + pm->CreateBackgroundHost(extension, extension->GetBackgroundURL()); + } } } } @@ -429,14 +433,6 @@ void ExtensionEventRouter::DispatchPendingEvents( return; } - // Temporarily increment the keepalive count while dispatching the events. - // This also ensures that if no events were dispatched, the extension returns - // to "idle" and is shut down. - const Extension* extension = - profile_->GetExtensionService()->extensions()->GetByID(extension_id); - ExtensionProcessManager* pm = profile_->GetExtensionProcessManager(); - pm->IncrementLazyKeepaliveCount(extension); - PendingEventsList* events_list = map_it->second.get(); for (PendingEventsList::const_iterator it = events_list->begin(); it != events_list->end(); ++it) @@ -445,6 +441,10 @@ void ExtensionEventRouter::DispatchPendingEvents( events_list->clear(); pending_events_.erase(extension_id); + // Balance the keepalive addref in LoadLazyBackgroundPagesForEvent. + const Extension* extension = + profile_->GetExtensionService()->extensions()->GetByID(extension_id); + ExtensionProcessManager* pm = profile_->GetExtensionProcessManager(); pm->DecrementLazyKeepaliveCount(extension); } diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index e9fcfce..145f53e 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -54,6 +54,12 @@ int& GetLazyKeepaliveCount(Profile* profile, const Extension* extension) { return *count; } +std::string GetExtensionID(RenderViewHost* render_view_host) { + // This works for both apps and extensions because the site has been + // normalized to the extension URL for apps. + return render_view_host->GetSiteInstance()->GetSite().host(); +} + // Incognito profiles use this process manager. It is mostly a shim that decides // whether to fall back on the original profile's ExtensionProcessManager based // on whether a given extension uses "split" or "spanning" incognito behavior. @@ -124,6 +130,8 @@ ExtensionProcessManager::ExtensionProcessManager(Profile* profile) content::Source<Profile>(profile)); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, content::Source<Profile>(profile)); + registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED, + content::NotificationService::AllSources()); registrar_.Add(this, content::NOTIFICATION_APP_TERMINATING, content::NotificationService::AllSources()); } @@ -277,10 +285,10 @@ std::set<RenderViewHost*> return result; // Gather up all the views for that site. - for (RenderViewHostSet::iterator view = all_extension_views_.begin(); + for (ExtensionRenderViews::iterator view = all_extension_views_.begin(); view != all_extension_views_.end(); ++view) { - if ((*view)->GetSiteInstance() == site_instance) - result.insert(*view); + if (view->first->GetSiteInstance() == site_instance) + result.insert(view->first); } return result; @@ -289,16 +297,53 @@ std::set<RenderViewHost*> void ExtensionProcessManager::RegisterRenderViewHost( RenderViewHost* render_view_host, const Extension* extension) { - all_extension_views_.insert(render_view_host); + all_extension_views_[render_view_host] = content::VIEW_TYPE_INVALID; } void ExtensionProcessManager::UnregisterRenderViewHost( RenderViewHost* render_view_host) { - all_extension_views_.erase(render_view_host); + ExtensionRenderViews::iterator view = + all_extension_views_.find(render_view_host); + if (view == all_extension_views_.end()) + return; + + content::ViewType view_type = view->second; + all_extension_views_.erase(view); + + // Keepalive count, balanced in UpdateRegisteredRenderView. + if (view_type != content::VIEW_TYPE_INVALID && + view_type != chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { + const Extension* extension = + GetProfile()->GetExtensionService()->extensions()->GetByID( + GetExtensionID(render_view_host)); + if (extension) + DecrementLazyKeepaliveCount(extension); + } } -SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL( - const GURL& url) { +void ExtensionProcessManager::UpdateRegisteredRenderView( + RenderViewHost* render_view_host) { + ExtensionRenderViews::iterator view = + all_extension_views_.find(render_view_host); + if (view == all_extension_views_.end()) + return; + + view->second = render_view_host->GetDelegate()->GetRenderViewType(); + + // Keep the lazy background page alive as long as any non-background-page + // extension views are visible. Keepalive count balanced in + // UnregisterRenderViewHost. + if (view->second != content::VIEW_TYPE_INVALID && + view->second != chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { + const Extension* extension = + GetProfile()->GetExtensionService()->extensions()->GetByID( + GetExtensionID(render_view_host)); + if (extension) + IncrementLazyKeepaliveCount(extension); + } +} + +SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL(const GURL& url) { return site_instance_->GetRelatedSiteInstance(url); } @@ -318,7 +363,6 @@ int ExtensionProcessManager::IncrementLazyKeepaliveCount( if (extension->background_page_persists()) return 0; - // TODO(mpcomplete): Handle visible views changing. int& count = ::GetLazyKeepaliveCount(GetProfile(), extension); if (++count == 1) OnLazyBackgroundPageActive(extension->id()); @@ -342,7 +386,7 @@ int ExtensionProcessManager::DecrementLazyKeepaliveCount( void ExtensionProcessManager::OnLazyBackgroundPageIdle( const std::string& extension_id) { ExtensionHost* host = GetBackgroundHostForExtension(extension_id); - if (host && !HasVisibleViews(extension_id)) + if (host) host->SendShouldClose(); } @@ -360,19 +404,20 @@ void ExtensionProcessManager::OnShouldCloseAck( host->OnShouldCloseAck(sequence_id); } -bool ExtensionProcessManager::HasVisibleViews(const std::string& extension_id) { - const std::set<RenderViewHost*>& views = - GetRenderViewHostsForExtension(extension_id); - for (std::set<RenderViewHost*>::const_iterator it = views.begin(); - it != views.end(); ++it) { - const RenderViewHost* host = *it; - if (host->GetSiteInstance()->GetSite().host() == extension_id && - host->GetDelegate()->GetRenderViewType() != - chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { - return true; - } - } - return false; +void ExtensionProcessManager::OnNetworkRequestStarted( + RenderViewHost* render_view_host) { + ExtensionHost* host = GetBackgroundHostForExtension( + GetExtensionID(render_view_host)); + if (host) + IncrementLazyKeepaliveCount(host->extension()); +} + +void ExtensionProcessManager::OnNetworkRequestDone( + RenderViewHost* render_view_host) { + ExtensionHost* host = GetBackgroundHostForExtension( + GetExtensionID(render_view_host)); + if (host) + DecrementLazyKeepaliveCount(host->extension()); } void ExtensionProcessManager::Observe( @@ -428,6 +473,13 @@ void ExtensionProcessManager::Observe( break; } + case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: { + content::WebContents* contents = + content::Source<content::WebContents>(source).ptr(); + UpdateRegisteredRenderView(contents->GetRenderViewHost()); + break; + } + case content::NOTIFICATION_APP_TERMINATING: { // Close background hosts when the last browser is closed so that they // have time to shutdown various objects on different threads. Our diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h index bbf2109..70a2e28 100644 --- a/chrome/browser/extensions/extension_process_manager.h +++ b/chrome/browser/extensions/extension_process_manager.h @@ -97,15 +97,15 @@ class ExtensionProcessManager : public content::NotificationObserver { int IncrementLazyKeepaliveCount(const Extension* extension); int DecrementLazyKeepaliveCount(const Extension* extension); - // These are called when the extension transitions between idle and active. - // They control the process of closing the background page when idle. - void OnLazyBackgroundPageIdle(const std::string& extension_id); - void OnLazyBackgroundPageActive(const std::string& extension_id); - - // Handle a response to the ShouldClose message, used for lazy background + // Handles a response to the ShouldClose message, used for lazy background // pages. void OnShouldCloseAck(const std::string& extension_id, int sequence_id); + // Tracks network requests for a given RenderViewHost, used to know + // when network activity is idle for lazy background pages. + void OnNetworkRequestStarted(RenderViewHost* render_view_host); + void OnNetworkRequestDone(RenderViewHost* render_view_host); + typedef std::set<ExtensionHost*> ExtensionHostSet; typedef ExtensionHostSet::const_iterator const_iterator; const_iterator begin() const { return all_hosts_.begin(); } @@ -144,14 +144,23 @@ class ExtensionProcessManager : public content::NotificationObserver { private: // Contains all extension-related RenderViewHost instances for all extensions. - typedef std::set<RenderViewHost*> RenderViewHostSet; - RenderViewHostSet all_extension_views_; + // We also keep a cache of the host's view type, because that information + // is not accessible at registration/deregistration time. + typedef std::map<RenderViewHost*, content::ViewType> ExtensionRenderViews; + ExtensionRenderViews all_extension_views_; // Close the given |host| iff it's a background page. void CloseBackgroundHost(ExtensionHost* host); - // Excludes background page. - bool HasVisibleViews(const std::string& extension_id); + // These are called when the extension transitions between idle and active. + // They control the process of closing the background page when idle. + void OnLazyBackgroundPageIdle(const std::string& extension_id); + void OnLazyBackgroundPageActive(const std::string& extension_id); + + // Updates a potentially-registered RenderViewHost once it has been + // associated with a WebContents. This allows us to gather information that + // was not available when the host was first registered. + void UpdateRegisteredRenderView(RenderViewHost* render_view_host); DISALLOW_COPY_AND_ASSIGN(ExtensionProcessManager); }; diff --git a/chrome/browser/extensions/lazy_background_page_apitest.cc b/chrome/browser/extensions/lazy_background_page_apitest.cc index 593a422..1b3b6dd 100644 --- a/chrome/browser/extensions/lazy_background_page_apitest.cc +++ b/chrome/browser/extensions/lazy_background_page_apitest.cc @@ -6,6 +6,7 @@ #include "base/file_path.h" #include "chrome/browser/extensions/browser_action_test_util.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -18,6 +19,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/web_contents.h" #include "googleurl/src/gurl.h" +#include "net/base/mock_host_resolver.h" namespace { // Helper class to wait for a lazy background page to load and close again. @@ -146,6 +148,67 @@ IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnInstalled) { EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); } +// Tests that the lazy background page stays alive until all visible views are +// closed. +IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForView) { + LazyBackgroundObserver page_complete; + ResultCatcher catcher; + FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page"). + AppendASCII("wait_for_view"); + const Extension* extension = LoadExtension(extdir); + ASSERT_TRUE(extension); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + + // The extension should've opened a new tab to an extension page. + EXPECT_EQ(extension->GetResourceURL("extension_page.html").spec(), + browser()->GetSelectedWebContents()->GetURL().spec()); + + // Lazy Background Page still exists, because the extension created a new tab + // to an extension page. + ExtensionProcessManager* pm = + browser()->profile()->GetExtensionProcessManager(); + EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); + + // Close the new tab. + browser()->CloseTabContents(browser()->GetSelectedWebContents()); + page_complete.Wait(); + + // Lazy Background Page has been shut down. + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); +} + +// Tests that the lazy background page stays alive until all network requests +// are complete. +IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForRequest) { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(StartTestServer()); + + LazyBackgroundObserver page_complete; + ResultCatcher catcher; + FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page"). + AppendASCII("wait_for_request"); + const Extension* extension = LoadExtension(extdir); + ASSERT_TRUE(extension); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + + // Lazy Background Page still exists, because the extension started a request. + ExtensionProcessManager* pm = + browser()->profile()->GetExtensionProcessManager(); + ExtensionHost* host = + pm->GetBackgroundHostForExtension(last_loaded_extension_id_); + ASSERT_TRUE(host); + + // Abort the request. + bool result = false; + EXPECT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool( + host->render_view_host(), std::wstring(), L"abortRequest()", &result)); + EXPECT_TRUE(result); + page_complete.Wait(); + + // Lazy Background Page has been shut down. + EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_)); +} + // TODO: background page with timer. // TODO: background page that interacts with popup. // TODO: background page with menu. diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc index 595ad4b..043f789 100644 --- a/chrome/browser/net/chrome_network_delegate.cc +++ b/chrome/browser/net/chrome_network_delegate.cc @@ -5,14 +5,19 @@ #include "chrome/browser/net/chrome_network_delegate.h" #include "base/logging.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/extensions/api/webrequest/webrequest_api.h" #include "chrome/browser/extensions/extension_event_router_forwarder.h" #include "chrome/browser/extensions/extension_info_map.h" +#include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_proxy_api.h" #include "chrome/browser/prefs/pref_member.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/task_manager/task_manager.h" #include "chrome/common/pref_names.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "content/browser/renderer_host/resource_dispatcher_host.h" #include "content/public/browser/browser_thread.h" #include "net/base/host_port_pair.h" #include "net/base/net_errors.h" @@ -45,6 +50,47 @@ void ForwardProxyErrors(net::URLRequest* request, } } +enum RequestStatus { REQUEST_STARTED, REQUEST_DONE }; + +// Notifies the ExtensionProcessManager that a request has started or stopped +// for a particular RenderView. +void NotifyEPMRequestStatus(RequestStatus status, + void* profile_id, + int process_id, + int render_view_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + Profile* profile = reinterpret_cast<Profile*>(profile_id); + if (!g_browser_process->profile_manager()->IsValidProfile(profile)) + return; + + RenderViewHost* render_view_host = + RenderViewHost::FromID(process_id, render_view_id); + // Will be NULL if the request was not issued on behalf of a renderer (e.g. a + // system-level request). + if (render_view_host) { + if (status == REQUEST_STARTED) { + profile->GetExtensionProcessManager()->OnNetworkRequestStarted( + render_view_host); + } else if (status == REQUEST_DONE) { + profile->GetExtensionProcessManager()->OnNetworkRequestDone( + render_view_host); + } else { + NOTREACHED(); + } + } +} + +void ForwardRequestStatus( + RequestStatus status, net::URLRequest* request, void* profile_id) { + int process_id, render_view_id; + if (ResourceDispatcherHost::RenderViewForRequest( + request, &process_id, &render_view_id)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&NotifyEPMRequestStatus, + status, profile_id, process_id, render_view_id)); + } +} + } // namespace ChromeNetworkDelegate::ChromeNetworkDelegate( @@ -92,6 +138,8 @@ int ChromeNetworkDelegate::OnBeforeURLRequest( } #endif + ForwardRequestStatus(REQUEST_STARTED, request, profile_); + if (!enable_referrers_->GetValue()) request->set_referrer(std::string()); return ExtensionWebRequestEventRouter::GetInstance()->OnBeforeRequest( @@ -160,6 +208,8 @@ void ChromeNetworkDelegate::OnCompleted(net::URLRequest* request, NOTREACHED(); } ForwardProxyErrors(request, event_router_.get(), profile_); + + ForwardRequestStatus(REQUEST_DONE, request, profile_); } void ChromeNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) { diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc index e89f4fb3..7aff13c 100644 --- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc +++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc @@ -34,7 +34,8 @@ ChromeRenderViewHostObserver::ChromeRenderViewHostObserver( } ChromeRenderViewHostObserver::~ChromeRenderViewHostObserver() { - RemoveRenderViewHostForExtensions(render_view_host()); + if (render_view_host()) + RemoveRenderViewHostForExtensions(render_view_host()); } void ChromeRenderViewHostObserver::RenderViewHostInitialized() { |