Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / content / browser / frame_host / render_frame_host_manager_unittest.cc
blobc3aad10a064c59fa09b2a8fd6962d4b77e698698
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/site_isolation_policy.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/render_widget_host_iterator.h"
29 #include "content/public/browser/web_contents_delegate.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "content/public/browser/web_ui_controller.h"
32 #include "content/public/common/bindings_policy.h"
33 #include "content/public/common/content_switches.h"
34 #include "content/public/common/javascript_message_type.h"
35 #include "content/public/common/url_constants.h"
36 #include "content/public/common/url_utils.h"
37 #include "content/public/test/mock_render_process_host.h"
38 #include "content/public/test/test_notification_tracker.h"
39 #include "content/public/test/test_utils.h"
40 #include "content/test/test_content_browser_client.h"
41 #include "content/test/test_content_client.h"
42 #include "content/test/test_render_frame_host.h"
43 #include "content/test/test_render_view_host.h"
44 #include "content/test/test_web_contents.h"
45 #include "net/base/load_flags.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
48 #include "ui/base/page_transition_types.h"
50 namespace content {
51 namespace {
53 class RenderFrameHostManagerTestWebUIControllerFactory
54 : public WebUIControllerFactory {
55 public:
56 RenderFrameHostManagerTestWebUIControllerFactory()
57 : should_create_webui_(false) {
59 ~RenderFrameHostManagerTestWebUIControllerFactory() override {}
61 void set_should_create_webui(bool should_create_webui) {
62 should_create_webui_ = should_create_webui;
65 // WebUIFactory implementation.
66 WebUIController* CreateWebUIControllerForURL(WebUI* web_ui,
67 const GURL& url) const override {
68 if (!(should_create_webui_ && HasWebUIScheme(url)))
69 return NULL;
70 return new WebUIController(web_ui);
73 WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
74 const GURL& url) const override {
75 return WebUI::kNoWebUI;
78 bool UseWebUIForURL(BrowserContext* browser_context,
79 const GURL& url) const override {
80 return HasWebUIScheme(url);
83 bool UseWebUIBindingsForURL(BrowserContext* browser_context,
84 const GURL& url) const override {
85 return HasWebUIScheme(url);
88 private:
89 bool should_create_webui_;
91 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
94 class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
95 public:
96 BeforeUnloadFiredWebContentsDelegate() {}
97 ~BeforeUnloadFiredWebContentsDelegate() override {}
99 void BeforeUnloadFired(WebContents* web_contents,
100 bool proceed,
101 bool* proceed_to_fire_unload) override {
102 *proceed_to_fire_unload = proceed;
105 private:
106 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
109 class CloseWebContentsDelegate : public WebContentsDelegate {
110 public:
111 CloseWebContentsDelegate() : close_called_(false) {}
112 ~CloseWebContentsDelegate() override {}
114 void CloseContents(WebContents* web_contents) override {
115 close_called_ = true;
118 bool is_closed() { return close_called_; }
120 private:
121 DISALLOW_COPY_AND_ASSIGN(CloseWebContentsDelegate);
123 bool close_called_;
126 // This observer keeps track of the last deleted RenderViewHost to avoid
127 // accessing it and causing use-after-free condition.
128 class RenderViewHostDeletedObserver : public WebContentsObserver {
129 public:
130 RenderViewHostDeletedObserver(RenderViewHost* rvh)
131 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
132 process_id_(rvh->GetProcess()->GetID()),
133 routing_id_(rvh->GetRoutingID()),
134 deleted_(false) {
137 void RenderViewDeleted(RenderViewHost* render_view_host) override {
138 if (render_view_host->GetProcess()->GetID() == process_id_ &&
139 render_view_host->GetRoutingID() == routing_id_) {
140 deleted_ = true;
144 bool deleted() {
145 return deleted_;
148 private:
149 int process_id_;
150 int routing_id_;
151 bool deleted_;
153 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver);
156 // This observer keeps track of the last created RenderFrameHost to allow tests
157 // to ensure that no RenderFrameHost objects are created when not expected.
158 class RenderFrameHostCreatedObserver : public WebContentsObserver {
159 public:
160 RenderFrameHostCreatedObserver(WebContents* web_contents)
161 : WebContentsObserver(web_contents),
162 created_(false) {
165 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
166 created_ = true;
169 bool created() {
170 return created_;
173 private:
174 bool created_;
176 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver);
179 // This WebContents observer keep track of its RVH change.
180 class RenderViewHostChangedObserver : public WebContentsObserver {
181 public:
182 RenderViewHostChangedObserver(WebContents* web_contents)
183 : WebContentsObserver(web_contents), host_changed_(false) {}
185 // WebContentsObserver.
186 void RenderViewHostChanged(RenderViewHost* old_host,
187 RenderViewHost* new_host) override {
188 host_changed_ = true;
191 bool DidHostChange() {
192 bool host_changed = host_changed_;
193 Reset();
194 return host_changed;
197 void Reset() { host_changed_ = false; }
199 private:
200 bool host_changed_;
201 DISALLOW_COPY_AND_ASSIGN(RenderViewHostChangedObserver);
204 // This observer is used to check whether IPC messages are being filtered for
205 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
206 // update events, which the FilterMessagesWhileSwappedOut test simulates being
207 // sent. The test is successful if the event is not observed.
208 // See http://crbug.com/351815
209 class PluginFaviconMessageObserver : public WebContentsObserver {
210 public:
211 PluginFaviconMessageObserver(WebContents* web_contents)
212 : WebContentsObserver(web_contents),
213 plugin_crashed_(false),
214 favicon_received_(false) { }
216 void PluginCrashed(const base::FilePath& plugin_path,
217 base::ProcessId plugin_pid) override {
218 plugin_crashed_ = true;
221 void DidUpdateFaviconURL(const std::vector<FaviconURL>& candidates) override {
222 favicon_received_ = true;
225 bool plugin_crashed() {
226 return plugin_crashed_;
229 bool favicon_received() {
230 return favicon_received_;
233 private:
234 bool plugin_crashed_;
235 bool favicon_received_;
237 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
240 } // namespace
242 class RenderFrameHostManagerTest : public RenderViewHostImplTestHarness {
243 public:
244 void SetUp() override {
245 RenderViewHostImplTestHarness::SetUp();
246 WebUIControllerFactory::RegisterFactory(&factory_);
247 #if !defined(OS_ANDROID)
248 ImageTransportFactory::InitializeForUnitTests(
249 make_scoped_ptr(new NoTransportImageTransportFactory));
250 #endif
253 void TearDown() override {
254 #if !defined(OS_ANDROID)
255 ImageTransportFactory::Terminate();
256 #endif
257 RenderViewHostImplTestHarness::TearDown();
258 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
261 void set_should_create_webui(bool should_create_webui) {
262 factory_.set_should_create_webui(should_create_webui);
265 void NavigateActiveAndCommit(const GURL& url) {
266 // Note: we navigate the active RenderFrameHost because previous navigations
267 // won't have committed yet, so NavigateAndCommit does the wrong thing
268 // for us.
269 controller().LoadURL(
270 url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
271 int entry_id = controller().GetPendingEntry()->GetUniqueID();
273 // Simulate the BeforeUnload_ACK that is received from the current renderer
274 // for a cross-site navigation.
275 // PlzNavigate: it is necessary to call PrepareForCommit before getting the
276 // main and the pending frame because when we are trying to navigate to a
277 // WebUI from a new tab, a RenderFrameHost is created to display it that is
278 // committed immediately (since it is a new tab). Therefore the main frame
279 // is replaced without a pending frame being created, and we don't get the
280 // right values for the RFH to navigate: we try to use the old one that has
281 // been deleted in the meantime.
282 contents()->GetMainFrame()->PrepareForCommit();
284 TestRenderFrameHost* old_rfh = contents()->GetMainFrame();
285 TestRenderFrameHost* active_rfh = contents()->GetPendingMainFrame()
286 ? contents()->GetPendingMainFrame()
287 : old_rfh;
288 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, old_rfh->rfh_state());
290 // Commit the navigation with a new page ID.
291 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
292 active_rfh->GetSiteInstance());
294 // Use an observer to avoid accessing a deleted renderer later on when the
295 // state is being checked.
296 RenderFrameDeletedObserver rfh_observer(old_rfh);
297 RenderViewHostDeletedObserver rvh_observer(old_rfh->GetRenderViewHost());
298 active_rfh->SendNavigate(max_page_id + 1, entry_id, true, url);
300 // Make sure that we start to run the unload handler at the time of commit.
301 bool expecting_rfh_shutdown = false;
302 if (old_rfh != active_rfh && !rfh_observer.deleted()) {
303 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT,
304 old_rfh->rfh_state());
305 if (!old_rfh->GetSiteInstance()->active_frame_count() ||
306 SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
307 expecting_rfh_shutdown = true;
308 EXPECT_TRUE(
309 old_rfh->frame_tree_node()->render_manager()->IsPendingDeletion(
310 old_rfh));
314 // Simulate the swap out ACK coming from the pending renderer. This should
315 // either shut down the old RFH or leave it in a swapped out state.
316 if (old_rfh != active_rfh) {
317 old_rfh->OnSwappedOut();
318 if (expecting_rfh_shutdown) {
319 EXPECT_TRUE(rfh_observer.deleted());
320 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
321 EXPECT_TRUE(rvh_observer.deleted());
323 } else {
324 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT,
325 old_rfh->rfh_state());
328 EXPECT_EQ(active_rfh, contents()->GetMainFrame());
329 EXPECT_EQ(NULL, contents()->GetPendingMainFrame());
332 bool ShouldSwapProcesses(RenderFrameHostManager* manager,
333 const NavigationEntryImpl* current_entry,
334 const NavigationEntryImpl* new_entry) const {
335 CHECK(new_entry);
336 BrowserContext* browser_context =
337 manager->delegate_->GetControllerForRenderManager().GetBrowserContext();
338 const GURL& current_effective_url = current_entry ?
339 SiteInstanceImpl::GetEffectiveURL(browser_context,
340 current_entry->GetURL()) :
341 manager->render_frame_host_->GetSiteInstance()->GetSiteURL();
342 bool current_is_view_source_mode = current_entry ?
343 current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode();
344 return manager->ShouldSwapBrowsingInstancesForNavigation(
345 current_effective_url,
346 current_is_view_source_mode,
347 new_entry->site_instance(),
348 SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()),
349 new_entry->IsViewSourceMode());
352 // Creates a test RenderFrameHost that's swapped out.
353 TestRenderFrameHost* CreateSwappedOutRenderFrameHost() {
354 const GURL kChromeURL("chrome://foo");
355 const GURL kDestUrl("http://www.google.com/");
357 // Navigate our first tab to a chrome url and then to the destination.
358 NavigateActiveAndCommit(kChromeURL);
359 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
361 // Navigate to a cross-site URL.
362 contents()->GetController().LoadURL(
363 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
364 int entry_id = contents()->GetController().GetPendingEntry()->GetUniqueID();
365 contents()->GetMainFrame()->PrepareForCommit();
366 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
368 // Manually increase the number of active frames in the
369 // SiteInstance that ntp_rfh belongs to, to prevent it from being
370 // destroyed when it gets swapped out.
371 ntp_rfh->GetSiteInstance()->increment_active_frame_count();
373 TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame();
374 CHECK(dest_rfh);
375 EXPECT_NE(ntp_rfh, dest_rfh);
377 // BeforeUnload finishes.
378 ntp_rfh->SendBeforeUnloadACK(true);
380 dest_rfh->SendNavigate(101, entry_id, true, kDestUrl);
381 ntp_rfh->OnSwappedOut();
383 EXPECT_TRUE(ntp_rfh->is_swapped_out());
384 return ntp_rfh;
387 // Returns the RenderFrameHost that should be used in the navigation to
388 // |entry|.
389 RenderFrameHostImpl* NavigateToEntry(
390 RenderFrameHostManager* manager,
391 const NavigationEntryImpl& entry) {
392 // Tests currently only navigate using main frame FrameNavigationEntries.
393 FrameNavigationEntry* frame_entry = entry.root_node()->frame_entry.get();
394 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
395 switches::kEnableBrowserSideNavigation)) {
396 scoped_ptr<NavigationRequest> navigation_request =
397 NavigationRequest::CreateBrowserInitiated(
398 manager->frame_tree_node_, frame_entry->url(),
399 frame_entry->referrer(), *frame_entry, entry,
400 FrameMsg_Navigate_Type::NORMAL, false, base::TimeTicks::Now(),
401 static_cast<NavigationControllerImpl*>(&controller()));
402 TestRenderFrameHost* frame_host = static_cast<TestRenderFrameHost*>(
403 manager->GetFrameHostForNavigation(*navigation_request));
404 CHECK(frame_host);
405 frame_host->set_pending_commit(true);
406 return frame_host;
409 return manager->Navigate(frame_entry->url(), *frame_entry, entry);
412 // Returns the pending RenderFrameHost.
413 // PlzNavigate: returns the speculative RenderFrameHost.
414 RenderFrameHostImpl* GetPendingFrameHost(
415 RenderFrameHostManager* manager) {
416 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
417 switches::kEnableBrowserSideNavigation)) {
418 return manager->speculative_render_frame_host_.get();
420 return manager->pending_frame_host();
423 private:
424 RenderFrameHostManagerTestWebUIControllerFactory factory_;
427 // Tests that when you navigate from a chrome:// url to another page, and
428 // then do that same thing in another tab, that the two resulting pages have
429 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
430 // a regression test for bug 9364.
431 TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
432 set_should_create_webui(true);
433 const GURL kChromeUrl("chrome://foo");
434 const GURL kDestUrl("http://www.google.com/");
436 // Navigate our first tab to the chrome url and then to the destination,
437 // ensuring we grant bindings to the chrome URL.
438 NavigateActiveAndCommit(kChromeUrl);
439 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
440 NavigateActiveAndCommit(kDestUrl);
442 EXPECT_FALSE(contents()->GetPendingMainFrame());
444 // Make a second tab.
445 scoped_ptr<TestWebContents> contents2(
446 TestWebContents::Create(browser_context(), NULL));
448 // Load the two URLs in the second tab. Note that the first navigation creates
449 // a RFH that's not pending (since there is no cross-site transition), so
450 // we use the committed one.
451 contents2->GetController().LoadURL(
452 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
453 int entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
454 contents2->GetMainFrame()->PrepareForCommit();
455 TestRenderFrameHost* ntp_rfh2 = contents2->GetMainFrame();
456 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
457 ntp_rfh2->SendNavigate(100, entry_id, true, kChromeUrl);
459 // The second one is the opposite, creating a cross-site transition and
460 // requiring a beforeunload ack.
461 contents2->GetController().LoadURL(
462 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
463 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
464 contents2->GetMainFrame()->PrepareForCommit();
465 EXPECT_TRUE(contents2->CrossProcessNavigationPending());
466 TestRenderFrameHost* dest_rfh2 = contents2->GetPendingMainFrame();
467 ASSERT_TRUE(dest_rfh2);
469 dest_rfh2->SendNavigate(101, entry_id, true, kDestUrl);
471 // The two RFH's should be different in every way.
472 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2->GetProcess());
473 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
474 dest_rfh2->GetSiteInstance());
475 EXPECT_FALSE(dest_rfh2->GetSiteInstance()->IsRelatedSiteInstance(
476 contents()->GetMainFrame()->GetSiteInstance()));
478 // Navigate both to the new tab page, and verify that they share a
479 // RenderProcessHost (not a SiteInstance).
480 NavigateActiveAndCommit(kChromeUrl);
481 EXPECT_FALSE(contents()->GetPendingMainFrame());
483 contents2->GetController().LoadURL(
484 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
485 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
486 contents2->GetMainFrame()->PrepareForCommit();
487 contents2->GetPendingMainFrame()->SendNavigate(102, entry_id, true,
488 kChromeUrl);
490 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
491 contents2->GetMainFrame()->GetSiteInstance());
492 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
493 contents2->GetMainFrame()->GetSiteInstance()->GetProcess());
496 // Ensure that the browser ignores most IPC messages that arrive from a
497 // RenderViewHost that has been swapped out. We do not want to take
498 // action on requests from a non-active renderer. The main exception is
499 // for synchronous messages, which cannot be ignored without leaving the
500 // renderer in a stuck state. See http://crbug.com/93427.
501 TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
502 const GURL kChromeURL("chrome://foo");
503 const GURL kDestUrl("http://www.google.com/");
504 std::vector<FaviconURL> icons;
506 // Navigate our first tab to a chrome url and then to the destination.
507 NavigateActiveAndCommit(kChromeURL);
508 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
509 TestRenderViewHost* ntp_rvh = ntp_rfh->GetRenderViewHost();
511 // Send an update favicon message and make sure it works.
513 PluginFaviconMessageObserver observer(contents());
514 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->OnMessageReceived(
515 ViewHostMsg_UpdateFaviconURL(
516 ntp_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
517 EXPECT_TRUE(observer.favicon_received());
519 // Create one more frame in the same SiteInstance where ntp_rfh
520 // exists so that it doesn't get deleted on navigation to another
521 // site.
522 ntp_rfh->GetSiteInstance()->increment_active_frame_count();
524 // Navigate to a cross-site URL.
525 NavigateActiveAndCommit(kDestUrl);
526 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
527 ASSERT_TRUE(dest_rfh);
528 EXPECT_NE(ntp_rfh, dest_rfh);
530 // The new RVH should be able to update its favicon.
532 PluginFaviconMessageObserver observer(contents());
533 EXPECT_TRUE(
534 dest_rfh->GetRenderViewHost()->OnMessageReceived(
535 ViewHostMsg_UpdateFaviconURL(
536 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
537 EXPECT_TRUE(observer.favicon_received());
540 // The old renderer, being slow, now updates the favicon. It should be
541 // filtered out and not take effect.
543 PluginFaviconMessageObserver observer(contents());
544 EXPECT_TRUE(
545 ntp_rvh->OnMessageReceived(
546 ViewHostMsg_UpdateFaviconURL(
547 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
548 EXPECT_FALSE(observer.favicon_received());
551 // In --site-per-process, the RenderFrameHost is deleted on cross-process
552 // navigation, so the rest of the test case doesn't apply.
553 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
554 return;
557 #if defined(ENABLE_PLUGINS)
558 // The same logic should apply to RenderFrameHosts as well and routing through
559 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
560 // if the IPC message is allowed through or not.
562 PluginFaviconMessageObserver observer(contents());
563 EXPECT_TRUE(ntp_rfh->OnMessageReceived(
564 FrameHostMsg_PluginCrashed(
565 ntp_rfh->GetRoutingID(), base::FilePath(), 0)));
566 EXPECT_FALSE(observer.plugin_crashed());
568 #endif
570 // We cannot filter out synchronous IPC messages, because the renderer would
571 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
572 // that can run easily within a unit test, and that needs to receive a reply
573 // without showing an actual dialog.
574 MockRenderProcessHost* ntp_process_host = ntp_rfh->GetProcess();
575 ntp_process_host->sink().ClearMessages();
576 const base::string16 msg = base::ASCIIToUTF16("Message");
577 bool result = false;
578 base::string16 unused;
579 FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
580 ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
581 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
582 before_unload_msg.EnableMessagePumping();
583 EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
584 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
586 // Also test RunJavaScriptMessage.
587 ntp_process_host->sink().ClearMessages();
588 FrameHostMsg_RunJavaScriptMessage js_msg(
589 ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
590 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
591 js_msg.EnableMessagePumping();
592 EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
593 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
596 // Test that the ViewHostMsg_UpdateFaviconURL IPC message is ignored if the
597 // renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
598 // that it only gets ViewHostMsg_UpdateFaviconURL messages for the most recently
599 // committed navigation for each WebContentsImpl.
600 TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingSwapOut) {
601 const GURL kChromeURL("chrome://foo");
602 const GURL kDestUrl("http://www.google.com/");
603 std::vector<FaviconURL> icons;
605 // Navigate our first tab to a chrome url and then to the destination.
606 NavigateActiveAndCommit(kChromeURL);
607 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
609 // Send an update favicon message and make sure it works.
611 PluginFaviconMessageObserver observer(contents());
612 EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived(
613 ViewHostMsg_UpdateFaviconURL(
614 rfh1->GetRenderViewHost()->GetRoutingID(), icons)));
615 EXPECT_TRUE(observer.favicon_received());
618 // Create one more frame in the same SiteInstance where |rfh1| exists so that
619 // it doesn't get deleted on navigation to another site.
620 rfh1->GetSiteInstance()->increment_active_frame_count();
622 // Navigate to a cross-site URL and commit the new page.
623 controller().LoadURL(
624 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
625 int entry_id = controller().GetPendingEntry()->GetUniqueID();
626 contents()->GetMainFrame()->PrepareForCommit();
627 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
628 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kDestUrl,
629 ui::PAGE_TRANSITION_TYPED);
630 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
631 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
633 // The new RVH should be able to update its favicons.
635 PluginFaviconMessageObserver observer(contents());
636 EXPECT_TRUE(rfh2->GetRenderViewHost()->OnMessageReceived(
637 ViewHostMsg_UpdateFaviconURL(rfh2->GetRenderViewHost()->GetRoutingID(),
638 icons)));
639 EXPECT_TRUE(observer.favicon_received());
642 // The old renderer, being slow, now updates its favicons. The message should
643 // be ignored.
645 PluginFaviconMessageObserver observer(contents());
646 EXPECT_TRUE(rfh1->GetRenderViewHost()->OnMessageReceived(
647 ViewHostMsg_UpdateFaviconURL(rfh1->GetRenderViewHost()->GetRoutingID(),
648 icons)));
649 EXPECT_FALSE(observer.favicon_received());
653 // Ensure that frames aren't added to the frame tree, if the message is coming
654 // from a process different than the parent frame's current RenderFrameHost
655 // process. Otherwise it is possible to have collisions of routing ids, as they
656 // are scoped per process. See https://crbug.com/415059.
657 TEST_F(RenderFrameHostManagerTest, DropCreateChildFrameWhileSwappedOut) {
658 const GURL kUrl1("http://foo.com");
659 const GURL kUrl2("http://www.google.com/");
661 // This test is invalid in --site-per-process mode, as swapped-out is no
662 // longer used.
663 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
664 return;
667 // Navigate to the first site.
668 NavigateActiveAndCommit(kUrl1);
669 TestRenderFrameHost* initial_rfh = contents()->GetMainFrame();
671 RenderFrameHostCreatedObserver observer(contents());
672 initial_rfh->OnCreateChildFrame(
673 initial_rfh->GetProcess()->GetNextRoutingID(),
674 blink::WebTreeScopeType::Document, std::string(),
675 blink::WebSandboxFlags::None);
676 EXPECT_TRUE(observer.created());
679 // Create one more frame in the same SiteInstance where initial_rfh
680 // exists so that initial_rfh doesn't get deleted on navigation to another
681 // site.
682 initial_rfh->GetSiteInstance()->increment_active_frame_count();
684 // Navigate to a cross-site URL.
685 NavigateActiveAndCommit(kUrl2);
686 EXPECT_TRUE(initial_rfh->is_swapped_out());
688 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
689 ASSERT_TRUE(dest_rfh);
690 EXPECT_NE(initial_rfh, dest_rfh);
693 // Since the old RFH is now swapped out, it shouldn't process any messages
694 // to create child frames.
695 RenderFrameHostCreatedObserver observer(contents());
696 initial_rfh->OnCreateChildFrame(
697 initial_rfh->GetProcess()->GetNextRoutingID(),
698 blink::WebTreeScopeType::Document, std::string(),
699 blink::WebSandboxFlags::None);
700 EXPECT_FALSE(observer.created());
704 TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
705 // TODO(nasko): Check with kenrb whether this test can be rewritten and
706 // whether it makes sense when swapped out is replaced with proxies.
707 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
708 return;
710 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
711 TestRenderWidgetHostView* swapped_out_rwhv =
712 static_cast<TestRenderWidgetHostView*>(
713 swapped_out_rfh->GetRenderViewHost()->GetView());
714 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame());
716 MockRenderProcessHost* process_host = swapped_out_rfh->GetProcess();
717 process_host->sink().ClearMessages();
719 cc::CompositorFrame frame;
720 ViewHostMsg_SwapCompositorFrame msg(
721 rvh()->GetRoutingID(), 0, frame, std::vector<IPC::Message>());
723 EXPECT_TRUE(swapped_out_rfh->render_view_host()->OnMessageReceived(msg));
724 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
727 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
728 // widgets.
729 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
730 // This test is invalid in --site-per-process mode, as swapped-out is no
731 // longer used.
732 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
733 return;
736 TestRenderFrameHost* swapped_out_rfh = CreateSwappedOutRenderFrameHost();
737 EXPECT_TRUE(swapped_out_rfh->is_swapped_out());
739 scoped_ptr<RenderWidgetHostIterator> widgets(
740 RenderWidgetHost::GetRenderWidgetHosts());
741 // We know that there is the only one active widget. Another view is
742 // now swapped out, so the swapped out view is not included in the
743 // list.
744 RenderWidgetHost* widget = widgets->GetNextHost();
745 EXPECT_FALSE(widgets->GetNextHost());
746 RenderViewHost* rvh = RenderViewHost::From(widget);
747 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(rvh)->is_active());
750 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
751 // RenderViewHostImpl::GetAllRenderWidgetHosts().
752 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
753 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
754 // including swapped out ones.
755 TEST_F(RenderFrameHostManagerTest,
756 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
757 // This test is invalid in --site-per-process mode, as swapped-out is no
758 // longer used.
759 if (SiteIsolationPolicy::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());
769 while (RenderWidgetHost* w = widgets->GetNextHost()) {
770 bool found = false;
771 scoped_ptr<RenderWidgetHostIterator> all_widgets(
772 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
773 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
774 if (w == widget) {
775 found = true;
776 break;
779 EXPECT_TRUE(found);
783 // Test if SiteInstanceImpl::active_frame_count() is correctly updated
784 // as frames in a SiteInstance get swapped out and in.
785 TEST_F(RenderFrameHostManagerTest, ActiveFrameCountWhileSwappingInAndOut) {
786 const GURL kUrl1("http://www.google.com/");
787 const GURL kUrl2("http://www.chromium.org/");
789 // Navigate to an initial URL.
790 contents()->NavigateAndCommit(kUrl1);
791 TestRenderFrameHost* rfh1 = main_test_rfh();
793 SiteInstanceImpl* instance1 = rfh1->GetSiteInstance();
794 EXPECT_EQ(instance1->active_frame_count(), 1U);
796 // Create 2 new tabs and simulate them being the opener chain for the main
797 // tab. They should be in the same SiteInstance.
798 scoped_ptr<TestWebContents> opener1(
799 TestWebContents::Create(browser_context(), instance1));
800 contents()->SetOpener(opener1.get());
802 scoped_ptr<TestWebContents> opener2(
803 TestWebContents::Create(browser_context(), instance1));
804 opener1->SetOpener(opener2.get());
806 EXPECT_EQ(instance1->active_frame_count(), 3U);
808 // Navigate to a cross-site URL (different SiteInstance but same
809 // BrowsingInstance).
810 contents()->NavigateAndCommit(kUrl2);
811 TestRenderFrameHost* rfh2 = main_test_rfh();
812 SiteInstanceImpl* instance2 = rfh2->GetSiteInstance();
814 // rvh2 is on chromium.org which is different from google.com on
815 // which other tabs are.
816 EXPECT_EQ(instance2->active_frame_count(), 1U);
818 // There are two active views on google.com now.
819 EXPECT_EQ(instance1->active_frame_count(), 2U);
821 // Navigate to the original origin (google.com).
822 contents()->NavigateAndCommit(kUrl1);
824 EXPECT_EQ(instance1->active_frame_count(), 3U);
827 // This deletes a WebContents when the given RVH is deleted. This is
828 // only for testing whether deleting an RVH does not cause any UaF in
829 // other parts of the system. For now, this class is only used for the
830 // next test cases to detect the bug mentioned at
831 // http://crbug.com/259859.
832 class RenderViewHostDestroyer : public WebContentsObserver {
833 public:
834 RenderViewHostDestroyer(RenderViewHost* render_view_host,
835 WebContents* web_contents)
836 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
837 render_view_host_(render_view_host),
838 web_contents_(web_contents) {}
840 void RenderViewDeleted(RenderViewHost* render_view_host) override {
841 if (render_view_host == render_view_host_)
842 delete web_contents_;
845 private:
846 RenderViewHost* render_view_host_;
847 WebContents* web_contents_;
849 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
852 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
853 // RenderWidget that has been freed while deleting a RenderViewHost in
854 // a previous iteration. This is a regression test for
855 // http://crbug.com/259859.
856 TEST_F(RenderFrameHostManagerTest,
857 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {
858 const GURL kChromeURL("chrome://newtab");
859 const GURL kUrl1("http://www.google.com");
860 const GURL kUrl2("http://www.chromium.org");
862 // Navigate our first tab to a chrome url and then to the destination.
863 NavigateActiveAndCommit(kChromeURL);
864 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
866 // Create one more tab and navigate to kUrl1. web_contents is not
867 // wrapped as scoped_ptr since it intentionally deleted by destroyer
868 // below as part of this test.
869 TestWebContents* web_contents =
870 TestWebContents::Create(browser_context(), ntp_rfh->GetSiteInstance());
871 web_contents->NavigateAndCommit(kUrl1);
872 RenderViewHostDestroyer destroyer(ntp_rfh->GetRenderViewHost(),
873 web_contents);
875 // This causes the first tab to navigate to kUrl2, which destroys
876 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
877 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
878 // too. This can test whether
879 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
880 // touch any object freed in this way or not while iterating through
881 // all widgets.
882 contents()->NavigateAndCommit(kUrl2);
885 // When there is an error with the specified page, renderer exits view-source
886 // mode. See WebFrameImpl::DidFail(). We check by this test that
887 // EnableViewSourceMode message is sent on every navigation regardless
888 // RenderView is being newly created or reused.
889 TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
890 const GURL kChromeUrl("chrome://foo/");
891 const GURL kUrl("http://foo/");
892 const GURL kViewSourceUrl("view-source:http://foo/");
894 // We have to navigate to some page at first since without this, the first
895 // navigation will reuse the SiteInstance created by Init(), and the second
896 // one will create a new SiteInstance. Because current_instance and
897 // new_instance will be different, a new RenderViewHost will be created for
898 // the second navigation. We have to avoid this in order to exercise the
899 // target code path.
900 NavigateActiveAndCommit(kChromeUrl);
902 // Navigate. Note that "view source" URLs are implemented by putting the RFH
903 // into a view-source mode and then navigating to the inner URL, so that's why
904 // the bare URL is what's committed and returned by the last committed entry's
905 // GetURL() call.
906 controller().LoadURL(
907 kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
908 int entry_id = controller().GetPendingEntry()->GetUniqueID();
910 // Simulate response from RenderFrame for DispatchBeforeUnload.
911 contents()->GetMainFrame()->PrepareForCommit();
912 ASSERT_TRUE(contents()->GetPendingMainFrame())
913 << "Expected new pending RenderFrameHost to be created.";
914 RenderFrameHost* last_rfh = contents()->GetPendingMainFrame();
915 int32 new_id =
916 contents()->GetMaxPageIDForSiteInstance(last_rfh->GetSiteInstance()) + 1;
917 contents()->GetPendingMainFrame()->SendNavigate(new_id, entry_id, true, kUrl);
919 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
920 NavigationEntry* last_committed = controller().GetLastCommittedEntry();
921 ASSERT_NE(nullptr, last_committed);
922 EXPECT_EQ(kUrl, last_committed->GetURL());
923 EXPECT_EQ(kViewSourceUrl, last_committed->GetVirtualURL());
924 EXPECT_FALSE(controller().GetPendingEntry());
925 // Because we're using TestWebContents and TestRenderViewHost in this
926 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
927 // EnableViewSourceMode message, here.
929 // Clear queued messages before load.
930 process()->sink().ClearMessages();
932 // Navigate, again.
933 controller().LoadURL(
934 kViewSourceUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
935 entry_id = controller().GetPendingEntry()->GetUniqueID();
936 contents()->GetMainFrame()->PrepareForCommit();
938 // The same RenderViewHost should be reused.
939 EXPECT_FALSE(contents()->GetPendingMainFrame());
940 EXPECT_EQ(last_rfh, contents()->GetMainFrame());
942 // The renderer sends a commit.
943 contents()->GetMainFrame()->SendNavigateWithTransition(
944 new_id, entry_id, false, kUrl, ui::PAGE_TRANSITION_TYPED);
945 EXPECT_EQ(1, controller().GetLastCommittedEntryIndex());
946 EXPECT_FALSE(controller().GetPendingEntry());
948 // New message should be sent out to make sure to enter view-source mode.
949 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
950 ViewMsg_EnableViewSourceMode::ID));
953 // Tests the Init function by checking the initial RenderViewHost.
954 TEST_F(RenderFrameHostManagerTest, Init) {
955 // Using TestBrowserContext.
956 SiteInstanceImpl* instance =
957 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
958 EXPECT_FALSE(instance->HasSite());
960 scoped_ptr<TestWebContents> web_contents(
961 TestWebContents::Create(browser_context(), instance));
963 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
964 RenderViewHostImpl* rvh = manager->current_host();
965 RenderFrameHostImpl* rfh = manager->current_frame_host();
966 ASSERT_TRUE(rvh);
967 ASSERT_TRUE(rfh);
968 EXPECT_EQ(rvh, rfh->render_view_host());
969 EXPECT_EQ(instance, rvh->GetSiteInstance());
970 EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
971 EXPECT_EQ(web_contents.get(), rfh->delegate());
972 EXPECT_TRUE(manager->GetRenderWidgetHostView());
973 EXPECT_FALSE(manager->pending_render_view_host());
976 // Tests the Navigate function. We navigate three sites consecutively and check
977 // how the pending/committed RenderViewHost are modified.
978 TEST_F(RenderFrameHostManagerTest, Navigate) {
979 SiteInstance* instance = SiteInstance::Create(browser_context());
981 scoped_ptr<TestWebContents> web_contents(
982 TestWebContents::Create(browser_context(), instance));
983 RenderViewHostChangedObserver change_observer(web_contents.get());
985 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
986 RenderFrameHostImpl* host = NULL;
988 // 1) The first navigation. --------------------------
989 const GURL kUrl1("http://www.google.com/");
990 NavigationEntryImpl entry1(
991 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
992 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
993 false /* is_renderer_init */);
994 host = NavigateToEntry(manager, entry1);
996 // The RenderFrameHost created in Init will be reused.
997 EXPECT_TRUE(host == manager->current_frame_host());
998 EXPECT_FALSE(GetPendingFrameHost(manager));
1000 // Commit.
1001 manager->DidNavigateFrame(host, true);
1002 // Commit to SiteInstance should be delayed until RenderFrame commit.
1003 EXPECT_TRUE(host == manager->current_frame_host());
1004 ASSERT_TRUE(host);
1005 EXPECT_FALSE(host->GetSiteInstance()->HasSite());
1006 host->GetSiteInstance()->SetSite(kUrl1);
1008 // 2) Navigate to next site. -------------------------
1009 const GURL kUrl2("http://www.google.com/foo");
1010 NavigationEntryImpl entry2(
1011 NULL /* instance */, -1 /* page_id */, kUrl2,
1012 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1013 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1014 true /* is_renderer_init */);
1015 host = NavigateToEntry(manager, entry2);
1017 // The RenderFrameHost created in Init will be reused.
1018 EXPECT_TRUE(host == manager->current_frame_host());
1019 EXPECT_FALSE(GetPendingFrameHost(manager));
1021 // Commit.
1022 manager->DidNavigateFrame(host, true);
1023 EXPECT_TRUE(host == manager->current_frame_host());
1024 ASSERT_TRUE(host);
1025 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1027 // 3) Cross-site navigate to next site. --------------
1028 const GURL kUrl3("http://webkit.org/");
1029 NavigationEntryImpl entry3(
1030 NULL /* instance */, -1 /* page_id */, kUrl3,
1031 Referrer(kUrl2, blink::WebReferrerPolicyDefault),
1032 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1033 false /* is_renderer_init */);
1034 host = NavigateToEntry(manager, entry3);
1036 // A new RenderFrameHost should be created.
1037 EXPECT_TRUE(GetPendingFrameHost(manager));
1038 ASSERT_EQ(host, GetPendingFrameHost(manager));
1040 change_observer.Reset();
1042 // Commit.
1043 manager->DidNavigateFrame(GetPendingFrameHost(manager), true);
1044 EXPECT_TRUE(host == manager->current_frame_host());
1045 ASSERT_TRUE(host);
1046 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1047 // Check the pending RenderFrameHost has been committed.
1048 EXPECT_FALSE(GetPendingFrameHost(manager));
1050 // We should observe RVH changed event.
1051 EXPECT_TRUE(change_observer.DidHostChange());
1054 // Tests WebUI creation.
1055 TEST_F(RenderFrameHostManagerTest, WebUI) {
1056 set_should_create_webui(true);
1057 SiteInstance* instance = SiteInstance::Create(browser_context());
1059 scoped_ptr<TestWebContents> web_contents(
1060 TestWebContents::Create(browser_context(), instance));
1061 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1062 RenderFrameHostImpl* initial_rfh = manager->current_frame_host();
1064 EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
1065 EXPECT_FALSE(manager->web_ui());
1066 EXPECT_TRUE(initial_rfh);
1068 const GURL kUrl("chrome://foo");
1069 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
1070 Referrer(), base::string16() /* title */,
1071 ui::PAGE_TRANSITION_TYPED,
1072 false /* is_renderer_init */);
1073 RenderFrameHostImpl* host = NavigateToEntry(manager, entry);
1075 // We commit the pending RenderFrameHost immediately because the previous
1076 // RenderFrameHost was not live. We test a case where it is live in
1077 // WebUIInNewTab.
1078 EXPECT_TRUE(host);
1079 EXPECT_NE(initial_rfh, host);
1080 EXPECT_EQ(host, manager->current_frame_host());
1081 EXPECT_FALSE(GetPendingFrameHost(manager));
1083 // It's important that the SiteInstance get set on the Web UI page as soon
1084 // as the navigation starts, rather than lazily after it commits, so we don't
1085 // try to re-use the SiteInstance/process for non Web UI things that may
1086 // get loaded in between.
1087 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1088 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
1090 // The Web UI is committed immediately because the RenderViewHost has not been
1091 // used yet. UpdateStateForNavigate() took the short cut path.
1092 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1093 switches::kEnableBrowserSideNavigation)) {
1094 EXPECT_FALSE(manager->speculative_web_ui());
1095 } else {
1096 EXPECT_FALSE(manager->pending_web_ui());
1098 EXPECT_TRUE(manager->web_ui());
1100 // Commit.
1101 manager->DidNavigateFrame(host, true);
1102 EXPECT_TRUE(
1103 host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1106 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
1107 // grant the correct bindings. http://crbug.com/189101.
1108 TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
1109 set_should_create_webui(true);
1110 SiteInstance* blank_instance = SiteInstance::Create(browser_context());
1111 blank_instance->GetProcess()->Init();
1113 // Create a blank tab.
1114 scoped_ptr<TestWebContents> web_contents1(
1115 TestWebContents::Create(browser_context(), blank_instance));
1116 RenderFrameHostManager* manager1 =
1117 web_contents1->GetRenderManagerForTesting();
1118 // Test the case that new RVH is considered live.
1119 manager1->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE, -1,
1120 FrameReplicationState(), false);
1121 EXPECT_TRUE(manager1->current_host()->IsRenderViewLive());
1122 EXPECT_TRUE(manager1->current_frame_host()->IsRenderFrameLive());
1124 // Navigate to a WebUI page.
1125 const GURL kUrl1("chrome://foo");
1126 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1127 Referrer(), base::string16() /* title */,
1128 ui::PAGE_TRANSITION_TYPED,
1129 false /* is_renderer_init */);
1130 RenderFrameHostImpl* host1 = NavigateToEntry(manager1, entry1);
1132 // We should have a pending navigation to the WebUI RenderViewHost.
1133 // It should already have bindings.
1134 EXPECT_EQ(host1, GetPendingFrameHost(manager1));
1135 EXPECT_NE(host1, manager1->current_frame_host());
1136 EXPECT_TRUE(
1137 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1139 // Commit and ensure we still have bindings.
1140 manager1->DidNavigateFrame(host1, true);
1141 SiteInstance* webui_instance = host1->GetSiteInstance();
1142 EXPECT_EQ(host1, manager1->current_frame_host());
1143 EXPECT_TRUE(
1144 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1146 // Now simulate clicking a link that opens in a new tab.
1147 scoped_ptr<TestWebContents> web_contents2(
1148 TestWebContents::Create(browser_context(), webui_instance));
1149 RenderFrameHostManager* manager2 =
1150 web_contents2->GetRenderManagerForTesting();
1151 // Make sure the new RVH is considered live. This is usually done in
1152 // RenderWidgetHost::Init when opening a new tab from a link.
1153 manager2->current_host()->CreateRenderView(-1, MSG_ROUTING_NONE, -1,
1154 FrameReplicationState(), false);
1155 EXPECT_TRUE(manager2->current_host()->IsRenderViewLive());
1157 const GURL kUrl2("chrome://foo/bar");
1158 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
1159 Referrer(), base::string16() /* title */,
1160 ui::PAGE_TRANSITION_LINK,
1161 true /* is_renderer_init */);
1162 RenderFrameHostImpl* host2 = NavigateToEntry(manager2, entry2);
1164 // No cross-process transition happens because we are already in the right
1165 // SiteInstance. We should grant bindings immediately.
1166 EXPECT_EQ(host2, manager2->current_frame_host());
1167 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1168 switches::kEnableBrowserSideNavigation)) {
1169 EXPECT_TRUE(manager2->speculative_web_ui());
1170 } else {
1171 EXPECT_TRUE(manager2->pending_web_ui());
1173 EXPECT_TRUE(
1174 host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1176 manager2->DidNavigateFrame(host2, true);
1179 // Tests that a WebUI is correctly reused between chrome:// pages.
1180 TEST_F(RenderFrameHostManagerTest, WebUIWasReused) {
1181 set_should_create_webui(true);
1183 // Navigate to a WebUI page.
1184 const GURL kUrl1("chrome://foo");
1185 contents()->NavigateAndCommit(kUrl1);
1186 RenderFrameHostManager* manager =
1187 main_test_rfh()->frame_tree_node()->render_manager();
1188 WebUIImpl* web_ui = manager->web_ui();
1189 EXPECT_TRUE(web_ui);
1191 // Navigate to another WebUI page which should be same-site and keep the
1192 // current WebUI.
1193 const GURL kUrl2("chrome://foo/bar");
1194 contents()->NavigateAndCommit(kUrl2);
1195 EXPECT_EQ(web_ui, manager->web_ui());
1198 // Tests that a WebUI is correctly cleaned up when navigating from a chrome://
1199 // page to a non-chrome:// page.
1200 TEST_F(RenderFrameHostManagerTest, WebUIWasCleared) {
1201 set_should_create_webui(true);
1203 // Navigate to a WebUI page.
1204 const GURL kUrl1("chrome://foo");
1205 contents()->NavigateAndCommit(kUrl1);
1206 EXPECT_TRUE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1208 // Navigate to a non-WebUI page.
1209 const GURL kUrl2("http://www.google.com");
1210 contents()->NavigateAndCommit(kUrl2);
1211 EXPECT_FALSE(main_test_rfh()->frame_tree_node()->render_manager()->web_ui());
1214 // Tests that we don't end up in an inconsistent state if a page does a back and
1215 // then reload. http://crbug.com/51680
1216 // Also tests that only user-gesture navigations can interrupt cross-process
1217 // navigations. http://crbug.com/75195
1218 TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
1219 const GURL kUrl1("http://www.google.com/");
1220 const GURL kUrl2("http://www.evil-site.com/");
1222 // Navigate to a safe site, then an evil site.
1223 // This will switch RenderFrameHosts. We cannot assert that the first and
1224 // second RFHs are different, though, because the first one may be promptly
1225 // deleted.
1226 contents()->NavigateAndCommit(kUrl1);
1227 contents()->NavigateAndCommit(kUrl2);
1228 TestRenderFrameHost* evil_rfh = contents()->GetMainFrame();
1230 // Now let's simulate the evil page calling history.back().
1231 contents()->OnGoToEntryAtOffset(-1);
1232 contents()->GetMainFrame()->PrepareForCommit();
1233 // We should have a new pending RFH.
1234 // Note that in this case, the navigation has not committed, so evil_rfh will
1235 // not be deleted yet.
1236 EXPECT_NE(evil_rfh, contents()->GetPendingMainFrame());
1237 EXPECT_NE(evil_rfh->GetRenderViewHost(),
1238 contents()->GetPendingMainFrame()->GetRenderViewHost());
1240 // Before that RFH has committed, the evil page reloads itself.
1241 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1242 params.page_id = 0;
1243 params.nav_entry_id = 0;
1244 params.did_create_new_entry = false;
1245 params.url = kUrl2;
1246 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
1247 params.should_update_history = false;
1248 params.gesture = NavigationGestureAuto;
1249 params.was_within_same_page = false;
1250 params.is_post = false;
1251 params.page_state = PageState::CreateFromURL(kUrl2);
1253 evil_rfh->SimulateNavigationStart(kUrl2);
1254 evil_rfh->SendNavigateWithParams(&params);
1255 evil_rfh->SimulateNavigationStop();
1257 // That should NOT have cancelled the pending RFH, because the reload did
1258 // not have a user gesture. Thus, the pending back navigation will still
1259 // eventually commit.
1260 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1261 pending_render_view_host() != NULL);
1262 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() !=
1263 NULL);
1264 EXPECT_EQ(evil_rfh,
1265 contents()->GetRenderManagerForTesting()->current_frame_host());
1266 EXPECT_EQ(evil_rfh->GetRenderViewHost(),
1267 contents()->GetRenderManagerForTesting()->current_host());
1269 // Also we should not have a pending navigation entry.
1270 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1271 NavigationEntry* entry = contents()->GetController().GetVisibleEntry();
1272 ASSERT_TRUE(entry != NULL);
1273 EXPECT_EQ(kUrl2, entry->GetURL());
1275 // Now do the same but as a user gesture.
1276 params.gesture = NavigationGestureUser;
1277 evil_rfh->SimulateNavigationStart(kUrl2);
1278 evil_rfh->SendNavigateWithParams(&params);
1279 evil_rfh->SimulateNavigationStop();
1281 // User navigation should have cancelled the pending RFH.
1282 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1283 pending_render_view_host() == NULL);
1284 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1285 NULL);
1286 EXPECT_EQ(evil_rfh,
1287 contents()->GetRenderManagerForTesting()->current_frame_host());
1288 EXPECT_EQ(evil_rfh->GetRenderViewHost(),
1289 contents()->GetRenderManagerForTesting()->current_host());
1291 // Also we should not have a pending navigation entry.
1292 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1293 entry = contents()->GetController().GetVisibleEntry();
1294 ASSERT_TRUE(entry != NULL);
1295 EXPECT_EQ(kUrl2, entry->GetURL());
1298 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1299 // See http://crbug.com/93427.
1300 TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1301 const GURL kUrl1("http://www.google.com/");
1302 const GURL kUrl2("http://www.chromium.org/");
1304 // Navigate to two pages.
1305 contents()->NavigateAndCommit(kUrl1);
1306 TestRenderFrameHost* rfh1 = main_test_rfh();
1308 // Keep active_frame_count nonzero so that no swapped out frames in
1309 // this SiteInstance get forcefully deleted.
1310 rfh1->GetSiteInstance()->increment_active_frame_count();
1312 contents()->NavigateAndCommit(kUrl2);
1313 TestRenderFrameHost* rfh2 = main_test_rfh();
1314 rfh2->GetSiteInstance()->increment_active_frame_count();
1316 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1317 // happen, but we have seen it when going back quickly across many entries
1318 // (http://crbug.com/93427).
1319 contents()->GetController().GoBack();
1320 EXPECT_TRUE(rfh2->is_waiting_for_beforeunload_ack());
1321 contents()->GetMainFrame()->PrepareForCommit();
1322 EXPECT_FALSE(rfh2->is_waiting_for_beforeunload_ack());
1324 // The back navigation commits.
1325 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1326 contents()->GetPendingMainFrame()->SendNavigate(
1327 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1328 EXPECT_TRUE(rfh2->IsWaitingForUnloadACK());
1329 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh2->rfh_state());
1331 // We should be able to navigate forward.
1332 contents()->GetController().GoForward();
1333 contents()->GetMainFrame()->PrepareForCommit();
1334 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
1335 contents()->GetPendingMainFrame()->SendNavigate(
1336 entry2->GetPageID(), entry2->GetUniqueID(), false, entry2->GetURL());
1337 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state());
1338 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1339 EXPECT_EQ(rfh2, main_test_rfh());
1340 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1341 rfh1->OnSwappedOut();
1342 EXPECT_TRUE(rfh1->is_swapped_out());
1343 EXPECT_EQ(RenderFrameHostImpl::STATE_SWAPPED_OUT, rfh1->rfh_state());
1347 // Test that we create swapped out RFHs for the opener chain when navigating an
1348 // opened tab cross-process. This allows us to support certain cross-process
1349 // JavaScript calls (http://crbug.com/99202).
1350 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRFHs) {
1351 const GURL kUrl1("http://www.google.com/");
1352 const GURL kUrl2("http://www.chromium.org/");
1353 const GURL kChromeUrl("chrome://foo");
1355 // Navigate to an initial URL.
1356 contents()->NavigateAndCommit(kUrl1);
1357 RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1358 TestRenderFrameHost* rfh1 = main_test_rfh();
1359 scoped_refptr<SiteInstanceImpl> site_instance1 = rfh1->GetSiteInstance();
1360 RenderFrameDeletedObserver rfh1_deleted_observer(rfh1);
1361 TestRenderViewHost* rvh1 = test_rvh();
1363 // Create 2 new tabs and simulate them being the opener chain for the main
1364 // tab. They should be in the same SiteInstance.
1365 scoped_ptr<TestWebContents> opener1(
1366 TestWebContents::Create(browser_context(), site_instance1.get()));
1367 RenderFrameHostManager* opener1_manager =
1368 opener1->GetRenderManagerForTesting();
1369 contents()->SetOpener(opener1.get());
1371 scoped_ptr<TestWebContents> opener2(
1372 TestWebContents::Create(browser_context(), site_instance1.get()));
1373 RenderFrameHostManager* opener2_manager =
1374 opener2->GetRenderManagerForTesting();
1375 opener1->SetOpener(opener2.get());
1377 // Navigate to a cross-site URL (different SiteInstance but same
1378 // BrowsingInstance).
1379 contents()->NavigateAndCommit(kUrl2);
1380 TestRenderFrameHost* rfh2 = main_test_rfh();
1381 TestRenderViewHost* rvh2 = test_rvh();
1382 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1383 EXPECT_TRUE(site_instance1->IsRelatedSiteInstance(rfh2->GetSiteInstance()));
1385 // Ensure rvh1 is placed on swapped out list of the current tab.
1386 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1387 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
1388 EXPECT_FALSE(rfh1_deleted_observer.deleted());
1389 EXPECT_TRUE(manager->IsOnSwappedOutList(rfh1));
1390 EXPECT_EQ(rfh1,
1391 manager->GetRenderFrameProxyHost(site_instance1.get())
1392 ->render_frame_host());
1393 } else {
1394 EXPECT_TRUE(rfh1_deleted_observer.deleted());
1395 EXPECT_TRUE(manager->GetRenderFrameProxyHost(site_instance1.get()));
1397 EXPECT_EQ(rvh1,
1398 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
1400 // Ensure a swapped out RFH and RFH is created in the first opener tab.
1401 RenderFrameProxyHost* opener1_proxy =
1402 opener1_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance());
1403 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host();
1404 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1405 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1406 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1407 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh));
1408 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1409 EXPECT_TRUE(opener1_rfh->is_swapped_out());
1410 } else {
1411 EXPECT_FALSE(opener1_rfh);
1413 EXPECT_FALSE(opener1_rvh->is_active());
1415 // Ensure a swapped out RFH and RVH is created in the second opener tab.
1416 RenderFrameProxyHost* opener2_proxy =
1417 opener2_manager->GetRenderFrameProxyHost(rfh2->GetSiteInstance());
1418 RenderFrameHostImpl* opener2_rfh = opener2_proxy->render_frame_host();
1419 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
1420 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1421 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1422 EXPECT_TRUE(opener2_manager->IsOnSwappedOutList(opener2_rfh));
1423 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
1424 EXPECT_TRUE(opener2_rfh->is_swapped_out());
1425 } else {
1426 EXPECT_FALSE(opener2_rfh);
1428 EXPECT_FALSE(opener2_rvh->is_active());
1430 // Navigate to a cross-BrowsingInstance URL.
1431 contents()->NavigateAndCommit(kChromeUrl);
1432 TestRenderFrameHost* rfh3 = main_test_rfh();
1433 EXPECT_NE(site_instance1, rfh3->GetSiteInstance());
1434 EXPECT_FALSE(site_instance1->IsRelatedSiteInstance(rfh3->GetSiteInstance()));
1436 // No scripting is allowed across BrowsingInstances, so we should not create
1437 // swapped out RVHs for the opener chain in this case.
1438 EXPECT_FALSE(opener1_manager->GetRenderFrameProxyHost(
1439 rfh3->GetSiteInstance()));
1440 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1441 rfh3->GetSiteInstance()));
1442 EXPECT_FALSE(opener2_manager->GetRenderFrameProxyHost(
1443 rfh3->GetSiteInstance()));
1444 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost(
1445 rfh3->GetSiteInstance()));
1448 // Test that a page can disown the opener of the WebContents.
1449 TEST_F(RenderFrameHostManagerTest, DisownOpener) {
1450 const GURL kUrl1("http://www.google.com/");
1451 const GURL kUrl2("http://www.chromium.org/");
1453 // Navigate to an initial URL.
1454 contents()->NavigateAndCommit(kUrl1);
1455 TestRenderFrameHost* rfh1 = main_test_rfh();
1456 scoped_refptr<SiteInstanceImpl> site_instance1 = rfh1->GetSiteInstance();
1458 // Create a new tab and simulate having it be the opener for the main tab.
1459 scoped_ptr<TestWebContents> opener1(
1460 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1461 contents()->SetOpener(opener1.get());
1462 EXPECT_TRUE(contents()->HasOpener());
1464 // Navigate to a cross-site URL (different SiteInstance but same
1465 // BrowsingInstance).
1466 contents()->NavigateAndCommit(kUrl2);
1467 TestRenderFrameHost* rfh2 = main_test_rfh();
1468 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1470 // Disown the opener from rfh2.
1471 rfh2->DidDisownOpener();
1473 // Ensure the opener is cleared.
1474 EXPECT_FALSE(contents()->HasOpener());
1477 // Test that a page can disown a same-site opener of the WebContents.
1478 TEST_F(RenderFrameHostManagerTest, DisownSameSiteOpener) {
1479 const GURL kUrl1("http://www.google.com/");
1481 // Navigate to an initial URL.
1482 contents()->NavigateAndCommit(kUrl1);
1483 TestRenderFrameHost* rfh1 = main_test_rfh();
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 // Disown the opener from rfh1.
1492 rfh1->DidDisownOpener();
1494 // Ensure the opener is cleared even if it is in the same process.
1495 EXPECT_FALSE(contents()->HasOpener());
1498 // Test that a page can disown the opener just as a cross-process navigation is
1499 // in progress.
1500 TEST_F(RenderFrameHostManagerTest, DisownOpenerDuringNavigation) {
1501 const GURL kUrl1("http://www.google.com/");
1502 const GURL kUrl2("http://www.chromium.org/");
1504 // Navigate to an initial URL.
1505 contents()->NavigateAndCommit(kUrl1);
1506 scoped_refptr<SiteInstanceImpl> site_instance1 =
1507 main_test_rfh()->GetSiteInstance();
1509 // Create a new tab and simulate having it be the opener for the main tab.
1510 scoped_ptr<TestWebContents> opener1(
1511 TestWebContents::Create(browser_context(), site_instance1.get()));
1512 contents()->SetOpener(opener1.get());
1513 EXPECT_TRUE(contents()->HasOpener());
1515 // Navigate to a cross-site URL (different SiteInstance but same
1516 // BrowsingInstance).
1517 contents()->NavigateAndCommit(kUrl2);
1518 TestRenderFrameHost* rfh2 = main_test_rfh();
1519 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1521 // Start a back navigation.
1522 contents()->GetController().GoBack();
1523 contents()->GetMainFrame()->PrepareForCommit();
1525 // Disown the opener from rfh2.
1526 rfh2->DidDisownOpener();
1528 // Ensure the opener is cleared.
1529 EXPECT_FALSE(contents()->HasOpener());
1531 // The back navigation commits.
1532 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1533 contents()->GetPendingMainFrame()->SendNavigate(
1534 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1536 // Ensure the opener is still cleared.
1537 EXPECT_FALSE(contents()->HasOpener());
1540 // Test that a page can disown the opener just after a cross-process navigation
1541 // commits.
1542 TEST_F(RenderFrameHostManagerTest, DisownOpenerAfterNavigation) {
1543 const GURL kUrl1("http://www.google.com/");
1544 const GURL kUrl2("http://www.chromium.org/");
1546 // Navigate to an initial URL.
1547 contents()->NavigateAndCommit(kUrl1);
1548 scoped_refptr<SiteInstanceImpl> site_instance1 =
1549 main_test_rfh()->GetSiteInstance();
1551 // Create a new tab and simulate having it be the opener for the main tab.
1552 scoped_ptr<TestWebContents> opener1(
1553 TestWebContents::Create(browser_context(), site_instance1.get()));
1554 contents()->SetOpener(opener1.get());
1555 EXPECT_TRUE(contents()->HasOpener());
1557 // Navigate to a cross-site URL (different SiteInstance but same
1558 // BrowsingInstance).
1559 contents()->NavigateAndCommit(kUrl2);
1560 TestRenderFrameHost* rfh2 = main_test_rfh();
1561 EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
1563 // Commit a back navigation before the DidDisownOpener message arrives.
1564 contents()->GetController().GoBack();
1565 contents()->GetMainFrame()->PrepareForCommit();
1566 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1567 contents()->GetPendingMainFrame()->SendNavigate(
1568 entry1->GetPageID(), entry1->GetUniqueID(), false, entry1->GetURL());
1570 // Disown the opener from rfh2.
1571 rfh2->DidDisownOpener();
1572 EXPECT_FALSE(contents()->HasOpener());
1575 // Test that we clean up swapped out RenderViewHosts when a process hosting
1576 // those associated RenderViews crashes. http://crbug.com/258993
1577 TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1578 const GURL kUrl1("http://www.google.com/");
1579 const GURL kUrl2("http://www.chromium.org/");
1581 // Navigate to an initial URL.
1582 contents()->NavigateAndCommit(kUrl1);
1583 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1585 // Create a new tab as an opener for the main tab.
1586 scoped_ptr<TestWebContents> opener1(
1587 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1588 RenderFrameHostManager* opener1_manager =
1589 opener1->GetRenderManagerForTesting();
1590 contents()->SetOpener(opener1.get());
1592 // Make sure the new opener RVH is considered live.
1593 opener1_manager->current_host()->CreateRenderView(
1594 -1, MSG_ROUTING_NONE, -1, FrameReplicationState(), false);
1595 EXPECT_TRUE(opener1_manager->current_host()->IsRenderViewLive());
1596 EXPECT_TRUE(opener1_manager->current_frame_host()->IsRenderFrameLive());
1598 // Use a cross-process navigation in the opener to swap out the old RVH.
1599 EXPECT_FALSE(
1600 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1601 opener1->NavigateAndCommit(kUrl2);
1602 RenderViewHostImpl* swapped_out_rvh =
1603 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance());
1604 EXPECT_TRUE(swapped_out_rvh);
1605 EXPECT_TRUE(swapped_out_rvh->is_swapped_out_);
1606 EXPECT_FALSE(swapped_out_rvh->is_active());
1608 // Fake a process crash.
1609 rfh1->GetProcess()->SimulateCrash();
1611 // Ensure that the RenderFrameProxyHost stays around and the RenderFrameProxy
1612 // is deleted.
1613 RenderFrameProxyHost* render_frame_proxy_host =
1614 opener1_manager->GetRenderFrameProxyHost(rfh1->GetSiteInstance());
1615 EXPECT_TRUE(render_frame_proxy_host);
1616 EXPECT_FALSE(render_frame_proxy_host->is_render_frame_proxy_live());
1618 // Expect the swapped out RVH to exist but not be live.
1619 EXPECT_TRUE(
1620 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance()));
1621 EXPECT_FALSE(
1622 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance())
1623 ->IsRenderViewLive());
1625 // Reload the initial tab. This should recreate the opener's swapped out RVH
1626 // in the original SiteInstance.
1627 contents()->GetController().Reload(true);
1628 contents()->GetMainFrame()->PrepareForCommit();
1629 EXPECT_TRUE(
1630 opener1_manager->GetSwappedOutRenderViewHost(rfh1->GetSiteInstance())
1631 ->IsRenderViewLive());
1632 EXPECT_EQ(
1633 opener1_manager->GetRoutingIdForSiteInstance(rfh1->GetSiteInstance()),
1634 contents()->GetMainFrame()->GetRenderViewHost()->opener_frame_route_id());
1637 // Test that RenderViewHosts created for WebUI navigations are properly
1638 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1639 // is in the same process (http://crbug.com/79918).
1640 TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1641 set_should_create_webui(true);
1642 const GURL kSettingsUrl("chrome://chrome/settings");
1643 const GURL kPluginUrl("chrome://plugins");
1645 // Navigate to an initial WebUI URL.
1646 contents()->NavigateAndCommit(kSettingsUrl);
1648 // Ensure the RVH has WebUI bindings.
1649 TestRenderViewHost* rvh1 = test_rvh();
1650 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1652 // Create a new tab and simulate it being the opener for the main
1653 // tab. It should be in the same SiteInstance.
1654 scoped_ptr<TestWebContents> opener1(
1655 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1656 RenderFrameHostManager* opener1_manager =
1657 opener1->GetRenderManagerForTesting();
1658 contents()->SetOpener(opener1.get());
1660 // Navigate to a different WebUI URL (different SiteInstance, same
1661 // BrowsingInstance).
1662 contents()->NavigateAndCommit(kPluginUrl);
1663 TestRenderViewHost* rvh2 = test_rvh();
1664 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1665 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1666 rvh2->GetSiteInstance()));
1668 // Ensure a swapped out RFH and RVH is created in the first opener tab.
1669 RenderFrameProxyHost* opener1_proxy =
1670 opener1_manager->GetRenderFrameProxyHost(rvh2->GetSiteInstance());
1671 RenderFrameHostImpl* opener1_rfh = opener1_proxy->render_frame_host();
1672 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1673 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1674 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1675 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rfh));
1676 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1677 EXPECT_TRUE(opener1_rfh->is_swapped_out());
1678 } else {
1679 EXPECT_FALSE(opener1_rfh);
1681 EXPECT_FALSE(opener1_rvh->is_active());
1683 // Ensure the new RVH has WebUI bindings.
1684 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1687 // Test that we reuse the same guest SiteInstance if we navigate across sites.
1688 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1689 GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1690 SiteInstance* instance =
1691 SiteInstance::CreateForURL(browser_context(), guest_url);
1692 scoped_ptr<TestWebContents> web_contents(
1693 TestWebContents::Create(browser_context(), instance));
1695 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1697 RenderFrameHostImpl* host = NULL;
1699 // 1) The first navigation. --------------------------
1700 const GURL kUrl1("http://www.google.com/");
1701 NavigationEntryImpl entry1(
1702 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1703 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1704 false /* is_renderer_init */);
1705 host = NavigateToEntry(manager, entry1);
1707 // The RenderFrameHost created in Init will be reused.
1708 EXPECT_TRUE(host == manager->current_frame_host());
1709 EXPECT_FALSE(manager->pending_frame_host());
1710 EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1712 // Commit.
1713 manager->DidNavigateFrame(host, true);
1714 // Commit to SiteInstance should be delayed until RenderFrame commit.
1715 EXPECT_EQ(host, manager->current_frame_host());
1716 ASSERT_TRUE(host);
1717 EXPECT_TRUE(host->GetSiteInstance()->HasSite());
1719 // 2) Navigate to a different domain. -------------------------
1720 // Guests stay in the same process on navigation.
1721 const GURL kUrl2("http://www.chromium.org");
1722 NavigationEntryImpl entry2(
1723 NULL /* instance */, -1 /* page_id */, kUrl2,
1724 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1725 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1726 true /* is_renderer_init */);
1727 host = NavigateToEntry(manager, entry2);
1729 // The RenderFrameHost created in Init will be reused.
1730 EXPECT_EQ(host, manager->current_frame_host());
1731 EXPECT_FALSE(manager->pending_frame_host());
1733 // Commit.
1734 manager->DidNavigateFrame(host, true);
1735 EXPECT_EQ(host, manager->current_frame_host());
1736 ASSERT_TRUE(host);
1737 EXPECT_EQ(host->GetSiteInstance(), instance);
1740 // Test that we cancel a pending RVH if we close the tab while it's pending.
1741 // http://crbug.com/294697.
1742 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1743 TestNotificationTracker notifications;
1745 SiteInstance* instance = SiteInstance::Create(browser_context());
1747 BeforeUnloadFiredWebContentsDelegate delegate;
1748 scoped_ptr<TestWebContents> web_contents(
1749 TestWebContents::Create(browser_context(), instance));
1750 RenderViewHostChangedObserver change_observer(web_contents.get());
1751 web_contents->SetDelegate(&delegate);
1753 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1755 // 1) The first navigation. --------------------------
1756 const GURL kUrl1("http://www.google.com/");
1757 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1758 Referrer(), base::string16() /* title */,
1759 ui::PAGE_TRANSITION_TYPED,
1760 false /* is_renderer_init */);
1761 RenderFrameHostImpl* host = NavigateToEntry(manager, entry1);
1763 // The RenderFrameHost created in Init will be reused.
1764 EXPECT_EQ(host, manager->current_frame_host());
1765 EXPECT_FALSE(GetPendingFrameHost(manager));
1767 // We should observe RVH changed event.
1768 EXPECT_TRUE(change_observer.DidHostChange());
1770 // Commit.
1771 manager->DidNavigateFrame(host, true);
1773 // Commit to SiteInstance should be delayed until RenderFrame commits.
1774 EXPECT_EQ(host, manager->current_frame_host());
1775 EXPECT_FALSE(host->GetSiteInstance()->HasSite());
1776 host->GetSiteInstance()->SetSite(kUrl1);
1778 // 2) Cross-site navigate to next site. -------------------------
1779 const GURL kUrl2("http://www.example.com");
1780 NavigationEntryImpl entry2(
1781 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1782 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1783 false /* is_renderer_init */);
1784 RenderFrameHostImpl* host2 = NavigateToEntry(manager, entry2);
1786 // A new RenderFrameHost should be created.
1787 ASSERT_EQ(host2, GetPendingFrameHost(manager));
1788 EXPECT_NE(host2, host);
1790 EXPECT_EQ(host, manager->current_frame_host());
1791 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1792 EXPECT_EQ(host2, GetPendingFrameHost(manager));
1794 // 3) Close the tab. -------------------------
1795 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1796 Source<RenderWidgetHost>(host2->render_view_host()));
1797 manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
1799 EXPECT_TRUE(
1800 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1801 EXPECT_FALSE(GetPendingFrameHost(manager));
1802 EXPECT_EQ(host, manager->current_frame_host());
1805 TEST_F(RenderFrameHostManagerTest, CloseWithPendingWhileUnresponsive) {
1806 const GURL kUrl1("http://www.google.com/");
1807 const GURL kUrl2("http://www.chromium.org/");
1809 CloseWebContentsDelegate close_delegate;
1810 contents()->SetDelegate(&close_delegate);
1812 // Navigate to the first page.
1813 contents()->NavigateAndCommit(kUrl1);
1814 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1816 // Start to close the tab, but assume it's unresponsive.
1817 rfh1->render_view_host()->ClosePage();
1818 EXPECT_TRUE(rfh1->render_view_host()->is_waiting_for_close_ack());
1820 // Start a navigation to a new site.
1821 controller().LoadURL(
1822 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1823 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1824 switches::kEnableBrowserSideNavigation)) {
1825 rfh1->PrepareForCommit();
1827 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1829 // Simulate the unresponsiveness timer. The tab should close.
1830 contents()->RendererUnresponsive(rfh1->render_view_host());
1831 EXPECT_TRUE(close_delegate.is_closed());
1834 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1835 // received. (SwapOut and the corresponding ACK always occur after commit.)
1836 // Also tests that an early SwapOutACK is properly ignored.
1837 TEST_F(RenderFrameHostManagerTest, DeleteFrameAfterSwapOutACK) {
1838 const GURL kUrl1("http://www.google.com/");
1839 const GURL kUrl2("http://www.chromium.org/");
1841 // Navigate to the first page.
1842 contents()->NavigateAndCommit(kUrl1);
1843 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1844 RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
1845 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1847 // Navigate to new site, simulating onbeforeunload approval.
1848 controller().LoadURL(
1849 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1850 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1851 contents()->GetMainFrame()->PrepareForCommit();
1852 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1853 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1854 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1856 // Simulate the swap out ack, unexpectedly early (before commit). It should
1857 // have no effect.
1858 rfh1->OnSwappedOut();
1859 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1860 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1862 // The new page commits.
1863 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1864 ui::PAGE_TRANSITION_TYPED);
1865 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1866 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1867 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1868 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1869 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1870 EXPECT_TRUE(
1871 rfh1->frame_tree_node()->render_manager()->IsPendingDeletion(rfh1));
1873 // Simulate the swap out ack.
1874 rfh1->OnSwappedOut();
1876 // rfh1 should have been deleted.
1877 EXPECT_TRUE(rfh_deleted_observer.deleted());
1878 rfh1 = NULL;
1881 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1882 // is received. (SwapOut and the corresponding ACK always occur after commit.)
1883 TEST_F(RenderFrameHostManagerTest, SwapOutFrameAfterSwapOutACK) {
1884 const GURL kUrl1("http://www.google.com/");
1885 const GURL kUrl2("http://www.chromium.org/");
1887 // Navigate to the first page.
1888 contents()->NavigateAndCommit(kUrl1);
1889 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1890 RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
1891 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1893 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1894 // not deleted on swap out.
1895 rfh1->GetSiteInstance()->increment_active_frame_count();
1897 // Navigate to new site, simulating onbeforeunload approval.
1898 controller().LoadURL(
1899 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1900 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1901 contents()->GetMainFrame()->PrepareForCommit();
1902 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1903 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1904 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1906 // The new page commits.
1907 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1908 ui::PAGE_TRANSITION_TYPED);
1909 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1910 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1911 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1912 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1913 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1915 // Simulate the swap out ack.
1916 rfh1->OnSwappedOut();
1918 // rfh1 should be swapped out or deleted in --site-per-process.
1919 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1920 EXPECT_FALSE(rfh_deleted_observer.deleted());
1921 EXPECT_TRUE(rfh1->is_swapped_out());
1922 } else {
1923 EXPECT_TRUE(rfh_deleted_observer.deleted());
1927 // Test that the RenderViewHost is properly swapped out if a navigation in the
1928 // new renderer commits before sending the SwapOut message to the old renderer.
1929 // This simulates a cross-site navigation to a synchronously committing URL
1930 // (e.g., a data URL) and ensures it works properly.
1931 TEST_F(RenderFrameHostManagerTest,
1932 CommitNewNavigationBeforeSendingSwapOut) {
1933 const GURL kUrl1("http://www.google.com/");
1934 const GURL kUrl2("http://www.chromium.org/");
1936 // Navigate to the first page.
1937 contents()->NavigateAndCommit(kUrl1);
1938 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1939 RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
1940 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1942 // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
1943 // not deleted on swap out.
1944 scoped_refptr<SiteInstanceImpl> site_instance = rfh1->GetSiteInstance();
1945 site_instance->increment_active_frame_count();
1947 // Navigate to new site, simulating onbeforeunload approval.
1948 controller().LoadURL(
1949 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1950 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1951 rfh1->PrepareForCommit();
1952 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1953 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1955 // The new page commits.
1956 contents()->TestDidNavigate(rfh2, 1, entry_id, true, kUrl2,
1957 ui::PAGE_TRANSITION_TYPED);
1958 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1959 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1960 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1961 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh2->rfh_state());
1962 EXPECT_EQ(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT, rfh1->rfh_state());
1964 // Simulate the swap out ack.
1965 rfh1->OnSwappedOut();
1967 // rfh1 should be swapped out.
1968 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
1969 EXPECT_TRUE(rfh_deleted_observer.deleted());
1970 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
1971 ->GetRenderFrameProxyHost(site_instance.get()));
1972 } else {
1973 EXPECT_FALSE(rfh_deleted_observer.deleted());
1974 EXPECT_TRUE(rfh1->is_swapped_out());
1978 // Test that a RenderFrameHost is properly deleted when a cross-site navigation
1979 // is cancelled.
1980 TEST_F(RenderFrameHostManagerTest,
1981 CancelPendingProperlyDeletesOrSwaps) {
1982 const GURL kUrl1("http://www.google.com/");
1983 const GURL kUrl2("http://www.chromium.org/");
1984 RenderFrameHostImpl* pending_rfh = NULL;
1985 base::TimeTicks now = base::TimeTicks::Now();
1987 // Navigate to the first page.
1988 contents()->NavigateAndCommit(kUrl1);
1989 TestRenderFrameHost* rfh1 = main_test_rfh();
1990 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
1992 // Navigate to a new site, starting a cross-site navigation.
1993 controller().LoadURL(
1994 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1996 pending_rfh = contents()->GetPendingMainFrame();
1997 RenderFrameDeletedObserver rfh_deleted_observer(pending_rfh);
1999 // Cancel the navigation by simulating a declined beforeunload dialog.
2000 contents()->GetMainFrame()->OnMessageReceived(
2001 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
2002 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2004 // Since the pending RFH is the only one for the new SiteInstance, it should
2005 // be deleted.
2006 EXPECT_TRUE(rfh_deleted_observer.deleted());
2009 // Start another cross-site navigation.
2010 controller().LoadURL(
2011 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2013 pending_rfh = contents()->GetPendingMainFrame();
2014 RenderFrameDeletedObserver rfh_deleted_observer(pending_rfh);
2016 // Increment the number of active frames in the new SiteInstance, which will
2017 // cause the pending RFH to be deleted and a RenderFrameProxyHost to be
2018 // created.
2019 scoped_refptr<SiteInstanceImpl> site_instance =
2020 pending_rfh->GetSiteInstance();
2021 site_instance->increment_active_frame_count();
2023 contents()->GetMainFrame()->OnMessageReceived(
2024 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
2025 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2027 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
2028 EXPECT_TRUE(rfh_deleted_observer.deleted());
2029 EXPECT_TRUE(contents()->GetFrameTree()->root()->render_manager()
2030 ->GetRenderFrameProxyHost(site_instance.get()));
2031 } else {
2032 EXPECT_FALSE(rfh_deleted_observer.deleted());
2037 // Test that a pending RenderFrameHost in a non-root frame tree node is properly
2038 // deleted when the node is detached. Motivated by http://crbug.com/441357 and
2039 // http://crbug.com/444955.
2040 TEST_F(RenderFrameHostManagerTest, DetachPendingChild) {
2041 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2043 const GURL kUrlA("http://www.google.com/");
2044 const GURL kUrlB("http://webkit.org/");
2046 // Create a page with two child frames.
2047 contents()->NavigateAndCommit(kUrlA);
2048 contents()->GetMainFrame()->OnCreateChildFrame(
2049 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2050 blink::WebTreeScopeType::Document, "frame_name",
2051 blink::WebSandboxFlags::None);
2052 contents()->GetMainFrame()->OnCreateChildFrame(
2053 contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2054 blink::WebTreeScopeType::Document, "frame_name",
2055 blink::WebSandboxFlags::None);
2056 RenderFrameHostManager* root_manager =
2057 contents()->GetFrameTree()->root()->render_manager();
2058 RenderFrameHostManager* iframe1 =
2059 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2060 RenderFrameHostManager* iframe2 =
2061 contents()->GetFrameTree()->root()->child_at(1)->render_manager();
2063 // 1) The first navigation.
2064 NavigationEntryImpl entryA(NULL /* instance */, -1 /* page_id */, kUrlA,
2065 Referrer(), base::string16() /* title */,
2066 ui::PAGE_TRANSITION_TYPED,
2067 false /* is_renderer_init */);
2068 RenderFrameHostImpl* host1 = NavigateToEntry(iframe1, entryA);
2070 // The RenderFrameHost created in Init will be reused.
2071 EXPECT_TRUE(host1 == iframe1->current_frame_host());
2072 EXPECT_FALSE(GetPendingFrameHost(iframe1));
2074 // Commit.
2075 iframe1->DidNavigateFrame(host1, true);
2076 // Commit to SiteInstance should be delayed until RenderFrame commit.
2077 EXPECT_TRUE(host1 == iframe1->current_frame_host());
2078 ASSERT_TRUE(host1);
2079 EXPECT_TRUE(host1->GetSiteInstance()->HasSite());
2081 // 2) Cross-site navigate both frames to next site.
2082 NavigationEntryImpl entryB(NULL /* instance */, -1 /* page_id */, kUrlB,
2083 Referrer(kUrlA, blink::WebReferrerPolicyDefault),
2084 base::string16() /* title */,
2085 ui::PAGE_TRANSITION_LINK,
2086 false /* is_renderer_init */);
2087 host1 = NavigateToEntry(iframe1, entryB);
2088 RenderFrameHostImpl* host2 = NavigateToEntry(iframe2, entryB);
2090 // A new, pending RenderFrameHost should be created in each FrameTreeNode.
2091 EXPECT_TRUE(GetPendingFrameHost(iframe1));
2092 EXPECT_TRUE(GetPendingFrameHost(iframe2));
2093 EXPECT_EQ(host1, GetPendingFrameHost(iframe1));
2094 EXPECT_EQ(host2, GetPendingFrameHost(iframe2));
2095 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2096 GetPendingFrameHost(iframe1)->rfh_state()));
2097 EXPECT_TRUE(RenderFrameHostImpl::IsRFHStateActive(
2098 GetPendingFrameHost(iframe2)->rfh_state()));
2099 EXPECT_NE(GetPendingFrameHost(iframe1), GetPendingFrameHost(iframe2));
2100 EXPECT_EQ(GetPendingFrameHost(iframe1)->GetSiteInstance(),
2101 GetPendingFrameHost(iframe2)->GetSiteInstance());
2102 EXPECT_NE(iframe1->current_frame_host(), GetPendingFrameHost(iframe1));
2103 EXPECT_NE(iframe2->current_frame_host(), GetPendingFrameHost(iframe2));
2104 EXPECT_FALSE(contents()->CrossProcessNavigationPending())
2105 << "There should be no top-level pending navigation.";
2107 RenderFrameDeletedObserver delete_watcher1(GetPendingFrameHost(iframe1));
2108 RenderFrameDeletedObserver delete_watcher2(GetPendingFrameHost(iframe2));
2109 EXPECT_FALSE(delete_watcher1.deleted());
2110 EXPECT_FALSE(delete_watcher2.deleted());
2112 // Keep the SiteInstance alive for testing.
2113 scoped_refptr<SiteInstanceImpl> site_instance =
2114 GetPendingFrameHost(iframe1)->GetSiteInstance();
2115 EXPECT_TRUE(site_instance->HasSite());
2116 EXPECT_NE(site_instance, contents()->GetSiteInstance());
2117 EXPECT_EQ(2U, site_instance->active_frame_count());
2119 // Proxies should exist.
2120 EXPECT_NE(nullptr,
2121 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2122 EXPECT_NE(nullptr,
2123 iframe1->GetRenderFrameProxyHost(site_instance.get()));
2124 EXPECT_NE(nullptr,
2125 iframe2->GetRenderFrameProxyHost(site_instance.get()));
2127 // Detach the first child FrameTreeNode. This should kill the pending host but
2128 // not yet destroy proxies in |site_instance| since the other child remains.
2129 iframe1->current_frame_host()->OnMessageReceived(
2130 FrameHostMsg_Detach(iframe1->current_frame_host()->GetRoutingID()));
2131 iframe1 = NULL; // Was just destroyed.
2133 EXPECT_TRUE(delete_watcher1.deleted());
2134 EXPECT_FALSE(delete_watcher2.deleted());
2135 EXPECT_EQ(1U, site_instance->active_frame_count());
2137 // Proxies should still exist.
2138 EXPECT_NE(nullptr,
2139 root_manager->GetRenderFrameProxyHost(site_instance.get()));
2140 EXPECT_NE(nullptr,
2141 iframe2->GetRenderFrameProxyHost(site_instance.get()));
2143 // Detach the second child FrameTreeNode. This should trigger cleanup of
2144 // RenderFrameProxyHosts in |site_instance|.
2145 iframe2->current_frame_host()->OnMessageReceived(
2146 FrameHostMsg_Detach(iframe2->current_frame_host()->GetRoutingID()));
2147 iframe2 = NULL; // Was just destroyed.
2149 EXPECT_TRUE(delete_watcher1.deleted());
2150 EXPECT_TRUE(delete_watcher2.deleted());
2152 EXPECT_EQ(0U, site_instance->active_frame_count());
2153 EXPECT_EQ(nullptr,
2154 root_manager->GetRenderFrameProxyHost(site_instance.get()))
2155 << "Proxies should have been cleaned up";
2156 EXPECT_TRUE(site_instance->HasOneRef())
2157 << "This SiteInstance should be destroyable now.";
2160 // Two tabs in the same process crash. The first tab is reloaded, and the second
2161 // tab navigates away without reloading. The second tab's navigation shouldn't
2162 // mess with the first tab's content. Motivated by http://crbug.com/473714.
2163 TEST_F(RenderFrameHostManagerTest, TwoTabsCrashOneReloadsOneLeaves) {
2164 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2166 const GURL kUrl1("http://www.google.com/");
2167 const GURL kUrl2("http://webkit.org/");
2168 const GURL kUrl3("http://whatwg.org/");
2170 // |contents1| and |contents2| navigate to the same page and then crash.
2171 TestWebContents* contents1 = contents();
2172 scoped_ptr<TestWebContents> contents2(
2173 TestWebContents::Create(browser_context(), contents1->GetSiteInstance()));
2174 contents1->NavigateAndCommit(kUrl1);
2175 contents2->NavigateAndCommit(kUrl1);
2176 MockRenderProcessHost* rph = contents1->GetMainFrame()->GetProcess();
2177 EXPECT_EQ(rph, contents2->GetMainFrame()->GetProcess());
2178 rph->SimulateCrash();
2179 EXPECT_FALSE(contents1->GetMainFrame()->IsRenderFrameLive());
2180 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2181 EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
2183 // Reload |contents1|.
2184 contents1->NavigateAndCommit(kUrl1);
2185 EXPECT_TRUE(contents1->GetMainFrame()->IsRenderFrameLive());
2186 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2187 EXPECT_EQ(contents1->GetSiteInstance(), contents2->GetSiteInstance());
2189 // |contents1| creates an out of process iframe.
2190 contents1->GetMainFrame()->OnCreateChildFrame(
2191 contents1->GetMainFrame()->GetProcess()->GetNextRoutingID(),
2192 blink::WebTreeScopeType::Document, "frame_name",
2193 blink::WebSandboxFlags::None);
2194 RenderFrameHostManager* iframe =
2195 contents()->GetFrameTree()->root()->child_at(0)->render_manager();
2196 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl2,
2197 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
2198 base::string16() /* title */,
2199 ui::PAGE_TRANSITION_LINK,
2200 false /* is_renderer_init */);
2201 RenderFrameHostImpl* cross_site = NavigateToEntry(iframe, entry);
2202 iframe->DidNavigateFrame(cross_site, true);
2204 // A proxy to the iframe should now exist in the SiteInstance of the main
2205 // frames.
2206 EXPECT_NE(cross_site->GetSiteInstance(), contents1->GetSiteInstance());
2207 EXPECT_NE(nullptr,
2208 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2209 EXPECT_NE(nullptr,
2210 iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
2212 // Navigate |contents2| away from the sad tab (and thus away from the
2213 // SiteInstance of |contents1|). This should not destroy the proxies needed by
2214 // |contents1| -- that was http://crbug.com/473714.
2215 EXPECT_FALSE(contents2->GetMainFrame()->IsRenderFrameLive());
2216 contents2->NavigateAndCommit(kUrl3);
2217 EXPECT_TRUE(contents2->GetMainFrame()->IsRenderFrameLive());
2218 EXPECT_NE(nullptr,
2219 iframe->GetRenderFrameProxyHost(contents1->GetSiteInstance()));
2220 EXPECT_EQ(nullptr,
2221 iframe->GetRenderFrameProxyHost(contents2->GetSiteInstance()));
2224 } // namespace content