// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/frame_host/cross_site_transferring_request.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigation_request.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/render_frame_host_manager.h"
-#include "content/browser/renderer_host/cross_site_transferring_request.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
-#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/test/test_notification_tracker.h"
#include "content/test/test_content_browser_client.h"
#include "content/test/test_content_client.h"
+#include "content/test/test_render_frame_host.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "testing/gtest/include/gtest/gtest.h"
DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
};
+// This observer keeps track of the last deleted RenderViewHost to avoid
+// accessing it and causing use-after-free condition.
+class RenderViewHostDeletedObserver : public WebContentsObserver {
+ public:
+ RenderViewHostDeletedObserver(RenderViewHost* rvh)
+ : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
+ process_id_(rvh->GetProcess()->GetID()),
+ routing_id_(rvh->GetRoutingID()),
+ deleted_(false) {
+ }
+
+ virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
+ if (render_view_host->GetProcess()->GetID() == process_id_ &&
+ render_view_host->GetRoutingID() == routing_id_) {
+ deleted_ = true;
+ }
+ }
+
+ bool deleted() {
+ return deleted_;
+ }
+
+ private:
+ int process_id_;
+ int routing_id_;
+ bool deleted_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver);
+};
+
+// This observer keeps track of the last deleted RenderFrameHost to avoid
+// accessing it and causing use-after-free condition.
+class RenderFrameHostDeletedObserver : public WebContentsObserver {
+ public:
+ RenderFrameHostDeletedObserver(RenderFrameHost* rfh)
+ : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)),
+ process_id_(rfh->GetProcess()->GetID()),
+ routing_id_(rfh->GetRoutingID()),
+ deleted_(false) {
+ }
+
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
+ if (render_frame_host->GetProcess()->GetID() == process_id_ &&
+ render_frame_host->GetRoutingID() == routing_id_) {
+ deleted_ = true;
+ }
+ }
+
+ bool deleted() {
+ return deleted_;
+ }
+
+ private:
+ int process_id_;
+ int routing_id_;
+ bool deleted_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver);
+};
+
+
+// This observer is used to check whether IPC messages are being filtered for
+// swapped out RenderFrameHost objects. It observes the plugin crash and favicon
+// update events, which the FilterMessagesWhileSwappedOut test simulates being
+// sent. The test is successful if the event is not observed.
+// See http://crbug.com/351815
+class PluginFaviconMessageObserver : public WebContentsObserver {
+ public:
+ PluginFaviconMessageObserver(WebContents* web_contents)
+ : WebContentsObserver(web_contents),
+ plugin_crashed_(false),
+ favicon_received_(false) { }
+
+ virtual void PluginCrashed(const base::FilePath& plugin_path,
+ base::ProcessId plugin_pid) OVERRIDE {
+ plugin_crashed_ = true;
+ }
+
+ virtual void DidUpdateFaviconURL(
+ const std::vector<FaviconURL>& candidates) OVERRIDE {
+ favicon_received_ = true;
+ }
+
+ bool plugin_crashed() {
+ return plugin_crashed_;
+ }
+
+ bool favicon_received() {
+ return favicon_received_;
+ }
+
+ private:
+ bool plugin_crashed_;
+ bool favicon_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
+};
+
+// Ensures that RenderFrameDeleted and RenderFrameCreated are called in a
+// consistent manner.
+class FrameLifetimeConsistencyChecker : public WebContentsObserver {
+ public:
+ explicit FrameLifetimeConsistencyChecker(WebContentsImpl* web_contents)
+ : WebContentsObserver(web_contents) {
+ RenderViewCreated(web_contents->GetRenderViewHost());
+ RenderFrameCreated(web_contents->GetMainFrame());
+ }
+
+ virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE {
+ std::pair<int, int> routing_pair =
+ std::make_pair(render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID());
+ bool was_live_already = !live_routes_.insert(routing_pair).second;
+ bool was_used_before = deleted_routes_.count(routing_pair) != 0;
+
+ if (was_live_already) {
+ FAIL() << "RenderFrameCreated called more than once for routing pair: "
+ << Format(render_frame_host);
+ } else if (was_used_before) {
+ FAIL() << "RenderFrameCreated called for routing pair "
+ << Format(render_frame_host) << " that was previously deleted.";
+ }
+ }
+
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
+ std::pair<int, int> routing_pair =
+ std::make_pair(render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID());
+ bool was_live = live_routes_.erase(routing_pair);
+ bool was_dead_already = !deleted_routes_.insert(routing_pair).second;
+
+ if (was_dead_already) {
+ FAIL() << "RenderFrameDeleted called more than once for routing pair "
+ << Format(render_frame_host);
+ } else if (!was_live) {
+ FAIL() << "RenderFrameDeleted called for routing pair "
+ << Format(render_frame_host)
+ << " for which RenderFrameCreated was never called";
+ }
+ }
+
+ private:
+ std::string Format(RenderFrameHost* render_frame_host) {
+ return base::StringPrintf(
+ "(%d, %d -> %s )",
+ render_frame_host->GetProcess()->GetID(),
+ render_frame_host->GetRoutingID(),
+ render_frame_host->GetSiteInstance()->GetSiteURL().spec().c_str());
+ }
+ std::set<std::pair<int, int> > live_routes_;
+ std::set<std::pair<int, int> > deleted_routes_;
+};
+
} // namespace
class RenderFrameHostManagerTest
virtual void SetUp() OVERRIDE {
RenderViewHostImplTestHarness::SetUp();
WebUIControllerFactory::RegisterFactory(&factory_);
+ lifetime_checker_.reset(new FrameLifetimeConsistencyChecker(contents()));
}
virtual void TearDown() OVERRIDE {
+ lifetime_checker_.reset();
RenderViewHostImplTestHarness::TearDown();
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
}
factory_.set_should_create_webui(should_create_webui);
}
+ void StartCrossSiteTransition(TestWebContents* contents) {
+ std::vector<GURL> url_chain;
+ contents->GetRenderManagerForTesting()->OnCrossSiteResponse(
+ contents->GetRenderManagerForTesting()->pending_frame_host(),
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
+ EXPECT_TRUE(contents->cross_navigation_pending());
+ RenderViewHostImpl* rvh = contents->GetRenderViewHost();
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
+ rvh->rvh_state());
+ }
+
void NavigateActiveAndCommit(const GURL& url) {
// Note: we navigate the active RenderViewHost because previous navigations
// won't have committed yet, so NavigateAndCommit does the wrong thing
controller().LoadURL(url, Referrer(), PAGE_TRANSITION_LINK, std::string());
TestRenderViewHost* old_rvh = test_rvh();
- // Simulate the ShouldClose_ACK that is received from the current renderer
+ // Simulate the BeforeUnload_ACK that is received from the current renderer
// for a cross-site navigation.
- if (old_rvh != active_rvh())
- old_rvh->SendShouldCloseACK(true);
+ if (old_rvh != active_rvh()) {
+ old_rvh->SendBeforeUnloadACK(true);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, old_rvh->rvh_state());
+ }
// Commit the navigation with a new page ID.
int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
active_rvh()->GetSiteInstance());
+ // Simulate the response coming from the pending renderer.
+ if (old_rvh != active_rvh())
+ StartCrossSiteTransition(contents());
+
// Simulate the SwapOut_ACK that fires if you commit a cross-site
// navigation.
- if (old_rvh != active_rvh())
+ if (old_rvh != active_rvh()) {
old_rvh->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT,
+ old_rvh->rvh_state());
+ }
+ // Use an observer to avoid accessing a deleted renderer later on when the
+ // state is being checked.
+ RenderViewHostDeletedObserver rvh_observer(old_rvh);
active_test_rvh()->SendNavigate(max_page_id + 1, url);
+
+ if (old_rvh != active_rvh() && !rvh_observer.deleted())
+ EXPECT_TRUE(old_rvh->IsSwappedOut());
}
bool ShouldSwapProcesses(RenderFrameHostManager* manager,
const NavigationEntryImpl* current_entry,
const NavigationEntryImpl* new_entry) const {
- return manager->ShouldSwapBrowsingInstancesForNavigation(current_entry,
- new_entry);
+ CHECK(new_entry);
+ BrowserContext* browser_context =
+ manager->delegate_->GetControllerForRenderManager().GetBrowserContext();
+ const GURL& current_effective_url = current_entry ?
+ SiteInstanceImpl::GetEffectiveURL(browser_context,
+ current_entry->GetURL()) :
+ manager->render_frame_host_->GetSiteInstance()->GetSiteURL();
+ bool current_is_view_source_mode = current_entry ?
+ current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode();
+ return manager->ShouldSwapBrowsingInstancesForNavigation(
+ current_effective_url,
+ current_is_view_source_mode,
+ new_entry->site_instance(),
+ SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()),
+ new_entry->IsViewSourceMode());
}
// Creates a test RenderViewHost that's swapped out.
// Navigate our first tab to a chrome url and then to the destination.
NavigateActiveAndCommit(kChromeURL);
- TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
- contents()->GetRenderManagerForTesting()->current_host());
+ TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
// Navigate to a cross-site URL.
contents()->GetController().LoadURL(
EXPECT_TRUE(contents()->cross_navigation_pending());
// Manually increase the number of active views in the
- // SiteInstance that ntp_rvh belongs to, to prevent it from being
+ // SiteInstance that ntp_rfh belongs to, to prevent it from being
// destroyed when it gets swapped out.
- static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
+ static_cast<SiteInstanceImpl*>(ntp_rfh->GetSiteInstance())->
increment_active_view_count();
- TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
- contents()->GetRenderManagerForTesting()->pending_render_view_host());
- CHECK(dest_rvh);
- EXPECT_NE(ntp_rvh, dest_rvh);
+ TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame();
+ CHECK(dest_rfh);
+ EXPECT_NE(ntp_rfh, dest_rfh);
// BeforeUnload finishes.
- ntp_rvh->SendShouldCloseACK(true);
+ ntp_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
- dest_rvh->SendNavigate(101, kDestUrl);
- ntp_rvh->OnSwappedOut(false);
+ dest_rfh->SendNavigate(101, kDestUrl);
+ ntp_rfh->OnSwappedOut(false);
- EXPECT_TRUE(ntp_rvh->IsSwappedOut());
- return ntp_rvh;
+ EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->IsSwappedOut());
+ return ntp_rfh->GetRenderViewHost();
+ }
+
+ NavigationRequest* NavigationRequestForRenderFrameManager(
+ RenderFrameHostManager* manager) const {
+ return manager->navigation_request_for_testing();
}
private:
RenderFrameHostManagerTestWebUIControllerFactory factory_;
+ scoped_ptr<FrameLifetimeConsistencyChecker> lifetime_checker_;
};
// Tests that when you navigate from a chrome:// url to another page, and
EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
NavigateActiveAndCommit(kDestUrl);
+ EXPECT_FALSE(contents()->GetPendingMainFrame());
+
// Make a second tab.
scoped_ptr<TestWebContents> contents2(
TestWebContents::Create(browser_context(), NULL));
// Load the two URLs in the second tab. Note that the first navigation creates
- // a RVH that's not pending (since there is no cross-site transition), so
+ // a RFH that's not pending (since there is no cross-site transition), so
// we use the committed one.
contents2->GetController().LoadURL(
kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
- TestRenderViewHost* ntp_rvh2 = static_cast<TestRenderViewHost*>(
- contents2->GetRenderManagerForTesting()->current_host());
+ TestRenderFrameHost* ntp_rfh2 = contents2->GetMainFrame();
EXPECT_FALSE(contents2->cross_navigation_pending());
- ntp_rvh2->SendNavigate(100, kChromeUrl);
+ ntp_rfh2->SendNavigate(100, kChromeUrl);
// The second one is the opposite, creating a cross-site transition and
// requiring a beforeunload ack.
contents2->GetController().LoadURL(
kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
EXPECT_TRUE(contents2->cross_navigation_pending());
- TestRenderViewHost* dest_rvh2 = static_cast<TestRenderViewHost*>(
- contents2->GetRenderManagerForTesting()->pending_render_view_host());
- ASSERT_TRUE(dest_rvh2);
+ TestRenderFrameHost* dest_rfh2 = contents2->GetPendingMainFrame();
+ ASSERT_TRUE(dest_rfh2);
- ntp_rvh2->SendShouldCloseACK(true);
- ntp_rvh2->OnSwappedOut(false);
- dest_rvh2->SendNavigate(101, kDestUrl);
+ ntp_rfh2->GetRenderViewHost()->SendBeforeUnloadACK(true);
+ StartCrossSiteTransition(contents2.get());
+ dest_rfh2->SendNavigate(101, kDestUrl);
- // 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_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance(
- dest_rvh2->GetSiteInstance()));
+ // The two RFH's should be different in every way.
+ EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2->GetProcess());
+ EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
+ dest_rfh2->GetSiteInstance());
+ EXPECT_FALSE(dest_rfh2->GetSiteInstance()->IsRelatedSiteInstance(
+ contents()->GetMainFrame()->GetSiteInstance()));
// Navigate both to the new tab page, and verify that they share a
// RenderProcessHost (not a SiteInstance).
NavigateActiveAndCommit(kChromeUrl);
+ EXPECT_FALSE(contents()->GetPendingMainFrame());
contents2->GetController().LoadURL(
kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
- dest_rvh2->SendShouldCloseACK(true);
- dest_rvh2->OnSwappedOut(false);
- static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()->
- pending_render_view_host())->SendNavigate(102, kChromeUrl);
-
- EXPECT_NE(active_rvh()->GetSiteInstance(),
- contents2->GetRenderViewHost()->GetSiteInstance());
- EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(),
- contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess());
+ dest_rfh2->GetRenderViewHost()->SendBeforeUnloadACK(true);
+ StartCrossSiteTransition(contents2.get());
+ contents2->GetPendingMainFrame()->SendNavigate(102, kChromeUrl);
+
+ EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
+ contents2->GetMainFrame()->GetSiteInstance());
+ EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
+ contents2->GetMainFrame()->GetSiteInstance()->GetProcess());
}
// Ensure that the browser ignores most IPC messages that arrive from a
TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
const GURL kChromeURL("chrome://foo");
const GURL kDestUrl("http://www.google.com/");
+ std::vector<FaviconURL> icons;
// Navigate our first tab to a chrome url and then to the destination.
NavigateActiveAndCommit(kChromeURL);
- TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
- contents()->GetRenderManagerForTesting()->current_host());
+ TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
- // Send an update title message and make sure it works.
+ // Send an update favicon message and make sure it works.
const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title");
- blink::WebTextDirection direction = blink::WebTextDirectionLeftToRight;
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(
- ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
- EXPECT_EQ(ntp_title, contents()->GetTitle());
-
- // Navigate to a cross-site URL.
- contents()->GetController().LoadURL(
- kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string());
- EXPECT_TRUE(contents()->cross_navigation_pending());
- TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
- contents()->GetRenderManagerForTesting()->pending_render_view_host());
- ASSERT_TRUE(dest_rvh);
- EXPECT_NE(ntp_rvh, dest_rvh);
-
- // Create one more view in the same SiteInstance where dest_rvh2
+ {
+ PluginFaviconMessageObserver observer(contents());
+ EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->OnMessageReceived(
+ ViewHostMsg_UpdateFaviconURL(
+ ntp_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
+ EXPECT_TRUE(observer.favicon_received());
+ }
+ // Create one more view in the same SiteInstance where ntp_rfh
// exists so that it doesn't get deleted on navigation to another
// site.
- static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())->
+ static_cast<SiteInstanceImpl*>(ntp_rfh->GetSiteInstance())->
increment_active_view_count();
- // BeforeUnload finishes.
- ntp_rvh->SendShouldCloseACK(true);
- // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
- dest_rvh->SendNavigate(101, kDestUrl);
+ // Navigate to a cross-site URL.
+ NavigateActiveAndCommit(kDestUrl);
+ TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
+ ASSERT_TRUE(dest_rfh);
+ EXPECT_NE(ntp_rfh, dest_rfh);
- // The new RVH should be able to update its title.
+ // The new RVH should be able to update its favicon.
const base::string16 dest_title = base::ASCIIToUTF16("Google");
- EXPECT_TRUE(dest_rvh->OnMessageReceived(
- ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 101, dest_title,
- direction)));
- EXPECT_EQ(dest_title, contents()->GetTitle());
-
- // The old renderer, being slow, now updates the title. It should be filtered
- // out and not take effect.
- EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, ntp_rvh->rvh_state());
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(
- ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
- EXPECT_EQ(dest_title, contents()->GetTitle());
+ {
+ PluginFaviconMessageObserver observer(contents());
+ EXPECT_TRUE(
+ dest_rfh->GetRenderViewHost()->OnMessageReceived(
+ ViewHostMsg_UpdateFaviconURL(
+ dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
+ EXPECT_TRUE(observer.favicon_received());
+ }
+
+ // The old renderer, being slow, now updates the favicon. It should be
+ // filtered out and not take effect.
+ EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->IsSwappedOut());
+ {
+ PluginFaviconMessageObserver observer(contents());
+ EXPECT_TRUE(
+ ntp_rfh->GetRenderViewHost()->OnMessageReceived(
+ ViewHostMsg_UpdateFaviconURL(
+ dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
+ EXPECT_FALSE(observer.favicon_received());
+ }
+
+ // The same logic should apply to RenderFrameHosts as well and routing through
+ // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
+ // if the IPC message is allowed through or not.
+ {
+ PluginFaviconMessageObserver observer(contents());
+ EXPECT_TRUE(ntp_rfh->OnMessageReceived(
+ FrameHostMsg_PluginCrashed(
+ ntp_rfh->GetRoutingID(), base::FilePath(), 0)));
+ EXPECT_FALSE(observer.plugin_crashed());
+ }
// We cannot filter out synchronous IPC messages, because the renderer would
// be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
// that can run easily within a unit test, and that needs to receive a reply
// without showing an actual dialog.
MockRenderProcessHost* ntp_process_host =
- static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess());
+ static_cast<MockRenderProcessHost*>(ntp_rfh->GetProcess());
ntp_process_host->sink().ClearMessages();
const base::string16 msg = base::ASCIIToUTF16("Message");
bool result = false;
base::string16 unused;
- ViewHostMsg_RunBeforeUnloadConfirm before_unload_msg(
- rvh()->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
+ FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
+ ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
// Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
before_unload_msg.EnableMessagePumping();
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(before_unload_msg));
+ EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
// Also test RunJavaScriptMessage.
ntp_process_host->sink().ClearMessages();
- ViewHostMsg_RunJavaScriptMessage js_msg(
- rvh()->GetRoutingID(), msg, msg, kChromeURL,
+ FrameHostMsg_RunJavaScriptMessage js_msg(
+ ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
js_msg.EnableMessagePumping();
- EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg));
+ EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
}
process_host->sink().ClearMessages();
cc::CompositorFrame frame;
- ViewHostMsg_SwapCompositorFrame msg(rvh()->GetRoutingID(), 0, frame);
+ ViewHostMsg_SwapCompositorFrame msg(
+ rvh()->GetRoutingID(), 0, frame, std::vector<IPC::Message>());
EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
}
-TEST_F(RenderFrameHostManagerTest, WhiteListDidActivateAcceleratedCompositing) {
- TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
-
- MockRenderProcessHost* process_host =
- static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
- process_host->sink().ClearMessages();
- ViewHostMsg_DidActivateAcceleratedCompositing msg(
- rvh()->GetRoutingID(), true);
- EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
- EXPECT_TRUE(swapped_out_rvh->is_accelerated_compositing_active());
-}
-
// Test if RenderViewHost::GetRenderWidgetHosts() only returns active
// widgets.
TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
// Navigate our first tab to a chrome url and then to the destination.
NavigateActiveAndCommit(kChromeURL);
- TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
- contents()->GetRenderManagerForTesting()->current_host());
+ TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
// Create one more tab and navigate to kUrl1. web_contents is not
// wrapped as scoped_ptr since it intentionally deleted by destroyer
// below as part of this test.
TestWebContents* web_contents =
- TestWebContents::Create(browser_context(), ntp_rvh->GetSiteInstance());
+ TestWebContents::Create(browser_context(), ntp_rfh->GetSiteInstance());
web_contents->NavigateAndCommit(kUrl1);
- RenderViewHostDestroyer destroyer(ntp_rvh, web_contents);
+ RenderViewHostDestroyer destroyer(ntp_rfh->GetRenderViewHost(),
+ web_contents);
// This causes the first tab to navigate to kUrl2, which destroys
- // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When
- // ntp_rvh is destroyed, it also destroys the RVHs in web_contents
+ // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
+ // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
// too. This can test whether
// SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
// touch any object freed in this way or not while iterating through
// Navigate.
controller().LoadURL(
kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
- // Simulate response from RenderView for FirePageBeforeUnload.
+ // Simulate response from RenderFrame for DispatchBeforeUnload.
base::TimeTicks now = base::TimeTicks::Now();
- test_rvh()->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(
- rvh()->GetRoutingID(), true, now, now));
- ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created.
- RenderViewHost* last_rvh = pending_rvh();
- int32 new_id = contents()->GetMaxPageIDForSiteInstance(
- active_rvh()->GetSiteInstance()) + 1;
- pending_test_rvh()->SendNavigate(new_id, kUrl);
+ contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(
+ contents()->GetMainFrame()->GetRoutingID(), true, now, now));
+ ASSERT_TRUE(contents()->GetPendingMainFrame())
+ << "Expected new pending RenderFrameHost to be created.";
+ RenderFrameHost* last_rfh = contents()->GetPendingMainFrame();
+ int32 new_id =
+ contents()->GetMaxPageIDForSiteInstance(last_rfh->GetSiteInstance()) + 1;
+ contents()->GetPendingMainFrame()->SendNavigate(new_id, kUrl);
EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
ASSERT_TRUE(controller().GetLastCommittedEntry());
EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL());
controller().LoadURL(
kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
// The same RenderViewHost should be reused.
- EXPECT_FALSE(pending_rvh());
- EXPECT_TRUE(last_rvh == rvh());
- test_rvh()->SendNavigate(new_id, kUrl); // The same page_id returned.
+ EXPECT_FALSE(contents()->GetPendingMainFrame());
+ EXPECT_TRUE(last_rfh == contents()->GetMainFrame());
+ // Navigate using the returned page_id.
+ contents()->GetMainFrame()->SendNavigate(new_id, kUrl);
EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
EXPECT_FALSE(controller().GetPendingEntry());
// New message should be sent out to make sure to enter view-source mode.
scoped_ptr<TestWebContents> web_contents(
TestWebContents::Create(browser_context(), instance));
- FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
- web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
- RenderFrameHostManager* manager = tree.root()->render_manager();
-
- manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
- MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
RenderViewHostImpl* rvh = manager->current_host();
RenderFrameHostImpl* rfh = manager->current_frame_host();
ASSERT_TRUE(rvh);
notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
Source<WebContents>(web_contents.get()));
- // Create.
- FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
- web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
- RenderFrameHostManager* manager = tree.root()->render_manager();
-
- manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
- MSG_ROUTING_NONE);
-
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
RenderFrameHostImpl* host;
// 1) The first navigation. --------------------------
EXPECT_FALSE(manager->pending_frame_host());
// Commit.
- manager->DidNavigateMainFrame(host->render_view_host());
+ manager->DidNavigateFrame(host);
// Commit to SiteInstance should be delayed until RenderView commit.
EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_FALSE(manager->pending_frame_host());
// Commit.
- manager->DidNavigateMainFrame(host->render_view_host());
+ manager->DidNavigateFrame(host);
EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
notifications.Reset();
// Commit.
- manager->DidNavigateMainFrame(manager->pending_render_view_host());
+ manager->DidNavigateFrame(manager->pending_frame_host());
EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
Source<WebContents>(web_contents.get()));
- // Create.
- FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
- web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
- RenderFrameHostManager* manager = tree.root()->render_manager();
-
- manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
- MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
// 1) The first navigation. --------------------------
const GURL kUrl1("http://www.google.com/");
notifications.Reset();
// Commit.
- manager->DidNavigateMainFrame(host->render_view_host());
+ manager->DidNavigateFrame(host);
// Commit to SiteInstance should be delayed until RenderView commit.
EXPECT_TRUE(host == manager->current_frame_host());
// Check that the navigation is still suspended because the old RVH
// is not swapped out, yet.
- EXPECT_TRUE(host2->render_view_host()->are_navigations_suspended());
+ EXPECT_TRUE(host2->are_navigations_suspended());
MockRenderProcessHost* test_process_host2 =
static_cast<MockRenderProcessHost*>(host2->GetProcess());
test_process_host2->sink().ClearMessages();
FrameMsg_Navigate::ID));
// Allow closing the current Render View (precondition for swapping out
- // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by
- // FirePageBeforeUnload.
+ // the RVH): Simulate response from RenderFrame for FrameMsg_BeforeUnload sent
+ // by DispatchBeforeUnload.
TestRenderViewHost* test_host =
static_cast<TestRenderViewHost*>(host->render_view_host());
MockRenderProcessHost* test_process_host =
static_cast<MockRenderProcessHost*>(test_host->GetProcess());
EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_ShouldClose::ID));
- test_host->SendShouldCloseACK(true);
+ FrameMsg_BeforeUnload::ID));
+ test_host->SendBeforeUnloadACK(true);
// CrossSiteResourceHandler::StartCrossSiteTransition triggers a
// call of RenderFrameHostManager::SwapOutOldPage before
- // RenderFrameHostManager::DidNavigateMainFrame is called.
+ // RenderFrameHostManager::DidNavigateFrame is called.
// The RVH is swapped out after receiving the unload ack.
manager->SwapOutOldPage();
EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_SwapOut::ID));
+ FrameMsg_SwapOut::ID));
test_host->OnSwappedOut(false);
EXPECT_EQ(host, manager->current_frame_host());
EXPECT_NE(host3, host);
EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id);
- // Navigations in the new RVH should be suspended.
- EXPECT_TRUE(static_cast<RenderViewHostImpl*>(
- host3->render_view_host())->are_navigations_suspended());
+ // Navigations in the new RFH should be suspended.
+ EXPECT_TRUE(host3->are_navigations_suspended());
EXPECT_EQ(host, manager->current_frame_host());
EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
// Simulate a response to the second beforeunload request.
EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_ShouldClose::ID));
- test_host->SendShouldCloseACK(true);
+ FrameMsg_BeforeUnload::ID));
+ test_host->SendBeforeUnloadACK(true);
// CrossSiteResourceHandler::StartCrossSiteTransition triggers a
// call of RenderFrameHostManager::SwapOutOldPage before
- // RenderFrameHostManager::DidNavigateMainFrame is called.
+ // RenderFrameHostManager::DidNavigateFrame is called. Since the previous
+ // navigation has already caused the renderer to start swapping out, there
+ // will be no more SwapOut messages being sent.
manager->SwapOutOldPage();
- EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
- ViewMsg_SwapOut::ID));
+ EXPECT_FALSE(test_process_host->sink().GetUniqueMessageMatching(
+ FrameMsg_SwapOut::ID));
test_host->OnSwappedOut(false);
// Commit.
- manager->DidNavigateMainFrame(host3->render_view_host());
+ manager->DidNavigateFrame(host3);
EXPECT_TRUE(host3 == manager->current_frame_host());
ASSERT_TRUE(host3);
EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())->
notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
}
+// Test that navigation is not blocked when we make new navigation before
+// previous one has been committed. This is also a regression test for
+// http://crbug.com/104600.
+TEST_F(RenderFrameHostManagerTest, NewCrossNavigationBetweenSwapOutAndCommit) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+ const GURL kUrl3("http://www.youtube.com/");
+
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+
+ // Keep active_view_count nonzero so that no swapped out views in
+ // this SiteInstance get forcefully deleted.
+ static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
+ increment_active_view_count();
+
+ // Navigate but don't commit.
+ contents()->GetController().LoadURL(
+ kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ EXPECT_TRUE(rvh1->is_waiting_for_beforeunload_ack());
+ contents()->ProceedWithCrossSiteNavigation();
+ EXPECT_FALSE(rvh1->is_waiting_for_beforeunload_ack());
+ StartCrossSiteTransition(contents());
+ EXPECT_TRUE(rvh1->IsWaitingForUnloadACK());
+
+ rvh1->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
+
+ TestRenderViewHost* rvh2 = pending_test_rvh();
+ EXPECT_TRUE(rvh2);
+ static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
+ increment_active_view_count();
+
+ contents()->GetController().LoadURL(
+ kUrl3, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ // Pending rvh2 is already deleted.
+ contents()->ProceedWithCrossSiteNavigation();
+
+ TestRenderFrameHost* rfh3 = contents()->GetPendingMainFrame();
+ EXPECT_TRUE(rfh3);
+ // Navigation should be already unblocked by rvh1.
+ EXPECT_FALSE(rfh3->are_navigations_suspended());
+}
+
// Tests WebUI creation.
TEST_F(RenderFrameHostManagerTest, WebUI) {
set_should_create_webui(true);
scoped_ptr<TestWebContents> web_contents(
TestWebContents::Create(browser_context(), instance));
- FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
- web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
- RenderFrameHostManager* manager = tree.root()->render_manager();
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
- manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
- MSG_ROUTING_NONE);
EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
const GURL kUrl("chrome://foo");
EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
// The Web UI is committed immediately because the RenderViewHost has not been
- // used yet. UpdateRendererStateForNavigate() took the short cut path.
+ // used yet. UpdateStateForNavigate() took the short cut path.
EXPECT_FALSE(manager->pending_web_ui());
EXPECT_TRUE(manager->web_ui());
// Commit.
- manager->DidNavigateMainFrame(host->render_view_host());
+ manager->DidNavigateFrame(host);
EXPECT_TRUE(
host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
}
// Create a blank tab.
scoped_ptr<TestWebContents> web_contents1(
TestWebContents::Create(browser_context(), blank_instance));
- FrameTree tree1(web_contents1->GetFrameTree()->root()->navigator(),
- web_contents1.get(), web_contents1.get(),
- web_contents1.get(), web_contents1.get());
- RenderFrameHostManager* manager1 = tree1.root()->render_manager();
- manager1->Init(
- browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager1 =
+ web_contents1->GetRenderManagerForTesting();
// Test the case that new RVH is considered live.
- manager1->current_host()->CreateRenderView(base::string16(), -1, -1);
+ manager1->current_host()->CreateRenderView(
+ base::string16(), -1, MSG_ROUTING_NONE, -1, false);
// Navigate to a WebUI page.
const GURL kUrl1("chrome://foo");
host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
// Commit and ensure we still have bindings.
- manager1->DidNavigateMainFrame(host1->render_view_host());
+ manager1->DidNavigateFrame(host1);
SiteInstance* webui_instance = host1->GetSiteInstance();
EXPECT_EQ(host1, manager1->current_frame_host());
EXPECT_TRUE(
// Now simulate clicking a link that opens in a new tab.
scoped_ptr<TestWebContents> web_contents2(
TestWebContents::Create(browser_context(), webui_instance));
- FrameTree tree2(web_contents2->GetFrameTree()->root()->navigator(),
- web_contents2.get(), web_contents2.get(),
- web_contents2.get(), web_contents2.get());
- RenderFrameHostManager* manager2 = tree2.root()->render_manager();
- manager2->Init(
- browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager2 =
+ web_contents2->GetRenderManagerForTesting();
// Make sure the new RVH is considered live. This is usually done in
// RenderWidgetHost::Init when opening a new tab from a link.
- manager2->current_host()->CreateRenderView(base::string16(), -1, -1);
+ manager2->current_host()->CreateRenderView(
+ base::string16(), -1, MSG_ROUTING_NONE, -1, false);
const GURL kUrl2("chrome://foo/bar");
NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
EXPECT_TRUE(
host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
- manager2->DidNavigateMainFrame(host2->render_view_host());
+ manager2->DidNavigateFrame(host2);
}
// Tests that we don't end up in an inconsistent state if a page does a back and
const GURL kUrl2("http://www.evil-site.com/");
// Navigate to a safe site, then an evil site.
- // This will switch RenderViewHosts. We cannot assert that the first and
- // second RVHs are different, though, because the first one may be promptly
+ // This will switch RenderFrameHosts. We cannot assert that the first and
+ // second RFHs are different, though, because the first one may be promptly
// deleted.
contents()->NavigateAndCommit(kUrl1);
contents()->NavigateAndCommit(kUrl2);
- RenderViewHost* evil_rvh = contents()->GetRenderViewHost();
+ TestRenderFrameHost* evil_rfh = contents()->GetMainFrame();
// Now let's simulate the evil page calling history.back().
contents()->OnGoToEntryAtOffset(-1);
- // We should have a new pending RVH.
- // Note that in this case, the navigation has not committed, so evil_rvh will
+ // We should have a new pending RFH.
+ // Note that in this case, the navigation has not committed, so evil_rfh will
// not be deleted yet.
- EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()->
- pending_render_view_host());
+ EXPECT_NE(evil_rfh, contents()->GetPendingMainFrame());
+ EXPECT_NE(evil_rfh->GetRenderViewHost(),
+ contents()->GetPendingMainFrame()->GetRenderViewHost());
- // Before that RVH has committed, the evil page reloads itself.
+ // Before that RFH has committed, the evil page reloads itself.
FrameHostMsg_DidCommitProvisionalLoad_Params params;
params.page_id = 1;
params.url = kUrl2;
params.is_post = false;
params.page_state = PageState::CreateFromURL(kUrl2);
- RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(evil_rvh);
- RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(
- rvh->GetProcess()->GetID(), rvh->main_frame_routing_id());
- contents()->GetFrameTree()->root()->navigator()->DidNavigate(rfh, params);
+ contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh,
+ params);
- // That should have cancelled the pending RVH, and the evil RVH should be the
+ // That should have cancelled the pending RFH, and the evil RFH should be the
// current one.
EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
pending_render_view_host() == NULL);
- EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host());
+ EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
+ NULL);
+ EXPECT_EQ(evil_rfh,
+ contents()->GetRenderManagerForTesting()->current_frame_host());
+ EXPECT_EQ(evil_rfh->GetRenderViewHost(),
+ contents()->GetRenderManagerForTesting()->current_host());
// Also we should not have a pending navigation entry.
EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack());
contents()->ProceedWithCrossSiteNavigation();
EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack());
- rvh2->SwapOut();
+ StartCrossSiteTransition(contents());
EXPECT_TRUE(rvh2->IsWaitingForUnloadACK());
// The back navigation commits.
// We should be able to navigate forward.
contents()->GetController().GoForward();
contents()->ProceedWithCrossSiteNavigation();
+ StartCrossSiteTransition(contents());
const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL());
EXPECT_EQ(rvh2, rvh());
contents()->SetOpener(opener1.get());
// Make sure the new opener RVH is considered live.
- opener1_manager->current_host()->CreateRenderView(base::string16(), -1, -1);
+ opener1_manager->current_host()->CreateRenderView(
+ base::string16(), -1, MSG_ROUTING_NONE, -1, false);
// Use a cross-process navigation in the opener to swap out the old RVH.
EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
scoped_ptr<TestWebContents> web_contents(
TestWebContents::Create(browser_context(), instance));
- // Create.
- FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
- web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
- RenderFrameHostManager* manager = tree.root()->render_manager();
-
- manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
- MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
RenderFrameHostImpl* host;
EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
// Commit.
- manager->DidNavigateMainFrame(host->render_view_host());
+ manager->DidNavigateFrame(host);
// Commit to SiteInstance should be delayed until RenderView commit.
EXPECT_EQ(host, manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_FALSE(manager->pending_frame_host());
// Commit.
- manager->DidNavigateMainFrame(host->render_view_host());
+ manager->DidNavigateFrame(host);
EXPECT_EQ(host, manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()),
notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
Source<WebContents>(web_contents.get()));
- // Create.
- FrameTree tree(web_contents->GetFrameTree()->root()->navigator(),
- web_contents.get(), web_contents.get(),
- web_contents.get(), web_contents.get());
- RenderFrameHostManager* manager = tree.root()->render_manager();
-
- manager->Init(browser_context(), instance, MSG_ROUTING_NONE,
- MSG_ROUTING_NONE);
+ RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
// 1) The first navigation. --------------------------
const GURL kUrl1("http://www.google.com/");
notifications.Reset();
// Commit.
- manager->DidNavigateMainFrame(host->render_view_host());
+ manager->DidNavigateFrame(host);
// Commit to SiteInstance should be delayed until RenderFrame commits.
EXPECT_EQ(host, manager->current_frame_host());
// 3) Close the tab. -------------------------
notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
Source<RenderWidgetHost>(host2->render_view_host()));
- manager->ShouldClosePage(false, true, base::TimeTicks());
+ manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
EXPECT_TRUE(
notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
EXPECT_EQ(host, manager->current_frame_host());
}
-// This checks that the given RVH has been properly deleted.
-class RenderViewHostDestructionObserver : public WebContentsObserver {
- public:
- RenderViewHostDestructionObserver(RenderViewHost* render_view_host)
- : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
- render_view_host_(render_view_host),
- rvh_deleted_(false) {}
-
- bool rvh_deleted() { return rvh_deleted_; }
-
- virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
- if (render_view_host == render_view_host_)
- rvh_deleted_ = true;
- }
-
- private:
- RenderViewHost* render_view_host_;
- bool rvh_deleted_;
-
- DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
-};
-
// Tests that the RenderViewHost is properly deleted when the SwapOutACK is
// received before the new page commits.
TEST_F(RenderFrameHostManagerTest,
// Navigate to the first page.
contents()->NavigateAndCommit(kUrl1);
- TestRenderViewHost* rvh1 = test_rvh();
- RenderViewHostDestructionObserver destruction_observer(rvh1);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+ TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh1->GetRenderViewHost()->rvh_state());
// Navigate to new site, simulating onbeforeunload approval.
controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
base::TimeTicks now = base::TimeTicks::Now();
- rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ rfh1->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_TRUE(contents()->cross_navigation_pending());
- TestRenderViewHost* rvh2 =
- static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+ TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
- // Simulate rvh2's response, which leads to an unload request being sent to
- // rvh1.
+ // Simulate rfh2's response, which leads to an unload request being sent to
+ // rfh1.
std::vector<GURL> url_chain;
url_chain.push_back(GURL());
contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
- rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
- url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false);
+ rfh2,
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
EXPECT_TRUE(contents()->cross_navigation_pending());
EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
- rvh1->rvh_state());
+ rfh1->GetRenderViewHost()->rvh_state());
// Simulate the swap out ack.
- rvh1->OnSwappedOut(false);
- EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
+ rfh1->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT,
+ rfh1->GetRenderViewHost()->rvh_state());
// The new page commits.
- contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
EXPECT_FALSE(contents()->cross_navigation_pending());
- EXPECT_EQ(rvh2, rvh());
- EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+ EXPECT_EQ(rfh2, contents()->GetMainFrame());
+ EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh2->GetRenderViewHost()->rvh_state());
- // rvh1 should have been deleted.
- EXPECT_TRUE(destruction_observer.rvh_deleted());
- rvh1 = NULL;
+ // rfh1's rvh should have been deleted.
+ EXPECT_TRUE(rvh_deleted_observer.deleted());
+ rfh1 = NULL;
}
// Tests that the RenderViewHost is properly swapped out when the SwapOutACK is
// Navigate to the first page.
contents()->NavigateAndCommit(kUrl1);
- TestRenderViewHost* rvh1 = test_rvh();
- RenderViewHostDestructionObserver destruction_observer(rvh1);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+ TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh1->GetRenderViewHost()->rvh_state());
- // Increment the number of active views in SiteInstanceImpl so that rvh2 is
+ // Increment the number of active views in SiteInstanceImpl so that rfh2 is
// not deleted on swap out.
static_cast<SiteInstanceImpl*>(
- rvh1->GetSiteInstance())->increment_active_view_count();
+ rfh1->GetSiteInstance())->increment_active_view_count();
// Navigate to new site, simulating onbeforeunload approval.
controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
base::TimeTicks now = base::TimeTicks::Now();
- rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ contents()->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_TRUE(contents()->cross_navigation_pending());
- TestRenderViewHost* rvh2 =
- static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+ TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
- // Simulate rvh2's response, which leads to an unload request being sent to
- // rvh1.
+ // Simulate rfh2's response, which leads to an unload request being sent to
+ // rfh1.
std::vector<GURL> url_chain;
url_chain.push_back(GURL());
contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
- rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
- url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false);
+ rfh2,
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
EXPECT_TRUE(contents()->cross_navigation_pending());
EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
- rvh1->rvh_state());
+ rfh1->GetRenderViewHost()->rvh_state());
// Simulate the swap out ack.
- rvh1->OnSwappedOut(false);
- EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state());
+ rfh1->OnSwappedOut(false);
+ EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT,
+ rfh1->GetRenderViewHost()->rvh_state());
// The new page commits.
- contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
EXPECT_FALSE(contents()->cross_navigation_pending());
- EXPECT_EQ(rvh2, rvh());
- EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
+ EXPECT_EQ(rfh2, contents()->GetMainFrame());
+ EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh2->GetRenderViewHost()->rvh_state());
- // rvh1 should be swapped out.
- EXPECT_FALSE(destruction_observer.rvh_deleted());
- EXPECT_TRUE(rvh1->IsSwappedOut());
+ // rfh1 should be swapped out.
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut());
}
// Tests that the RenderViewHost is properly deleted when the new
// Navigate to the first page.
contents()->NavigateAndCommit(kUrl1);
- TestRenderViewHost* rvh1 = test_rvh();
- RenderViewHostDestructionObserver destruction_observer(rvh1);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+ TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh1->GetRenderViewHost()->rvh_state());
// Navigate to new site, simulating onbeforeunload approval.
controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
base::TimeTicks now = base::TimeTicks::Now();
- rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ contents()->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_TRUE(contents()->cross_navigation_pending());
- TestRenderViewHost* rvh2 =
- static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+ TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
- // Simulate rvh2's response, which leads to an unload request being sent to
- // rvh1.
+ // Simulate rfh2's response, which leads to an unload request being sent to
+ // rfh1.
std::vector<GURL> url_chain;
url_chain.push_back(GURL());
contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
- rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
- url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false);
+ rfh2,
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
EXPECT_TRUE(contents()->cross_navigation_pending());
EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
- rvh1->rvh_state());
+ rfh1->GetRenderViewHost()->rvh_state());
// The new page commits.
- contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
EXPECT_FALSE(contents()->cross_navigation_pending());
- EXPECT_EQ(rvh2, rvh());
- EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
- EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state());
+ EXPECT_EQ(rfh2, contents()->GetMainFrame());
+ EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh2->GetRenderViewHost()->rvh_state());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN,
+ rfh1->GetRenderViewHost()->rvh_state());
// Simulate the swap out ack.
- rvh1->OnSwappedOut(false);
+ rfh1->OnSwappedOut(false);
- // rvh1 should have been deleted.
- EXPECT_TRUE(destruction_observer.rvh_deleted());
- rvh1 = NULL;
+ // rfh1 should have been deleted.
+ EXPECT_TRUE(rvh_deleted_observer.deleted());
+ rfh1 = NULL;
}
// Tests that the RenderViewHost is properly swapped out when the new page
// Navigate to the first page.
contents()->NavigateAndCommit(kUrl1);
- TestRenderViewHost* rvh1 = test_rvh();
- RenderViewHostDestructionObserver destruction_observer(rvh1);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+ TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh1->GetRenderViewHost()->rvh_state());
- // Increment the number of active views in SiteInstanceImpl so that rvh1 is
+ // Increment the number of active views in SiteInstanceImpl so that rfh1 is
// not deleted on swap out.
static_cast<SiteInstanceImpl*>(
- rvh1->GetSiteInstance())->increment_active_view_count();
+ rfh1->GetSiteInstance())->increment_active_view_count();
// Navigate to new site, simulating onbeforeunload approval.
controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
base::TimeTicks now = base::TimeTicks::Now();
- rvh1->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
+ contents()->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
EXPECT_TRUE(contents()->cross_navigation_pending());
- TestRenderViewHost* rvh2 =
- static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
+ TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
- // Simulate rvh2's response, which leads to an unload request being sent to
- // rvh1.
+ // Simulate rfh2's response, which leads to an unload request being sent to
+ // rfh1.
std::vector<GURL> url_chain;
url_chain.push_back(GURL());
contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
- rvh2, GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
- url_chain, Referrer(), PAGE_TRANSITION_TYPED, 1, false);
+ rfh2,
+ GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
+ url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
EXPECT_TRUE(contents()->cross_navigation_pending());
EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK,
- rvh1->rvh_state());
+ rfh1->GetRenderViewHost()->rvh_state());
// The new page commits.
- contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
EXPECT_FALSE(contents()->cross_navigation_pending());
- EXPECT_EQ(rvh2, rvh());
- EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
- EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
- EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
+ EXPECT_EQ(rfh2, contents()->GetMainFrame());
+ EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh2->GetRenderViewHost()->rvh_state());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT,
+ rfh1->GetRenderViewHost()->rvh_state());
// Simulate the swap out ack.
- rvh1->OnSwappedOut(false);
+ rfh1->OnSwappedOut(false);
- // rvh1 should be swapped out.
- EXPECT_FALSE(destruction_observer.rvh_deleted());
- EXPECT_TRUE(rvh1->IsSwappedOut());
+ // rfh1 should be swapped out.
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut());
+}
+
+// Test that the RenderViewHost is properly swapped out if a navigation in the
+// new renderer commits before sending the SwapOut message to the old renderer.
+// This simulates a cross-site navigation to a synchronously committing URL
+// (e.g., a data URL) and ensures it works properly.
+TEST_F(RenderFrameHostManagerTest,
+ CommitNewNavigationBeforeSendingSwapOut) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
+ RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh1->GetRenderViewHost()->rvh_state());
+
+ // Increment the number of active views in SiteInstanceImpl so that rfh1 is
+ // not deleted on swap out.
+ static_cast<SiteInstanceImpl*>(
+ rfh1->GetSiteInstance())->increment_active_view_count();
+
+ // Navigate to new site, simulating onbeforeunload approval.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ base::TimeTicks now = base::TimeTicks::Now();
+ rfh1->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
+
+ // The new page commits.
+ contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(rfh2, contents()->GetMainFrame());
+ EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
+ rfh2->GetRenderViewHost()->rvh_state());
+ EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT,
+ rfh1->GetRenderViewHost()->rvh_state());
+
+ // Simulate the swap out ack.
+ rfh1->OnSwappedOut(false);
+
+ // rfh1 should be swapped out.
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut());
+}
+
+// Test that a RenderFrameHost is properly deleted or swapped out when a
+// cross-site navigation is cancelled.
+TEST_F(RenderFrameHostManagerTest,
+ CancelPendingProperlyDeletesOrSwaps) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+ RenderFrameHostImpl* pending_rfh = NULL;
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderViewHost* rvh1 = test_rvh();
+ EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
+
+ // Navigate to a new site, starting a cross-site navigation.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ {
+ pending_rfh = contents()->GetFrameTree()->root()->render_manager()
+ ->pending_frame_host();
+ RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
+
+ // Cancel the navigation by simulating a declined beforeunload dialog.
+ contents()->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+
+ // Since the pending RFH is the only one for the new SiteInstance, it should
+ // be deleted.
+ EXPECT_TRUE(rvh_deleted_observer.deleted());
+ }
+
+ // Start another cross-site navigation.
+ controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string());
+ {
+ pending_rfh = contents()->GetFrameTree()->root()->render_manager()
+ ->pending_frame_host();
+ RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
+
+ // Increment the number of active views in the new SiteInstance, which will
+ // cause the pending RFH to be swapped out instead of deleted.
+ static_cast<SiteInstanceImpl*>(
+ pending_rfh->GetSiteInstance())->increment_active_view_count();
+
+ contents()->GetMainFrame()->OnMessageReceived(
+ FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_FALSE(rvh_deleted_observer.deleted());
+ }
+}
+
+// Browser-side navigation: Test that a proper NavigationRequest is created by
+// BeginNavigation.
+TEST_F(RenderFrameHostManagerTest, BrowserSideNavigationBeginNavigation) {
+ const GURL kUrl1("http://www.google.com/");
+ const GURL kUrl2("http://www.chromium.org/");
+ const GURL kUrl3("http://www.gmail.com/");
+
+ // Navigate to the first page.
+ contents()->NavigateAndCommit(kUrl1);
+
+ // Add a subframe.
+ TestRenderFrameHost* subframe_rfh = static_cast<TestRenderFrameHost*>(
+ contents()->GetFrameTree()->AddFrame(
+ contents()->GetFrameTree()->root(), 14, "Child"));
+
+ // Simulate a BeginNavigation IPC on the subframe.
+ subframe_rfh->SendBeginNavigationWithURL(kUrl2);
+ NavigationRequest* subframe_request =
+ NavigationRequestForRenderFrameManager(
+ subframe_rfh->frame_tree_node()->render_manager());
+ ASSERT_TRUE(subframe_request);
+ EXPECT_EQ(kUrl2, subframe_request->info_for_testing().navigation_params.url);
+ // First party for cookies url should be that of the main frame.
+ EXPECT_EQ(
+ kUrl1, subframe_request->info_for_testing().first_party_for_cookies);
+ EXPECT_FALSE(subframe_request->info_for_testing().is_main_frame);
+ EXPECT_TRUE(subframe_request->info_for_testing().parent_is_main_frame);
+
+ // Simulate a BeginNavigation IPC on the main frame.
+ contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl3);
+ NavigationRequest* main_request = NavigationRequestForRenderFrameManager(
+ contents()->GetMainFrame()->frame_tree_node()->render_manager());
+ ASSERT_TRUE(main_request);
+ EXPECT_EQ(kUrl3, main_request->info_for_testing().navigation_params.url);
+ EXPECT_EQ(kUrl3, main_request->info_for_testing().first_party_for_cookies);
+ EXPECT_TRUE(main_request->info_for_testing().is_main_frame);
+ EXPECT_FALSE(main_request->info_for_testing().parent_is_main_frame);
}
} // namespace content