diff options
author | creis@chromium.org <creis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-02 20:28:44 +0000 |
---|---|---|
committer | creis@chromium.org <creis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-02 20:28:44 +0000 |
commit | 14392a5ec9b1c2679f6f58193ef6736123d4c442 (patch) | |
tree | 85eb33b09622b1ffa629e6ab361f6be861bf10de | |
parent | adcf0f0434f7af0e399bb0a034ba706ce12f746c (diff) | |
download | chromium_src-14392a5ec9b1c2679f6f58193ef6736123d4c442.zip chromium_src-14392a5ec9b1c2679f6f58193ef6736123d4c442.tar.gz chromium_src-14392a5ec9b1c2679f6f58193ef6736123d4c442.tar.bz2 |
Create swapped-out opener RVHs after a process swap.
This is required to support cross-process JavaScript calls, like postMessage.
BUG=99202
TEST=window.opener present after a cross-process navigation.
Review URL: https://chromiumcodereview.appspot.com/10171018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134981 0039d316-1c4b-4281-b951-d872f2087c98
35 files changed, 425 insertions, 145 deletions
diff --git a/chrome/browser/visitedlink/visitedlink_unittest.cc b/chrome/browser/visitedlink/visitedlink_unittest.cc index b6ca5c0..89ccf42 100644 --- a/chrome/browser/visitedlink/visitedlink_unittest.cc +++ b/chrome/browser/visitedlink/visitedlink_unittest.cc @@ -652,7 +652,7 @@ TEST_F(VisitedLinkEventsTest, Coalescense) { TEST_F(VisitedLinkEventsTest, Basics) { VisitedLinkMaster* master = profile()->GetVisitedLinkMaster(); - rvh_tester()->CreateRenderView(string16(), -1); + rvh_tester()->CreateRenderView(string16(), MSG_ROUTING_NONE, -1); // Add a few URLs. master->AddURL(GURL("http://acidtests.org/")); @@ -676,7 +676,7 @@ TEST_F(VisitedLinkEventsTest, Basics) { TEST_F(VisitedLinkEventsTest, TabVisibility) { VisitedLinkMaster* master = profile()->GetVisitedLinkMaster(); - rvh_tester()->CreateRenderView(string16(), -1); + rvh_tester()->CreateRenderView(string16(), MSG_ROUTING_NONE, -1); // Simulate tab becoming inactive. rvh_tester()->SimulateWasHidden(); diff --git a/content/browser/renderer_host/render_view_host_factory.cc b/content/browser/renderer_host/render_view_host_factory.cc index c9f307a..7bab2d4 100644 --- a/content/browser/renderer_host/render_view_host_factory.cc +++ b/content/browser/renderer_host/render_view_host_factory.cc @@ -20,13 +20,15 @@ RenderViewHost* RenderViewHostFactory::Create( SiteInstance* instance, content::RenderViewHostDelegate* delegate, int routing_id, + bool swapped_out, SessionStorageNamespace* session_storage_namespace) { if (factory_) { - return factory_->CreateRenderViewHost(instance, delegate, routing_id, + return factory_->CreateRenderViewHost(instance, delegate, + routing_id, swapped_out, session_storage_namespace); } return new RenderViewHostImpl(instance, delegate, routing_id, - session_storage_namespace); + swapped_out, session_storage_namespace); } // static diff --git a/content/browser/renderer_host/render_view_host_factory.h b/content/browser/renderer_host/render_view_host_factory.h index fa33f07..99d7299 100644 --- a/content/browser/renderer_host/render_view_host_factory.h +++ b/content/browser/renderer_host/render_view_host_factory.h @@ -28,6 +28,7 @@ class RenderViewHostFactory { content::SiteInstance* instance, content::RenderViewHostDelegate* delegate, int routing_id, + bool swapped_out, content::SessionStorageNamespace* session_storage); // Returns true if there is currently a globally-registered factory. @@ -45,6 +46,7 @@ class RenderViewHostFactory { content::SiteInstance* instance, content::RenderViewHostDelegate* delegate, int routing_id, + bool swapped_out, content::SessionStorageNamespace* session_storage_namespace) = 0; // Registers your factory to be called when new RenderViewHosts are created. diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index 6ebe0bc..919aee0 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -135,6 +135,7 @@ RenderViewHostImpl* RenderViewHostImpl::FromID(int render_process_id, RenderViewHostImpl::RenderViewHostImpl(SiteInstance* instance, RenderViewHostDelegate* delegate, int routing_id, + bool swapped_out, SessionStorageNamespace* session_storage) : RenderWidgetHostImpl(instance->GetProcess(), routing_id), delegate_(delegate), @@ -145,7 +146,7 @@ RenderViewHostImpl::RenderViewHostImpl(SiteInstance* instance, pending_request_id_(-1), navigations_suspended_(false), suspended_nav_message_(NULL), - is_swapped_out_(false), + is_swapped_out_(swapped_out), run_modal_reply_msg_(NULL), is_waiting_for_beforeunload_ack_(false), is_waiting_for_unload_ack_(false), @@ -205,6 +206,7 @@ content::SiteInstance* RenderViewHostImpl::GetSiteInstance() const { } bool RenderViewHostImpl::CreateRenderView(const string16& frame_name, + int opener_route_id, int32 max_page_id) { DCHECK(!IsRenderViewLive()) << "Creating view twice"; @@ -237,6 +239,9 @@ bool RenderViewHostImpl::CreateRenderView(const string16& frame_name, params.surface_id = surface_id(); params.session_storage_namespace_id = session_storage_namespace_->id(); params.frame_name = frame_name; + // Ensure the RenderView sets its opener correctly. + params.opener_route_id = opener_route_id; + params.swapped_out = is_swapped_out_; params.next_page_id = next_page_id; #if defined(OS_POSIX) || defined(USE_AURA) if (GetView()) { diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h index 19eb9a8..903027d 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h @@ -119,10 +119,12 @@ class CONTENT_EXPORT RenderViewHostImpl // Convenience function, just like RenderViewHost::FromID. static RenderViewHostImpl* FromID(int render_process_id, int render_view_id); - // routing_id could be a valid route id, or it could be MSG_ROUTING_NONE, in - // which case RenderWidgetHost will create a new one. + // |routing_id| could be a valid route id, or it could be MSG_ROUTING_NONE, in + // which case RenderWidgetHost will create a new one. |swapped_out| indicates + // whether the view should initially be swapped out (e.g., for an opener + // frame being rendered by another process). // - // The session storage namespace parameter allows multiple render views and + // The |session_storage_namespace| parameter allows multiple render views and // WebContentses to share the same session storage (part of the WebStorage // spec) space. This is useful when restoring contentses, but most callers // should pass in NULL which will cause a new SessionStorageNamespace to be @@ -131,6 +133,7 @@ class CONTENT_EXPORT RenderViewHostImpl SiteInstance* instance, RenderViewHostDelegate* delegate, int routing_id, + bool swapped_out, SessionStorageNamespace* session_storage_namespace); virtual ~RenderViewHostImpl(); @@ -225,9 +228,13 @@ class CONTENT_EXPORT RenderViewHostImpl // Set up the RenderView child process. Virtual because it is overridden by // TestRenderViewHost. If the |frame_name| parameter is non-empty, it is used - // as the name of the new top-level frame. If |max_page_id| is larger than - // -1, the RenderView is told to start issuing page IDs at |max_page_id| + 1. - virtual bool CreateRenderView(const string16& frame_name, int32 max_page_id); + // as the name of the new top-level frame. The |opener_route_id| parameter + // indicates which RenderView created this (MSG_ROUTING_NONE if none). If + // |max_page_id| is larger than -1, the RenderView is told to start issuing + // page IDs at |max_page_id| + 1. + virtual bool CreateRenderView(const string16& frame_name, + int opener_route_id, + int32 max_page_id); base::TerminationStatus render_view_termination_status() const { return render_view_termination_status_; diff --git a/content/browser/renderer_host/test_render_view_host.cc b/content/browser/renderer_host/test_render_view_host.cc index b8c81d8..44423f7 100644 --- a/content/browser/renderer_host/test_render_view_host.cc +++ b/content/browser/renderer_host/test_render_view_host.cc @@ -210,10 +210,12 @@ void TestRenderWidgetHostView::UnlockMouse() { TestRenderViewHost::TestRenderViewHost(SiteInstance* instance, RenderViewHostDelegate* delegate, - int routing_id) + int routing_id, + bool swapped_out) : RenderViewHostImpl(instance, delegate, routing_id, + swapped_out, dom_storage::kInvalidSessionStorageNamespaceId), render_view_created_(false), delete_counter_(NULL), @@ -235,6 +237,7 @@ TestRenderViewHost::~TestRenderViewHost() { } bool TestRenderViewHost::CreateRenderView(const string16& frame_name, + int opener_route_id, int32 max_page_id) { DCHECK(!render_view_created_); render_view_created_ = true; diff --git a/content/browser/renderer_host/test_render_view_host.h b/content/browser/renderer_host/test_render_view_host.h index 966edfd..3704ddd 100644 --- a/content/browser/renderer_host/test_render_view_host.h +++ b/content/browser/renderer_host/test_render_view_host.h @@ -220,7 +220,8 @@ class TestRenderViewHost public: TestRenderViewHost(SiteInstance* instance, RenderViewHostDelegate* delegate, - int routing_id); + int routing_id, + bool swapped_out); virtual ~TestRenderViewHost(); // RenderViewHostTester implementation. Note that CreateRenderView @@ -275,6 +276,7 @@ class TestRenderViewHost // RenderViewHost overrides -------------------------------------------------- virtual bool CreateRenderView(const string16& frame_name, + int opener_route_id, int32 max_page_id) OVERRIDE; virtual bool IsRenderViewLive() const OVERRIDE; diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index 792afc5..131189e 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc @@ -142,6 +142,11 @@ SiteInstance* SiteInstanceImpl::GetRelatedSiteInstance(const GURL& url) { return browsing_instance_->GetSiteInstanceForURL(url); } +bool SiteInstanceImpl::IsRelatedSiteInstance(const SiteInstance* instance) { + return browsing_instance_ == + static_cast<const SiteInstanceImpl*>(instance)->browsing_instance_; +} + bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) const { // Having no process isn't a problem, since we'll assign it correctly. if (!HasProcess()) diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h index bee9383..439db12 100644 --- a/content/browser/site_instance_impl.h +++ b/content/browser/site_instance_impl.h @@ -26,6 +26,7 @@ class CONTENT_EXPORT SiteInstanceImpl : public content::SiteInstance, virtual content::RenderProcessHost* GetProcess() OVERRIDE; virtual const GURL& GetSite() const OVERRIDE; virtual SiteInstance* GetRelatedSiteInstance(const GURL& url) OVERRIDE; + virtual bool IsRelatedSiteInstance(const SiteInstance* instance) OVERRIDE; virtual content::BrowserContext* GetBrowserContext() const OVERRIDE; // Set the web site that this SiteInstance is rendering pages for. @@ -111,8 +112,6 @@ class CONTENT_EXPORT SiteInstanceImpl : public content::SiteInstance, // Whether SetSite has been called. bool has_site_; - FRIEND_TEST_ALL_PREFIXES(RenderViewHostManagerTest, NewTabPageProcesses); - DISALLOW_COPY_AND_ASSIGN(SiteInstanceImpl); }; diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc index fec7c79..fcda84a 100644 --- a/content/browser/site_instance_impl_unittest.cc +++ b/content/browser/site_instance_impl_unittest.cc @@ -251,11 +251,8 @@ TEST_F(SiteInstanceTest, SiteInstanceDestructor) { &site_delete_counter, &browsing_delete_counter); { - WebContentsImpl web_contents(browser_context.get(), - instance, - MSG_ROUTING_NONE, - NULL, - NULL); + WebContentsImpl web_contents(browser_context.get(), instance, + MSG_ROUTING_NONE, NULL, NULL, NULL); EXPECT_EQ(1, site_delete_counter); EXPECT_EQ(1, browsing_delete_counter); } @@ -416,6 +413,7 @@ TEST_F(SiteInstanceTest, OneSiteInstancePerSite) { static_cast<SiteInstanceImpl*>( browsing_instance->GetSiteInstanceForURL(url_b1))); EXPECT_NE(site_instance_a1.get(), site_instance_b1.get()); + EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1)); // Getting the new SiteInstance from the BrowsingInstance and from another // SiteInstance in the BrowsingInstance should give the same result. @@ -439,6 +437,7 @@ TEST_F(SiteInstanceTest, OneSiteInstancePerSite) { static_cast<SiteInstanceImpl*>( browsing_instance2->GetSiteInstanceForURL(url_a2))); EXPECT_NE(site_instance_a1.get(), site_instance_a2_2.get()); + EXPECT_FALSE(site_instance_a1->IsRelatedSiteInstance(site_instance_a2_2)); // Should be able to see that we do have SiteInstances. EXPECT_TRUE(browsing_instance->HasSiteInstance( @@ -477,6 +476,7 @@ TEST_F(SiteInstanceTest, OneSiteInstancePerSiteInBrowserContext) { static_cast<SiteInstanceImpl*>( browsing_instance->GetSiteInstanceForURL(url_b1))); EXPECT_NE(site_instance_a1.get(), site_instance_b1.get()); + EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1)); // Getting the new SiteInstance from the BrowsingInstance and from another // SiteInstance in the BrowsingInstance should give the same result. diff --git a/content/browser/web_contents/interstitial_page_impl.cc b/content/browser/web_contents/interstitial_page_impl.cc index 05dd03c..f7a62a6 100644 --- a/content/browser/web_contents/interstitial_page_impl.cc +++ b/content/browser/web_contents/interstitial_page_impl.cc @@ -489,8 +489,8 @@ WebContents* InterstitialPageImpl::web_contents() const { RenderViewHost* InterstitialPageImpl::CreateRenderViewHost() { RenderViewHostImpl* render_view_host = new RenderViewHostImpl( - SiteInstance::Create(web_contents()->GetBrowserContext()), - this, MSG_ROUTING_NONE, dom_storage::kInvalidSessionStorageNamespaceId); + SiteInstance::Create(web_contents()->GetBrowserContext()), this, + MSG_ROUTING_NONE, false, dom_storage::kInvalidSessionStorageNamespaceId); return render_view_host; } @@ -505,7 +505,8 @@ WebContentsView* InterstitialPageImpl::CreateWebContentsView() { int32 max_page_id = web_contents()-> GetMaxPageIDForSiteInstance(render_view_host_->GetSiteInstance()); - render_view_host_->CreateRenderView(string16(), max_page_id); + render_view_host_->CreateRenderView(string16(), MSG_ROUTING_NONE, + max_page_id); view->SetSize(web_contents_view->GetContainerSize()); // Don't show the interstitial until we have navigated to it. view->Hide(); diff --git a/content/browser/web_contents/navigation_controller_impl_unittest.cc b/content/browser/web_contents/navigation_controller_impl_unittest.cc index 36e89daf..b0b09a5 100644 --- a/content/browser/web_contents/navigation_controller_impl_unittest.cc +++ b/content/browser/web_contents/navigation_controller_impl_unittest.cc @@ -1543,7 +1543,7 @@ TEST_F(NavigationControllerTest, RestoreNavigate) { entry->SetContentState("state"); entries.push_back(entry); WebContentsImpl our_contents( - browser_context(), NULL, MSG_ROUTING_NONE, NULL, NULL); + browser_context(), NULL, MSG_ROUTING_NONE, NULL, NULL, NULL); NavigationControllerImpl& our_controller = our_contents.GetControllerImpl(); our_controller.Restore(0, true, &entries); ASSERT_EQ(0u, entries.size()); @@ -1610,7 +1610,7 @@ TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) { entry->SetContentState("state"); entries.push_back(entry); WebContentsImpl our_contents( - browser_context(), NULL, MSG_ROUTING_NONE, NULL, NULL); + browser_context(), NULL, MSG_ROUTING_NONE, NULL, NULL, NULL); NavigationControllerImpl& our_controller = our_contents.GetControllerImpl(); our_controller.Restore(0, true, &entries); ASSERT_EQ(0u, entries.size()); diff --git a/content/browser/web_contents/render_view_host_manager.cc b/content/browser/web_contents/render_view_host_manager.cc index c167333..31cb7ed 100644 --- a/content/browser/web_contents/render_view_host_manager.cc +++ b/content/browser/web_contents/render_view_host_manager.cc @@ -75,7 +75,7 @@ void RenderViewHostManager::Init(content::BrowserContext* browser_context, site_instance = SiteInstance::Create(browser_context); render_view_host_ = static_cast<RenderViewHostImpl*>( RenderViewHostFactory::Create( - site_instance, render_view_delegate_, routing_id, delegate_-> + site_instance, render_view_delegate_, routing_id, false, delegate_-> GetControllerForRenderManager().GetSessionStorageNamespace())); // Keep track of renderer processes as they start to shut down. @@ -112,13 +112,14 @@ RenderViewHostImpl* RenderViewHostManager::Navigate( !render_view_host_->IsRenderViewLive()) { // Note: we don't call InitRenderView here because we are navigating away // soon anyway, and we don't have the NavigationEntry for this host. - delegate_->CreateRenderViewForRenderManager(render_view_host_); + delegate_->CreateRenderViewForRenderManager(render_view_host_, + MSG_ROUTING_NONE); } // If the renderer crashed, then try to create a new one to satisfy this // navigation request. if (!dest_render_view_host->IsRenderViewLive()) { - if (!InitRenderView(dest_render_view_host, entry)) + if (!InitRenderView(dest_render_view_host, MSG_ROUTING_NONE)) return NULL; // Now that we've created a new renderer, be sure to hide it if it isn't @@ -543,62 +544,64 @@ SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( // 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 - // CreatePendingRenderView. + // CreateRenderView. return curr_instance->GetRelatedSiteInstance(dest_url); } } -bool RenderViewHostManager::CreatePendingRenderView( - const NavigationEntryImpl& entry, SiteInstance* instance) { - NavigationEntry* curr_entry = - delegate_->GetControllerForRenderManager().GetLastCommittedEntry(); - if (curr_entry) { - DCHECK(!curr_entry->GetContentState().empty()); - // TODO(creis): Should send a message to the RenderView to let it know - // we're about to switch away, so that it sends an UpdateState message. - } - - // Check if we've already created an RVH for this SiteInstance. +int RenderViewHostManager::CreateRenderView( + SiteInstance* instance, + int opener_route_id, + bool swapped_out) { CHECK(instance); - RenderViewHostMap::iterator iter = - swapped_out_hosts_.find(instance->GetId()); - if (iter != swapped_out_hosts_.end()) { - // Re-use the existing RenderViewHost, which has already been initialized. - // We'll remove it from the list of swapped out hosts if it commits. - pending_render_view_host_ = iter->second; + // Check if we've already created an RVH for this SiteInstance. If so, try + // to re-use the existing one, which has already been initialized. We'll + // remove it from the list of swapped out hosts if it commits. + RenderViewHostImpl* new_render_view_host = static_cast<RenderViewHostImpl*>( + GetSwappedOutRenderViewHost(instance)); + if (new_render_view_host) { // Prevent the process from exiting while we're trying to use it. - pending_render_view_host_->GetProcess()->AddPendingView(); - - return true; - } + new_render_view_host->GetProcess()->AddPendingView(); + } else { + // Create a new RenderViewHost if we don't find an existing one. + new_render_view_host = static_cast<RenderViewHostImpl*>( + RenderViewHostFactory::Create(instance, + render_view_delegate_, MSG_ROUTING_NONE, swapped_out, delegate_-> + GetControllerForRenderManager().GetSessionStorageNamespace())); - pending_render_view_host_ = static_cast<RenderViewHostImpl*>( - RenderViewHostFactory::Create( - instance, render_view_delegate_, MSG_ROUTING_NONE, delegate_-> - GetControllerForRenderManager().GetSessionStorageNamespace())); + // Prevent the process from exiting while we're trying to use it. + new_render_view_host->GetProcess()->AddPendingView(); - // Prevent the process from exiting while we're trying to use it. - pending_render_view_host_->GetProcess()->AddPendingView(); + // Store the new RVH as swapped out if necessary. + if (swapped_out) + swapped_out_hosts_[instance->GetId()] = new_render_view_host; - bool success = InitRenderView(pending_render_view_host_, entry); - if (success) { - // Don't show the view until we get a DidNavigate from it. - pending_render_view_host_->GetView()->Hide(); - } else { - CancelPending(); + bool success = InitRenderView(new_render_view_host, opener_route_id); + if (success) { + // Don't show the view until we get a DidNavigate from it. + new_render_view_host->GetView()->Hide(); + } else if (!swapped_out) { + CancelPending(); + } } - return success; + + // Use this as our new pending RVH if it isn't swapped out. + if (!swapped_out) + pending_render_view_host_ = new_render_view_host; + + return new_render_view_host->GetRoutingID(); } bool RenderViewHostManager::InitRenderView(RenderViewHost* render_view_host, - const NavigationEntryImpl& entry) { + 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()) render_view_host->AllowBindings(pending_web_ui()->GetBindings()); - return delegate_->CreateRenderViewForRenderManager(render_view_host); + return delegate_->CreateRenderViewForRenderManager(render_view_host, + opener_route_id); } void RenderViewHostManager::CommitPending() { @@ -737,9 +740,19 @@ RenderViewHostImpl* RenderViewHostManager::UpdateRendererStateForNavigate( delegate_->CreateWebUIForRenderManager(entry.GetURL())); pending_and_current_web_ui_.reset(); - // Create a pending RVH and navigate it. - bool success = CreatePendingRenderView(entry, new_instance); - if (!success) + // Ensure that we have created RVHs for the new RVH's opener chain if + // we are staying in the same BrowsingInstance. This allows the pending RVH + // to send cross-process script calls to its opener(s). + int opener_route_id = MSG_ROUTING_NONE; + if (new_instance->IsRelatedSiteInstance(curr_instance)) { + opener_route_id = + delegate_->CreateOpenerRenderViewsForRenderManager(new_instance); + } + + // Create a non-swapped-out pending RVH with the given opener and navigate + // it. + int route_id = CreateRenderView(new_instance, opener_route_id, false); + if (route_id == MSG_ROUTING_NONE) return NULL; // Check if our current RVH is live before we set up a transition. @@ -878,3 +891,12 @@ bool RenderViewHostManager::IsSwappedOut(RenderViewHost* rvh) { return swapped_out_hosts_.find(rvh->GetSiteInstance()->GetId()) != swapped_out_hosts_.end(); } + +RenderViewHost* RenderViewHostManager::GetSwappedOutRenderViewHost( + SiteInstance* instance) { + RenderViewHostMap::iterator iter = swapped_out_hosts_.find(instance->GetId()); + if (iter != swapped_out_hosts_.end()) + return iter->second; + + return NULL; +} diff --git a/content/browser/web_contents/render_view_host_manager.h b/content/browser/web_contents/render_view_host_manager.h index aecc889..2d9a384 100644 --- a/content/browser/web_contents/render_view_host_manager.h +++ b/content/browser/web_contents/render_view_host_manager.h @@ -57,7 +57,7 @@ class CONTENT_EXPORT RenderViewHostManager // If you are attaching to an already-existing RenderView, you should call // InitWithExistingID. virtual bool CreateRenderViewForRenderManager( - content::RenderViewHost* render_view_host) = 0; + content::RenderViewHost* render_view_host, int opener_route_id) = 0; virtual void BeforeUnloadFiredFromRenderManager( bool proceed, bool* proceed_to_fire_unload) = 0; virtual void DidStartLoadingFromRenderManager( @@ -68,6 +68,13 @@ class CONTENT_EXPORT RenderViewHostManager virtual void NotifySwappedFromRenderManager() = 0; virtual NavigationControllerImpl& GetControllerForRenderManager() = 0; + // Create swapped out RenderViews in the given SiteInstance for each tab in + // the opener chain of this tab, if any. This allows the current tab to + // make cross-process script calls to its opener(s). Returns the route ID + // of the immediate opener, if one exists (otherwise MSG_ROUTING_NONE). + virtual int CreateOpenerRenderViewsForRenderManager( + content::SiteInstance* instance) = 0; + // Creates a WebUI object for the given URL if one applies. Ownership of the // returned pointer will be passed to the caller. If no WebUI applies, // returns NULL. @@ -154,6 +161,13 @@ class CONTENT_EXPORT RenderViewHostManager // Called when a renderer's main frame navigates. void DidNavigateMainFrame(content::RenderViewHost* render_view_host); + // Helper method to create a RenderViewHost. If |swapped_out| is true, it + // will be initially placed on the swapped out hosts list. Otherwise, it + // will be used for a pending cross-site navigation. + int CreateRenderView(content::SiteInstance* instance, + int opener_route_id, + bool swapped_out); + // Set the WebUI after committing a page load. This is useful for navigations // initiated from a renderer, where we want to give the new renderer WebUI // privileges from the originating renderer. @@ -204,6 +218,10 @@ class CONTENT_EXPORT RenderViewHostManager // RenderViewHosts. bool IsSwappedOut(content::RenderViewHost* rvh); + // Returns the swapped out RenderViewHost for the given SiteInstance, if any. + content::RenderViewHost* GetSwappedOutRenderViewHost( + content::SiteInstance* instance); + private: friend class content::TestWebContents; friend class RenderViewHostManagerTest; @@ -234,15 +252,9 @@ class CONTENT_EXPORT RenderViewHostManager const content::NavigationEntryImpl& entry, content::SiteInstance* curr_instance); - // Helper method to create a pending RenderViewHost for a cross-site - // navigation. - bool CreatePendingRenderView(const content::NavigationEntryImpl& entry, - content::SiteInstance* instance); - - // Sets up the necessary state for a new RenderViewHost navigating to the - // given entry. + // Sets up the necessary state for a new RenderViewHost with the given opener. bool InitRenderView(content::RenderViewHost* render_view_host, - const content::NavigationEntryImpl& entry); + int opener_route_id); // Sets the pending RenderViewHost/WebUI to be the active one. Note that this // doesn't require the pending render_view_host_ pointer to be non-NULL, since 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 fe17a0e..57b3b96 100644 --- a/content/browser/web_contents/render_view_host_manager_unittest.cc +++ b/content/browser/web_contents/render_view_host_manager_unittest.cc @@ -233,10 +233,8 @@ TEST_F(RenderViewHostManagerTest, NewTabPageProcesses) { // The two RVH's should be different in every way. EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess()); EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance()); - EXPECT_NE(static_cast<SiteInstanceImpl*>(active_rvh()->GetSiteInstance())-> - browsing_instance_, - static_cast<SiteInstanceImpl*>(dest_rvh2->GetSiteInstance())-> - browsing_instance_); + EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance( + dest_rvh2->GetSiteInstance())); // Navigate both to the new tab page, and verify that they share a // SiteInstance. @@ -772,3 +770,68 @@ TEST_F(RenderViewHostManagerTest, NavigateAfterMissingSwapOutACK) { EXPECT_FALSE(rvh2->is_swapped_out()); EXPECT_TRUE(rvh1->is_swapped_out()); } + +// Test that we create swapped out RVHs for the opener chain when navigating an +// opened tab cross-process. This allows us to support certain cross-process +// JavaScript calls (http://crbug.com/99202). +TEST_F(RenderViewHostManagerTest, CreateSwappedOutOpenerRVHs) { + const GURL kUrl1("http://www.google.com/"); + const GURL kUrl2("http://www.chromium.org/"); + const GURL kNtpUrl(chrome::kTestNewTabURL); + + // Navigate to an initial URL. + contents()->NavigateAndCommit(kUrl1); + RenderViewHostManager* manager = contents()->GetRenderManagerForTesting(); + + // Pretend the RVH is alive so it doesn't go away. + TestRenderViewHost* rvh1 = test_rvh(); + + // Create 2 new tabs and simulate them being the opener chain for the main + // tab. They should be in the same SiteInstance. + TestWebContents opener1(browser_context(), rvh1->GetSiteInstance()); + RenderViewHostManager* opener1_manager = opener1.GetRenderManagerForTesting(); + contents()->SetOpener(&opener1); + + TestWebContents opener2(browser_context(), rvh1->GetSiteInstance()); + RenderViewHostManager* opener2_manager = opener2.GetRenderManagerForTesting(); + opener1.SetOpener(&opener2); + + // Navigate to a cross-site URL (different SiteInstance but same + // BrowsingInstance). + contents()->NavigateAndCommit(kUrl2); + TestRenderViewHost* rvh2 = test_rvh(); + EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance()); + EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( + rvh2->GetSiteInstance())); + + // Ensure rvh1 is placed on swapped out list of the current tab. + EXPECT_TRUE(manager->IsSwappedOut(rvh1)); + EXPECT_EQ(rvh1, + manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance())); + + // Ensure a swapped out RVH is created in the first opener tab. + TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( + opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); + EXPECT_TRUE(opener1_manager->IsSwappedOut(opener1_rvh)); + EXPECT_TRUE(opener1_rvh->is_swapped_out()); + + // Ensure a swapped out RVH is created in the second opener tab. + TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>( + opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); + EXPECT_TRUE(opener2_manager->IsSwappedOut(opener2_rvh)); + EXPECT_TRUE(opener2_rvh->is_swapped_out()); + + // Navigate to a cross-BrowsingInstance URL. + contents()->NavigateAndCommit(kNtpUrl); + TestRenderViewHost* rvh3 = test_rvh(); + EXPECT_NE(rvh1->GetSiteInstance(), rvh3->GetSiteInstance()); + EXPECT_FALSE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( + rvh3->GetSiteInstance())); + + // No scripting is allowed across BrowsingInstances, so we should not create + // swapped out RVHs for the opener chain in this case. + EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost( + rvh3->GetSiteInstance())); + EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost( + rvh3->GetSiteInstance())); +} diff --git a/content/browser/web_contents/test_web_contents.cc b/content/browser/web_contents/test_web_contents.cc index 69e22e4..c5bf1fb 100644 --- a/content/browser/web_contents/test_web_contents.cc +++ b/content/browser/web_contents/test_web_contents.cc @@ -12,6 +12,9 @@ #include "content/browser/site_instance_impl.h" #include "content/browser/web_contents/navigation_entry_impl.h" #include "content/common/view_messages.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/notification_types.h" #include "content/public/common/page_transition_types.h" #include "content/test/mock_render_process_host.h" #include "webkit/forms/password_form.h" @@ -21,7 +24,8 @@ namespace content { TestWebContents::TestWebContents(BrowserContext* browser_context, SiteInstance* instance) - : WebContentsImpl(browser_context, instance, MSG_ROUTING_NONE, NULL, NULL), + : WebContentsImpl(browser_context, instance, MSG_ROUTING_NONE, NULL, NULL, + NULL), transition_cross_site(false), delegate_view_override_(NULL), expect_set_history_length_and_prune_(false), @@ -83,10 +87,10 @@ WebPreferences TestWebContents::TestGetWebkitPrefs() { } bool TestWebContents::CreateRenderViewForRenderManager( - RenderViewHost* render_view_host) { + RenderViewHost* render_view_host, int opener_route_id) { // This will go to a TestRenderViewHost. static_cast<RenderViewHostImpl*>( - render_view_host)->CreateRenderView(string16(), -1); + render_view_host)->CreateRenderView(string16(), opener_route_id, -1); return true; } @@ -156,6 +160,14 @@ RenderViewHostDelegate::View* TestWebContents::GetViewDelegate() { return WebContentsImpl::GetViewDelegate(); } +void TestWebContents::SetOpener(TestWebContents* opener) { + // This is normally only set in the WebContents constructor, which also + // registers an observer for when the opener gets closed. + opener_ = opener; + registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, + content::Source<WebContents>(opener_)); +} + void TestWebContents::ExpectSetHistoryLengthAndPrune( const SiteInstance* site_instance, int history_length, diff --git a/content/browser/web_contents/test_web_contents.h b/content/browser/web_contents/test_web_contents.h index 7267cab..c862d62 100644 --- a/content/browser/web_contents/test_web_contents.h +++ b/content/browser/web_contents/test_web_contents.h @@ -56,7 +56,7 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester { // Prevent interaction with views. virtual bool CreateRenderViewForRenderManager( - RenderViewHost* render_view_host) OVERRIDE; + RenderViewHost* render_view_host, int opener_route_id) OVERRIDE; virtual void UpdateRenderViewSizeForRenderManager() OVERRIDE {} // Returns a clone of this TestWebContents. The returned object is also a @@ -72,6 +72,9 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester { delegate_view_override_ = view; } + // Allows us to simulate this tab having an opener. + void SetOpener(TestWebContents* opener); + // Establish expected arguments for |SetHistoryLengthAndPrune()|. When // |SetHistoryLengthAndPrune()| is called, the arguments are compared // with the expected arguments specified here. diff --git a/content/browser/web_contents/web_contents_delegate_unittest.cc b/content/browser/web_contents/web_contents_delegate_unittest.cc index e147153..72cd369 100644 --- a/content/browser/web_contents/web_contents_delegate_unittest.cc +++ b/content/browser/web_contents/web_contents_delegate_unittest.cc @@ -25,11 +25,13 @@ TEST(WebContentsDelegateTest, UnregisterInDestructor) { NULL, MSG_ROUTING_NONE, NULL, + NULL, NULL)); scoped_ptr<WebContentsImpl> contents_b(new WebContentsImpl(&browser_context, NULL, MSG_ROUTING_NONE, NULL, + NULL, NULL)); EXPECT_TRUE(contents_a->GetDelegate() == NULL); EXPECT_TRUE(contents_b->GetDelegate() == NULL); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 08d28b3..05dafcd 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -239,6 +239,7 @@ WebContents* WebContents::Create( site_instance, routing_id, static_cast<const WebContentsImpl*>(base_web_contents), + NULL, static_cast<SessionStorageNamespaceImpl*>(session_storage_namespace)); } } @@ -250,10 +251,12 @@ WebContentsImpl::WebContentsImpl( SiteInstance* site_instance, int routing_id, const WebContentsImpl* base_web_contents, + WebContentsImpl* opener, SessionStorageNamespaceImpl* session_storage_namespace) : delegate_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(controller_( this, browser_context, session_storage_namespace)), + opener_(opener), ALLOW_THIS_IN_INITIALIZER_LIST(render_manager_(this, this)), is_loading_(false), crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING), @@ -280,7 +283,6 @@ WebContentsImpl::WebContentsImpl( temporary_zoom_settings_(false), content_restrictions_(0), view_type_(content::VIEW_TYPE_WEB_CONTENTS), - has_opener_(false), color_chooser_(NULL) { render_manager_.Init(browser_context, site_instance, routing_id); @@ -310,6 +312,12 @@ WebContentsImpl::WebContentsImpl( view_->CreateView(base_web_contents ? base_web_contents->GetView()->GetContainerSize() : gfx::Size()); + // Listen for whether our opener gets destroyed. + if (opener_) { + registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, + content::Source<WebContents>(opener_)); + } + #if defined(ENABLE_JAVA_BRIDGE) java_bridge_dispatcher_host_manager_.reset( new JavaBridgeDispatcherHostManager(this)); @@ -920,13 +928,12 @@ void WebContentsImpl::Stop() { } WebContents* WebContentsImpl::Clone() { - // We create a new SiteInstance so that the new tab won't share processes - // with the old one. This can be changed in the future if we need it to share - // processes for some reason. + // We use our current SiteInstance since the cloned entry will use it anyway. + // We pass |this| for the |base_web_contents| to size the view correctly, and + // our own opener so that the cloned page can access it if it was before. WebContentsImpl* tc = new WebContentsImpl( - GetBrowserContext(), - SiteInstance::Create(GetBrowserContext()), - MSG_ROUTING_NONE, this, NULL); + GetBrowserContext(), GetSiteInstance(), + MSG_ROUTING_NONE, this, opener_, NULL); tc->GetControllerImpl().CopyStateFrom(controller_); return tc; } @@ -958,6 +965,28 @@ void WebContentsImpl::Focus() { view_->Focus(); } +void WebContentsImpl::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: + OnWebContentsDestroyed( + content::Source<content::WebContents>(source).ptr()); + break; + default: + NOTREACHED(); + } +} + +void WebContentsImpl::OnWebContentsDestroyed(WebContents* web_contents) { + // Clear the opener if it has been closed. + if (web_contents == opener_) { + registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, + content::Source<WebContents>(opener_)); + opener_ = NULL; + } +} + void WebContentsImpl::AddObserver(WebContentsObserver* observer) { observers_.AddObserver(observer); } @@ -1408,7 +1437,7 @@ bool WebContentsImpl::GotResponseToLockMouseRequest(bool allowed) { } bool WebContentsImpl::HasOpener() const { - return has_opener_; + return opener_ != NULL; } void WebContentsImpl::DidChooseColorInColorChooser(int color_chooser_id, @@ -2000,6 +2029,12 @@ gfx::Rect WebContentsImpl::GetRootWindowResizerRect() const { } void WebContentsImpl::RenderViewCreated(RenderViewHost* render_view_host) { + // Don't send notifications if we are just creating a swapped-out RVH for + // the opener chain. These won't be used for view-source or WebUI, so it's + // ok to return early. + if (static_cast<RenderViewHostImpl*>(render_view_host)->is_swapped_out()) + return; + content::NotificationService::current()->Notify( content::NOTIFICATION_RENDER_VIEW_HOST_CREATED_FOR_TAB, content::Source<WebContents>(this), @@ -2547,6 +2582,28 @@ void WebContentsImpl::NotifySwappedFromRenderManager() { NotifySwapped(); } +int WebContentsImpl::CreateOpenerRenderViewsForRenderManager( + SiteInstance* instance) { + if (!opener_) + return MSG_ROUTING_NONE; + + // Recursively create RenderViews for anything else in the opener chain. + return opener_->CreateOpenerRenderViews(instance); +} + +int WebContentsImpl::CreateOpenerRenderViews(SiteInstance* instance) { + int opener_route_id = MSG_ROUTING_NONE; + + // If this tab has an opener, ensure it has a RenderView in the given + // SiteInstance as well. + if (opener_) + opener_route_id = opener_->CreateOpenerRenderViews(instance); + + // Create a swapped out RenderView in the given SiteInstance if none exists, + // setting its opener to the given route_id. Return the new view's route_id. + return render_manager_.CreateRenderView(instance, opener_route_id, true); +} + NavigationControllerImpl& WebContentsImpl::GetControllerForRenderManager() { return GetControllerImpl(); } @@ -2561,7 +2618,7 @@ NavigationEntry* } bool WebContentsImpl::CreateRenderViewForRenderManager( - RenderViewHost* render_view_host) { + RenderViewHost* render_view_host, int opener_route_id) { // Can be NULL during tests. RenderWidgetHostView* rwh_view = view_->CreateViewForWidget(render_view_host); @@ -2575,8 +2632,10 @@ bool WebContentsImpl::CreateRenderViewForRenderManager( GetMaxPageIDForSiteInstance(render_view_host->GetSiteInstance()); if (!static_cast<RenderViewHostImpl*>( - render_view_host)->CreateRenderView(string16(), max_page_id)) + render_view_host)->CreateRenderView(string16(), opener_route_id, + max_page_id)) { return false; + } #if defined(OS_LINUX) || defined(OS_OPENBSD) // Force a ViewMsg_Resize to be sent, needed to make plugins show up on diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index a2fcba7..b330375 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -19,6 +19,8 @@ #include "content/browser/web_contents/navigation_controller_impl.h" #include "content/browser/web_contents/render_view_host_manager.h" #include "content/common/content_export.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" #include "content/public/browser/render_view_host_delegate.h" #include "content/public/browser/web_contents.h" #include "content/public/common/renderer_preferences.h" @@ -56,13 +58,15 @@ struct WebIntentData; class CONTENT_EXPORT WebContentsImpl : public NON_EXPORTED_BASE(content::WebContents), public content::RenderViewHostDelegate, - public RenderViewHostManager::Delegate { + public RenderViewHostManager::Delegate, + public content::NotificationObserver { public: // See WebContents::Create for a description of these parameters. WebContentsImpl(content::BrowserContext* browser_context, content::SiteInstance* site_instance, int routing_id, const WebContentsImpl* base_web_contents, + WebContentsImpl* opener, SessionStorageNamespaceImpl* session_storage_namespace); virtual ~WebContentsImpl(); @@ -117,10 +121,6 @@ class CONTENT_EXPORT WebContentsImpl opener_web_ui_type_ = opener_web_ui_type; } - void set_has_opener(bool has_opener) { - has_opener_ = has_opener; - } - JavaBridgeDispatcherHostManager* java_bridge_dispatcher_host_manager() const { return java_bridge_dispatcher_host_manager_.get(); } @@ -340,7 +340,7 @@ class CONTENT_EXPORT WebContentsImpl // RenderViewHostManager::Delegate ------------------------------------------- virtual bool CreateRenderViewForRenderManager( - content::RenderViewHost* render_view_host) OVERRIDE; + content::RenderViewHost* render_view_host, int opener_route_id) OVERRIDE; virtual void BeforeUnloadFiredFromRenderManager( bool proceed, bool* proceed_to_fire_unload) OVERRIDE; @@ -350,6 +350,8 @@ class CONTENT_EXPORT WebContentsImpl content::RenderViewHost* render_view_host) OVERRIDE; virtual void UpdateRenderViewSizeForRenderManager() OVERRIDE; virtual void NotifySwappedFromRenderManager() OVERRIDE; + virtual int CreateOpenerRenderViewsForRenderManager( + content::SiteInstance* instance) OVERRIDE; virtual NavigationControllerImpl& GetControllerForRenderManager() OVERRIDE; virtual WebUIImpl* CreateWebUIForRenderManager(const GURL& url) OVERRIDE; virtual content::NavigationEntry* @@ -359,6 +361,12 @@ class CONTENT_EXPORT WebContentsImpl virtual void CreateViewAndSetSizeForRVH( content::RenderViewHost* rvh) OVERRIDE; + // content::NotificationObserver --------------------------------------------- + + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + protected: friend class content::WebContentsObserver; @@ -386,6 +394,9 @@ class CONTENT_EXPORT WebContentsImpl // TODO(brettw) TestWebContents shouldn't exist! friend class content::TestWebContents; + // Clears this tab's opener if it has been closed. + void OnWebContentsDestroyed(content::WebContents* web_contents); + // Callback function when showing JS dialogs. void OnDialogClosed(content::RenderViewHost* rvh, IPC::Message* reply_msg, @@ -512,6 +523,12 @@ class CONTENT_EXPORT WebContentsImpl int merge_history_length, int32 minimum_page_id); + // Recursively creates swapped out RenderViews for this tab's opener chain + // (including this tab) in the given SiteInstance, allowing other tabs to send + // cross-process JavaScript calls to their opener(s). Returns the route ID of + // this tab's RenderView for |instance|. + int CreateOpenerRenderViews(content::SiteInstance* instance); + // Misc non-view stuff ------------------------------------------------------- // Helper functions for sending notifications. @@ -530,6 +547,9 @@ class CONTENT_EXPORT WebContentsImpl // WARNING: this needs to be deleted after NavigationController. base::PropertyBag property_bag_; + // Listen for notifications as well. + content::NotificationRegistrar registrar_; + // Data for core operation --------------------------------------------------- // Delegate for notifying our owner about stuff. Not owned by us. @@ -547,6 +567,10 @@ class CONTENT_EXPORT WebContentsImpl // the observer list then. ObserverList<content::WebContentsObserver> observers_; + // The tab that opened this tab, if any. Will be set to null if the opener + // is closed. + WebContentsImpl* opener_; + // Helper classes ------------------------------------------------------------ // Manages creation and swapping of render views. @@ -674,9 +698,6 @@ class CONTENT_EXPORT WebContentsImpl // Our view type. Default is VIEW_TYPE_WEB_CONTENTS. content::ViewType view_type_; - // Is there an opener associated with this? - bool has_opener_; - // Color chooser that was opened by this tab. content::ColorChooser* color_chooser_; diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index ea9ae58..4d726be 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc @@ -221,7 +221,7 @@ class TestInterstitialPage : public InterstitialPageImpl { virtual content::RenderViewHost* CreateRenderViewHost() OVERRIDE { return new TestRenderViewHost( SiteInstance::Create(web_contents()->GetBrowserContext()), - this, MSG_ROUTING_NONE); + this, MSG_ROUTING_NONE, false); } virtual content::WebContentsView* CreateWebContentsView() OVERRIDE { diff --git a/content/browser/web_contents/web_contents_view_helper.cc b/content/browser/web_contents/web_contents_view_helper.cc index b3bf9be..a79f8d0 100644 --- a/content/browser/web_contents/web_contents_view_helper.cc +++ b/content/browser/web_contents/web_contents_view_helper.cc @@ -21,6 +21,7 @@ using content::RenderWidgetHost; using content::RenderWidgetHostImpl; using content::RenderWidgetHostView; using content::RenderWidgetHostViewPort; +using content::SiteInstance; WebContentsViewHelper::WebContentsViewHelper() { registrar_.Add(this, @@ -46,13 +47,13 @@ void WebContentsViewHelper::Observe( } WebContentsImpl* WebContentsViewHelper::CreateNewWindow( - WebContentsImpl* web_contents, + WebContentsImpl* opener, int route_id, const ViewHostMsg_CreateWindow_Params& params) { bool should_create = true; - if (web_contents->GetDelegate()) { - should_create = web_contents->GetDelegate()->ShouldCreateWebContents( - web_contents, + if (opener->GetDelegate()) { + should_create = opener->GetDelegate()->ShouldCreateWebContents( + opener, route_id, params.window_container_type, params.frame_name, @@ -66,22 +67,22 @@ WebContentsImpl* WebContentsViewHelper::CreateNewWindow( // script-related windows), by passing in the current SiteInstance. However, // if the opener is being suppressed, we create a new SiteInstance in its own // BrowsingInstance. - scoped_refptr<content::SiteInstance> site_instance = + scoped_refptr<SiteInstance> site_instance = params.opener_suppressed ? - content::SiteInstance::Create(web_contents->GetBrowserContext()) : - web_contents->GetSiteInstance(); + SiteInstance::Create(opener->GetBrowserContext()) : + opener->GetSiteInstance(); // Create the new web contents. This will automatically create the new // WebContentsView. In the future, we may want to create the view separately. WebContentsImpl* new_contents = - new WebContentsImpl(web_contents->GetBrowserContext(), + new WebContentsImpl(opener->GetBrowserContext(), site_instance, route_id, - web_contents, + opener, + params.opener_suppressed ? NULL : opener, NULL); new_contents->set_opener_web_ui_type( - web_contents->GetWebUITypeForCurrentState()); - new_contents->set_has_opener(!params.opener_url.is_empty()); + opener->GetWebUITypeForCurrentState()); if (!params.opener_suppressed) { content::WebContentsView* new_view = new_contents->GetView(); @@ -96,20 +97,21 @@ WebContentsImpl* WebContentsViewHelper::CreateNewWindow( pending_contents_[route_id] = new_contents; } - if (web_contents->GetDelegate()) - web_contents->GetDelegate()->WebContentsCreated(web_contents, - params.opener_frame_id, - params.target_url, - new_contents); + if (opener->GetDelegate()) { + opener->GetDelegate()->WebContentsCreated(opener, + params.opener_frame_id, + params.target_url, + new_contents); + } if (params.opener_suppressed) { // When the opener is suppressed, the original renderer cannot access the // new window. As a result, we need to show and navigate the window here. gfx::Rect initial_pos; - web_contents->AddNewContents(new_contents, - params.disposition, - initial_pos, - params.user_gesture); + opener->AddNewContents(new_contents, + params.disposition, + initial_pos, + params.user_gesture); content::OpenURLParams open_params(params.target_url, content::Referrer(), CURRENT_TAB, diff --git a/content/browser/web_contents/web_contents_view_helper.h b/content/browser/web_contents/web_contents_view_helper.h index e39755a..46d58de 100644 --- a/content/browser/web_contents/web_contents_view_helper.h +++ b/content/browser/web_contents/web_contents_view_helper.h @@ -37,7 +37,7 @@ class CONTENT_EXPORT WebContentsViewHelper // Creates a new window; call |ShowCreatedWindow| below to show it. WebContentsImpl* CreateNewWindow( - WebContentsImpl* web_contents, + WebContentsImpl* opener, int route_id, const ViewHostMsg_CreateWindow_Params& params); diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 8edcf91..0c7bd54 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -632,6 +632,13 @@ IPC_STRUCT_BEGIN(ViewMsg_New_Params) // The name of the frame associated with this view (or empty if none). IPC_STRUCT_MEMBER(string16, frame_name) + // The route ID of the opener RenderView if we need to set one + // (MSG_ROUTING_NONE otherwise). + IPC_STRUCT_MEMBER(int, opener_route_id) + + // Whether the RenderView should initially be swapped out. + IPC_STRUCT_MEMBER(bool, swapped_out) + // The initial page ID to use for this view, which must be larger than any // existing navigation that might be loaded in the view. Page IDs are unique // to a view and are only updated by the renderer after this initial value. diff --git a/content/public/browser/site_instance.h b/content/public/browser/site_instance.h index 66081ec..d68989e 100644 --- a/content/public/browser/site_instance.h +++ b/content/public/browser/site_instance.h @@ -82,6 +82,11 @@ class CONTENT_EXPORT SiteInstance : public base::RefCounted<SiteInstance> { // Darin suggests. virtual SiteInstance* GetRelatedSiteInstance(const GURL& url) = 0; + // Returns whether the given SiteInstance is in the same BrowsingInstance as + // this one. If so, JavaScript interactions that are permitted across + // origins (e.g., postMessage) should be supported. + virtual bool IsRelatedSiteInstance(const SiteInstance* instance) = 0; + // Factory method to create a new SiteInstance. This will create a new // new BrowsingInstance, so it should only be used when creating a new tab // from scratch (or similar circumstances). Callers should ensure that diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index aa3c988..b4c2b68 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -899,7 +899,7 @@ void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) { // When bringing in render_view, also bring in webkit's glue and jsbindings. RenderViewImpl::Create( params.parent_window, - MSG_ROUTING_NONE, + params.opener_route_id, params.renderer_preferences, params.web_preferences, new SharedRenderViewCounter(0), @@ -907,6 +907,8 @@ void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) { params.surface_id, params.session_storage_namespace_id, params.frame_name, + false, + params.swapped_out, params.next_page_id, params.screen_info, params.guest, diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 8ad9470..d3b95b0 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -26,6 +26,7 @@ #include "base/time.h" #include "base/utf_string_conversions.h" #include "content/common/appcache/appcache_dispatcher.h" +#include "content/common/child_thread.h" #include "content/common/clipboard_messages.h" #include "content/common/database_messages.h" #include "content/common/drag_messages.h" @@ -436,11 +437,13 @@ RenderViewImpl::RenderViewImpl( int32 surface_id, int64 session_storage_namespace_id, const string16& frame_name, + bool is_renderer_created, + bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, bool guest, AccessibilityMode accessibility_mode) - : RenderWidget(WebKit::WebPopupTypeNone, screen_info), + : RenderWidget(WebKit::WebPopupTypeNone, screen_info, swapped_out), webkit_preferences_(webkit_prefs), send_content_state_immediately_(false), enabled_bindings_(0), @@ -483,7 +486,7 @@ RenderViewImpl::RenderViewImpl( ALLOW_THIS_IN_INITIALIZER_LIST(pepper_delegate_(this)) { routing_id_ = routing_id; surface_id_ = surface_id; - if (opener_id != MSG_ROUTING_NONE) + if (opener_id != MSG_ROUTING_NONE && is_renderer_created) opener_id_ = opener_id; // Ensure we start with a valid next_page_id_ from the browser. @@ -500,7 +503,9 @@ RenderViewImpl::RenderViewImpl( if (counter) { shared_popup_counter_ = counter; - shared_popup_counter_->data++; + // Only count this if it isn't swapped out upon creation. + if (!swapped_out) + shared_popup_counter_->data++; decrement_shared_popup_at_destruction_ = true; } else { shared_popup_counter_ = new SharedRenderViewCounter(0); @@ -514,7 +519,7 @@ RenderViewImpl::RenderViewImpl( // If this is a popup, we must wait for the CreatingNew_ACK message before // completing initialization. Otherwise, we can finish it now. - if (opener_id == MSG_ROUTING_NONE) { + if (opener_id_ == MSG_ROUTING_NONE) { did_show_ = true; CompleteInit(parent_hwnd); } @@ -563,6 +568,20 @@ RenderViewImpl::RenderViewImpl( ProcessViewLayoutFlags(command_line); content::GetContentClient()->renderer()->RenderViewCreated(this); + + // If we have an opener_id but we weren't created by a renderer, then + // it's the browser asking us to set our opener to another RenderView. + // TODO(creis): This doesn't yet handle openers that are subframes. + if (opener_id != MSG_ROUTING_NONE && !is_renderer_created) { + RenderViewImpl* opener_view = static_cast<RenderViewImpl*>( + ChildThread::current()->ResolveRoute(opener_id)); + webview()->mainFrame()->setOpener(opener_view->webview()->mainFrame()); + } + + // If we are initially swapped out, navigate to kSwappedOutURL. + // This ensures we are in a unique origin that others cannot script. + if (is_swapped_out_) + NavigateToSwappedOutURL(); } RenderViewImpl::~RenderViewImpl() { @@ -634,6 +653,8 @@ RenderViewImpl* RenderViewImpl::Create( int32 surface_id, int64 session_storage_namespace_id, const string16& frame_name, + bool is_renderer_created, + bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, bool guest, @@ -649,6 +670,8 @@ RenderViewImpl* RenderViewImpl::Create( surface_id, session_storage_namespace_id, frame_name, + is_renderer_created, + swapped_out, next_page_id, screen_info, guest, @@ -1514,6 +1537,8 @@ WebView* RenderViewImpl::createView( surface_id, cloned_session_storage_namespace_id, frame_name, + true, + false, 1, screen_info_, guest_, @@ -4472,21 +4497,25 @@ void RenderViewImpl::OnSwapOut(const ViewMsg_SwapOut_Params& params) { // Replace the page with a blank dummy URL. The unload handler will not be // run a second time, thanks to a check in FrameLoader::stopLoading. - // We use loadRequest instead of loadHTMLString because the former commits - // synchronously. Otherwise a new navigation can interrupt the navigation - // to content::kSwappedOutURL. If that happens to be to the page we had been - // showing, then WebKit will never send a commit and we'll be left spinning. // TODO(creis): Need to add a better way to do this that avoids running the // beforeunload handler. For now, we just run it a second time silently. - GURL swappedOutURL(content::kSwappedOutURL); - WebURLRequest request(swappedOutURL); - webview()->mainFrame()->loadRequest(request); + NavigateToSwappedOutURL(); } // Just echo back the params in the ACK. Send(new ViewHostMsg_SwapOut_ACK(routing_id_, params)); } +void RenderViewImpl::NavigateToSwappedOutURL() { + // We use loadRequest instead of loadHTMLString because the former commits + // synchronously. Otherwise a new navigation can interrupt the navigation + // to content::kSwappedOutURL. If that happens to be to the page we had been + // showing, then WebKit will never send a commit and we'll be left spinning. + GURL swappedOutURL(content::kSwappedOutURL); + WebURLRequest request(swappedOutURL); + webview()->mainFrame()->loadRequest(request); +} + void RenderViewImpl::OnClosePage() { FOR_EACH_OBSERVER(RenderViewObserver, observers_, ClosePage()); // TODO(creis): We'd rather use webview()->Close() here, but that currently diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index 77264de..8030060 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -193,6 +193,8 @@ class RenderViewImpl : public RenderWidget, int32 surface_id, int64 session_storage_namespace_id, const string16& frame_name, + bool is_renderer_created, + bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, bool guest, @@ -747,6 +749,8 @@ class RenderViewImpl : public RenderWidget, int32 surface_id, int64 session_storage_namespace_id, const string16& frame_name, + bool is_renderer_created, + bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, bool guest, @@ -984,6 +988,9 @@ class RenderViewImpl : public RenderWidget, const WebKit::WebURLError& error, bool replace); + // Make this RenderView show an empty, unscriptable page. + void NavigateToSwappedOutURL(); + // If we initiated a navigation, this function will populate |document_state| // with the navigation information saved in OnNavigate(). void PopulateDocumentStateFromPending(content::DocumentState* document_state); diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index dae7853..eb78d81 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -93,7 +93,8 @@ bool CanSendMessageWhileClosing(const IPC::Message* msg) { } // namespace RenderWidget::RenderWidget(WebKit::WebPopupType popup_type, - const WebKit::WebScreenInfo& screen_info) + const WebKit::WebScreenInfo& screen_info, + bool swapped_out) : routing_id_(MSG_ROUTING_NONE), surface_id_(0), webwidget_(NULL), @@ -113,7 +114,7 @@ RenderWidget::RenderWidget(WebKit::WebPopupType popup_type, has_focus_(false), handling_input_event_(false), closing_(false), - is_swapped_out_(false), + is_swapped_out_(swapped_out), input_method_is_active_(false), text_input_type_(ui::TEXT_INPUT_TYPE_NONE), can_compose_inline_(true), @@ -149,7 +150,7 @@ RenderWidget* RenderWidget::Create(int32 opener_id, const WebKit::WebScreenInfo& screen_info) { DCHECK(opener_id != MSG_ROUTING_NONE); scoped_refptr<RenderWidget> widget( - new RenderWidget(popup_type, screen_info)); + new RenderWidget(popup_type, screen_info, false)); widget->Init(opener_id); // adds reference return widget; } diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index c6257bb..ffc0b8b 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h @@ -167,7 +167,8 @@ class CONTENT_EXPORT RenderWidget }; RenderWidget(WebKit::WebPopupType popup_type, - const WebKit::WebScreenInfo& screen_info); + const WebKit::WebScreenInfo& screen_info, + bool swapped_out); virtual ~RenderWidget(); // Initializes this view with the given opener. CompleteInit must be called diff --git a/content/renderer/render_widget_fullscreen.cc b/content/renderer/render_widget_fullscreen.cc index 7030e5d..dfb5c13 100644 --- a/content/renderer/render_widget_fullscreen.cc +++ b/content/renderer/render_widget_fullscreen.cc @@ -31,7 +31,7 @@ void RenderWidgetFullscreen::show(WebKit::WebNavigationPolicy) { } RenderWidgetFullscreen::RenderWidgetFullscreen() - : RenderWidget(WebKit::WebPopupTypeNone, WebKit::WebScreenInfo()) { + : RenderWidget(WebKit::WebPopupTypeNone, WebKit::WebScreenInfo(), false) { } RenderWidgetFullscreen::~RenderWidgetFullscreen() {} diff --git a/content/test/render_view_fake_resources_test.cc b/content/test/render_view_fake_resources_test.cc index d922307..807a4e1 100644 --- a/content/test/render_view_fake_resources_test.cc +++ b/content/test/render_view_fake_resources_test.cc @@ -72,6 +72,7 @@ void RenderViewFakeResourcesTest::SetUp() { ViewMsg_New_Params params; params.parent_window = 0; params.view_id = kViewId; + params.opener_route_id = MSG_ROUTING_NONE; params.session_storage_namespace_id = dom_storage::kInvalidSessionStorageNamespaceId; ASSERT_TRUE(channel_->Send(new ViewMsg_New(params))); diff --git a/content/test/render_view_test.cc b/content/test/render_view_test.cc index 25e5488..9454109 100644 --- a/content/test/render_view_test.cc +++ b/content/test/render_view_test.cc @@ -46,7 +46,7 @@ using WebKit::WebString; using WebKit::WebURLRequest; namespace { -const int32 kOpenerId = 7; +const int32 kOpenerId = -2; const int32 kRouteId = 5; const int32 kNewWindowRouteId = 6; const int32 kSurfaceId = 42; @@ -164,6 +164,8 @@ void RenderViewTest::SetUp() { kSurfaceId, dom_storage::kInvalidSessionStorageNamespaceId, string16(), + false, + false, 1, WebKit::WebScreenInfo(), false, diff --git a/content/test/test_renderer_host.cc b/content/test/test_renderer_host.cc index cd9aa34..58d3ac9 100644 --- a/content/test/test_renderer_host.cc +++ b/content/test/test_renderer_host.cc @@ -44,6 +44,7 @@ class TestRenderViewHostFactory : public RenderViewHostFactory { content::SiteInstance* instance, content::RenderViewHostDelegate* delegate, int routing_id, + bool swapped_out, content::SessionStorageNamespace* session_storage) OVERRIDE; private: @@ -78,11 +79,12 @@ content::RenderViewHost* TestRenderViewHostFactory::CreateRenderViewHost( SiteInstance* instance, RenderViewHostDelegate* delegate, int routing_id, + bool swapped_out, SessionStorageNamespace* session_storage) { // See declaration of render_process_host_factory_ below. static_cast<SiteInstanceImpl*>(instance)-> set_render_process_host_factory(render_process_host_factory_); - return new TestRenderViewHost(instance, delegate, routing_id); + return new TestRenderViewHost(instance, delegate, routing_id, swapped_out); } // static diff --git a/content/test/test_renderer_host.h b/content/test/test_renderer_host.h index 76ffdff..367fbea 100644 --- a/content/test/test_renderer_host.h +++ b/content/test/test_renderer_host.h @@ -66,6 +66,7 @@ class RenderViewHostTester { // Gives tests access to RenderViewHostImpl::CreateRenderView. virtual bool CreateRenderView(const string16& frame_name, + int opener_route_id, int32 max_page_id) = 0; // Calls OnMsgNavigate on the RenderViewHost with the given information, |