Add IsSwappedOutStateForbidden() to RenderFrameHostManager and RenderFrameProxy.
[chromium-blink-merge.git] / content / browser / frame_host / render_frame_host_manager_unittest.cc
blob182791b7c88cd15d0c7eba37232a85cc73232cf5
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/files/file_path.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/test/histogram_tester.h"
9 #include "base/time/time.h"
10 #include "content/browser/compositor/test/no_transport_image_transport_factory.h"
11 #include "content/browser/frame_host/cross_site_transferring_request.h"
12 #include "content/browser/frame_host/navigation_controller_impl.h"
13 #include "content/browser/frame_host/navigation_entry_impl.h"
14 #include "content/browser/frame_host/navigation_request.h"
15 #include "content/browser/frame_host/navigator.h"
16 #include "content/browser/frame_host/render_frame_host_manager.h"
17 #include "content/browser/frame_host/render_frame_proxy_host.h"
18 #include "content/browser/site_instance_impl.h"
19 #include "content/browser/webui/web_ui_controller_factory_registry.h"
20 #include "content/common/frame_messages.h"
21 #include "content/common/view_messages.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "content/public/browser/render_widget_host_iterator.h"
28 #include "content/public/browser/web_contents_delegate.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/browser/web_ui_controller.h"
31 #include "content/public/common/bindings_policy.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/javascript_message_type.h"
34 #include "content/public/common/url_constants.h"
35 #include "content/public/common/url_utils.h"
36 #include "content/public/test/mock_render_process_host.h"
37 #include "content/public/test/test_notification_tracker.h"
38 #include "content/test/test_content_browser_client.h"
39 #include "content/test/test_content_client.h"
40 #include "content/test/test_render_frame_host.h"
41 #include "content/test/test_render_view_host.h"
42 #include "content/test/test_web_contents.h"
43 #include "net/base/load_flags.h"
44 #include "testing/gtest/include/gtest/gtest.h"
45 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
46 #include "ui/base/page_transition_types.h"
48 namespace content {
49 namespace {
51 class RenderFrameHostManagerTestWebUIControllerFactory
52 : public WebUIControllerFactory {
53 public:
54 RenderFrameHostManagerTestWebUIControllerFactory()
55 : should_create_webui_(false) {
57 ~RenderFrameHostManagerTestWebUIControllerFactory() override {}
59 void set_should_create_webui(bool should_create_webui) {
60 should_create_webui_ = should_create_webui;
63 // WebUIFactory implementation.
64 WebUIController* CreateWebUIControllerForURL(WebUI* web_ui,
65 const GURL& url) const override {
66 if (!(should_create_webui_ && HasWebUIScheme(url)))
67 return NULL;
68 return new WebUIController(web_ui);
71 WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
72 const GURL& url) const override {
73 return WebUI::kNoWebUI;
76 bool UseWebUIForURL(BrowserContext* browser_context,
77 const GURL& url) const override {
78 return HasWebUIScheme(url);
81 bool UseWebUIBindingsForURL(BrowserContext* browser_context,
82 const GURL& url) const override {
83 return HasWebUIScheme(url);
86 private:
87 bool should_create_webui_;
89 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
92 class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
93 public:
94 BeforeUnloadFiredWebContentsDelegate() {}
95 ~BeforeUnloadFiredWebContentsDelegate() override {}
97 void BeforeUnloadFired(WebContents* web_contents,
98 bool proceed,
99 bool* proceed_to_fire_unload) override {
100 *proceed_to_fire_unload = proceed;
103 private:
104 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
107 class CloseWebContentsDelegate : public WebContentsDelegate {
108 public:
109 CloseWebContentsDelegate() : close_called_(false) {}
110 ~CloseWebContentsDelegate() override {}
112 void CloseContents(WebContents* web_contents) override {
113 close_called_ = true;
116 bool is_closed() { return close_called_; }
118 private:
119 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate);
121 bool close_called_;
124 // This observer keeps track of the last deleted RenderViewHost to avoid
125 // accessing it and causing use-after-free condition.
126 class RenderViewHostDeletedObserver : public WebContentsObserver {
127 public:
128 RenderViewHostDeletedObserver(RenderViewHost* rvh)
129 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
130 process_id_(rvh->GetProcess()->GetID()),
131 routing_id_(rvh->GetRoutingID()),
132 deleted_(false) {
135 void RenderViewDeleted(RenderViewHost* render_view_host) override {
136 if (render_view_host->GetProcess()->GetID() == process_id_ &&
137 render_view_host->GetRoutingID() == routing_id_) {
138 deleted_ = true;
142 bool deleted() {
143 return deleted_;
146 private:
147 int process_id_;
148 int routing_id_;
149 bool deleted_;
151 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver);
154 // This observer keeps track of the last created RenderFrameHost to allow tests
155 // to ensure that no RenderFrameHost objects are created when not expected.
156 class RenderFrameHostCreatedObserver : public WebContentsObserver {
157 public:
158 RenderFrameHostCreatedObserver(WebContents* web_contents)
159 : WebContentsObserver(web_contents),
160 created_(false) {
163 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
164 created_ = true;
167 bool created() {
168 return created_;
171 private:
172 bool created_;
174 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver);
177 // This observer keeps track of the last deleted RenderFrameHost to avoid
178 // accessing it and causing use-after-free condition.
179 class RenderFrameHostDeletedObserver : public WebContentsObserver {
180 public:
181 RenderFrameHostDeletedObserver(RenderFrameHost* rfh)
182 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)),
183 process_id_(rfh->GetProcess()->GetID()),
184 routing_id_(rfh->GetRoutingID()),
185 deleted_(false) {
188 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
189 if (render_frame_host->GetProcess()->GetID() == process_id_ &&
190 render_frame_host->GetRoutingID() == routing_id_) {
191 deleted_ = true;
195 bool deleted() {
196 return deleted_;
199 private:
200 int process_id_;
201 int routing_id_;
202 bool deleted_;
204 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver);
207 // This WebContents observer keep track of its RVH change.
208 class RenderViewHostChangedObserver : public WebContentsObserver {
209 public:
210 RenderViewHostChangedObserver(WebContents* web_contents)
211 : WebContentsObserver(web_contents), host_changed_(false) {}
213 // WebContentsObserver.
214 void RenderViewHostChanged(RenderViewHost* old_host,
215 RenderViewHost* new_host) override {
216 host_changed_ = true;
219 bool DidHostChange() {
220 bool host_changed = host_changed_;
221 Reset();
222 return host_changed;
225 void Reset() { host_changed_ = false; }
227 private:
228 bool host_changed_;
229 DISALLOW_COPY_AND_ASSIGN(RenderViewHostChangedObserver);
232 // This observer is used to check whether IPC messages are being filtered for
233 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
234 // update events, which the FilterMessagesWhileSwappedOut test simulates being
235 // sent. The test is successful if the event is not observed.
236 // See http://crbug.com/351815
237 class PluginFaviconMessageObserver : public WebContentsObserver {
238 public:
239 PluginFaviconMessageObserver(WebContents* web_contents)
240 : WebContentsObserver(web_contents),
241 plugin_crashed_(false),
242 favicon_received_(false) { }
244 void PluginCrashed(const base::FilePath& plugin_path,
245 base::ProcessId plugin_pid) override {
246 plugin_crashed_ = true;
249 void DidUpdateFaviconURL(const std::vector<FaviconURL>& candidates) override {
250 favicon_received_ = true;
253 bool plugin_crashed() {
254 return plugin_crashed_;
257 bool favicon_received() {
258 return favicon_received_;
261 private:
262 bool plugin_crashed_;
263 bool favicon_received_;
265 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
268 } // namespace
270 class RenderFrameHostManagerTest : public RenderViewHostImplTestHarness {
271 public:
272 void SetUp() override {
273 RenderViewHostImplTestHarness::SetUp();
274 WebUIControllerFactory::RegisterFactory(&factory_);
275 #if !defined(OS_ANDROID)
276 ImageTransportFactory::InitializeForUnitTests(
277 make_scoped_ptr(new NoTransportImageTransportFactory));
278 #endif
281 void TearDown() override {
282 #if !defined(OS_ANDROID)
283 ImageTransportFactory::Terminate();
284 #endif
285 RenderViewHostImplTestHarness::TearDown();
286 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
289 void set_should_create_webui(bool should_create_webui) {
290 factory_.set_should_create_webui(should_create_webui);
293 void NavigateActiveAndCommit(const GURL& url) {
294 // Note: we navigate the active RenderFrameHost because previous navigations
295 // won't have committed yet, so NavigateAndCommit does the wrong thing
296 // for us.
297 controller().LoadURL(
298 url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
299 int entry_id = controller().GetPendingEntry()->GetUniqueID();
301 // Simulate the BeforeUnload_ACK that is received from the current renderer
302 // for a cross-site navigation.
303 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
304 // main and the pending frame because when we are trying to navigate to a
305 // WebUI from a new tab, a RenderFrameHost is created to display it that is
306 // committed immediately (since it is a new tab). Therefore the main frame
307 // is replaced without a pending frame being created, and we don't get the
308 // right values for the RFH to navigate: we try to use the old one that has
309 // been deleted in the meantime.
310 contents()->GetMainFrame()->PrepareForCommit();
312 TestRenderFrameHost* old_rfh = contents()->GetMainFrame();
313 TestRenderFrameHost* active_rfh = contents()->GetPendingMainFrame()
314 ? contents()->GetPendingMainFrame()
315 : old_rfh;
316 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, old_rfh->rfh_state());
318 // Commit the navigation with a new page ID.
319 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
320 active_rfh->GetSiteInstance());
322 // Use an observer to avoid accessing a deleted renderer later on when the
323 // state is being checked.
324 RenderFrameHostDeletedObserver rfh_observer(old_rfh);
325 RenderViewHostDeletedObserver rvh_observer(old_rfh->GetRenderViewHost());
326 active_rfh->SendNavigate(max_page_id + 1, entry_id, true, url);
328 // Make sure that we start to run the unload handler at the time of commit.
329 bool expecting_rfh_shutdown = false;
330 if (old_rfh != active_rfh && !rfh_observer.deleted()) {
331 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT,
332 old_rfh->rfh_state());
333 if (!old_rfh->GetSiteInstance()->active_frame_count() ||
334 RenderFrameHostManager::IsSwappedOutStateForbidden()) {
335 expecting_rfh_shutdown = true;
336 EXPECT_TRUE(
337 old_rfh->frame_tree_node()->render_manager()->IsPendingDeletion(
338 old_rfh));
342 // Simulate the swap out ACK coming from the pending renderer. This should
343 // either shut down the old RFH or leave it in a swapped out state.
344 if (old_rfh != active_rfh) {
345 old_rfh->OnSwappedOut();
346 if (expecting_rfh_shutdown) {
347 EXPECT_TRUE(rfh_observer.deleted());
348 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
349 EXPECT_TRUE(rvh_observer.deleted());
351 } else {
352 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT,
353 old_rfh->rfh_state());
356 EXPECT_EQ(active_rfh, contents()->GetMainFrame());
357 EXPECT_EQ(NULL, contents()->GetPendingMainFrame());
360 bool ShouldSwapProcesses(RenderFrameHostManager* manager,
361 const NavigationEntryImpl* current_entry,
362 const NavigationEntryImpl* new_entry) const {
363 CHECK(new_entry);
364 BrowserContext* browser_context =
365 manager->delegate_->GetControllerForRenderManager().GetBrowserContext();
366 const GURL& current_effective_url = current_entry ?
367 SiteInstanceImpl::GetEffectiveURL(browser_context,
368 current_entry->GetURL()) :
369 manager->render_frame_host_->GetSiteInstance()->GetSiteURL();
370 bool current_is_view_source_mode = current_entry ?
371 current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode();
372 return manager->ShouldSwapBrowsingInstancesForNavigation(
373 current_effective_url,
374 current_is_view_source_mode,
375 new_entry->site_instance(),
376 SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()),
377 new_entry->IsViewSourceMode());
380 // Creates a test RenderFrameHost that's swapped out.
381 TestRenderFrameHost* CreateSwappedOutRenderFrameHost() {
382 const GURL kChromeURL("chrome://foo");
383 const GURL kDestUrl("http://www.google.com/");
385 // Navigate our first tab to a chrome url and then to the destination.
386 NavigateActiveAndCommit(kChromeURL);
387 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
389 // Navigate to a cross-site URL.
390 contents()->GetController().LoadURL(
391 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
392 int entry_id = contents()->GetController().GetPendingEntry()->GetUniqueID();
393 contents()->GetMainFrame()->PrepareForCommit();
394 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
396 // Manually increase the number of active frames in the
397 // SiteInstance that ntp_rfh belongs to, to prevent it from being
398 // destroyed when it gets swapped out.
399 ntp_rfh->GetSiteInstance()->increment_active_frame_count();
401 TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame();
402 CHECK(dest_rfh);
403 EXPECT_NE(ntp_rfh, dest_rfh);
405 // BeforeUnload finishes.
406 ntp_rfh->SendBeforeUnloadACK(true);
408 dest_rfh->SendNavigate(101, entry_id, true, kDestUrl);
409 ntp_rfh->OnSwappedOut();
411 EXPECT_TRUE(ntp_rfh->is_swapped_out());
412 return ntp_rfh;
415 // Returns the RenderFrameHost that should be used in the navigation to
416 // |entry|.
417 RenderFrameHostImpl* NavigateToEntry(
418 RenderFrameHostManager* manager,
419 const NavigationEntryImpl& entry) {
420 // Tests currently only navigate using main frame FrameNavigationEntries.
421 FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get();
422 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
423 switches::kEnableBrowserSideNavigation)) {
424 scoped_ptr<NavigationRequest> navigation_request =
425 NavigationRequest::CreateBrowserInitiated(
426 manager->frame_tree_node_, *frame_entry, entry,
427 FrameMsg_Navigate_Type::NORMAL, false, base::TimeTicks::Now(),
428 static_cast<NavigationControllerImpl*>(&controller()));
429 TestRenderFrameHost* frame_host = static_cast<TestRenderFrameHost*>(
430 manager->GetFrameHostForNavigation(*navigation_request));
431 CHECK(frame_host);
432 frame_host->set_pending_commit(true);
433 return frame_host;
436 return manager->Navigate(*frame_entry, entry);
439 // Returns the pending RenderFrameHost.
440 // PlzNavigate: returns the speculative RenderFrameHost.
441 RenderFrameHostImpl* GetPendingFrameHost(
442 RenderFrameHostManager* manager) {
443 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
444 switches::kEnableBrowserSideNavigation)) {
445 return manager->speculative_render_frame_host_.get();
447 return manager->pending_frame_host();
450 private:
451 RenderFrameHostManagerTestWebUIControllerFactory factory_;
454 // Tests that when you navigate from a chrome:// url to another page, and
455 // then do that same thing in another tab, that the two resulting pages have
456 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
457 // a regression test for bug 9364.
458 TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
459 set_should_create_webui(true);
460 const GURL kChromeUrl("chrome://foo");
461 const GURL kDestUrl("http://www.google.com/");
463 // Navigate our first tab to the chrome url and then to the destination,
464 // ensuring we grant bindings to the chrome URL.
465 NavigateActiveAndCommit(kChromeUrl);
466 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
467 NavigateActiveAndCommit(kDestUrl);
469 EXPECT_FALSE(contents()->GetPendingMainFrame());
471 // Make a second tab.
472 scoped_ptr<TestWebContents> contents2(
473 TestWebContents::Create(browser_context(), NULL));
475 // Load the two URLs in the second tab. Note that the first navigation creates
476 // a RFH that's not pending (since there is no cross-site transition), so
477 // we use the committed one.
478 contents2->GetController().LoadURL(
479 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
480 int entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
481 contents2->GetMainFrame()->PrepareForCommit();
482 TestRenderFrameHost* ntp_rfh2 = contents2->GetMainFrame();
483 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
484 ntp_rfh2->SendNavigate(100, entry_id, true, kChromeUrl);
486 // The second one is the opposite, creating a cross-site transition and
487 // requiring a beforeunload ack.
488 contents2->GetController().LoadURL(
489 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
490 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
491 contents2->GetMainFrame()->PrepareForCommit();
492 EXPECT_TRUE(contents2->CrossProcessNavigationPending());
493 TestRenderFrameHost* dest_rfh2 = contents2->GetPendingMainFrame();
494 ASSERT_TRUE(dest_rfh2);
496 dest_rfh2->SendNavigate(101, entry_id, true, kDestUrl);
498 // The two RFH's should be different in every way.
499 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2->GetProcess());
500 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
501 dest_rfh2->GetSiteInstance());
502 EXPECT_FALSE(dest_rfh2->GetSiteInstance()->IsRelatedSiteInstance(
503 contents()->GetMainFrame()->GetSiteInstance()));
505 // Navigate both to the new tab page, and verify that they share a
506 // RenderProcessHost (not a SiteInstance).
507 NavigateActiveAndCommit(kChromeUrl);
508 EXPECT_FALSE(contents()->GetPendingMainFrame());
510 contents2->GetController().LoadURL(
511 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
512 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
513 contents2->GetMainFrame()->PrepareForCommit();
514 contents2->GetPendingMainFrame()->SendNavigate(102, entry_id, true,
515 kChromeUrl);
517 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
518 contents2->GetMainFrame()->GetSiteInstance());
519 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
520 contents2->GetMainFrame()->GetSiteInstance()->GetProcess());
523 // Ensure that the browser ignores most IPC messages that arrive from a
524 // RenderViewHost that has been swapped out. We do not want to take
525 // action on requests from a non-active renderer. The main exception is
526 // for synchronous messages, which cannot be ignored without leaving the
527 // renderer in a stuck state. See http://crbug.com/93427.
528 TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
529 const GURL kChromeURL("chrome://foo");
530 const GURL kDestUrl("http://www.google.com/");
531 std::vector<FaviconURL> icons;
533 // Navigate our first tab to a chrome url and then to the destination.
534 NavigateActiveAndCommit(kChromeURL);
535 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
536 TestRenderViewHost* ntp_rvh = ntp_rfh->GetRenderViewHost();
538 // Send an update favicon message and make sure it works.
540 PluginFaviconMessageObserver observer(contents());
541 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->OnMessageReceived(
542 ViewHostMsg_UpdateFaviconURL(
543 ntp_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
544 EXPECT_TRUE(observer.favicon_received());
546 // Create one more frame in the same SiteInstance where ntp_rfh
547 // exists so that it doesn't get deleted on navigation to another
548 // site.
549 ntp_rfh->GetSiteInstance()->increment_active_frame_count();
551 // Navigate to a cross-site URL.
552 NavigateActiveAndCommit(kDestUrl);
553 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
554 ASSERT_TRUE(dest_rfh);
555 EXPECT_NE(ntp_rfh, dest_rfh);
557 // The new RVH should be able to update its favicon.
559 PluginFaviconMessageObserver observer(contents());
560 EXPECT_TRUE(
561 dest_rfh->GetRenderViewHost()->OnMessageReceived(
562 ViewHostMsg_UpdateFaviconURL(
563 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
564 EXPECT_TRUE(observer.favicon_received());
567 // The old renderer, being slow, now updates the favicon. It should be
568 // filtered out and not take effect.
570 PluginFaviconMessageObserver observer(contents());
571 EXPECT_TRUE(
572 ntp_rvh->OnMessageReceived(
573 ViewHostMsg_UpdateFaviconURL(
574 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
575 EXPECT_FALSE(observer.favicon_received());
578 // In --site-per-process, the RenderFrameHost is deleted on cross-process
579 // navigation, so the rest of the test case doesn't apply.
580 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
581 return;
584 #if defined(ENABLE_PLUGINS)
585 // The same logic should apply to RenderFrameHosts as well and routing through
586 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
587 // if the IPC message is allowed through or not.
589 PluginFaviconMessageObserver observer(contents());
590 EXPECT_TRUE(ntp_rfh->OnMessageReceived(
591 FrameHostMsg_PluginCrashed(
592 ntp_rfh->GetRoutingID(), base::FilePath(), 0)));
593 EXPECT_FALSE(observer.plugin_crashed());
595 #endif
597 // We cannot filter out synchronous IPC messages, because the renderer would
598 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
599 // that can run easily within a unit test, and that needs to receive a reply
600 // without showing an actual dialog.
601 MockRenderProcessHost* ntp_process_host = ntp_rfh->GetProcess();
602 ntp_process_host->sink().ClearMessages();
603 const base::string16 msg = base::ASCIIToUTF16("Message");
604 bool result = false;
605 base::string16 unused;
606 FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
607 ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
608 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
609 before_unload_msg.EnableMessagePumping();
610 EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
611 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
613 // Also test RunJavaScriptMessage.
614 ntp_process_host->sink().ClearMessages();
615 FrameHostMsg_RunJavaScriptMessage js_msg(
616 ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
617 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
618 js_msg.EnableMessagePumping();
619 EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
620 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
623 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
624 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
625 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
626 // committed navigation for each WebContentsImpl.
627 TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingSwapOut) {
628 const GURL kChromeURL("chrome://foo");
629 const GURL kDestUrl("http://www.google.com/");
630 std::vector<FaviconURL> icons;
632 // Navigate our first tab to a chrome url and then to the destination.
633 NavigateActiveAndCommit(kChromeURL);
634 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
636 // Send an update favicon message and make sure it works.
638 PluginFaviconMessageObserver observer(contents());
639 EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived(
640 ViewHostMsg_UpdateFaviconURL(
641 rfh1->GetRenderViewHost()->GetRoutingID(), icons)));
642 EXPECT_TRUE(observer.favicon_received());
645 // Create one more frame in the same SiteInstance where |rfh1| exists so that
646 // it doesn't get deleted on navigation to another site.
647 rfh1->GetSiteInstance()->increment_active_frame_count();
649 // Navigate to a cross-site URL and commit the new page.
650 controller().LoadURL(
651 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
652 int entry_id = controller().GetPendingEntry()->GetUniqueID();
653 contents()->GetMainFrame()->PrepareForCommit();
654 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
655 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kDestUrl,
656 ui::PAGE_TRANSITION_TYPED);
657 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
658 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
660 // The new RVH should be able to update its favicons.
662 PluginFaviconMessageObserver observer(contents());
663 EXPECT_TRUE(rfh2->GetRenderViewHost()->OnMessageReceived(
664 ViewHostMsg_UpdateFaviconURL(rfh2->GetRenderViewHost()->GetRoutingID(),
665 icons)));
666 EXPECT_TRUE(observer.favicon_received());
669 // The old renderer, being slow, now updates its favicons. The message should
670 // be ignored.
672 PluginFaviconMessageObserver observer(contents());
673 EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived(
674 ViewHostMsg_UpdateFaviconURL(rfh1->GetRenderViewHost()->GetRoutingID(),
675 icons)));
676 EXPECT_FALSE(observer.favicon_received());
680 // Ensure that frames aren't added to the frame tree, if the message is coming
681 // from a process different than the parent frame's current RenderFrameHost
682 // process. Otherwise it is possible to have collisions of routing ids, as they
683 // are scoped per process. See https://crbug.com/415059.
684 TEST_F(RenderFrameHostManagerTest, DropCreateChildFrameWhileSwappedOut) {
685 const GURL kUrl1("http://foo.com");
686 const GURL kUrl2("http://www.google.com/");
688 // This test is invalid in --site-per-process mode, as swapped-out is no
689 // longer used.
690 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
691 return;
694 // Navigate to the first site.
695 NavigateActiveAndCommit(kUrl1);
696 TestRenderFrameHost* initial_rfh = contents()->GetMainFrame();
698 RenderFrameHostCreatedObserver observer(contents());
699 initial_rfh->OnCreateChildFrame(
700 initial_rfh->GetProcess()->GetNextRoutingID(),
701 blink::WebTreeScopeType::Document, std::string(),
702 blink::WebSandboxFlags::None);
703 EXPECT_TRUE(observer.created());
706 // Create one more frame in the same SiteInstance where initial_rfh
707 // exists so that initial_rfh doesn't get deleted on navigation to another
708 // site.
709 initial_rfh->GetSiteInstance()->increment_active_frame_count();
711 // Navigate to a cross-site URL.
712 NavigateActiveAndCommit(kUrl2);
713 EXPECT_TRUE(initial_rfh->is_swapped_out());
715 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
716 ASSERT_TRUE(dest_rfh);
717 EXPECT_NE(initial_rfh, dest_rfh);
720 // Since the old RFH is now swapped out, it shouldn't process any messages
721 // to create child frames.
722 RenderFrameHostCreatedObserver observer(contents());
723 initial_rfh->OnCreateChildFrame(
724 initial_rfh->GetProcess()->GetNextRoutingID(),
725 blink::WebTreeScopeType::Document, std::string(),
726 blink::WebSandboxFlags::None);
727 EXPECT_FALSE(observer.created());
731 TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
732 // TODO(nasko): Check with kenrb whether this test can be rewritten and
733 // whether it makes sense when swapped out is replaced with proxies.
734 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
735 return;
737 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
738 TestRenderWidgetHostView* swapped_out_rwhv =
739 static_cast<TestRenderWidgetHostView*>(
740 swapped_out_rfh->GetRenderViewHost()->GetView());
741 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame());
743 MockRenderProcessHost* process_host = swapped_out_rfh->GetProcess();
744 process_host->sink().ClearMessages();
746 cc::CompositorFrame frame;
747 ViewHostMsg_SwapCompositorFrame msg(
748 rvh()->GetRoutingID(), 0, frame, std::vector<IPC::Message>());
750 EXPECT_TRUE(swapped_out_rfh->render_view_host()->OnMessageReceived(msg));
751 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
754 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
755 // widgets.
756 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
757 // This test is invalid in --site-per-process mode, as swapped-out is no
758 // longer used.
759 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
760 return;
763 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
764 EXPECT_TRUE(swapped_out_rfh->is_swapped_out());
766 scoped_ptr<RenderWidgetHostIterator> widgets(
767 RenderWidgetHost::GetRenderWidgetHosts());
768 // We know that there is the only one active widget. Another view is
769 // now swapped out, so the swapped out view is not included in the
770 // list.
771 RenderWidgetHost* widget = widgets->GetNextHost();
772 EXPECT_FALSE(widgets->GetNextHost());
773 RenderViewHost* rvh = RenderViewHost::From(widget);
774 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(rvh)->is_active());
777 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
778 // RenderViewHostImpl::GetAllRenderWidgetHosts().
779 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
780 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
781 // including swapped out ones.
782 TEST_F(RenderFrameHostManagerTest,
783 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
784 // This test is invalid in --site-per-process mode, as swapped-out is no
785 // longer used.
786 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
787 return;
790 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
791 EXPECT_TRUE(swapped_out_rfh->is_swapped_out());
793 scoped_ptr<RenderWidgetHostIterator> widgets(
794 RenderWidgetHost::GetRenderWidgetHosts());
796 while (RenderWidgetHost* w = widgets->GetNextHost()) {
797 bool found = false;
798 scoped_ptr<RenderWidgetHostIterator> all_widgets(
799 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
800 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
801 if (w == widget) {
802 found = true;
803 break;
806 EXPECT_TRUE(found);
810 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
811 // as frames in a SiteInstance get swapped out and in.
812 TEST_F(RenderFrameHostManagerTest, ActiveFrameCountWhileSwappingInAndOut) {
813 const GURL kUrl1("http://www.google.com/");
814 const GURL kUrl2("http://www.chromium.org/");
816 // Navigate to an initial URL.
817 contents()->NavigateAndCommit(kUrl1);
818 TestRenderFrameHost* rfh1 = main_test_rfh();
820 SiteInstanceImpl* instance1 = rfh1->GetSiteInstance();
821 EXPECT_EQ(instance1->active_frame_count(), 1U);
823 // Create 2 new tabs and simulate them being the opener chain for the main
824 // tab. They should be in the same SiteInstance.
825 scoped_ptr<TestWebContents> opener1(
826 TestWebContents::Create(browser_context(), instance1));
827 contents()->SetOpener(opener1.get());
829 scoped_ptr<TestWebContents> opener2(
830 TestWebContents::Create(browser_context(), instance1));
831 opener1->SetOpener(opener2.get());
833 EXPECT_EQ(instance1->active_frame_count(), 3U);
835 // Navigate to a cross-site URL (different SiteInstance but same
836 // BrowsingInstance).
837 contents()->NavigateAndCommit(kUrl2);
838 TestRenderFrameHost* rfh2 = main_test_rfh();
839 SiteInstanceImpl* instance2 = rfh2->GetSiteInstance();
841 // rvh2 is on chromium.org which is different from google.com on
842 // which other tabs are.
843 EXPECT_EQ(instance2->active_frame_count(), 1U);
845 // There are two active views on google.com now.
846 EXPECT_EQ(instance1->active_frame_count(), 2U);
848 // Navigate to the original origin (google.com).
849 contents()->NavigateAndCommit(kUrl1);
851 EXPECT_EQ(instance1->active_frame_count(), 3U);
854 // This deletes a WebContents when the given RVH is deleted. This is
855 // only for testing whether deleting an RVH does not cause any UaF in
856 // other parts of the system. For now, this class is only used for the
857 // next test cases to detect the bug mentioned at
858 // http://crbug.com/259859.
859 class RenderViewHostDestroyer : public WebContentsObserver {
860 public:
861 RenderViewHostDestroyer(RenderViewHost* render_view_host,
862 WebContents* web_contents)
863 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
864 render_view_host_(render_view_host),
865 web_contents_(web_contents) {}
867 void RenderViewDeleted(RenderViewHost* render_view_host) override {
868 if (render_view_host == render_view_host_)
869 delete web_contents_;
872 private:
873 RenderViewHost* render_view_host_;
874 WebContents* web_contents_;
876 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
879 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
880 // RenderWidget that has been freed while deleting a RenderViewHost in
881 // a previous iteration. This is a regression test for
882 // http://crbug.com/259859.
883 TEST_F(RenderFrameHostManagerTest,
884 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {
885 const GURL kChromeURL("chrome://newtab");
886 const GURL kUrl1("http://www.google.com");
887 const GURL kUrl2("http://www.chromium.org");
889 // Navigate our first tab to a chrome url and then to the destination.
890 NavigateActiveAndCommit(kChromeURL);
891 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
893 // Create one more tab and navigate to kUrl1. web_contents is not
894 // wrapped as scoped_ptr since it intentionally deleted by destroyer
895 // below as part of this test.
896 TestWebContents* web_contents =
897 TestWebContents::Create(browser_context(), ntp_rfh->GetSiteInstance());
898 web_contents->NavigateAndCommit(kUrl1);
899 RenderViewHostDestroyer destroyer(ntp_rfh->GetRenderViewHost(),
900 web_contents);
902 // This causes the first tab to navigate to kUrl2, which destroys
903 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
904 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
905 // too. This can test whether
906 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
907 // touch any object freed in this way or not while iterating through
908 // all widgets.
909 contents()->NavigateAndCommit(kUrl2);
912 // When there is an error with the specified page, renderer exits view-source
913 // mode. See WebFrameImpl::DidFail(). We check by this test that
914 // EnableViewSourceMode message is sent on every navigation regardless
915 // RenderView is being newly created or reused.
916 TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
917 const GURL kChromeUrl("chrome://foo/");
918 const GURL kUrl("http://foo/");
919 const GURL kViewSourceUrl("view-source:http://foo/");
921 // We have to navigate to some page at first since without this, the first
922 // navigation will reuse the SiteInstance created by Init(), and the second
923 // one will create a new SiteInstance. Because current_instance and
924 // new_instance will be different, a new RenderViewHost will be created for
925 // the second navigation. We have to avoid this in order to exercise the
926 // target code path.
927 NavigateActiveAndCommit(kChromeUrl);
929 // Navigate. Note that "view source" URLs are implemented by putting the RFH
930 // into a view-source mode and then navigating to the inner URL, so that's why
931 // the bare URL is what's committed and returned by the last committed entry's
932 // GetURL() call.
933 controller().LoadURL(
934 kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
935 int entry_id = controller().GetPendingEntry()->GetUniqueID();
937 // Simulate response from RenderFrame for DispatchBeforeUnload.
938 contents()->GetMainFrame()->PrepareForCommit();
939 ASSERT_TRUE(contents()->GetPendingMainFrame())
940 << "Expected new pending RenderFrameHost to be created.";
941 RenderFrameHost* last_rfh = contents()->GetPendingMainFrame();
942 int32 new_id =
943 contents()->GetMaxPageIDForSiteInstance(last_rfh->GetSiteInstance()) + 1;
944 contents()->GetPendingMainFrame()->SendNavigate(new_id, entry_id, true, kUrl);
946 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
947 NavigationEntry* last_committed = controller().GetLastCommittedEntry();
948 ASSERT_NE(nullptr, last_committed);
949 EXPECT_EQ(kUrl, last_committed->GetURL());
950 EXPECT_EQ(kViewSourceUrl, last_committed->GetVirtualURL());
951 EXPECT_FALSE(controller().GetPendingEntry());
952 // Because we're using TestWebContents and TestRenderViewHost in this
953 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
954 // EnableViewSourceMode message, here.
956 // Clear queued messages before load.
957 process()->sink().ClearMessages();
959 // Navigate, again.
960 controller().LoadURL(
961 kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
962 entry_id = controller().GetPendingEntry()->GetUniqueID();
963 contents()->GetMainFrame()->PrepareForCommit();
965 // The same RenderViewHost should be reused.
966 EXPECT_FALSE(contents()->GetPendingMainFrame());
967 EXPECT_EQ(last_rfh, contents()->GetMainFrame());
969 // The renderer sends a commit.
970 contents()->GetMainFrame()->SendNavigateWithTransition(
971 new_id, entry_id, false, kUrl, ui::PAGE_TRANSITION_TYPED);
972 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
973 EXPECT_FALSE(controller().GetPendingEntry());
975 // New message should be sent out to make sure to enter view-source mode.
976 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
977 ViewMsg_EnableViewSourceMode::ID));
980 // Tests the Init function by checking the initial RenderViewHost.
981 TEST_F(RenderFrameHostManagerTest, Init) {
982 // Using TestBrowserContext.
983 SiteInstanceImpl* instance =
984 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
985 EXPECT_FALSE(instance->HasSite());
987 scoped_ptr<TestWebContents> web_contents(
988 TestWebContents::Create(browser_context(), instance));
990 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
991 RenderViewHostImpl* rvh = manager->current_host();
992 RenderFrameHostImpl* rfh = manager->current_frame_host();
993 ASSERT_TRUE(rvh);
994 ASSERT_TRUE(rfh);
995 EXPECT_EQ(rvh, rfh->render_view_host());
996 EXPECT_EQ(instance, rvh->GetSiteInstance());
997 EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
998 EXPECT_EQ(web_contents.get(), rfh->delegate());
999 EXPECT_TRUE(manager->GetRenderWidgetHostView());
1000 EXPECT_FALSE(manager->pending_render_view_host());
1003 // Tests the Navigate function. We navigate three sites consecutively and check
1004 // how the pending/committed RenderViewHost are modified.
1005 TEST_F(RenderFrameHostManagerTest, Navigate) {
1006 SiteInstance* instance = SiteInstance::Create(browser_context());
1008 scoped_ptr<TestWebContents> web_contents(
1009 TestWebContents::Create(browser_context(), instance));
1010 RenderViewHostChangedObserver change_observer(web_contents.get());
1012 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1013 RenderFrameHostImpl* host = NULL;
1015 // 1) The first navigation. --------------------------
1016 const GURL kUrl1("http://www.google.com/");
1017 NavigationEntryImpl entry1(
1018 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1019 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1020 false /* is_renderer_init */);
1021 host = NavigateToEntry(manager, entry1);
1023 // The RenderFrameHost created in Init will be reused.
1024 EXPECT_TRUE(host == manager->current_frame_host());
1025 EXPECT_FALSE(GetPendingFrameHost(manager));
1027 // Commit.
1028 manager->DidNavigateFrame(host, true);
1029 // Commit to SiteInstance should be delayed until RenderFrame commit.
1030 EXPECT_TRUE(host == manager->current_frame_host());
1031 ASSERT_TRUE(host);
1032 EXPECT_FALSE(host->GetSiteInstance()->HasSite());
1033 host->GetSiteInstance()->SetSite(kUrl1);
1035 // 2) Navigate to next site. -------------------------
1036 const GURL kUrl2("http://www.google.com/foo");
1037 NavigationEntryImpl entry2(
1038 NULL /* instance */, -1 /* page_id */, kUrl2,
1039 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1040 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1041 true /* is_renderer_init */);
1042 host = NavigateToEntry(manager, entry2);
1044 // The RenderFrameHost created in Init will be reused.
1045 EXPECT_TRUE(host == manager->current_frame_host());
1046 EXPECT_FALSE(GetPendingFrameHost(manager));
1048 // Commit.
1049 manager->DidNavigateFrame(host, true);
1050 EXPECT_TRUE(host == manager->current_frame_host());
1051 ASSERT_TRUE(host);
1052 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1054 // 3) Cross-site navigate to next site. --------------
1055 const GURL kUrl3("http://webkit.org/");
1056 NavigationEntryImpl entry3(
1057 NULL /* instance */, -1 /* page_id */, kUrl3,
1058 Referrer(kUrl2, blink::WebReferrerPolicyDefault),
1059 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1060 false /* is_renderer_init */);
1061 host = NavigateToEntry(manager, entry3);
1063 // A new RenderFrameHost should be created.
1064 EXPECT_TRUE(GetPendingFrameHost(manager));
1065 ASSERT_EQ(host, GetPendingFrameHost(manager));
1067 change_observer.Reset();
1069 // Commit.
1070 manager->DidNavigateFrame(GetPendingFrameHost(manager), true);
1071 EXPECT_TRUE(host == manager->current_frame_host());
1072 ASSERT_TRUE(host);
1073 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1074 // Check the pending RenderFrameHost has been committed.
1075 EXPECT_FALSE(GetPendingFrameHost(manager));
1077 // We should observe RVH changed event.
1078 EXPECT_TRUE(change_observer.DidHostChange());
1081 // Tests WebUI creation.
1082 TEST_F(RenderFrameHostManagerTest, WebUI) {
1083 set_should_create_webui(true);
1084 SiteInstance* instance = SiteInstance::Create(browser_context());
1086 scoped_ptr<TestWebContents> web_contents(
1087 TestWebContents::Create(browser_context(), instance));
1088 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1089 RenderFrameHostImpl* initial_rfh = manager->current_frame_host();
1091 EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
1092 EXPECT_FALSE(manager->web_ui());
1093 EXPECT_TRUE(initial_rfh);
1095 const GURL kUrl("chrome://foo");
1096 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
1097 Referrer(), base::string16() /* title */,
1098 ui::PAGE_TRANSITION_TYPED,
1099 false /* is_renderer_init */);
1100 RenderFrameHostImpl* host = NavigateToEntry(manager, entry);
1102 // We commit the pending RenderFrameHost immediately because the previous
1103 // RenderFrameHost was not live. We test a case where it is live in
1104 // WebUIInNewTab.
1105 EXPECT_TRUE(host);
1106 EXPECT_NE(initial_rfh, host);
1107 EXPECT_EQ(host, manager->current_frame_host());
1108 EXPECT_FALSE(GetPendingFrameHost(manager));
1110 // It's important that the SiteInstance get set on the Web UI page as soon
1111 // as the navigation starts, rather than lazily after it commits, so we don't
1112 // try to re-use the SiteInstance/process for non Web UI things that may
1113 // get loaded in between.
1114 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1115 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
1117 // The Web UI is committed immediately because the RenderViewHost has not been
1118 // used yet. UpdateStateForNavigate() took the short cut path.
1119 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1120 switches::kEnableBrowserSideNavigation)) {
1121 EXPECT_FALSE(manager->speculative_web_ui());
1122 } else {
1123 EXPECT_FALSE(manager->pending_web_ui());
1125 EXPECT_TRUE(manager->web_ui());
1127 // Commit.
1128 manager->DidNavigateFrame(host, true);
1129 EXPECT_TRUE(
1130 host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1133 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1134 // grant the correct bindings. http://crbug.com/189101.
1135 TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
1136 set_should_create_webui(true);
1137 SiteInstance* blank_instance = SiteInstance::Create(browser_context());
1138 blank_instance->GetProcess()->Init();
1140 // Create a blank tab.
1141 scoped_ptr<TestWebContents> web_contents1(
1142 TestWebContents::Create(browser_context(), blank_instance));
1143 RenderFrameHostManager* manager1 =
1144 web_contents1->GetRenderManagerForTesting();
1145 // Test the case that new RVH is considered live.
1146 manager1->current_host()->CreateRenderView(
1147 base::string16(), -1, MSG_ROUTING_NONE, -1,
1148 FrameReplicationState(), false);
1149 EXPECT_TRUE(manager1->current_host()->IsRenderViewLive());
1150 EXPECT_TRUE(manager1->current_frame_host()->IsRenderFrameLive());
1152 // Navigate to a WebUI page.
1153 const GURL kUrl1("chrome://foo");
1154 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1155 Referrer(), base::string16() /* title */,
1156 ui::PAGE_TRANSITION_TYPED,
1157 false /* is_renderer_init */);
1158 RenderFrameHostImpl* host1 = NavigateToEntry(manager1, entry1);
1160 // We should have a pending navigation to the WebUI RenderViewHost.
1161 // It should already have bindings.
1162 EXPECT_EQ(host1, GetPendingFrameHost(manager1));
1163 EXPECT_NE(host1, manager1->current_frame_host());
1164 EXPECT_TRUE(
1165 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1167 // Commit and ensure we still have bindings.
1168 manager1->DidNavigateFrame(host1, true);
1169 SiteInstance* webui_instance = host1->GetSiteInstance();
1170 EXPECT_EQ(host1, manager1->current_frame_host());
1171 EXPECT_TRUE(
1172 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1174 // Now simulate clicking a link that opens in a new tab.
1175 scoped_ptr<TestWebContents> web_contents2(
1176 TestWebContents::Create(browser_context(), webui_instance));
1177 RenderFrameHostManager* manager2 =
1178 web_contents2->GetRenderManagerForTesting();
1179 // Make sure the new RVH is considered live. This is usually done in
1180 // RenderWidgetHost::Init when opening a new tab from a link.
1181 manager2->current_host()->CreateRenderView(
1182 base::string16(), -1, MSG_ROUTING_NONE, -1,
1183 FrameReplicationState(), false);
1184 EXPECT_TRUE(manager2->current_host()->IsRenderViewLive());
1186 const GURL kUrl2("chrome://foo/bar");
1187 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
1188 Referrer(), base::string16() /* title */,
1189 ui::PAGE_TRANSITION_LINK,
1190 true /* is_renderer_init */);
1191 RenderFrameHostImpl* host2 = NavigateToEntry(manager2, entry2);
1193 // No cross-process transition happens because we are already in the right
1194 // SiteInstance. We should grant bindings immediately.
1195 EXPECT_EQ(host2, manager2->current_frame_host());
1196 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1197 switches::kEnableBrowserSideNavigation)) {
1198 EXPECT_TRUE(manager2->speculative_web_ui());
1199 } else {
1200 EXPECT_TRUE(manager2->pending_web_ui());
1202 EXPECT_TRUE(
1203 host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1205 manager2->DidNavigateFrame(host2, true);
1208 // Tests that a WebUI is correctly reused between chrome:// pages.
1209 TEST_F(RenderFrameHostManagerTest, WebUIWasReused) {
1210 set_should_create_webui(true);
1212 // Navigate to a WebUI page.
1213 const GURL kUrl1("chrome://foo");
1214 contents()->NavigateAndCommit(kUrl1);
1215 RenderFrameHostManager* manager =
1216 main_test_rfh()->frame_tree_node()->render_manager();
1217 WebUIImpl* web_ui = manager->web_ui();
1218 EXPECT_TRUE(web_ui);
1220 // Navigate to another WebUI page which should be same-site and keep the
1221 // current WebUI.
1222 const GURL kUrl2("chrome://foo/bar");
1223 contents()->NavigateAndCommit(kUrl2);
1224 EXPECT_EQ(web_ui, manager->web_ui());
1227 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1228 // page to a non-chrome:// page.
1229 TEST_F(RenderFrameHostManagerTest, WebUIWasCleared) {
1230 set_should_create_webui(true);
1232 // Navigate to a WebUI page.
1233 const GURL kUrl1("chrome://foo");
1234 contents()->NavigateAndCommit(kUrl1);
1235 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1237 // Navigate to a non-WebUI page.
1238 const GURL kUrl2("http://www.google.com");
1239 contents()->NavigateAndCommit(kUrl2);
1240 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1243 // Tests that we don't end up in an inconsistent state if a page does a back and
1244 // then reload. http://crbug.com/51680
1245 // Also tests that only user-gesture navigations can interrupt cross-process
1246 // navigations. http://crbug.com/75195
1247 TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
1248 const GURL kUrl1("http://www.google.com/");
1249 const GURL kUrl2("http://www.evil-site.com/");
1251 // Navigate to a safe site, then an evil site.
1252 // This will switch RenderFrameHosts. We cannot assert that the first and
1253 // second RFHs are different, though, because the first one may be promptly
1254 // deleted.
1255 contents()->NavigateAndCommit(kUrl1);
1256 contents()->NavigateAndCommit(kUrl2);
1257 TestRenderFrameHost* evil_rfh = contents()->GetMainFrame();
1259 // Now let's simulate the evil page calling history.back().
1260 contents()->OnGoToEntryAtOffset(-1);
1261 contents()->GetMainFrame()->PrepareForCommit();
1262 // We should have a new pending RFH.
1263 // Note that in this case, the navigation has not committed, so evil_rfh will
1264 // not be deleted yet.
1265 EXPECT_NE(evil_rfh, contents()->GetPendingMainFrame());
1266 EXPECT_NE(evil_rfh->GetRenderViewHost(),
1267 contents()->GetPendingMainFrame()->GetRenderViewHost());
1269 // Before that RFH has committed, the evil page reloads itself.
1270 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1271 params.page_id = 0;
1272 params.nav_entry_id = 0;
1273 params.did_create_new_entry = false;
1274 params.url = kUrl2;
1275 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
1276 params.should_update_history = false;
1277 params.gesture = NavigationGestureAuto;
1278 params.was_within_same_page = false;
1279 params.is_post = false;
1280 params.page_state = PageState::CreateFromURL(kUrl2);
1282 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh,
1283 params);
1285 // That should NOT have cancelled the pending RFH, because the reload did
1286 // not have a user gesture. Thus, the pending back navigation will still
1287 // eventually commit.
1288 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1289 pending_render_view_host() != NULL);
1290 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1291 NULL);
1292 EXPECT_EQ(evil_rfh,
1293 contents()->GetRenderManagerForTesting()->current_frame_host());
1294 EXPECT_EQ(evil_rfh->GetRenderViewHost(),
1295 contents()->GetRenderManagerForTesting()->current_host());
1297 // Also we should not have a pending navigation entry.
1298 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1299 NavigationEntry* entry = contents()->GetController().GetVisibleEntry();
1300 ASSERT_TRUE(entry != NULL);
1301 EXPECT_EQ(kUrl2, entry->GetURL());
1303 // Now do the same but as a user gesture.
1304 params.gesture = NavigationGestureUser;
1305 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh,
1306 params);
1308 // User navigation should have cancelled the pending RFH.
1309 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1310 pending_render_view_host() == NULL);
1311 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1312 NULL);
1313 EXPECT_EQ(evil_rfh,
1314 contents()->GetRenderManagerForTesting()->current_frame_host());
1315 EXPECT_EQ(evil_rfh->GetRenderViewHost(),
1316 contents()->GetRenderManagerForTesting()->current_host());
1318 // Also we should not have a pending navigation entry.
1319 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1320 entry = contents()->GetController().GetVisibleEntry();
1321 ASSERT_TRUE(entry != NULL);
1322 EXPECT_EQ(kUrl2, entry->GetURL());
1325 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1326 // See http://crbug.com/93427.
1327 TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1328 const GURL kUrl1("http://www.google.com/");
1329 const GURL kUrl2("http://www.chromium.org/");
1331 // Navigate to two pages.
1332 contents()->NavigateAndCommit(kUrl1);
1333 TestRenderFrameHost* rfh1 = main_test_rfh();
1335 // Keep active_frame_count nonzero so that no swapped out frames in
1336 // this SiteInstance get forcefully deleted.
1337 rfh1->GetSiteInstance()->increment_active_frame_count();
1339 contents()->NavigateAndCommit(kUrl2);
1340 TestRenderFrameHost* rfh2 = main_test_rfh();
1341 rfh2->GetSiteInstance()->increment_active_frame_count();
1343 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1344 // happen, but we have seen it when going back quickly across many entries
1345 // (http://crbug.com/93427).
1346 contents()->GetController().GoBack();
1347 EXPECT_TRUE(rfh2->IsWaitingForBeforeUnloadACK());
1348 contents()->GetMainFrame()->PrepareForCommit();
1349 EXPECT_FALSE(rfh2->IsWaitingForBeforeUnloadACK());
1351 // The back navigation commits.
1352 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1353 contents()->GetPendingMainFrame()->SendNavigate(
1354 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1355 EXPECT_TRUE(rfh2->IsWaitingForUnloadACK());
1356 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh2->rfh_state());
1358 // We should be able to navigate forward.
1359 contents()->GetController().GoForward();
1360 contents()->GetMainFrame()->PrepareForCommit();
1361 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
1362 contents()->GetPendingMainFrame()->SendNavigate(
1363 entry2->GetPageID(), entry2->GetUniqueID(), false, entry2->GetURL());
1364 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state());
1365 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1366 EXPECT_EQ(rfh2, main_test_rfh());
1367 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1368 rfh1->OnSwappedOut();
1369 EXPECT_TRUE(rfh1->is_swapped_out());
1370 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT, rfh1->rfh_state());
1374 // Test that we create swapped out RFHs for the opener chain when navigating an
1375 // opened tab cross-process. This allows us to support certain cross-process
1376 // JavaScript calls (http://crbug.com/99202).
1377 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRFHs) {
1378 const GURL kUrl1("http://www.google.com/");
1379 const GURL kUrl2("http://www.chromium.org/");
1380 const GURL kChromeUrl("chrome://foo");
1382 // Navigate to an initial URL.
1383 contents()->NavigateAndCommit(kUrl1);
1384 RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1385 TestRenderFrameHost* rfh1 = main_test_rfh();
1386 scoped_refptr<SiteInstanceImpl> site_instance1 = rfh1->GetSiteInstance();
1387 RenderFrameHostDeletedObserver rfh1_deleted_observer(rfh1);
1388 TestRenderViewHost* rvh1 = test_rvh();
1390 // Create 2 new tabs and simulate them being the opener chain for the main
1391 // tab. They should be in the same SiteInstance.
1392 scoped_ptr<TestWebContents> opener1(
1393 TestWebContents::Create(browser_context(), site_instance1.get()));
1394 RenderFrameHostManager* opener1_manager =
1395 opener1->GetRenderManagerForTesting();
1396 contents()->SetOpener(opener1.get());
1398 scoped_ptr<TestWebContents> opener2(
1399 TestWebContents::Create(browser_context(), site_instance1.get()));
1400 RenderFrameHostManager* opener2_manager =
1401 opener2->GetRenderManagerForTesting();
1402 opener1->SetOpener(opener2.get());
1404 // Navigate to a cross-site URL (different SiteInstance but same
1405 // BrowsingInstance).
1406 contents()->NavigateAndCommit(kUrl2);
1407 TestRenderFrameHost* rfh2 = main_test_rfh();
1408 TestRenderViewHost* rvh2 = test_rvh();
1409 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1410 EXPECT_TRUE(site_instance1->IsRelatedSiteInstance(rfh2->GetSiteInstance()));
1412 // Ensure rvh1 is placed on swapped out list of the current tab.
1413 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1414 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
1415 EXPECT_FALSE(rfh1_deleted_observer.deleted());
1416 EXPECT_TRUE(manager->IsOnSwappedOutList(rfh1));
1417 EXPECT_EQ(rfh1,
1418 manager->GetRenderFrameProxyHost(site_instance1.get())
1419 ->render_frame_host());
1420 } else {
1421 EXPECT_TRUE(rfh1_deleted_observer.deleted());
1422 EXPECT_TRUE(manager->GetRenderFrameProxyHost(site_instance1.get()));
1424 EXPECT_EQ(rvh1,
1425 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
1427 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1428 RenderFrameProxyHost* opener1_proxy =
1429 opener1_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance());
1430 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host();
1431 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1432 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1433 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1434 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh));
1435 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1436 EXPECT_TRUE(opener1_rfh->is_swapped_out());
1437 } else {
1438 EXPECT_FALSE(opener1_rfh);
1440 EXPECT_FALSE(opener1_rvh->is_active());
1442 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1443 RenderFrameProxyHost* opener2_proxy =
1444 opener2_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance());
1445 RenderFrameHostImpl* opener2_rfh = opener2_proxy->render_frame_host();
1446 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
1447 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1448 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1449 EXPECT_TRUE(opener2_manager->IsOnSwappedOutList(opener2_rfh));
1450 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
1451 EXPECT_TRUE(opener2_rfh->is_swapped_out());
1452 } else {
1453 EXPECT_FALSE(opener2_rfh);
1455 EXPECT_FALSE(opener2_rvh->is_active());
1457 // Navigate to a cross-BrowsingInstance URL.
1458 contents()->NavigateAndCommit(kChromeUrl);
1459 TestRenderFrameHost* rfh3 = main_test_rfh();
1460 EXPECT_NE(site_instance1, rfh3->GetSiteInstance());
1461 EXPECT_FALSE(site_instance1->IsRelatedSiteInstance(rfh3->GetSiteInstance()));
1463 // No scripting is allowed across BrowsingInstances, so we should not create
1464 // swapped out RVHs for the opener chain in this case.
1465 EXPECT_FALSE(opener1_manager->GetRenderFrameProxyHost(
1466 rfh3->GetSiteInstance()));
1467 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1468 rfh3->GetSiteInstance()));
1469 EXPECT_FALSE(opener2_manager->GetRenderFrameProxyHost(
1470 rfh3->GetSiteInstance()));
1471 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost(
1472 rfh3->GetSiteInstance()));
1475 // Test that a page can disown the opener of the WebContents.
1476 TEST_F(RenderFrameHostManagerTest, DisownOpener) {
1477 const GURL kUrl1("http://www.google.com/");
1478 const GURL kUrl2("http://www.chromium.org/");
1480 // Navigate to an initial URL.
1481 contents()->NavigateAndCommit(kUrl1);
1482 TestRenderFrameHost* rfh1 = main_test_rfh();
1483 scoped_refptr<SiteInstanceImpl> site_instance1 = rfh1->GetSiteInstance();
1485 // Create a new tab and simulate having it be the opener for the main tab.
1486 scoped_ptr<TestWebContents> opener1(
1487 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1488 contents()->SetOpener(opener1.get());
1489 EXPECT_TRUE(contents()->HasOpener());
1491 // Navigate to a cross-site URL (different SiteInstance but same
1492 // BrowsingInstance).
1493 contents()->NavigateAndCommit(kUrl2);
1494 TestRenderFrameHost* rfh2 = main_test_rfh();
1495 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1497 // Disown the opener from rfh2.
1498 rfh2->DidDisownOpener();
1500 // Ensure the opener is cleared.
1501 EXPECT_FALSE(contents()->HasOpener());
1504 // Test that a page can disown a same-site opener of the WebContents.
1505 TEST_F(RenderFrameHostManagerTest, DisownSameSiteOpener) {
1506 const GURL kUrl1("http://www.google.com/");
1508 // Navigate to an initial URL.
1509 contents()->NavigateAndCommit(kUrl1);
1510 TestRenderFrameHost* rfh1 = main_test_rfh();
1512 // Create a new tab and simulate having it be the opener for the main tab.
1513 scoped_ptr<TestWebContents> opener1(
1514 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1515 contents()->SetOpener(opener1.get());
1516 EXPECT_TRUE(contents()->HasOpener());
1518 // Disown the opener from rfh1.
1519 rfh1->DidDisownOpener();
1521 // Ensure the opener is cleared even if it is in the same process.
1522 EXPECT_FALSE(contents()->HasOpener());
1525 // Test that a page can disown the opener just as a cross-process navigation is
1526 // in progress.
1527 TEST_F(RenderFrameHostManagerTest, DisownOpenerDuringNavigation) {
1528 const GURL kUrl1("http://www.google.com/");
1529 const GURL kUrl2("http://www.chromium.org/");
1531 // Navigate to an initial URL.
1532 contents()->NavigateAndCommit(kUrl1);
1533 scoped_refptr<SiteInstanceImpl> site_instance1 =
1534 main_test_rfh()->GetSiteInstance();
1536 // Create a new tab and simulate having it be the opener for the main tab.
1537 scoped_ptr<TestWebContents> opener1(
1538 TestWebContents::Create(browser_context(), site_instance1.get()));
1539 contents()->SetOpener(opener1.get());
1540 EXPECT_TRUE(contents()->HasOpener());
1542 // Navigate to a cross-site URL (different SiteInstance but same
1543 // BrowsingInstance).
1544 contents()->NavigateAndCommit(kUrl2);
1545 TestRenderFrameHost* rfh2 = main_test_rfh();
1546 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1548 // Start a back navigation.
1549 contents()->GetController().GoBack();
1550 contents()->GetMainFrame()->PrepareForCommit();
1552 // Disown the opener from rfh2.
1553 rfh2->DidDisownOpener();
1555 // Ensure the opener is cleared.
1556 EXPECT_FALSE(contents()->HasOpener());
1558 // The back navigation commits.
1559 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1560 contents()->GetPendingMainFrame()->SendNavigate(
1561 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1563 // Ensure the opener is still cleared.
1564 EXPECT_FALSE(contents()->HasOpener());
1567 // Test that a page can disown the opener just after a cross-process navigation
1568 // commits.
1569 TEST_F(RenderFrameHostManagerTest, DisownOpenerAfterNavigation) {
1570 const GURL kUrl1("http://www.google.com/");
1571 const GURL kUrl2("http://www.chromium.org/");
1573 // Navigate to an initial URL.
1574 contents()->NavigateAndCommit(kUrl1);
1575 scoped_refptr<SiteInstanceImpl> site_instance1 =
1576 main_test_rfh()->GetSiteInstance();
1578 // Create a new tab and simulate having it be the opener for the main tab.
1579 scoped_ptr<TestWebContents> opener1(
1580 TestWebContents::Create(browser_context(), site_instance1.get()));
1581 contents()->SetOpener(opener1.get());
1582 EXPECT_TRUE(contents()->HasOpener());
1584 // Navigate to a cross-site URL (different SiteInstance but same
1585 // BrowsingInstance).
1586 contents()->NavigateAndCommit(kUrl2);
1587 TestRenderFrameHost* rfh2 = main_test_rfh();
1588 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1590 // Commit a back navigation before the DidDisownOpener message arrives.
1591 contents()->GetController().GoBack();
1592 contents()->GetMainFrame()->PrepareForCommit();
1593 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1594 contents()->GetPendingMainFrame()->SendNavigate(
1595 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1597 // Disown the opener from rfh2.
1598 rfh2->DidDisownOpener();
1599 EXPECT_FALSE(contents()->HasOpener());
1602 // Test that we clean up swapped out RenderViewHosts when a process hosting
1603 // those associated RenderViews crashes. http://crbug.com/258993
1604 TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1605 const GURL kUrl1("http://www.google.com/");
1606 const GURL kUrl2("http://www.chromium.org/");
1608 // Navigate to an initial URL.
1609 contents()->NavigateAndCommit(kUrl1);
1610 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1612 // Create a new tab as an opener for the main tab.
1613 scoped_ptr<TestWebContents> opener1(
1614 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1615 RenderFrameHostManager* opener1_manager =
1616 opener1->GetRenderManagerForTesting();
1617 contents()->SetOpener(opener1.get());
1619 // Make sure the new opener RVH is considered live.
1620 opener1_manager->current_host()->CreateRenderView(
1621 base::string16(), -1, MSG_ROUTING_NONE, -1,
1622 FrameReplicationState(), false);
1623 EXPECT_TRUE(opener1_manager->current_host()->IsRenderViewLive());
1624 EXPECT_TRUE(opener1_manager->current_frame_host()->IsRenderFrameLive());
1626 // Use a cross-process navigation in the opener to swap out the old RVH.
1627 EXPECT_FALSE(
1628 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1629 opener1->NavigateAndCommit(kUrl2);
1630 EXPECT_TRUE(
1631 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1633 // Fake a process crash.
1634 rfh1->GetProcess()->SimulateCrash();
1636 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1637 // is deleted.
1638 RenderFrameProxyHost* render_frame_proxy_host =
1639 opener1_manager->GetRenderFrameProxyHost(rfh1->GetSiteInstance());
1640 EXPECT_TRUE(render_frame_proxy_host);
1641 EXPECT_FALSE(render_frame_proxy_host->is_render_frame_proxy_live());
1643 // Expect the swapped out RVH to exist.
1644 EXPECT_TRUE(
1645 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1647 // Reload the initial tab. This should recreate the opener's swapped out RVH
1648 // in the original SiteInstance.
1649 contents()->GetController().Reload(true);
1650 contents()->GetMainFrame()->PrepareForCommit();
1651 EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost(
1652 rfh1->GetSiteInstance())->GetRoutingID(),
1653 contents()->GetMainFrame()->GetRenderViewHost()->opener_route_id());
1656 // Test that RenderViewHosts created for WebUI navigations are properly
1657 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1658 // is in the same process (http://crbug.com/79918).
1659 TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1660 set_should_create_webui(true);
1661 const GURL kSettingsUrl("chrome://chrome/settings");
1662 const GURL kPluginUrl("chrome://plugins");
1664 // Navigate to an initial WebUI URL.
1665 contents()->NavigateAndCommit(kSettingsUrl);
1667 // Ensure the RVH has WebUI bindings.
1668 TestRenderViewHost* rvh1 = test_rvh();
1669 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1671 // Create a new tab and simulate it being the opener for the main
1672 // tab. It should be in the same SiteInstance.
1673 scoped_ptr<TestWebContents> opener1(
1674 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1675 RenderFrameHostManager* opener1_manager =
1676 opener1->GetRenderManagerForTesting();
1677 contents()->SetOpener(opener1.get());
1679 // Navigate to a different WebUI URL (different SiteInstance, same
1680 // BrowsingInstance).
1681 contents()->NavigateAndCommit(kPluginUrl);
1682 TestRenderViewHost* rvh2 = test_rvh();
1683 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1684 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1685 rvh2->GetSiteInstance()));
1687 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1688 RenderFrameProxyHost* opener1_proxy =
1689 opener1_manager->GetRenderFrameProxyHost(rvh2->GetSiteInstance());
1690 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host();
1691 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1692 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1693 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1694 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh));
1695 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1696 EXPECT_TRUE(opener1_rfh->is_swapped_out());
1697 } else {
1698 EXPECT_FALSE(opener1_rfh);
1700 EXPECT_FALSE(opener1_rvh->is_active());
1702 // Ensure the new RVH has WebUI bindings.
1703 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1706 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1707 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1708 GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1709 SiteInstance* instance =
1710 SiteInstance::CreateForURL(browser_context(), guest_url);
1711 scoped_ptr<TestWebContents> web_contents(
1712 TestWebContents::Create(browser_context(), instance));
1714 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1716 RenderFrameHostImpl* host = NULL;
1718 // 1) The first navigation. --------------------------
1719 const GURL kUrl1("http://www.google.com/");
1720 NavigationEntryImpl entry1(
1721 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1722 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1723 false /* is_renderer_init */);
1724 host = NavigateToEntry(manager, entry1);
1726 // The RenderFrameHost created in Init will be reused.
1727 EXPECT_TRUE(host == manager->current_frame_host());
1728 EXPECT_FALSE(manager->pending_frame_host());
1729 EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1731 // Commit.
1732 manager->DidNavigateFrame(host, true);
1733 // Commit to SiteInstance should be delayed until RenderFrame commit.
1734 EXPECT_EQ(host, manager->current_frame_host());
1735 ASSERT_TRUE(host);
1736 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1738 // 2) Navigate to a different domain. -------------------------
1739 // Guests stay in the same process on navigation.
1740 const GURL kUrl2("http://www.chromium.org");
1741 NavigationEntryImpl entry2(
1742 NULL /* instance */, -1 /* page_id */, kUrl2,
1743 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1744 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1745 true /* is_renderer_init */);
1746 host = NavigateToEntry(manager, entry2);
1748 // The RenderFrameHost created in Init will be reused.
1749 EXPECT_EQ(host, manager->current_frame_host());
1750 EXPECT_FALSE(manager->pending_frame_host());
1752 // Commit.
1753 manager->DidNavigateFrame(host, true);
1754 EXPECT_EQ(host, manager->current_frame_host());
1755 ASSERT_TRUE(host);
1756 EXPECT_EQ(host->GetSiteInstance(), instance);
1759 // Test that we cancel a pending RVH if we close the tab while it's pending.
1760 // http://crbug.com/294697.
1761 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1762 TestNotificationTracker notifications;
1764 SiteInstance* instance = SiteInstance::Create(browser_context());
1766 BeforeUnloadFiredWebContentsDelegate delegate;
1767 scoped_ptr<TestWebContents> web_contents(
1768 TestWebContents::Create(browser_context(), instance));
1769 RenderViewHostChangedObserver change_observer(web_contents.get());
1770 web_contents->SetDelegate(&delegate);
1772 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1774 // 1) The first navigation. --------------------------
1775 const GURL kUrl1("http://www.google.com/");
1776 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1777 Referrer(), base::string16() /* title */,
1778 ui::PAGE_TRANSITION_TYPED,
1779 false /* is_renderer_init */);
1780 RenderFrameHostImpl* host = NavigateToEntry(manager, entry1);
1782 // The RenderFrameHost created in Init will be reused.
1783 EXPECT_EQ(host, manager->current_frame_host());
1784 EXPECT_FALSE(GetPendingFrameHost(manager));
1786 // We should observe RVH changed event.
1787 EXPECT_TRUE(change_observer.DidHostChange());
1789 // Commit.
1790 manager->DidNavigateFrame(host, true);
1792 // Commit to SiteInstance should be delayed until RenderFrame commits.
1793 EXPECT_EQ(host, manager->current_frame_host());
1794 EXPECT_FALSE(host->GetSiteInstance()->HasSite());
1795 host->GetSiteInstance()->SetSite(kUrl1);
1797 // 2) Cross-site navigate to next site. -------------------------
1798 const GURL kUrl2("http://www.example.com");
1799 NavigationEntryImpl entry2(
1800 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1801 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1802 false /* is_renderer_init */);
1803 RenderFrameHostImpl* host2 = NavigateToEntry(manager, entry2);
1805 // A new RenderFrameHost should be created.
1806 ASSERT_EQ(host2, GetPendingFrameHost(manager));
1807 EXPECT_NE(host2, host);
1809 EXPECT_EQ(host, manager->current_frame_host());
1810 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1811 EXPECT_EQ(host2, GetPendingFrameHost(manager));
1813 // 3) Close the tab. -------------------------
1814 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1815 Source<RenderWidgetHost>(host2->render_view_host()));
1816 manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
1818 EXPECT_TRUE(
1819 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1820 EXPECT_FALSE(GetPendingFrameHost(manager));
1821 EXPECT_EQ(host, manager->current_frame_host());
1824 TEST_F(RenderFrameHostManagerTest, CloseWithPendingWhileUnresponsive) {
1825 const GURL kUrl1("http://www.google.com/");
1826 const GURL kUrl2("http://www.chromium.org/");
1828 CloseWebContentsDelegate close_delegate;
1829 contents()->SetDelegate(&close_delegate);
1831 // Navigate to the first page.
1832 contents()->NavigateAndCommit(kUrl1);
1833 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1835 // Start to close the tab, but assume it's unresponsive.
1836 rfh1->render_view_host()->ClosePage();
1837 EXPECT_TRUE(rfh1->render_view_host()->is_waiting_for_close_ack());
1839 // Start a navigation to a new site.
1840 controller().LoadURL(
1841 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1842 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1843 switches::kEnableBrowserSideNavigation)) {
1844 rfh1->PrepareForCommit();
1846 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1848 // Simulate the unresponsiveness timer. The tab should close.
1849 contents()->RendererUnresponsive(rfh1->render_view_host());
1850 EXPECT_TRUE(close_delegate.is_closed());
1853 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1854 // received. (SwapOut and the corresponding ACK always occur after commit.)
1855 // Also tests that an early SwapOutACK is properly ignored.
1856 TEST_F(RenderFrameHostManagerTest, DeleteFrameAfterSwapOutACK) {
1857 const GURL kUrl1("http://www.google.com/");
1858 const GURL kUrl2("http://www.chromium.org/");
1860 // Navigate to the first page.
1861 contents()->NavigateAndCommit(kUrl1);
1862 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1863 RenderFrameHostDeletedObserver rfh_deleted_observer(rfh1);
1864 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1866 // Navigate to new site, simulating onbeforeunload approval.
1867 controller().LoadURL(
1868 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1869 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1870 contents()->GetMainFrame()->PrepareForCommit();
1871 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1872 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1873 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1875 // Simulate the swap out ack, unexpectedly early (before commit). It should
1876 // have no effect.
1877 rfh1->OnSwappedOut();
1878 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1879 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1881 // The new page commits.
1882 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1883 ui::PAGE_TRANSITION_TYPED);
1884 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1885 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1886 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1887 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1888 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1889 EXPECT_TRUE(
1890 rfh1->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1));
1892 // Simulate the swap out ack.
1893 rfh1->OnSwappedOut();
1895 // rfh1 should have been deleted.
1896 EXPECT_TRUE(rfh_deleted_observer.deleted());
1897 rfh1 = NULL;
1900 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1901 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1902 TEST_F(RenderFrameHostManagerTest, SwapOutFrameAfterSwapOutACK) {
1903 const GURL kUrl1("http://www.google.com/");
1904 const GURL kUrl2("http://www.chromium.org/");
1906 // Navigate to the first page.
1907 contents()->NavigateAndCommit(kUrl1);
1908 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1909 RenderFrameHostDeletedObserver rfh_deleted_observer(rfh1);
1910 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1912 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1913 // not deleted on swap out.
1914 rfh1->GetSiteInstance()->increment_active_frame_count();
1916 // Navigate to new site, simulating onbeforeunload approval.
1917 controller().LoadURL(
1918 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1919 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1920 contents()->GetMainFrame()->PrepareForCommit();
1921 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1922 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1923 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1925 // The new page commits.
1926 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1927 ui::PAGE_TRANSITION_TYPED);
1928 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1929 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1930 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1931 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1932 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1934 // Simulate the swap out ack.
1935 rfh1->OnSwappedOut();
1937 // rfh1 should be swapped out or deleted in --site-per-process.
1938 if (!RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1939 EXPECT_FALSE(rfh_deleted_observer.deleted());
1940 EXPECT_TRUE(rfh1->is_swapped_out());
1941 } else {
1942 EXPECT_TRUE(rfh_deleted_observer.deleted());
1946 // Test that the RenderViewHost is properly swapped out if a navigation in the
1947 // new renderer commits before sending the SwapOut message to the old renderer.
1948 // This simulates a cross-site navigation to a synchronously committing URL
1949 // (e.g., a data URL) and ensures it works properly.
1950 TEST_F(RenderFrameHostManagerTest,
1951 CommitNewNavigationBeforeSendingSwapOut) {
1952 const GURL kUrl1("http://www.google.com/");
1953 const GURL kUrl2("http://www.chromium.org/");
1955 // Navigate to the first page.
1956 contents()->NavigateAndCommit(kUrl1);
1957 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1958 RenderFrameHostDeletedObserver rfh_deleted_observer(rfh1);
1959 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1961 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1962 // not deleted on swap out.
1963 scoped_refptr<SiteInstanceImpl> site_instance = rfh1->GetSiteInstance();
1964 site_instance->increment_active_frame_count();
1966 // Navigate to new site, simulating onbeforeunload approval.
1967 controller().LoadURL(
1968 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1969 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1970 rfh1->PrepareForCommit();
1971 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1972 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1974 // The new page commits.
1975 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1976 ui::PAGE_TRANSITION_TYPED);
1977 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1978 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1979 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1980 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1981 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1983 // Simulate the swap out ack.
1984 rfh1->OnSwappedOut();
1986 // rfh1 should be swapped out.
1987 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
1988 EXPECT_TRUE(rfh_deleted_observer.deleted());
1989 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
1990 ->GetRenderFrameProxyHost(site_instance.get()));
1991 } else {
1992 EXPECT_FALSE(rfh_deleted_observer.deleted());
1993 EXPECT_TRUE(rfh1->is_swapped_out());
1997 // Test that a RenderFrameHost is properly deleted when a cross-site navigation
1998 // is cancelled.
1999 TEST_F(RenderFrameHostManagerTest,
2000 CancelPendingProperlyDeletesOrSwaps) {
2001 const GURL kUrl1("http://www.google.com/");
2002 const GURL kUrl2("http://www.chromium.org/");
2003 RenderFrameHostImpl* pending_rfh = NULL;
2004 base::TimeTicks now = base::TimeTicks::Now();
2006 // Navigate to the first page.
2007 contents()->NavigateAndCommit(kUrl1);
2008 TestRenderFrameHost* rfh1 = main_test_rfh();
2009 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
2011 // Navigate to a new site, starting a cross-site navigation.
2012 controller().LoadURL(
2013 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2015 pending_rfh = contents()->GetFrameTree()->root()->render_manager()
2016 ->pending_frame_host();
2017 RenderFrameHostDeletedObserver rfh_deleted_observer(pending_rfh);
2019 // Cancel the navigation by simulating a declined beforeunload dialog.
2020 contents()->GetMainFrame()->OnMessageReceived(
2021 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
2022 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2024 // Since the pending RFH is the only one for the new SiteInstance, it should
2025 // be deleted.
2026 EXPECT_TRUE(rfh_deleted_observer.deleted());
2029 // Start another cross-site navigation.
2030 controller().LoadURL(
2031 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2033 pending_rfh = contents()->GetFrameTree()->root()->render_manager()
2034 ->pending_frame_host();
2035 RenderFrameHostDeletedObserver rfh_deleted_observer(pending_rfh);
2037 // Increment the number of active frames in the new SiteInstance, which will
2038 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be
2039 // created.
2040 scoped_refptr<SiteInstanceImpl> site_instance =
2041 pending_rfh->GetSiteInstance();
2042 site_instance->increment_active_frame_count();
2044 contents()->GetMainFrame()->OnMessageReceived(
2045 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
2046 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2048 if (RenderFrameHostManager::IsSwappedOutStateForbidden()) {
2049 EXPECT_TRUE(rfh_deleted_observer.deleted());
2050 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2051 ->GetRenderFrameProxyHost(site_instance.get()));
2052 } else {
2053 EXPECT_FALSE(rfh_deleted_observer.deleted());
2058 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
2059 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
2060 // http://crbug.com/444955.
2061 TEST_F(RenderFrameHostManagerTest, DetachPendingChild) {
2062 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2063 switches::kSitePerProcess);
2065 const GURL kUrlA("http://www.google.com/");
2066 const GURL kUrlB("http://webkit.org/");
2068 // Create a page with two child frames.
2069 contents()->NavigateAndCommit(kUrlA);
2070 contents()->GetMainFrame()->OnCreateChildFrame(
2071 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2072 blink::WebTreeScopeType::Document, "frame_name",
2073 blink::WebSandboxFlags::None);
2074 contents()->GetMainFrame()->OnCreateChildFrame(
2075 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2076 blink::WebTreeScopeType::Document, "frame_name",
2077 blink::WebSandboxFlags::None);
2078 RenderFrameHostManager* root_manager =
2079 contents()->GetFrameTree()->root()->render_manager();
2080 RenderFrameHostManager* iframe1 =
2081 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2082 RenderFrameHostManager* iframe2 =
2083 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
2085 // 1) The first navigation.
2086 NavigationEntryImpl entryA(NULL /* instance */, -1 /* page_id */, kUrlA,
2087 Referrer(), base::string16() /* title */,
2088 ui::PAGE_TRANSITION_TYPED,
2089 false /* is_renderer_init */);
2090 RenderFrameHostImpl* host1 = NavigateToEntry(iframe1, entryA);
2092 // The RenderFrameHost created in Init will be reused.
2093 EXPECT_TRUE(host1 == iframe1->current_frame_host());
2094 EXPECT_FALSE(GetPendingFrameHost(iframe1));
2096 // Commit.
2097 iframe1->DidNavigateFrame(host1, true);
2098 // Commit to SiteInstance should be delayed until RenderFrame commit.
2099 EXPECT_TRUE(host1 == iframe1->current_frame_host());
2100 ASSERT_TRUE(host1);
2101 EXPECT_TRUE(host1->GetSiteInstance()->HasSite());
2103 // 2) Cross-site navigate both frames to next site.
2104 NavigationEntryImpl entryB(NULL /* instance */, -1 /* page_id */, kUrlB,
2105 Referrer(kUrlA, blink::WebReferrerPolicyDefault),
2106 base::string16() /* title */,
2107 ui::PAGE_TRANSITION_LINK,
2108 false /* is_renderer_init */);
2109 host1 = NavigateToEntry(iframe1, entryB);
2110 RenderFrameHostImpl* host2 = NavigateToEntry(iframe2, entryB);
2112 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2113 EXPECT_TRUE(GetPendingFrameHost(iframe1));
2114 EXPECT_TRUE(GetPendingFrameHost(iframe2));
2115 EXPECT_EQ(host1, GetPendingFrameHost(iframe1));
2116 EXPECT_EQ(host2, GetPendingFrameHost(iframe2));
2117 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2118 GetPendingFrameHost(iframe1)->rfh_state()));
2119 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2120 GetPendingFrameHost(iframe2)->rfh_state()));
2121 EXPECT_NE(GetPendingFrameHost(iframe1), GetPendingFrameHost(iframe2));
2122 EXPECT_EQ(GetPendingFrameHost(iframe1)->GetSiteInstance(),
2123 GetPendingFrameHost(iframe2)->GetSiteInstance());
2124 EXPECT_NE(iframe1->current_frame_host(), GetPendingFrameHost(iframe1));
2125 EXPECT_NE(iframe2->current_frame_host(), GetPendingFrameHost(iframe2));
2126 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2127 << "There should be no top-level pending navigation.";
2129 RenderFrameHostDeletedObserver delete_watcher1(GetPendingFrameHost(iframe1));
2130 RenderFrameHostDeletedObserver delete_watcher2(GetPendingFrameHost(iframe2));
2131 EXPECT_FALSE(delete_watcher1.deleted());
2132 EXPECT_FALSE(delete_watcher2.deleted());
2134 // Keep the SiteInstance alive for testing.
2135 scoped_refptr<SiteInstanceImpl> site_instance =
2136 GetPendingFrameHost(iframe1)->GetSiteInstance();
2137 EXPECT_TRUE(site_instance->HasSite());
2138 EXPECT_NE(site_instance, contents()->GetSiteInstance());
2139 EXPECT_EQ(2U, site_instance->active_frame_count());
2141 // Proxies should exist.
2142 EXPECT_NE(nullptr,
2143 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2144 EXPECT_NE(nullptr,
2145 iframe1->GetRenderFrameProxyHost(site_instance.get()));
2146 EXPECT_NE(nullptr,
2147 iframe2->GetRenderFrameProxyHost(site_instance.get()));
2149 // Detach the first child FrameTreeNode. This should kill the pending host but
2150 // not yet destroy proxies in |site_instance| since the other child remains.
2151 iframe1->current_frame_host()->OnMessageReceived(
2152 FrameHostMsg_Detach(iframe1->current_frame_host()->GetRoutingID()));
2153 iframe1 = NULL; // Was just destroyed.
2155 EXPECT_TRUE(delete_watcher1.deleted());
2156 EXPECT_FALSE(delete_watcher2.deleted());
2157 EXPECT_EQ(1U, site_instance->active_frame_count());
2159 // Proxies should still exist.
2160 EXPECT_NE(nullptr,
2161 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2162 EXPECT_NE(nullptr,
2163 iframe2->GetRenderFrameProxyHost(site_instance.get()));
2165 // Detach the second child FrameTreeNode. This should trigger cleanup of
2166 // RenderFrameProxyHosts in |site_instance|.
2167 iframe2->current_frame_host()->OnMessageReceived(
2168 FrameHostMsg_Detach(iframe2->current_frame_host()->GetRoutingID()));
2169 iframe2 = NULL; // Was just destroyed.
2171 EXPECT_TRUE(delete_watcher1.deleted());
2172 EXPECT_TRUE(delete_watcher2.deleted());
2174 EXPECT_EQ(0U, site_instance->active_frame_count());
2175 EXPECT_EQ(nullptr,
2176 root_manager->GetRenderFrameProxyHost(site_instance.get()))
2177 << "Proxies should have been cleaned up";
2178 EXPECT_TRUE(site_instance->HasOneRef())
2179 << "This SiteInstance should be destroyable now.";
2182 // Two tabs in the same process crash. The first tab is reloaded, and the second
2183 // tab navigates away without reloading. The second tab's navigation shouldn't
2184 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2185 TEST_F(RenderFrameHostManagerTest, TwoTabsCrashOneReloadsOneLeaves) {
2186 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2187 switches::kSitePerProcess);
2189 const GURL kUrl1("http://www.google.com/");
2190 const GURL kUrl2("http://webkit.org/");
2191 const GURL kUrl3("http://whatwg.org/");
2193 // |contents1| and |contents2| navigate to the same page and then crash.
2194 TestWebContents* contents1 = contents();
2195 scoped_ptr<TestWebContents> contents2(
2196 TestWebContents::Create(browser_context(), contents1->GetSiteInstance()));
2197 contents1->NavigateAndCommit(kUrl1);
2198 contents2->NavigateAndCommit(kUrl1);
2199 MockRenderProcessHost* rph = contents1->GetMainFrame()->GetProcess();
2200 EXPECT_EQ(rph, contents2->GetMainFrame()->GetProcess());
2201 rph->SimulateCrash();
2202 EXPECT_FALSE(contents1->GetMainFrame()->IsRenderFrameLive());
2203 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2204 EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
2206 // Reload |contents1|.
2207 contents1->NavigateAndCommit(kUrl1);
2208 EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
2209 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2210 EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
2212 // |contents1| creates an out of process iframe.
2213 contents1->GetMainFrame()->OnCreateChildFrame(
2214 contents1->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2215 blink::WebTreeScopeType::Document, "frame_name",
2216 blink::WebSandboxFlags::None);
2217 RenderFrameHostManager* iframe =
2218 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2219 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl2,
2220 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
2221 base::string16() /* title */,
2222 ui::PAGE_TRANSITION_LINK,
2223 false /* is_renderer_init */);
2224 RenderFrameHostImpl* cross_site = NavigateToEntry(iframe, entry);
2225 iframe->DidNavigateFrame(cross_site, true);
2227 // A proxy to the iframe should now exist in the SiteInstance of the main
2228 // frames.
2229 EXPECT_NE(cross_site->GetSiteInstance(), contents1->GetSiteInstance());
2230 EXPECT_NE(nullptr,
2231 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2232 EXPECT_NE(nullptr,
2233 iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
2235 // Navigate |contents2| away from the sad tab (and thus away from the
2236 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2237 // |contents1| -- that was http://crbug.com/473714.
2238 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2239 contents2->NavigateAndCommit(kUrl3);
2240 EXPECT_TRUE(contents2->GetMainFrame()->IsRenderFrameLive());
2241 EXPECT_NE(nullptr,
2242 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2243 EXPECT_EQ(nullptr,
2244 iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
2247 } // namespace content