diff options
author | creis@chromium.org <creis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-01 00:12:28 +0000 |
---|---|---|
committer | creis@chromium.org <creis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-01 00:12:28 +0000 |
commit | 15a36f4314b55a421270356c1c7e62722be06b9e (patch) | |
tree | 64847345d36b9ac18c8992c7b53b15aa8a6dcada /content | |
parent | 1ad63fd9ec46ece52956994ae98dfa7c77405c85 (diff) | |
download | chromium_src-15a36f4314b55a421270356c1c7e62722be06b9e.zip chromium_src-15a36f4314b55a421270356c1c7e62722be06b9e.tar.gz chromium_src-15a36f4314b55a421270356c1c7e62722be06b9e.tar.bz2 |
Clean up RenderViewHostManager swapping logic.
Makes the difference between swapping SiteInstances and swapping
BrowsingInstances explicit, and adds CHECKs to enforce invariants
more effectively.
BUG=123007
TEST=No functionality change.
Review URL: https://chromiumcodereview.appspot.com/9965091
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139933 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
6 files changed, 132 insertions, 93 deletions
diff --git a/content/browser/debugger/devtools_manager_unittest.cc b/content/browser/debugger/devtools_manager_unittest.cc index 5f28d3a..51f705b 100644 --- a/content/browser/debugger/devtools_manager_unittest.cc +++ b/content/browser/debugger/devtools_manager_unittest.cc @@ -97,7 +97,8 @@ class DevToolsManagerTestBrowserClient DevToolsManagerTestBrowserClient() { } - virtual bool ShouldSwapProcessesForNavigation( + virtual bool ShouldSwapBrowsingInstanceForNavigation( + content::BrowserContext* browser_context, const GURL& current_url, const GURL& new_url) OVERRIDE { return true; diff --git a/content/browser/web_contents/render_view_host_manager.cc b/content/browser/web_contents/render_view_host_manager.cc index cb7ead5..3b1081a 100644 --- a/content/browser/web_contents/render_view_host_manager.cc +++ b/content/browser/web_contents/render_view_host_manager.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/logging.h" +#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/debugger/devtools_manager_impl.h" #include "content/browser/renderer_host/render_view_host_factory.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -23,6 +24,7 @@ #include "content/public/browser/web_contents_view.h" #include "content/public/browser/web_ui_controller.h" #include "content/public/browser/web_ui_controller_factory.h" +#include "content/public/common/bindings_policy.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" @@ -358,51 +360,63 @@ bool RenderViewHostManager::ShouldTransitionCrossSite() { return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerTab); } -bool RenderViewHostManager::ShouldSwapProcessesForNavigation( - const NavigationEntry* curr_entry, +bool RenderViewHostManager::ShouldSwapBrowsingInstanceForNavigation( + const NavigationEntry* current_entry, const NavigationEntryImpl* new_entry) const { DCHECK(new_entry); + // If new_entry already has a SiteInstance, assume it is correct and use it. + if (new_entry->site_instance()) + return false; + // Check for reasons to swap processes even if we are in a process model that - // doesn't usually swap (e.g., process-per-tab). + // doesn't usually swap (e.g., process-per-tab). Any time we return true, + // the new_entry will be rendered in a new SiteInstance AND BrowsingInstance. // For security, we should transition between processes when one is a Web UI - // page and one isn't. If there's no curr_entry, check the current RVH's + // page and one isn't. If there's no current_entry, check the current RVH's // site, which might already be committed to a Web UI URL (such as the NTP). - const GURL& current_url = (curr_entry) ? curr_entry->GetURL() : + const GURL& current_url = current_entry ? current_entry->GetURL() : render_view_host_->GetSiteInstance()->GetSite(); + const GURL& new_url = new_entry->GetURL(); content::BrowserContext* browser_context = delegate_->GetControllerForRenderManager().GetBrowserContext(); const WebUIControllerFactory* web_ui_factory = content::GetContentClient()->browser()->GetWebUIControllerFactory(); if (web_ui_factory) { - if (web_ui_factory->UseWebUIForURL(browser_context, current_url)) { - // Force swap if it's not an acceptable URL for Web UI. + int enabled_bindings = render_view_host_->GetEnabledBindings(); + + // Check if we're currently in a WebUI RenderViewHost, based on either URL + // or bindings. + if (enabled_bindings & content::BINDINGS_POLICY_WEB_UI || + web_ui_factory->UseWebUIForURL(browser_context, current_url)) { + // If so, force a swap if destination not an acceptable URL for Web UI. // Here, data URLs are never allowed. - if (!web_ui_factory->IsURLAcceptableForWebUI(browser_context, - new_entry->GetURL(), false)) + if (!web_ui_factory->IsURLAcceptableForWebUI(browser_context, new_url, + false)) return true; } else { - // Force swap if it's a Web UI URL. - if (web_ui_factory->UseWebUIForURL(browser_context, new_entry->GetURL())) + // Force a swap if it's a Web UI URL. + if (web_ui_factory->UseWebUIForURL(browser_context, new_url)) return true; } } - if (content::GetContentClient()->browser()->ShouldSwapProcessesForNavigation( - curr_entry ? curr_entry->GetURL() : GURL(), new_entry->GetURL())) { + // Also let the embedder decide if a BrowsingInstance swap is required. + if (content::GetContentClient()->browser()-> + ShouldSwapBrowsingInstanceForNavigation(browser_context, + current_url, new_url)) { return true; } - if (!curr_entry) - return false; - // We can't switch a RenderView between view source and non-view source mode // without screwing up the session history sometimes (when navigating between // "view-source:http://foo.com/" and "http://foo.com/", WebKit doesn't treat - // it as a new navigation). So require a view switch. - if (curr_entry->IsViewSourceMode() != new_entry->IsViewSourceMode()) + // it as a new navigation). So require a BrowsingInstance switch. + if (current_entry && + current_entry->IsViewSourceMode() != new_entry->IsViewSourceMode()) { return true; + } return false; } @@ -423,28 +437,37 @@ bool RenderViewHostManager::ShouldReuseWebUI( SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( const NavigationEntryImpl& entry, - SiteInstance* curr_instance) { - // NOTE: This is only called when ShouldTransitionCrossSite is true. - + SiteInstance* curr_instance, + bool force_swap) { + // Determine which SiteInstance to use for navigating to |entry|. const GURL& dest_url = entry.GetURL(); NavigationControllerImpl& controller = delegate_->GetControllerForRenderManager(); content::BrowserContext* browser_context = controller.GetBrowserContext(); + // If a swap is required, we need to force the SiteInstance AND + // BrowsingInstance to be different ones. This addresses special cases where + // we use a single BrowsingInstance for all pages of a certain type (e.g., New + // Tab Pages), keeping them in the same process. When you navigate away from + // that page, we want to explicity ignore that BrowsingInstance and group this + // page into the appropriate SiteInstance for its URL. + if (force_swap) { + // We shouldn't be forcing a swap if an entry already has a SiteInstance. + DCHECK(!entry.site_instance()); + return SiteInstance::CreateForURL(browser_context, dest_url); + } + // If the entry has an instance already we should use it. if (entry.site_instance()) return entry.site_instance(); // (UGLY) HEURISTIC, process-per-site only: - // // If this navigation is generated, then it probably corresponds to a search // query. Given that search results typically lead to users navigating to // other sites, we don't really want to use the search engine hostname to // determine the site instance for this navigation. - // // NOTE: This can be removed once we have a way to transition between // RenderViews in response to a link click. - // if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerSite) && entry.GetTransitionType() == content::PAGE_TRANSITION_GENERATED) return curr_instance; @@ -487,7 +510,7 @@ SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( return curr_site_instance; } - // Otherwise, only create a new SiteInstance for cross-site navigation. + // Otherwise, only create a new SiteInstance for a cross-site navigation. // TODO(creis): Once we intercept links and script-based navigations, we // will be able to enforce that all entries in a SiteInstance actually have @@ -528,25 +551,16 @@ SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( // process type is correct. (The URL may have been installed as an app since // the last time we visited it.) if (SiteInstance::IsSameWebSite(browser_context, current_url, dest_url) && - !static_cast<SiteInstanceImpl*>(curr_instance)->HasWrongProcessForURL( - dest_url)) { + !curr_site_instance->HasWrongProcessForURL(dest_url)) { return curr_instance; - } else if (ShouldSwapProcessesForNavigation(curr_entry, &entry)) { - // When we're swapping, we need to force the site instance AND browsing - // instance to be different ones. This addresses special cases where we use - // a single BrowsingInstance for all pages of a certain type (e.g., New Tab - // Pages), keeping them in the same process. When you navigate away from - // that page, we want to explicity ignore that BrowsingInstance and group - // this page into the appropriate SiteInstance for its URL. - return SiteInstance::CreateForURL(browser_context, dest_url); - } else { - // Start the new renderer in a new SiteInstance, but in the current - // BrowsingInstance. It is important to immediately give this new - // SiteInstance to a RenderViewHost (if it is different than our current - // SiteInstance), so that it is ref counted. This will happen in - // CreateRenderView. - return curr_instance->GetRelatedSiteInstance(dest_url); } + + // Otherwise start the new renderer in a new SiteInstance, but in the current + // BrowsingInstance. It is important to immediately give this new + // SiteInstance to a RenderViewHost (if it is different than our current + // SiteInstance), so that it is ref counted. This will happen in + // CreateRenderView. + return curr_instance->GetRelatedSiteInstance(dest_url); } int RenderViewHostManager::CreateRenderView( @@ -600,8 +614,14 @@ bool RenderViewHostManager::InitRenderView(RenderViewHost* render_view_host, int opener_route_id) { // If the pending navigation is to a WebUI, tell the RenderView about any // bindings it will need enabled. - if (pending_web_ui()) + if (pending_web_ui()) { render_view_host->AllowBindings(pending_web_ui()->GetBindings()); + } else { + // Ensure that we don't create an unprivileged view in a WebUI-enabled + // process. + CHECK(!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( + render_view_host->GetProcess()->GetID())); + } return delegate_->CreateRenderViewForRenderManager(render_view_host, opener_route_id); @@ -707,30 +727,40 @@ void RenderViewHostManager::CommitPending() { RenderViewHostImpl* RenderViewHostManager::UpdateRendererStateForNavigate( const NavigationEntryImpl& entry) { - // If we are cross-navigating, then we want to get back to normal and navigate - // as usual. + // If we are currently navigating cross-process, we want to get back to normal + // and then navigate as usual. if (cross_navigation_pending_) { if (pending_render_view_host_) CancelPending(); cross_navigation_pending_ = false; } - // render_view_host_ will not be deleted before the end of this method, so we - // don't have to worry about this SiteInstance's ref count dropping to zero. + // render_view_host_'s SiteInstance and new_instance will not be deleted + // before the end of this method, so we don't have to worry about their ref + // counts dropping to zero. SiteInstance* curr_instance = render_view_host_->GetSiteInstance(); - - // Determine if we need a new SiteInstance for this entry. - // Again, new_instance won't be deleted before the end of this method, so it - // is safe to use a normal pointer here. SiteInstance* new_instance = curr_instance; + + // Determine if we need a new BrowsingInstance for this entry. If true, + // this implies it will get a new SiteInstance (and likely process), and + // that other tabs in the current BrowsingInstance will be unable to script + // it. This is used for cases that require a process swap even in the + // process-per-tab model, such as WebUI pages. const content::NavigationEntry* curr_entry = delegate_->GetLastCommittedNavigationEntryForRenderManager(); - bool force_swap = ShouldSwapProcessesForNavigation(curr_entry, &entry); + bool force_swap = ShouldSwapBrowsingInstanceForNavigation(curr_entry, + &entry); if (ShouldTransitionCrossSite() || force_swap) - new_instance = GetSiteInstanceForEntry(entry, curr_instance); + new_instance = GetSiteInstanceForEntry(entry, curr_instance, force_swap); + + // If force_swap is true, we must use a different SiteInstance. If we didn't, + // we would have two RenderViewHosts in the same SiteInstance and the same + // tab, resulting in page_id conflicts for their NavigationEntries. + if (force_swap) + CHECK_NE(new_instance, curr_instance); - if (new_instance != curr_instance || force_swap) { - // New SiteInstance. + if (new_instance != curr_instance) { + // New SiteInstance: create a pending RVH to navigate. DCHECK(!cross_navigation_pending_); // This will possibly create (set to NULL) a Web UI object for the pending @@ -801,30 +831,29 @@ RenderViewHostImpl* RenderViewHostManager::UpdateRendererStateForNavigate( render_view_host_->FirePageBeforeUnload(true); return pending_render_view_host_; + } + + // Otherwise the same SiteInstance can be used. Navigate render_view_host_. + DCHECK(!cross_navigation_pending_); + if (ShouldReuseWebUI(curr_entry, &entry)) { + pending_web_ui_.reset(); + pending_and_current_web_ui_ = web_ui_->AsWeakPtr(); } else { - if (ShouldReuseWebUI(curr_entry, &entry)) { - pending_web_ui_.reset(); - pending_and_current_web_ui_ = web_ui_->AsWeakPtr(); - } else { - pending_and_current_web_ui_.reset(); - pending_web_ui_.reset( - delegate_->CreateWebUIForRenderManager(entry.GetURL())); - } + pending_and_current_web_ui_.reset(); + pending_web_ui_.reset( + delegate_->CreateWebUIForRenderManager(entry.GetURL())); + } - if (pending_web_ui() && render_view_host_->IsRenderViewLive()) - pending_web_ui()->GetController()->RenderViewReused(render_view_host_); + if (pending_web_ui() && render_view_host_->IsRenderViewLive()) + pending_web_ui()->GetController()->RenderViewReused(render_view_host_); - // The renderer can exit view source mode when any error or cancellation - // happen. We must overwrite to recover the mode. - if (entry.IsViewSourceMode()) { - render_view_host_->Send( - new ViewMsg_EnableViewSourceMode(render_view_host_->GetRoutingID())); - } + // The renderer can exit view source mode when any error or cancellation + // happen. We must overwrite to recover the mode. + if (entry.IsViewSourceMode()) { + render_view_host_->Send( + new ViewMsg_EnableViewSourceMode(render_view_host_->GetRoutingID())); } - // Same SiteInstance can be used. Navigate render_view_host_ if we are not - // cross navigating. - DCHECK(!cross_navigation_pending_); return render_view_host_; } diff --git a/content/browser/web_contents/render_view_host_manager.h b/content/browser/web_contents/render_view_host_manager.h index 68b1704..32d2fca 100644 --- a/content/browser/web_contents/render_view_host_manager.h +++ b/content/browser/web_contents/render_view_host_manager.h @@ -235,26 +235,30 @@ class CONTENT_EXPORT RenderViewHostManager // switch. Can be overridden in unit tests. bool ShouldTransitionCrossSite(); - // Returns true if the two navigation entries are incompatible in some way - // other than site instances. Cases where this can happen include Web UI - // to regular web pages. It will cause us to swap RenderViewHosts (and hence - // RenderProcessHosts) even if the site instance would otherwise be the same. - // As part of this, we'll also force new SiteInstances and BrowsingInstances. + // Returns true if for the navigation from |current_entry| to |new_entry|, + // a new SiteInstance and BrowsingInstance should be created (even if we are + // in a process model that doesn't usually swap). This forces a process swap + // and severs script connections with existing tabs. Cases where this can + // happen include transitions between WebUI and regular web pages. // Either of the entries may be NULL. - bool ShouldSwapProcessesForNavigation( - const content::NavigationEntry* curr_entry, + bool ShouldSwapBrowsingInstanceForNavigation( + const content::NavigationEntry* current_entry, const content::NavigationEntryImpl* new_entry) const; + // Returns true if it is safe to reuse the current WebUI when navigating from + // |curr_entry| to |new_entry|. bool ShouldReuseWebUI( const content::NavigationEntry* curr_entry, const content::NavigationEntryImpl* new_entry) const; // Returns an appropriate SiteInstance object for the given NavigationEntry, - // possibly reusing the current SiteInstance. - // Never called if --process-per-tab is used. + // possibly reusing the current SiteInstance. If --process-per-tab is used, + // this is only called when ShouldSwapBrowsingInstancesForNavigation returns + // true. content::SiteInstance* GetSiteInstanceForEntry( const content::NavigationEntryImpl& entry, - content::SiteInstance* curr_instance); + content::SiteInstance* curr_instance, + bool force_swap); // Sets up the necessary state for a new RenderViewHost with the given opener. bool InitRenderView(content::RenderViewHost* render_view_host, diff --git a/content/browser/web_contents/render_view_host_manager_unittest.cc b/content/browser/web_contents/render_view_host_manager_unittest.cc index 2def6b3..2971e79 100644 --- a/content/browser/web_contents/render_view_host_manager_unittest.cc +++ b/content/browser/web_contents/render_view_host_manager_unittest.cc @@ -180,7 +180,8 @@ class RenderViewHostManagerTest bool ShouldSwapProcesses(RenderViewHostManager* manager, const NavigationEntryImpl* cur_entry, const NavigationEntryImpl* new_entry) const { - return manager->ShouldSwapProcessesForNavigation(cur_entry, new_entry); + return manager->ShouldSwapBrowsingInstanceForNavigation(cur_entry, + new_entry); } private: diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index 859204f..db158b4 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc @@ -53,9 +53,10 @@ bool ContentBrowserClient::ShouldTryToUseExistingProcessHost( return false; } -bool ContentBrowserClient::ShouldSwapProcessesForNavigation( - const GURL& current_url, - const GURL& new_url) { +bool ContentBrowserClient::ShouldSwapBrowsingInstanceForNavigation( + BrowserContext* browser_context, + const GURL& current_url, + const GURL& new_url) { return false; } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 54569e7..d8f0e9c 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -148,10 +148,13 @@ class CONTENT_EXPORT ContentBrowserClient { virtual void SiteInstanceDeleting(SiteInstance* site_instance) {} // Returns true if for the navigation from |current_url| to |new_url|, - // processes should be swapped (even if we are in a process model that - // doesn't usually swap). - virtual bool ShouldSwapProcessesForNavigation(const GURL& current_url, - const GURL& new_url); + // a new SiteInstance and BrowsingInstance should be created (even if we are + // in a process model that doesn't usually swap). This forces a process swap + // and severs script connections with existing tabs. + virtual bool ShouldSwapBrowsingInstanceForNavigation( + BrowserContext* browser_context, + const GURL& current_url, + const GURL& new_url); // Returns true if the given navigation redirect should cause a renderer // process swap. |