summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/extensions/extension_event_router.cc18
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc96
-rw-r--r--chrome/browser/extensions/extension_process_manager.h29
-rw-r--r--chrome/browser/extensions/lazy_background_page_apitest.cc63
-rw-r--r--chrome/browser/net/chrome_network_delegate.cc50
-rw-r--r--chrome/browser/renderer_host/chrome_render_view_host_observer.cc3
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() {