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.
4
5 #include "base/command_line.h"
6 #include "base/files/file_path.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/time/time.h"
9 #include "content/browser/frame_host/cross_site_transferring_request.h"
10 #include "content/browser/frame_host/navigation_before_commit_info.h"
11 #include "content/browser/frame_host/navigation_controller_impl.h"
12 #include "content/browser/frame_host/navigation_entry_impl.h"
13 #include "content/browser/frame_host/navigation_request.h"
14 #include "content/browser/frame_host/navigator.h"
15 #include "content/browser/frame_host/navigator_impl.h"
16 #include "content/browser/frame_host/render_frame_host_manager.h"
17 #include "content/browser/site_instance_impl.h"
18 #include "content/browser/webui/web_ui_controller_factory_registry.h"
19 #include "content/common/view_messages.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_widget_host_iterator.h"
26 #include "content/public/browser/web_contents_delegate.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/browser/web_ui_controller.h"
29 #include "content/public/common/bindings_policy.h"
30 #include "content/public/common/content_switches.h"
31 #include "content/public/common/javascript_message_type.h"
32 #include "content/public/common/url_constants.h"
33 #include "content/public/common/url_utils.h"
34 #include "content/public/test/mock_render_process_host.h"
35 #include "content/public/test/test_notification_tracker.h"
36 #include "content/test/test_content_browser_client.h"
37 #include "content/test/test_content_client.h"
38 #include "content/test/test_render_frame_host.h"
39 #include "content/test/test_render_view_host.h"
40 #include "content/test/test_web_contents.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42 #include "ui/base/page_transition_types.h"
43
44 namespace content {
45 namespace {
46
47 class RenderFrameHostManagerTestWebUIControllerFactory
48 : public WebUIControllerFactory {
49 public:
RenderFrameHostManagerTestWebUIControllerFactory()50 RenderFrameHostManagerTestWebUIControllerFactory()
51 : should_create_webui_(false) {
52 }
~RenderFrameHostManagerTestWebUIControllerFactory()53 virtual ~RenderFrameHostManagerTestWebUIControllerFactory() {}
54
set_should_create_webui(bool should_create_webui)55 void set_should_create_webui(bool should_create_webui) {
56 should_create_webui_ = should_create_webui;
57 }
58
59 // WebUIFactory implementation.
CreateWebUIControllerForURL(WebUI * web_ui,const GURL & url) const60 virtual WebUIController* CreateWebUIControllerForURL(
61 WebUI* web_ui, const GURL& url) const OVERRIDE {
62 if (!(should_create_webui_ && HasWebUIScheme(url)))
63 return NULL;
64 return new WebUIController(web_ui);
65 }
66
GetWebUIType(BrowserContext * browser_context,const GURL & url) const67 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
68 const GURL& url) const OVERRIDE {
69 return WebUI::kNoWebUI;
70 }
71
UseWebUIForURL(BrowserContext * browser_context,const GURL & url) const72 virtual bool UseWebUIForURL(BrowserContext* browser_context,
73 const GURL& url) const OVERRIDE {
74 return HasWebUIScheme(url);
75 }
76
UseWebUIBindingsForURL(BrowserContext * browser_context,const GURL & url) const77 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
78 const GURL& url) const OVERRIDE {
79 return HasWebUIScheme(url);
80 }
81
82 private:
83 bool should_create_webui_;
84
85 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostManagerTestWebUIControllerFactory);
86 };
87
88 class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {
89 public:
BeforeUnloadFiredWebContentsDelegate()90 BeforeUnloadFiredWebContentsDelegate() {}
~BeforeUnloadFiredWebContentsDelegate()91 virtual ~BeforeUnloadFiredWebContentsDelegate() {}
92
BeforeUnloadFired(WebContents * web_contents,bool proceed,bool * proceed_to_fire_unload)93 virtual void BeforeUnloadFired(WebContents* web_contents,
94 bool proceed,
95 bool* proceed_to_fire_unload) OVERRIDE {
96 *proceed_to_fire_unload = proceed;
97 }
98
99 private:
100 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate);
101 };
102
103 // This observer keeps track of the last deleted RenderViewHost to avoid
104 // accessing it and causing use-after-free condition.
105 class RenderViewHostDeletedObserver : public WebContentsObserver {
106 public:
RenderViewHostDeletedObserver(RenderViewHost * rvh)107 RenderViewHostDeletedObserver(RenderViewHost* rvh)
108 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
109 process_id_(rvh->GetProcess()->GetID()),
110 routing_id_(rvh->GetRoutingID()),
111 deleted_(false) {
112 }
113
RenderViewDeleted(RenderViewHost * render_view_host)114 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
115 if (render_view_host->GetProcess()->GetID() == process_id_ &&
116 render_view_host->GetRoutingID() == routing_id_) {
117 deleted_ = true;
118 }
119 }
120
deleted()121 bool deleted() {
122 return deleted_;
123 }
124
125 private:
126 int process_id_;
127 int routing_id_;
128 bool deleted_;
129
130 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver);
131 };
132
133 // This observer keeps track of the last deleted RenderFrameHost to avoid
134 // accessing it and causing use-after-free condition.
135 class RenderFrameHostDeletedObserver : public WebContentsObserver {
136 public:
RenderFrameHostDeletedObserver(RenderFrameHost * rfh)137 RenderFrameHostDeletedObserver(RenderFrameHost* rfh)
138 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh)),
139 process_id_(rfh->GetProcess()->GetID()),
140 routing_id_(rfh->GetRoutingID()),
141 deleted_(false) {
142 }
143
RenderFrameDeleted(RenderFrameHost * render_frame_host)144 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
145 if (render_frame_host->GetProcess()->GetID() == process_id_ &&
146 render_frame_host->GetRoutingID() == routing_id_) {
147 deleted_ = true;
148 }
149 }
150
deleted()151 bool deleted() {
152 return deleted_;
153 }
154
155 private:
156 int process_id_;
157 int routing_id_;
158 bool deleted_;
159
160 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostDeletedObserver);
161 };
162
163
164 // This observer is used to check whether IPC messages are being filtered for
165 // swapped out RenderFrameHost objects. It observes the plugin crash and favicon
166 // update events, which the FilterMessagesWhileSwappedOut test simulates being
167 // sent. The test is successful if the event is not observed.
168 // See http://crbug.com/351815
169 class PluginFaviconMessageObserver : public WebContentsObserver {
170 public:
PluginFaviconMessageObserver(WebContents * web_contents)171 PluginFaviconMessageObserver(WebContents* web_contents)
172 : WebContentsObserver(web_contents),
173 plugin_crashed_(false),
174 favicon_received_(false) { }
175
PluginCrashed(const base::FilePath & plugin_path,base::ProcessId plugin_pid)176 virtual void PluginCrashed(const base::FilePath& plugin_path,
177 base::ProcessId plugin_pid) OVERRIDE {
178 plugin_crashed_ = true;
179 }
180
DidUpdateFaviconURL(const std::vector<FaviconURL> & candidates)181 virtual void DidUpdateFaviconURL(
182 const std::vector<FaviconURL>& candidates) OVERRIDE {
183 favicon_received_ = true;
184 }
185
plugin_crashed()186 bool plugin_crashed() {
187 return plugin_crashed_;
188 }
189
favicon_received()190 bool favicon_received() {
191 return favicon_received_;
192 }
193
194 private:
195 bool plugin_crashed_;
196 bool favicon_received_;
197
198 DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
199 };
200
201 // Ensures that RenderFrameDeleted and RenderFrameCreated are called in a
202 // consistent manner.
203 class FrameLifetimeConsistencyChecker : public WebContentsObserver {
204 public:
FrameLifetimeConsistencyChecker(WebContentsImpl * web_contents)205 explicit FrameLifetimeConsistencyChecker(WebContentsImpl* web_contents)
206 : WebContentsObserver(web_contents) {
207 RenderViewCreated(web_contents->GetRenderViewHost());
208 RenderFrameCreated(web_contents->GetMainFrame());
209 }
210
RenderFrameCreated(RenderFrameHost * render_frame_host)211 virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE {
212 std::pair<int, int> routing_pair =
213 std::make_pair(render_frame_host->GetProcess()->GetID(),
214 render_frame_host->GetRoutingID());
215 bool was_live_already = !live_routes_.insert(routing_pair).second;
216 bool was_used_before = deleted_routes_.count(routing_pair) != 0;
217
218 if (was_live_already) {
219 FAIL() << "RenderFrameCreated called more than once for routing pair: "
220 << Format(render_frame_host);
221 } else if (was_used_before) {
222 FAIL() << "RenderFrameCreated called for routing pair "
223 << Format(render_frame_host) << " that was previously deleted.";
224 }
225 }
226
RenderFrameDeleted(RenderFrameHost * render_frame_host)227 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
228 std::pair<int, int> routing_pair =
229 std::make_pair(render_frame_host->GetProcess()->GetID(),
230 render_frame_host->GetRoutingID());
231 bool was_live = live_routes_.erase(routing_pair);
232 bool was_dead_already = !deleted_routes_.insert(routing_pair).second;
233
234 if (was_dead_already) {
235 FAIL() << "RenderFrameDeleted called more than once for routing pair "
236 << Format(render_frame_host);
237 } else if (!was_live) {
238 FAIL() << "RenderFrameDeleted called for routing pair "
239 << Format(render_frame_host)
240 << " for which RenderFrameCreated was never called";
241 }
242 }
243
244 private:
Format(RenderFrameHost * render_frame_host)245 std::string Format(RenderFrameHost* render_frame_host) {
246 return base::StringPrintf(
247 "(%d, %d -> %s )",
248 render_frame_host->GetProcess()->GetID(),
249 render_frame_host->GetRoutingID(),
250 render_frame_host->GetSiteInstance()->GetSiteURL().spec().c_str());
251 }
252 std::set<std::pair<int, int> > live_routes_;
253 std::set<std::pair<int, int> > deleted_routes_;
254 };
255
256 } // namespace
257
258 class RenderFrameHostManagerTest
259 : public RenderViewHostImplTestHarness {
260 public:
SetUp()261 virtual void SetUp() OVERRIDE {
262 RenderViewHostImplTestHarness::SetUp();
263 WebUIControllerFactory::RegisterFactory(&factory_);
264 lifetime_checker_.reset(new FrameLifetimeConsistencyChecker(contents()));
265 }
266
TearDown()267 virtual void TearDown() OVERRIDE {
268 lifetime_checker_.reset();
269 RenderViewHostImplTestHarness::TearDown();
270 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
271 }
272
set_should_create_webui(bool should_create_webui)273 void set_should_create_webui(bool should_create_webui) {
274 factory_.set_should_create_webui(should_create_webui);
275 }
276
NavigateActiveAndCommit(const GURL & url)277 void NavigateActiveAndCommit(const GURL& url) {
278 // Note: we navigate the active RenderFrameHost because previous navigations
279 // won't have committed yet, so NavigateAndCommit does the wrong thing
280 // for us.
281 controller().LoadURL(
282 url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
283 TestRenderViewHost* old_rvh = test_rvh();
284
285 // Simulate the BeforeUnload_ACK that is received from the current renderer
286 // for a cross-site navigation.
287 if (old_rvh != active_rvh()) {
288 old_rvh->SendBeforeUnloadACK(true);
289 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, old_rvh->rvh_state());
290 }
291
292 // Commit the navigation with a new page ID.
293 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
294 active_rvh()->GetSiteInstance());
295
296 // Use an observer to avoid accessing a deleted renderer later on when the
297 // state is being checked.
298 RenderViewHostDeletedObserver rvh_observer(old_rvh);
299 active_test_rvh()->SendNavigate(max_page_id + 1, url);
300
301 // Make sure that we start to run the unload handler at the time of commit.
302 bool expecting_rvh_shutdown = false;
303 if (old_rvh != active_rvh() && !rvh_observer.deleted()) {
304 if (!static_cast<SiteInstanceImpl*>(
305 old_rvh->GetSiteInstance())->active_view_count()) {
306 expecting_rvh_shutdown = true;
307 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN,
308 old_rvh->rvh_state());
309 } else {
310 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT,
311 old_rvh->rvh_state());
312 }
313 }
314
315 // Simulate the swap out ACK coming from the pending renderer. This should
316 // either shut down the old RVH or leave it in a swapped out state.
317 if (old_rvh != active_rvh()) {
318 old_rvh->OnSwappedOut(false);
319 if (expecting_rvh_shutdown) {
320 EXPECT_TRUE(rvh_observer.deleted());
321 } else {
322 EXPECT_EQ(RenderViewHostImpl::STATE_SWAPPED_OUT,
323 old_rvh->rvh_state());
324 }
325 }
326 }
327
ShouldSwapProcesses(RenderFrameHostManager * manager,const NavigationEntryImpl * current_entry,const NavigationEntryImpl * new_entry) const328 bool ShouldSwapProcesses(RenderFrameHostManager* manager,
329 const NavigationEntryImpl* current_entry,
330 const NavigationEntryImpl* new_entry) const {
331 CHECK(new_entry);
332 BrowserContext* browser_context =
333 manager->delegate_->GetControllerForRenderManager().GetBrowserContext();
334 const GURL& current_effective_url = current_entry ?
335 SiteInstanceImpl::GetEffectiveURL(browser_context,
336 current_entry->GetURL()) :
337 manager->render_frame_host_->GetSiteInstance()->GetSiteURL();
338 bool current_is_view_source_mode = current_entry ?
339 current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode();
340 return manager->ShouldSwapBrowsingInstancesForNavigation(
341 current_effective_url,
342 current_is_view_source_mode,
343 new_entry->site_instance(),
344 SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()),
345 new_entry->IsViewSourceMode());
346 }
347
348 // Creates a test RenderViewHost that's swapped out.
CreateSwappedOutRenderViewHost()349 TestRenderViewHost* CreateSwappedOutRenderViewHost() {
350 const GURL kChromeURL("chrome://foo");
351 const GURL kDestUrl("http://www.google.com/");
352
353 // Navigate our first tab to a chrome url and then to the destination.
354 NavigateActiveAndCommit(kChromeURL);
355 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
356
357 // Navigate to a cross-site URL.
358 contents()->GetController().LoadURL(
359 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
360 EXPECT_TRUE(contents()->cross_navigation_pending());
361
362 // Manually increase the number of active views in the
363 // SiteInstance that ntp_rfh belongs to, to prevent it from being
364 // destroyed when it gets swapped out.
365 static_cast<SiteInstanceImpl*>(ntp_rfh->GetSiteInstance())->
366 increment_active_view_count();
367
368 TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame();
369 CHECK(dest_rfh);
370 EXPECT_NE(ntp_rfh, dest_rfh);
371
372 // BeforeUnload finishes.
373 ntp_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
374
375 dest_rfh->SendNavigate(101, kDestUrl);
376 ntp_rfh->OnSwappedOut(false);
377
378 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->IsSwappedOut());
379 return ntp_rfh->GetRenderViewHost();
380 }
381
GetNavigationRequestForRenderFrameManager(RenderFrameHostManager * manager) const382 NavigationRequest* GetNavigationRequestForRenderFrameManager(
383 RenderFrameHostManager* manager) const {
384 return manager->navigation_request_for_testing();
385 }
386
EnableBrowserSideNavigation()387 void EnableBrowserSideNavigation() {
388 CommandLine::ForCurrentProcess()->AppendSwitch(
389 switches::kEnableBrowserSideNavigation);
390 }
391 private:
392 RenderFrameHostManagerTestWebUIControllerFactory factory_;
393 scoped_ptr<FrameLifetimeConsistencyChecker> lifetime_checker_;
394 };
395
396 // Tests that when you navigate from a chrome:// url to another page, and
397 // then do that same thing in another tab, that the two resulting pages have
398 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
399 // a regression test for bug 9364.
TEST_F(RenderFrameHostManagerTest,NewTabPageProcesses)400 TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) {
401 set_should_create_webui(true);
402 const GURL kChromeUrl("chrome://foo");
403 const GURL kDestUrl("http://www.google.com/");
404
405 // Navigate our first tab to the chrome url and then to the destination,
406 // ensuring we grant bindings to the chrome URL.
407 NavigateActiveAndCommit(kChromeUrl);
408 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
409 NavigateActiveAndCommit(kDestUrl);
410
411 EXPECT_FALSE(contents()->GetPendingMainFrame());
412
413 // Make a second tab.
414 scoped_ptr<TestWebContents> contents2(
415 TestWebContents::Create(browser_context(), NULL));
416
417 // Load the two URLs in the second tab. Note that the first navigation creates
418 // a RFH that's not pending (since there is no cross-site transition), so
419 // we use the committed one.
420 contents2->GetController().LoadURL(
421 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
422 TestRenderFrameHost* ntp_rfh2 = contents2->GetMainFrame();
423 EXPECT_FALSE(contents2->cross_navigation_pending());
424 ntp_rfh2->SendNavigate(100, kChromeUrl);
425
426 // The second one is the opposite, creating a cross-site transition and
427 // requiring a beforeunload ack.
428 contents2->GetController().LoadURL(
429 kDestUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
430 EXPECT_TRUE(contents2->cross_navigation_pending());
431 TestRenderFrameHost* dest_rfh2 = contents2->GetPendingMainFrame();
432 ASSERT_TRUE(dest_rfh2);
433
434 ntp_rfh2->GetRenderViewHost()->SendBeforeUnloadACK(true);
435 dest_rfh2->SendNavigate(101, kDestUrl);
436
437 // The two RFH's should be different in every way.
438 EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2->GetProcess());
439 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
440 dest_rfh2->GetSiteInstance());
441 EXPECT_FALSE(dest_rfh2->GetSiteInstance()->IsRelatedSiteInstance(
442 contents()->GetMainFrame()->GetSiteInstance()));
443
444 // Navigate both to the new tab page, and verify that they share a
445 // RenderProcessHost (not a SiteInstance).
446 NavigateActiveAndCommit(kChromeUrl);
447 EXPECT_FALSE(contents()->GetPendingMainFrame());
448
449 contents2->GetController().LoadURL(
450 kChromeUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
451 dest_rfh2->GetRenderViewHost()->SendBeforeUnloadACK(true);
452 contents2->GetPendingMainFrame()->SendNavigate(102, kChromeUrl);
453
454 EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(),
455 contents2->GetMainFrame()->GetSiteInstance());
456 EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(),
457 contents2->GetMainFrame()->GetSiteInstance()->GetProcess());
458 }
459
460 // Ensure that the browser ignores most IPC messages that arrive from a
461 // RenderViewHost that has been swapped out. We do not want to take
462 // action on requests from a non-active renderer. The main exception is
463 // for synchronous messages, which cannot be ignored without leaving the
464 // renderer in a stuck state. See http://crbug.com/93427.
TEST_F(RenderFrameHostManagerTest,FilterMessagesWhileSwappedOut)465 TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {
466 const GURL kChromeURL("chrome://foo");
467 const GURL kDestUrl("http://www.google.com/");
468 std::vector<FaviconURL> icons;
469
470 // Navigate our first tab to a chrome url and then to the destination.
471 NavigateActiveAndCommit(kChromeURL);
472 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
473
474 // Send an update favicon message and make sure it works.
475 const base::string16 ntp_title = base::ASCIIToUTF16("NTP Title");
476 {
477 PluginFaviconMessageObserver observer(contents());
478 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->OnMessageReceived(
479 ViewHostMsg_UpdateFaviconURL(
480 ntp_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
481 EXPECT_TRUE(observer.favicon_received());
482 }
483 // Create one more view in the same SiteInstance where ntp_rfh
484 // exists so that it doesn't get deleted on navigation to another
485 // site.
486 static_cast<SiteInstanceImpl*>(ntp_rfh->GetSiteInstance())->
487 increment_active_view_count();
488
489
490 // Navigate to a cross-site URL.
491 NavigateActiveAndCommit(kDestUrl);
492 TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
493 ASSERT_TRUE(dest_rfh);
494 EXPECT_NE(ntp_rfh, dest_rfh);
495
496 // The new RVH should be able to update its favicon.
497 const base::string16 dest_title = base::ASCIIToUTF16("Google");
498 {
499 PluginFaviconMessageObserver observer(contents());
500 EXPECT_TRUE(
501 dest_rfh->GetRenderViewHost()->OnMessageReceived(
502 ViewHostMsg_UpdateFaviconURL(
503 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
504 EXPECT_TRUE(observer.favicon_received());
505 }
506
507 // The old renderer, being slow, now updates the favicon. It should be
508 // filtered out and not take effect.
509 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->IsSwappedOut());
510 {
511 PluginFaviconMessageObserver observer(contents());
512 EXPECT_TRUE(
513 ntp_rfh->GetRenderViewHost()->OnMessageReceived(
514 ViewHostMsg_UpdateFaviconURL(
515 dest_rfh->GetRenderViewHost()->GetRoutingID(), icons)));
516 EXPECT_FALSE(observer.favicon_received());
517 }
518
519 // The same logic should apply to RenderFrameHosts as well and routing through
520 // swapped out RFH shouldn't be allowed. Use a PluginCrashObserver to check
521 // if the IPC message is allowed through or not.
522 {
523 PluginFaviconMessageObserver observer(contents());
524 EXPECT_TRUE(ntp_rfh->OnMessageReceived(
525 FrameHostMsg_PluginCrashed(
526 ntp_rfh->GetRoutingID(), base::FilePath(), 0)));
527 EXPECT_FALSE(observer.plugin_crashed());
528 }
529
530 // We cannot filter out synchronous IPC messages, because the renderer would
531 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
532 // that can run easily within a unit test, and that needs to receive a reply
533 // without showing an actual dialog.
534 MockRenderProcessHost* ntp_process_host =
535 static_cast<MockRenderProcessHost*>(ntp_rfh->GetProcess());
536 ntp_process_host->sink().ClearMessages();
537 const base::string16 msg = base::ASCIIToUTF16("Message");
538 bool result = false;
539 base::string16 unused;
540 FrameHostMsg_RunBeforeUnloadConfirm before_unload_msg(
541 ntp_rfh->GetRoutingID(), kChromeURL, msg, false, &result, &unused);
542 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
543 before_unload_msg.EnableMessagePumping();
544 EXPECT_TRUE(ntp_rfh->OnMessageReceived(before_unload_msg));
545 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
546
547 // Also test RunJavaScriptMessage.
548 ntp_process_host->sink().ClearMessages();
549 FrameHostMsg_RunJavaScriptMessage js_msg(
550 ntp_rfh->GetRoutingID(), msg, msg, kChromeURL,
551 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
552 js_msg.EnableMessagePumping();
553 EXPECT_TRUE(ntp_rfh->OnMessageReceived(js_msg));
554 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
555 }
556
TEST_F(RenderFrameHostManagerTest,WhiteListSwapCompositorFrame)557 TEST_F(RenderFrameHostManagerTest, WhiteListSwapCompositorFrame) {
558 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
559 TestRenderWidgetHostView* swapped_out_rwhv =
560 static_cast<TestRenderWidgetHostView*>(swapped_out_rvh->GetView());
561 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame());
562
563 MockRenderProcessHost* process_host =
564 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess());
565 process_host->sink().ClearMessages();
566
567 cc::CompositorFrame frame;
568 ViewHostMsg_SwapCompositorFrame msg(
569 rvh()->GetRoutingID(), 0, frame, std::vector<IPC::Message>());
570
571 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg));
572 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame());
573 }
574
575 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active
576 // widgets.
TEST_F(RenderFrameHostManagerTest,GetRenderWidgetHostsReturnsActiveViews)577 TEST_F(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {
578 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
579 EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
580
581 scoped_ptr<RenderWidgetHostIterator> widgets(
582 RenderWidgetHost::GetRenderWidgetHosts());
583 // We know that there is the only one active widget. Another view is
584 // now swapped out, so the swapped out view is not included in the
585 // list.
586 RenderWidgetHost* widget = widgets->GetNextHost();
587 EXPECT_FALSE(widgets->GetNextHost());
588 RenderViewHost* rvh = RenderViewHost::From(widget);
589 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
590 static_cast<RenderViewHostImpl*>(rvh)->rvh_state());
591 }
592
593 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
594 // RenderViewHostImpl::GetAllRenderWidgetHosts().
595 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
596 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
597 // including swapped out ones.
TEST_F(RenderFrameHostManagerTest,GetRenderWidgetHostsWithinGetAllRenderWidgetHosts)598 TEST_F(RenderFrameHostManagerTest,
599 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {
600 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost();
601 EXPECT_TRUE(swapped_out_rvh->IsSwappedOut());
602
603 scoped_ptr<RenderWidgetHostIterator> widgets(
604 RenderWidgetHost::GetRenderWidgetHosts());
605
606 while (RenderWidgetHost* w = widgets->GetNextHost()) {
607 bool found = false;
608 scoped_ptr<RenderWidgetHostIterator> all_widgets(
609 RenderWidgetHostImpl::GetAllRenderWidgetHosts());
610 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) {
611 if (w == widget) {
612 found = true;
613 break;
614 }
615 }
616 EXPECT_TRUE(found);
617 }
618 }
619
620 // Test if SiteInstanceImpl::active_view_count() is correctly updated
621 // as views in a SiteInstance get swapped out and in.
TEST_F(RenderFrameHostManagerTest,ActiveViewCountWhileSwappingInandOut)622 TEST_F(RenderFrameHostManagerTest, ActiveViewCountWhileSwappingInandOut) {
623 const GURL kUrl1("http://www.google.com/");
624 const GURL kUrl2("http://www.chromium.org/");
625
626 // Navigate to an initial URL.
627 contents()->NavigateAndCommit(kUrl1);
628 TestRenderViewHost* rvh1 = test_rvh();
629
630 SiteInstanceImpl* instance1 =
631 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance());
632 EXPECT_EQ(instance1->active_view_count(), 1U);
633
634 // Create 2 new tabs and simulate them being the opener chain for the main
635 // tab. They should be in the same SiteInstance.
636 scoped_ptr<TestWebContents> opener1(
637 TestWebContents::Create(browser_context(), instance1));
638 contents()->SetOpener(opener1.get());
639
640 scoped_ptr<TestWebContents> opener2(
641 TestWebContents::Create(browser_context(), instance1));
642 opener1->SetOpener(opener2.get());
643
644 EXPECT_EQ(instance1->active_view_count(), 3U);
645
646 // Navigate to a cross-site URL (different SiteInstance but same
647 // BrowsingInstance).
648 contents()->NavigateAndCommit(kUrl2);
649 TestRenderViewHost* rvh2 = test_rvh();
650 SiteInstanceImpl* instance2 =
651 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance());
652
653 // rvh2 is on chromium.org which is different from google.com on
654 // which other tabs are.
655 EXPECT_EQ(instance2->active_view_count(), 1U);
656
657 // There are two active views on google.com now.
658 EXPECT_EQ(instance1->active_view_count(), 2U);
659
660 // Navigate to the original origin (google.com).
661 contents()->NavigateAndCommit(kUrl1);
662
663 EXPECT_EQ(instance1->active_view_count(), 3U);
664 }
665
666 // This deletes a WebContents when the given RVH is deleted. This is
667 // only for testing whether deleting an RVH does not cause any UaF in
668 // other parts of the system. For now, this class is only used for the
669 // next test cases to detect the bug mentioned at
670 // http://crbug.com/259859.
671 class RenderViewHostDestroyer : public WebContentsObserver {
672 public:
RenderViewHostDestroyer(RenderViewHost * render_view_host,WebContents * web_contents)673 RenderViewHostDestroyer(RenderViewHost* render_view_host,
674 WebContents* web_contents)
675 : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
676 render_view_host_(render_view_host),
677 web_contents_(web_contents) {}
678
RenderViewDeleted(RenderViewHost * render_view_host)679 virtual void RenderViewDeleted(
680 RenderViewHost* render_view_host) OVERRIDE {
681 if (render_view_host == render_view_host_)
682 delete web_contents_;
683 }
684
685 private:
686 RenderViewHost* render_view_host_;
687 WebContents* web_contents_;
688
689 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer);
690 };
691
692 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
693 // RenderWidget that has been freed while deleting a RenderViewHost in
694 // a previous iteration. This is a regression test for
695 // http://crbug.com/259859.
TEST_F(RenderFrameHostManagerTest,DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance)696 TEST_F(RenderFrameHostManagerTest,
697 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {
698 const GURL kChromeURL("chrome://newtab");
699 const GURL kUrl1("http://www.google.com");
700 const GURL kUrl2("http://www.chromium.org");
701
702 // Navigate our first tab to a chrome url and then to the destination.
703 NavigateActiveAndCommit(kChromeURL);
704 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
705
706 // Create one more tab and navigate to kUrl1. web_contents is not
707 // wrapped as scoped_ptr since it intentionally deleted by destroyer
708 // below as part of this test.
709 TestWebContents* web_contents =
710 TestWebContents::Create(browser_context(), ntp_rfh->GetSiteInstance());
711 web_contents->NavigateAndCommit(kUrl1);
712 RenderViewHostDestroyer destroyer(ntp_rfh->GetRenderViewHost(),
713 web_contents);
714
715 // This causes the first tab to navigate to kUrl2, which destroys
716 // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When
717 // ntp_rfh is destroyed, it also destroys the RVHs in web_contents
718 // too. This can test whether
719 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can
720 // touch any object freed in this way or not while iterating through
721 // all widgets.
722 contents()->NavigateAndCommit(kUrl2);
723 }
724
725 // When there is an error with the specified page, renderer exits view-source
726 // mode. See WebFrameImpl::DidFail(). We check by this test that
727 // EnableViewSourceMode message is sent on every navigation regardless
728 // RenderView is being newly created or reused.
TEST_F(RenderFrameHostManagerTest,AlwaysSendEnableViewSourceMode)729 TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {
730 const GURL kChromeUrl("chrome://foo");
731 const GURL kUrl("view-source:http://foo");
732
733 // We have to navigate to some page at first since without this, the first
734 // navigation will reuse the SiteInstance created by Init(), and the second
735 // one will create a new SiteInstance. Because current_instance and
736 // new_instance will be different, a new RenderViewHost will be created for
737 // the second navigation. We have to avoid this in order to exercise the
738 // target code patch.
739 NavigateActiveAndCommit(kChromeUrl);
740
741 // Navigate.
742 controller().LoadURL(
743 kUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
744 // Simulate response from RenderFrame for DispatchBeforeUnload.
745 base::TimeTicks now = base::TimeTicks::Now();
746 contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(
747 contents()->GetMainFrame()->GetRoutingID(), true, now, now));
748 ASSERT_TRUE(contents()->GetPendingMainFrame())
749 << "Expected new pending RenderFrameHost to be created.";
750 RenderFrameHost* last_rfh = contents()->GetPendingMainFrame();
751 int32 new_id =
752 contents()->GetMaxPageIDForSiteInstance(last_rfh->GetSiteInstance()) + 1;
753 contents()->GetPendingMainFrame()->SendNavigate(new_id, kUrl);
754 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
755 ASSERT_TRUE(controller().GetLastCommittedEntry());
756 EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL());
757 EXPECT_FALSE(controller().GetPendingEntry());
758 // Because we're using TestWebContents and TestRenderViewHost in this
759 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no
760 // EnableViewSourceMode message, here.
761
762 // Clear queued messages before load.
763 process()->sink().ClearMessages();
764 // Navigate, again.
765 controller().LoadURL(
766 kUrl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
767 // The same RenderViewHost should be reused.
768 EXPECT_FALSE(contents()->GetPendingMainFrame());
769 EXPECT_TRUE(last_rfh == contents()->GetMainFrame());
770 // Navigate using the returned page_id.
771 contents()->GetMainFrame()->SendNavigate(new_id, kUrl);
772 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
773 EXPECT_FALSE(controller().GetPendingEntry());
774 // New message should be sent out to make sure to enter view-source mode.
775 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
776 ViewMsg_EnableViewSourceMode::ID));
777 }
778
779 // Tests the Init function by checking the initial RenderViewHost.
TEST_F(RenderFrameHostManagerTest,Init)780 TEST_F(RenderFrameHostManagerTest, Init) {
781 // Using TestBrowserContext.
782 SiteInstanceImpl* instance =
783 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
784 EXPECT_FALSE(instance->HasSite());
785
786 scoped_ptr<TestWebContents> web_contents(
787 TestWebContents::Create(browser_context(), instance));
788
789 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
790 RenderViewHostImpl* rvh = manager->current_host();
791 RenderFrameHostImpl* rfh = manager->current_frame_host();
792 ASSERT_TRUE(rvh);
793 ASSERT_TRUE(rfh);
794 EXPECT_EQ(rvh, rfh->render_view_host());
795 EXPECT_EQ(instance, rvh->GetSiteInstance());
796 EXPECT_EQ(web_contents.get(), rvh->GetDelegate());
797 EXPECT_EQ(web_contents.get(), rfh->delegate());
798 EXPECT_TRUE(manager->GetRenderWidgetHostView());
799 EXPECT_FALSE(manager->pending_render_view_host());
800 }
801
802 // Tests the Navigate function. We navigate three sites consecutively and check
803 // how the pending/committed RenderViewHost are modified.
TEST_F(RenderFrameHostManagerTest,Navigate)804 TEST_F(RenderFrameHostManagerTest, Navigate) {
805 TestNotificationTracker notifications;
806
807 SiteInstance* instance = SiteInstance::Create(browser_context());
808
809 scoped_ptr<TestWebContents> web_contents(
810 TestWebContents::Create(browser_context(), instance));
811 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
812 Source<WebContents>(web_contents.get()));
813
814 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
815 RenderFrameHostImpl* host;
816
817 // 1) The first navigation. --------------------------
818 const GURL kUrl1("http://www.google.com/");
819 NavigationEntryImpl entry1(
820 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
821 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
822 false /* is_renderer_init */);
823 host = manager->Navigate(entry1);
824
825 // The RenderFrameHost created in Init will be reused.
826 EXPECT_TRUE(host == manager->current_frame_host());
827 EXPECT_FALSE(manager->pending_frame_host());
828
829 // Commit.
830 manager->DidNavigateFrame(host);
831 // Commit to SiteInstance should be delayed until RenderView commit.
832 EXPECT_TRUE(host == manager->current_frame_host());
833 ASSERT_TRUE(host);
834 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
835 HasSite());
836 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
837
838 // 2) Navigate to next site. -------------------------
839 const GURL kUrl2("http://www.google.com/foo");
840 NavigationEntryImpl entry2(
841 NULL /* instance */, -1 /* page_id */, kUrl2,
842 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
843 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
844 true /* is_renderer_init */);
845 host = manager->Navigate(entry2);
846
847 // The RenderFrameHost created in Init will be reused.
848 EXPECT_TRUE(host == manager->current_frame_host());
849 EXPECT_FALSE(manager->pending_frame_host());
850
851 // Commit.
852 manager->DidNavigateFrame(host);
853 EXPECT_TRUE(host == manager->current_frame_host());
854 ASSERT_TRUE(host);
855 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
856 HasSite());
857
858 // 3) Cross-site navigate to next site. --------------
859 const GURL kUrl3("http://webkit.org/");
860 NavigationEntryImpl entry3(
861 NULL /* instance */, -1 /* page_id */, kUrl3,
862 Referrer(kUrl2, blink::WebReferrerPolicyDefault),
863 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
864 false /* is_renderer_init */);
865 host = manager->Navigate(entry3);
866
867 // A new RenderFrameHost should be created.
868 EXPECT_TRUE(manager->pending_frame_host());
869 ASSERT_EQ(host, manager->pending_frame_host());
870
871 notifications.Reset();
872
873 // Commit.
874 manager->DidNavigateFrame(manager->pending_frame_host());
875 EXPECT_TRUE(host == manager->current_frame_host());
876 ASSERT_TRUE(host);
877 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
878 HasSite());
879 // Check the pending RenderFrameHost has been committed.
880 EXPECT_FALSE(manager->pending_frame_host());
881
882 // We should observe a notification.
883 EXPECT_TRUE(
884 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
885 }
886
887 // Tests WebUI creation.
TEST_F(RenderFrameHostManagerTest,WebUI)888 TEST_F(RenderFrameHostManagerTest, WebUI) {
889 set_should_create_webui(true);
890 SiteInstance* instance = SiteInstance::Create(browser_context());
891
892 scoped_ptr<TestWebContents> web_contents(
893 TestWebContents::Create(browser_context(), instance));
894 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
895
896 EXPECT_FALSE(manager->current_host()->IsRenderViewLive());
897
898 const GURL kUrl("chrome://foo");
899 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
900 Referrer(), base::string16() /* title */,
901 ui::PAGE_TRANSITION_TYPED,
902 false /* is_renderer_init */);
903 RenderFrameHostImpl* host = manager->Navigate(entry);
904
905 // We commit the pending RenderFrameHost immediately because the previous
906 // RenderFrameHost was not live. We test a case where it is live in
907 // WebUIInNewTab.
908 EXPECT_TRUE(host);
909 EXPECT_EQ(host, manager->current_frame_host());
910 EXPECT_FALSE(manager->pending_frame_host());
911
912 // It's important that the site instance get set on the Web UI page as soon
913 // as the navigation starts, rather than lazily after it commits, so we don't
914 // try to re-use the SiteInstance/process for non Web UI things that may
915 // get loaded in between.
916 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
917 HasSite());
918 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL());
919
920 // The Web UI is committed immediately because the RenderViewHost has not been
921 // used yet. UpdateStateForNavigate() took the short cut path.
922 EXPECT_FALSE(manager->pending_web_ui());
923 EXPECT_TRUE(manager->web_ui());
924
925 // Commit.
926 manager->DidNavigateFrame(host);
927 EXPECT_TRUE(
928 host->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
929 }
930
931 // Tests that we can open a WebUI link in a new tab from a WebUI page and still
932 // grant the correct bindings. http://crbug.com/189101.
TEST_F(RenderFrameHostManagerTest,WebUIInNewTab)933 TEST_F(RenderFrameHostManagerTest, WebUIInNewTab) {
934 set_should_create_webui(true);
935 SiteInstance* blank_instance = SiteInstance::Create(browser_context());
936
937 // Create a blank tab.
938 scoped_ptr<TestWebContents> web_contents1(
939 TestWebContents::Create(browser_context(), blank_instance));
940 RenderFrameHostManager* manager1 =
941 web_contents1->GetRenderManagerForTesting();
942 // Test the case that new RVH is considered live.
943 manager1->current_host()->CreateRenderView(
944 base::string16(), -1, MSG_ROUTING_NONE, -1, false);
945 EXPECT_TRUE(manager1->current_host()->IsRenderViewLive());
946 EXPECT_TRUE(manager1->current_frame_host()->IsRenderFrameLive());
947
948 // Navigate to a WebUI page.
949 const GURL kUrl1("chrome://foo");
950 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
951 Referrer(), base::string16() /* title */,
952 ui::PAGE_TRANSITION_TYPED,
953 false /* is_renderer_init */);
954 RenderFrameHostImpl* host1 = manager1->Navigate(entry1);
955
956 // We should have a pending navigation to the WebUI RenderViewHost.
957 // It should already have bindings.
958 EXPECT_EQ(host1, manager1->pending_frame_host());
959 EXPECT_NE(host1, manager1->current_frame_host());
960 EXPECT_TRUE(
961 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
962
963 // Commit and ensure we still have bindings.
964 manager1->DidNavigateFrame(host1);
965 SiteInstance* webui_instance = host1->GetSiteInstance();
966 EXPECT_EQ(host1, manager1->current_frame_host());
967 EXPECT_TRUE(
968 host1->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
969
970 // Now simulate clicking a link that opens in a new tab.
971 scoped_ptr<TestWebContents> web_contents2(
972 TestWebContents::Create(browser_context(), webui_instance));
973 RenderFrameHostManager* manager2 =
974 web_contents2->GetRenderManagerForTesting();
975 // Make sure the new RVH is considered live. This is usually done in
976 // RenderWidgetHost::Init when opening a new tab from a link.
977 manager2->current_host()->CreateRenderView(
978 base::string16(), -1, MSG_ROUTING_NONE, -1, false);
979
980 const GURL kUrl2("chrome://foo/bar");
981 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2,
982 Referrer(), base::string16() /* title */,
983 ui::PAGE_TRANSITION_LINK,
984 true /* is_renderer_init */);
985 RenderFrameHostImpl* host2 = manager2->Navigate(entry2);
986
987 // No cross-process transition happens because we are already in the right
988 // SiteInstance. We should grant bindings immediately.
989 EXPECT_EQ(host2, manager2->current_frame_host());
990 EXPECT_TRUE(
991 host2->render_view_host()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
992
993 manager2->DidNavigateFrame(host2);
994 }
995
996 // Tests that we don't end up in an inconsistent state if a page does a back and
997 // then reload. http://crbug.com/51680
TEST_F(RenderFrameHostManagerTest,PageDoesBackAndReload)998 TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) {
999 const GURL kUrl1("http://www.google.com/");
1000 const GURL kUrl2("http://www.evil-site.com/");
1001
1002 // Navigate to a safe site, then an evil site.
1003 // This will switch RenderFrameHosts. We cannot assert that the first and
1004 // second RFHs are different, though, because the first one may be promptly
1005 // deleted.
1006 contents()->NavigateAndCommit(kUrl1);
1007 contents()->NavigateAndCommit(kUrl2);
1008 TestRenderFrameHost* evil_rfh = contents()->GetMainFrame();
1009
1010 // Now let's simulate the evil page calling history.back().
1011 contents()->OnGoToEntryAtOffset(-1);
1012 // We should have a new pending RFH.
1013 // Note that in this case, the navigation has not committed, so evil_rfh will
1014 // not be deleted yet.
1015 EXPECT_NE(evil_rfh, contents()->GetPendingMainFrame());
1016 EXPECT_NE(evil_rfh->GetRenderViewHost(),
1017 contents()->GetPendingMainFrame()->GetRenderViewHost());
1018
1019 // Before that RFH has committed, the evil page reloads itself.
1020 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1021 params.page_id = 1;
1022 params.url = kUrl2;
1023 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
1024 params.should_update_history = false;
1025 params.gesture = NavigationGestureAuto;
1026 params.was_within_same_page = false;
1027 params.is_post = false;
1028 params.page_state = PageState::CreateFromURL(kUrl2);
1029
1030 contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh,
1031 params);
1032
1033 // That should have cancelled the pending RFH, and the evil RFH should be the
1034 // current one.
1035 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
1036 pending_render_view_host() == NULL);
1037 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() ==
1038 NULL);
1039 EXPECT_EQ(evil_rfh,
1040 contents()->GetRenderManagerForTesting()->current_frame_host());
1041 EXPECT_EQ(evil_rfh->GetRenderViewHost(),
1042 contents()->GetRenderManagerForTesting()->current_host());
1043
1044 // Also we should not have a pending navigation entry.
1045 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL);
1046 NavigationEntry* entry = contents()->GetController().GetVisibleEntry();
1047 ASSERT_TRUE(entry != NULL);
1048 EXPECT_EQ(kUrl2, entry->GetURL());
1049 }
1050
1051 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
1052 // See http://crbug.com/93427.
TEST_F(RenderFrameHostManagerTest,NavigateAfterMissingSwapOutACK)1053 TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
1054 const GURL kUrl1("http://www.google.com/");
1055 const GURL kUrl2("http://www.chromium.org/");
1056
1057 // Navigate to two pages.
1058 contents()->NavigateAndCommit(kUrl1);
1059 TestRenderViewHost* rvh1 = test_rvh();
1060
1061 // Keep active_view_count nonzero so that no swapped out views in
1062 // this SiteInstance get forcefully deleted.
1063 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())->
1064 increment_active_view_count();
1065
1066 contents()->NavigateAndCommit(kUrl2);
1067 TestRenderViewHost* rvh2 = test_rvh();
1068 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())->
1069 increment_active_view_count();
1070
1071 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
1072 // happen, but we have seen it when going back quickly across many entries
1073 // (http://crbug.com/93427).
1074 contents()->GetController().GoBack();
1075 EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack());
1076 contents()->ProceedWithCrossSiteNavigation();
1077 EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack());
1078
1079 // The back navigation commits.
1080 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1081 rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
1082 EXPECT_TRUE(rvh2->IsWaitingForUnloadACK());
1083 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh2->rvh_state());
1084
1085 // We should be able to navigate forward.
1086 contents()->GetController().GoForward();
1087 contents()->ProceedWithCrossSiteNavigation();
1088 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
1089 rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL());
1090 EXPECT_EQ(rvh2, rvh());
1091 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state());
1092 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state());
1093 rvh1->OnSwappedOut(false);
1094 EXPECT_TRUE(rvh1->IsSwappedOut());
1095 EXPECT_EQ(RenderViewHostImpl::STATE_SWAPPED_OUT, rvh1->rvh_state());
1096 }
1097
1098 // Test that we create swapped out RVHs for the opener chain when navigating an
1099 // opened tab cross-process. This allows us to support certain cross-process
1100 // JavaScript calls (http://crbug.com/99202).
TEST_F(RenderFrameHostManagerTest,CreateSwappedOutOpenerRVHs)1101 TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRVHs) {
1102 const GURL kUrl1("http://www.google.com/");
1103 const GURL kUrl2("http://www.chromium.org/");
1104 const GURL kChromeUrl("chrome://foo");
1105
1106 // Navigate to an initial URL.
1107 contents()->NavigateAndCommit(kUrl1);
1108 RenderFrameHostManager* manager = contents()->GetRenderManagerForTesting();
1109 TestRenderViewHost* rvh1 = test_rvh();
1110
1111 // Create 2 new tabs and simulate them being the opener chain for the main
1112 // tab. They should be in the same SiteInstance.
1113 scoped_ptr<TestWebContents> opener1(
1114 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1115 RenderFrameHostManager* opener1_manager =
1116 opener1->GetRenderManagerForTesting();
1117 contents()->SetOpener(opener1.get());
1118
1119 scoped_ptr<TestWebContents> opener2(
1120 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1121 RenderFrameHostManager* opener2_manager =
1122 opener2->GetRenderManagerForTesting();
1123 opener1->SetOpener(opener2.get());
1124
1125 // Navigate to a cross-site URL (different SiteInstance but same
1126 // BrowsingInstance).
1127 contents()->NavigateAndCommit(kUrl2);
1128 TestRenderViewHost* rvh2 = test_rvh();
1129 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1130 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1131 rvh2->GetSiteInstance()));
1132
1133 // Ensure rvh1 is placed on swapped out list of the current tab.
1134 EXPECT_TRUE(manager->IsRVHOnSwappedOutList(rvh1));
1135 EXPECT_EQ(rvh1,
1136 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance()));
1137
1138 // Ensure a swapped out RVH is created in the first opener tab.
1139 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1140 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1141 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1142 EXPECT_TRUE(opener1_rvh->IsSwappedOut());
1143
1144 // Ensure a swapped out RVH is created in the second opener tab.
1145 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>(
1146 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1147 EXPECT_TRUE(opener2_manager->IsRVHOnSwappedOutList(opener2_rvh));
1148 EXPECT_TRUE(opener2_rvh->IsSwappedOut());
1149
1150 // Navigate to a cross-BrowsingInstance URL.
1151 contents()->NavigateAndCommit(kChromeUrl);
1152 TestRenderViewHost* rvh3 = test_rvh();
1153 EXPECT_NE(rvh1->GetSiteInstance(), rvh3->GetSiteInstance());
1154 EXPECT_FALSE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1155 rvh3->GetSiteInstance()));
1156
1157 // No scripting is allowed across BrowsingInstances, so we should not create
1158 // swapped out RVHs for the opener chain in this case.
1159 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1160 rvh3->GetSiteInstance()));
1161 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost(
1162 rvh3->GetSiteInstance()));
1163 }
1164
1165 // Test that a page can disown the opener of the WebContents.
TEST_F(RenderFrameHostManagerTest,DisownOpener)1166 TEST_F(RenderFrameHostManagerTest, DisownOpener) {
1167 const GURL kUrl1("http://www.google.com/");
1168 const GURL kUrl2("http://www.chromium.org/");
1169
1170 // Navigate to an initial URL.
1171 contents()->NavigateAndCommit(kUrl1);
1172 TestRenderFrameHost* rfh1 = main_test_rfh();
1173
1174 // Create a new tab and simulate having it be the opener for the main tab.
1175 scoped_ptr<TestWebContents> opener1(
1176 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1177 contents()->SetOpener(opener1.get());
1178 EXPECT_TRUE(contents()->HasOpener());
1179
1180 // Navigate to a cross-site URL (different SiteInstance but same
1181 // BrowsingInstance).
1182 contents()->NavigateAndCommit(kUrl2);
1183 TestRenderFrameHost* rfh2 = main_test_rfh();
1184 EXPECT_NE(rfh1->GetSiteInstance(), rfh2->GetSiteInstance());
1185
1186 // Disown the opener from rfh2.
1187 rfh2->DidDisownOpener();
1188
1189 // Ensure the opener is cleared.
1190 EXPECT_FALSE(contents()->HasOpener());
1191 }
1192
1193 // Test that a page can disown a same-site opener of the WebContents.
TEST_F(RenderFrameHostManagerTest,DisownSameSiteOpener)1194 TEST_F(RenderFrameHostManagerTest, DisownSameSiteOpener) {
1195 const GURL kUrl1("http://www.google.com/");
1196
1197 // Navigate to an initial URL.
1198 contents()->NavigateAndCommit(kUrl1);
1199 TestRenderFrameHost* rfh1 = main_test_rfh();
1200
1201 // Create a new tab and simulate having it be the opener for the main tab.
1202 scoped_ptr<TestWebContents> opener1(
1203 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1204 contents()->SetOpener(opener1.get());
1205 EXPECT_TRUE(contents()->HasOpener());
1206
1207 // Disown the opener from rfh1.
1208 rfh1->DidDisownOpener();
1209
1210 // Ensure the opener is cleared even if it is in the same process.
1211 EXPECT_FALSE(contents()->HasOpener());
1212 }
1213
1214 // Test that a page can disown the opener just as a cross-process navigation is
1215 // in progress.
TEST_F(RenderFrameHostManagerTest,DisownOpenerDuringNavigation)1216 TEST_F(RenderFrameHostManagerTest, DisownOpenerDuringNavigation) {
1217 const GURL kUrl1("http://www.google.com/");
1218 const GURL kUrl2("http://www.chromium.org/");
1219
1220 // Navigate to an initial URL.
1221 contents()->NavigateAndCommit(kUrl1);
1222 TestRenderFrameHost* rfh1 = main_test_rfh();
1223
1224 // Create a new tab and simulate having it be the opener for the main tab.
1225 scoped_ptr<TestWebContents> opener1(
1226 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1227 contents()->SetOpener(opener1.get());
1228 EXPECT_TRUE(contents()->HasOpener());
1229
1230 // Navigate to a cross-site URL (different SiteInstance but same
1231 // BrowsingInstance).
1232 contents()->NavigateAndCommit(kUrl2);
1233 TestRenderFrameHost* rfh2 = main_test_rfh();
1234 EXPECT_NE(rfh1->GetSiteInstance(), rfh2->GetSiteInstance());
1235
1236 // Start a back navigation so that rfh1 becomes the pending RFH.
1237 contents()->GetController().GoBack();
1238 contents()->ProceedWithCrossSiteNavigation();
1239
1240 // Disown the opener from rfh2.
1241 rfh2->DidDisownOpener();
1242
1243 // Ensure the opener is cleared.
1244 EXPECT_FALSE(contents()->HasOpener());
1245
1246 // The back navigation commits.
1247 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1248 rfh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
1249
1250 // Ensure the opener is still cleared.
1251 EXPECT_FALSE(contents()->HasOpener());
1252 }
1253
1254 // Test that a page can disown the opener just after a cross-process navigation
1255 // commits.
TEST_F(RenderFrameHostManagerTest,DisownOpenerAfterNavigation)1256 TEST_F(RenderFrameHostManagerTest, DisownOpenerAfterNavigation) {
1257 const GURL kUrl1("http://www.google.com/");
1258 const GURL kUrl2("http://www.chromium.org/");
1259
1260 // Navigate to an initial URL.
1261 contents()->NavigateAndCommit(kUrl1);
1262 TestRenderFrameHost* rfh1 = main_test_rfh();
1263
1264 // Create a new tab and simulate having it be the opener for the main tab.
1265 scoped_ptr<TestWebContents> opener1(
1266 TestWebContents::Create(browser_context(), rfh1->GetSiteInstance()));
1267 contents()->SetOpener(opener1.get());
1268 EXPECT_TRUE(contents()->HasOpener());
1269
1270 // Navigate to a cross-site URL (different SiteInstance but same
1271 // BrowsingInstance).
1272 contents()->NavigateAndCommit(kUrl2);
1273 TestRenderFrameHost* rfh2 = main_test_rfh();
1274 EXPECT_NE(rfh1->GetSiteInstance(), rfh2->GetSiteInstance());
1275
1276 // Commit a back navigation before the DidDisownOpener message arrives.
1277 // rfh1 will be kept alive because of the opener tab.
1278 contents()->GetController().GoBack();
1279 contents()->ProceedWithCrossSiteNavigation();
1280 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
1281 rfh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
1282
1283 // Disown the opener from rfh2.
1284 rfh2->DidDisownOpener();
1285 EXPECT_FALSE(contents()->HasOpener());
1286 }
1287
1288 // Test that we clean up swapped out RenderViewHosts when a process hosting
1289 // those associated RenderViews crashes. http://crbug.com/258993
TEST_F(RenderFrameHostManagerTest,CleanUpSwappedOutRVHOnProcessCrash)1290 TEST_F(RenderFrameHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) {
1291 const GURL kUrl1("http://www.google.com/");
1292 const GURL kUrl2("http://www.chromium.org/");
1293
1294 // Navigate to an initial URL.
1295 contents()->NavigateAndCommit(kUrl1);
1296 TestRenderViewHost* rvh1 = test_rvh();
1297
1298 // Create a new tab as an opener for the main tab.
1299 scoped_ptr<TestWebContents> opener1(
1300 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1301 RenderFrameHostManager* opener1_manager =
1302 opener1->GetRenderManagerForTesting();
1303 contents()->SetOpener(opener1.get());
1304
1305 // Make sure the new opener RVH is considered live.
1306 opener1_manager->current_host()->CreateRenderView(
1307 base::string16(), -1, MSG_ROUTING_NONE, -1, false);
1308 EXPECT_TRUE(opener1_manager->current_host()->IsRenderViewLive());
1309 EXPECT_TRUE(opener1_manager->current_frame_host()->IsRenderFrameLive());
1310
1311 // Use a cross-process navigation in the opener to swap out the old RVH.
1312 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1313 rvh1->GetSiteInstance()));
1314 opener1->NavigateAndCommit(kUrl2);
1315 EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost(
1316 rvh1->GetSiteInstance()));
1317
1318 // Fake a process crash.
1319 RenderProcessHost::RendererClosedDetails details(
1320 rvh1->GetProcess()->GetHandle(),
1321 base::TERMINATION_STATUS_PROCESS_CRASHED,
1322 0);
1323 NotificationService::current()->Notify(
1324 NOTIFICATION_RENDERER_PROCESS_CLOSED,
1325 Source<RenderProcessHost>(rvh1->GetProcess()),
1326 Details<RenderProcessHost::RendererClosedDetails>(&details));
1327 rvh1->set_render_view_created(false);
1328
1329 // Ensure that the swapped out RenderViewHost has been deleted.
1330 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost(
1331 rvh1->GetSiteInstance()));
1332
1333 // Reload the initial tab. This should recreate the opener's swapped out RVH
1334 // in the original SiteInstance.
1335 contents()->GetController().Reload(true);
1336 EXPECT_EQ(opener1_manager->GetSwappedOutRenderViewHost(
1337 rvh1->GetSiteInstance())->GetRoutingID(),
1338 test_rvh()->opener_route_id());
1339 }
1340
1341 // Test that RenderViewHosts created for WebUI navigations are properly
1342 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost
1343 // is in the same process (http://crbug.com/79918).
TEST_F(RenderFrameHostManagerTest,EnableWebUIWithSwappedOutOpener)1344 TEST_F(RenderFrameHostManagerTest, EnableWebUIWithSwappedOutOpener) {
1345 set_should_create_webui(true);
1346 const GURL kSettingsUrl("chrome://chrome/settings");
1347 const GURL kPluginUrl("chrome://plugins");
1348
1349 // Navigate to an initial WebUI URL.
1350 contents()->NavigateAndCommit(kSettingsUrl);
1351
1352 // Ensure the RVH has WebUI bindings.
1353 TestRenderViewHost* rvh1 = test_rvh();
1354 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1355
1356 // Create a new tab and simulate it being the opener for the main
1357 // tab. It should be in the same SiteInstance.
1358 scoped_ptr<TestWebContents> opener1(
1359 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance()));
1360 RenderFrameHostManager* opener1_manager =
1361 opener1->GetRenderManagerForTesting();
1362 contents()->SetOpener(opener1.get());
1363
1364 // Navigate to a different WebUI URL (different SiteInstance, same
1365 // BrowsingInstance).
1366 contents()->NavigateAndCommit(kPluginUrl);
1367 TestRenderViewHost* rvh2 = test_rvh();
1368 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance());
1369 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance(
1370 rvh2->GetSiteInstance()));
1371
1372 // Ensure a swapped out RVH is created in the first opener tab.
1373 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>(
1374 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance()));
1375 EXPECT_TRUE(opener1_manager->IsRVHOnSwappedOutList(opener1_rvh));
1376 EXPECT_TRUE(opener1_rvh->IsSwappedOut());
1377
1378 // Ensure the new RVH has WebUI bindings.
1379 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1380 }
1381
1382 // Test that we reuse the same guest SiteInstance if we navigate across sites.
TEST_F(RenderFrameHostManagerTest,NoSwapOnGuestNavigations)1383 TEST_F(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
1384 TestNotificationTracker notifications;
1385
1386 GURL guest_url(std::string(kGuestScheme).append("://abc123"));
1387 SiteInstance* instance =
1388 SiteInstance::CreateForURL(browser_context(), guest_url);
1389 scoped_ptr<TestWebContents> web_contents(
1390 TestWebContents::Create(browser_context(), instance));
1391
1392 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1393
1394 RenderFrameHostImpl* host;
1395
1396 // 1) The first navigation. --------------------------
1397 const GURL kUrl1("http://www.google.com/");
1398 NavigationEntryImpl entry1(
1399 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(),
1400 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1401 false /* is_renderer_init */);
1402 host = manager->Navigate(entry1);
1403
1404 // The RenderFrameHost created in Init will be reused.
1405 EXPECT_TRUE(host == manager->current_frame_host());
1406 EXPECT_FALSE(manager->pending_frame_host());
1407 EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
1408
1409 // Commit.
1410 manager->DidNavigateFrame(host);
1411 // Commit to SiteInstance should be delayed until RenderView commit.
1412 EXPECT_EQ(host, manager->current_frame_host());
1413 ASSERT_TRUE(host);
1414 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1415 HasSite());
1416
1417 // 2) Navigate to a different domain. -------------------------
1418 // Guests stay in the same process on navigation.
1419 const GURL kUrl2("http://www.chromium.org");
1420 NavigationEntryImpl entry2(
1421 NULL /* instance */, -1 /* page_id */, kUrl2,
1422 Referrer(kUrl1, blink::WebReferrerPolicyDefault),
1423 base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
1424 true /* is_renderer_init */);
1425 host = manager->Navigate(entry2);
1426
1427 // The RenderFrameHost created in Init will be reused.
1428 EXPECT_EQ(host, manager->current_frame_host());
1429 EXPECT_FALSE(manager->pending_frame_host());
1430
1431 // Commit.
1432 manager->DidNavigateFrame(host);
1433 EXPECT_EQ(host, manager->current_frame_host());
1434 ASSERT_TRUE(host);
1435 EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()),
1436 instance);
1437 }
1438
1439 // Test that we cancel a pending RVH if we close the tab while it's pending.
1440 // http://crbug.com/294697.
TEST_F(RenderFrameHostManagerTest,NavigateWithEarlyClose)1441 TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
1442 TestNotificationTracker notifications;
1443
1444 SiteInstance* instance = SiteInstance::Create(browser_context());
1445
1446 BeforeUnloadFiredWebContentsDelegate delegate;
1447 scoped_ptr<TestWebContents> web_contents(
1448 TestWebContents::Create(browser_context(), instance));
1449 web_contents->SetDelegate(&delegate);
1450 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
1451 Source<WebContents>(web_contents.get()));
1452
1453 RenderFrameHostManager* manager = web_contents->GetRenderManagerForTesting();
1454
1455 // 1) The first navigation. --------------------------
1456 const GURL kUrl1("http://www.google.com/");
1457 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
1458 Referrer(), base::string16() /* title */,
1459 ui::PAGE_TRANSITION_TYPED,
1460 false /* is_renderer_init */);
1461 RenderFrameHostImpl* host = manager->Navigate(entry1);
1462
1463 // The RenderFrameHost created in Init will be reused.
1464 EXPECT_EQ(host, manager->current_frame_host());
1465 EXPECT_FALSE(manager->pending_frame_host());
1466
1467 // We should observe a notification.
1468 EXPECT_TRUE(
1469 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
1470 notifications.Reset();
1471
1472 // Commit.
1473 manager->DidNavigateFrame(host);
1474
1475 // Commit to SiteInstance should be delayed until RenderFrame commits.
1476 EXPECT_EQ(host, manager->current_frame_host());
1477 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
1478 HasSite());
1479 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
1480
1481 // 2) Cross-site navigate to next site. -------------------------
1482 const GURL kUrl2("http://www.example.com");
1483 NavigationEntryImpl entry2(
1484 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(),
1485 base::string16() /* title */, ui::PAGE_TRANSITION_TYPED,
1486 false /* is_renderer_init */);
1487 RenderFrameHostImpl* host2 = manager->Navigate(entry2);
1488
1489 // A new RenderFrameHost should be created.
1490 ASSERT_EQ(host2, manager->pending_frame_host());
1491 EXPECT_NE(host2, host);
1492
1493 EXPECT_EQ(host, manager->current_frame_host());
1494 EXPECT_FALSE(manager->current_frame_host()->is_swapped_out());
1495 EXPECT_EQ(host2, manager->pending_frame_host());
1496
1497 // 3) Close the tab. -------------------------
1498 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1499 Source<RenderWidgetHost>(host2->render_view_host()));
1500 manager->OnBeforeUnloadACK(false, true, base::TimeTicks());
1501
1502 EXPECT_TRUE(
1503 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED));
1504 EXPECT_FALSE(manager->pending_frame_host());
1505 EXPECT_EQ(host, manager->current_frame_host());
1506 }
1507
1508 // Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
1509 // received. (SwapOut and the corresponding ACK always occur after commit.)
1510 // Also tests that an early SwapOutACK is properly ignored.
TEST_F(RenderFrameHostManagerTest,DeleteFrameAfterSwapOutACK)1511 TEST_F(RenderFrameHostManagerTest, DeleteFrameAfterSwapOutACK) {
1512 const GURL kUrl1("http://www.google.com/");
1513 const GURL kUrl2("http://www.chromium.org/");
1514
1515 // Navigate to the first page.
1516 contents()->NavigateAndCommit(kUrl1);
1517 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1518 RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
1519 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1520 rfh1->GetRenderViewHost()->rvh_state());
1521
1522 // Navigate to new site, simulating onbeforeunload approval.
1523 controller().LoadURL(
1524 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1525 base::TimeTicks now = base::TimeTicks::Now();
1526 contents()->GetMainFrame()->OnMessageReceived(
1527 FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1528 EXPECT_TRUE(contents()->cross_navigation_pending());
1529 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1530 rfh1->GetRenderViewHost()->rvh_state());
1531 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1532
1533 // Simulate the swap out ack, unexpectedly early (before commit). It should
1534 // have no effect.
1535 rfh1->OnSwappedOut(false);
1536 EXPECT_TRUE(contents()->cross_navigation_pending());
1537 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1538 rfh1->GetRenderViewHost()->rvh_state());
1539
1540 // The new page commits.
1541 contents()->TestDidNavigate(rfh2, 1, kUrl2, ui::PAGE_TRANSITION_TYPED);
1542 EXPECT_FALSE(contents()->cross_navigation_pending());
1543 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1544 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1545 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1546 rfh2->GetRenderViewHost()->rvh_state());
1547 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN,
1548 rfh1->GetRenderViewHost()->rvh_state());
1549
1550 // Simulate the swap out ack.
1551 rfh1->OnSwappedOut(false);
1552
1553 // rfh1 should have been deleted.
1554 EXPECT_TRUE(rvh_deleted_observer.deleted());
1555 rfh1 = NULL;
1556 }
1557
1558 // Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
1559 // is received. (SwapOut and the corresponding ACK always occur after commit.)
TEST_F(RenderFrameHostManagerTest,SwapOutFrameAfterSwapOutACK)1560 TEST_F(RenderFrameHostManagerTest, SwapOutFrameAfterSwapOutACK) {
1561 const GURL kUrl1("http://www.google.com/");
1562 const GURL kUrl2("http://www.chromium.org/");
1563
1564 // Navigate to the first page.
1565 contents()->NavigateAndCommit(kUrl1);
1566 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1567 RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
1568 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1569 rfh1->GetRenderViewHost()->rvh_state());
1570
1571 // Increment the number of active views in SiteInstanceImpl so that rfh1 is
1572 // not deleted on swap out.
1573 static_cast<SiteInstanceImpl*>(
1574 rfh1->GetSiteInstance())->increment_active_view_count();
1575
1576 // Navigate to new site, simulating onbeforeunload approval.
1577 controller().LoadURL(
1578 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1579 base::TimeTicks now = base::TimeTicks::Now();
1580 contents()->GetMainFrame()->OnMessageReceived(
1581 FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1582 EXPECT_TRUE(contents()->cross_navigation_pending());
1583 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1584 rfh1->GetRenderViewHost()->rvh_state());
1585 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1586
1587 // The new page commits.
1588 contents()->TestDidNavigate(rfh2, 1, kUrl2, ui::PAGE_TRANSITION_TYPED);
1589 EXPECT_FALSE(contents()->cross_navigation_pending());
1590 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1591 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1592 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1593 rfh2->GetRenderViewHost()->rvh_state());
1594 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT,
1595 rfh1->GetRenderViewHost()->rvh_state());
1596
1597 // Simulate the swap out ack.
1598 rfh1->OnSwappedOut(false);
1599
1600 // rfh1 should be swapped out.
1601 EXPECT_FALSE(rvh_deleted_observer.deleted());
1602 EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut());
1603 }
1604
1605 // Test that the RenderViewHost is properly swapped out if a navigation in the
1606 // new renderer commits before sending the SwapOut message to the old renderer.
1607 // This simulates a cross-site navigation to a synchronously committing URL
1608 // (e.g., a data URL) and ensures it works properly.
TEST_F(RenderFrameHostManagerTest,CommitNewNavigationBeforeSendingSwapOut)1609 TEST_F(RenderFrameHostManagerTest,
1610 CommitNewNavigationBeforeSendingSwapOut) {
1611 const GURL kUrl1("http://www.google.com/");
1612 const GURL kUrl2("http://www.chromium.org/");
1613
1614 // Navigate to the first page.
1615 contents()->NavigateAndCommit(kUrl1);
1616 TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
1617 RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost());
1618 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1619 rfh1->GetRenderViewHost()->rvh_state());
1620
1621 // Increment the number of active views in SiteInstanceImpl so that rfh1 is
1622 // not deleted on swap out.
1623 static_cast<SiteInstanceImpl*>(
1624 rfh1->GetSiteInstance())->increment_active_view_count();
1625
1626 // Navigate to new site, simulating onbeforeunload approval.
1627 controller().LoadURL(
1628 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1629 base::TimeTicks now = base::TimeTicks::Now();
1630 rfh1->OnMessageReceived(
1631 FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1632 EXPECT_TRUE(contents()->cross_navigation_pending());
1633 TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
1634
1635 // The new page commits.
1636 contents()->TestDidNavigate(rfh2, 1, kUrl2, ui::PAGE_TRANSITION_TYPED);
1637 EXPECT_FALSE(contents()->cross_navigation_pending());
1638 EXPECT_EQ(rfh2, contents()->GetMainFrame());
1639 EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL);
1640 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT,
1641 rfh2->GetRenderViewHost()->rvh_state());
1642 EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT,
1643 rfh1->GetRenderViewHost()->rvh_state());
1644
1645 // Simulate the swap out ack.
1646 rfh1->OnSwappedOut(false);
1647
1648 // rfh1 should be swapped out.
1649 EXPECT_FALSE(rvh_deleted_observer.deleted());
1650 EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut());
1651 }
1652
1653 // Test that a RenderFrameHost is properly deleted or swapped out when a
1654 // cross-site navigation is cancelled.
TEST_F(RenderFrameHostManagerTest,CancelPendingProperlyDeletesOrSwaps)1655 TEST_F(RenderFrameHostManagerTest,
1656 CancelPendingProperlyDeletesOrSwaps) {
1657 const GURL kUrl1("http://www.google.com/");
1658 const GURL kUrl2("http://www.chromium.org/");
1659 RenderFrameHostImpl* pending_rfh = NULL;
1660 base::TimeTicks now = base::TimeTicks::Now();
1661
1662 // Navigate to the first page.
1663 contents()->NavigateAndCommit(kUrl1);
1664 TestRenderViewHost* rvh1 = test_rvh();
1665 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1666
1667 // Navigate to a new site, starting a cross-site navigation.
1668 controller().LoadURL(
1669 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1670 {
1671 pending_rfh = contents()->GetFrameTree()->root()->render_manager()
1672 ->pending_frame_host();
1673 RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
1674
1675 // Cancel the navigation by simulating a declined beforeunload dialog.
1676 contents()->GetMainFrame()->OnMessageReceived(
1677 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
1678 EXPECT_FALSE(contents()->cross_navigation_pending());
1679
1680 // Since the pending RFH is the only one for the new SiteInstance, it should
1681 // be deleted.
1682 EXPECT_TRUE(rvh_deleted_observer.deleted());
1683 }
1684
1685 // Start another cross-site navigation.
1686 controller().LoadURL(
1687 kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1688 {
1689 pending_rfh = contents()->GetFrameTree()->root()->render_manager()
1690 ->pending_frame_host();
1691 RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh);
1692
1693 // Increment the number of active views in the new SiteInstance, which will
1694 // cause the pending RFH to be swapped out instead of deleted.
1695 static_cast<SiteInstanceImpl*>(
1696 pending_rfh->GetSiteInstance())->increment_active_view_count();
1697
1698 contents()->GetMainFrame()->OnMessageReceived(
1699 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
1700 EXPECT_FALSE(contents()->cross_navigation_pending());
1701 EXPECT_FALSE(rvh_deleted_observer.deleted());
1702 }
1703 }
1704
1705 // PlzNavigate: Test that a proper NavigationRequest is created by
1706 // BeginNavigation.
TEST_F(RenderFrameHostManagerTest,BrowserSideNavigationBeginNavigation)1707 TEST_F(RenderFrameHostManagerTest, BrowserSideNavigationBeginNavigation) {
1708 const GURL kUrl1("http://www.google.com/");
1709 const GURL kUrl2("http://www.chromium.org/");
1710 const GURL kUrl3("http://www.gmail.com/");
1711 const int64 kFirstNavRequestID = 1;
1712
1713 // TODO(clamy): we should be enabling browser side navigations here
1714 // when CommitNavigation is properly implemented.
1715 // Navigate to the first page.
1716 contents()->NavigateAndCommit(kUrl1);
1717
1718 EnableBrowserSideNavigation();
1719 // Add a subframe.
1720 TestRenderFrameHost* subframe_rfh = static_cast<TestRenderFrameHost*>(
1721 contents()->GetFrameTree()->AddFrame(
1722 contents()->GetFrameTree()->root(),
1723 contents()->GetMainFrame()->GetProcess()->GetID(),
1724 14, "Child"));
1725
1726 // Simulate a BeginNavigation IPC on the subframe.
1727 subframe_rfh->SendBeginNavigationWithURL(kUrl2);
1728 NavigationRequest* subframe_request =
1729 GetNavigationRequestForRenderFrameManager(
1730 subframe_rfh->frame_tree_node()->render_manager());
1731 ASSERT_TRUE(subframe_request);
1732 EXPECT_EQ(kUrl2, subframe_request->info().navigation_params.url);
1733 // First party for cookies url should be that of the main frame.
1734 EXPECT_EQ(
1735 kUrl1, subframe_request->info().first_party_for_cookies);
1736 EXPECT_FALSE(subframe_request->info().is_main_frame);
1737 EXPECT_TRUE(subframe_request->info().parent_is_main_frame);
1738 EXPECT_EQ(kFirstNavRequestID, subframe_request->navigation_request_id());
1739
1740 // Simulate a BeginNavigation IPC on the main frame.
1741 contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl3);
1742 NavigationRequest* main_request = GetNavigationRequestForRenderFrameManager(
1743 contents()->GetMainFrame()->frame_tree_node()->render_manager());
1744 ASSERT_TRUE(main_request);
1745 EXPECT_EQ(kUrl3, main_request->info().navigation_params.url);
1746 EXPECT_EQ(kUrl3, main_request->info().first_party_for_cookies);
1747 EXPECT_TRUE(main_request->info().is_main_frame);
1748 EXPECT_FALSE(main_request->info().parent_is_main_frame);
1749 EXPECT_EQ(kFirstNavRequestID + 1, main_request->navigation_request_id());
1750 }
1751
1752 // PlzNavigate: Test that RequestNavigation creates a NavigationRequest and that
1753 // RenderFrameHost is not modified when the navigation commits.
TEST_F(RenderFrameHostManagerTest,BrowserSideNavigationRequestNavigationNoLiveRenderer)1754 TEST_F(RenderFrameHostManagerTest,
1755 BrowserSideNavigationRequestNavigationNoLiveRenderer) {
1756 const GURL kUrl("http://www.google.com/");
1757
1758 EnableBrowserSideNavigation();
1759 EXPECT_FALSE(main_test_rfh()->render_view_host()->IsRenderViewLive());
1760 contents()->GetController().LoadURL(
1761 kUrl, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1762 RenderFrameHostManager* render_manager =
1763 main_test_rfh()->frame_tree_node()->render_manager();
1764 NavigationRequest* main_request =
1765 GetNavigationRequestForRenderFrameManager(render_manager);
1766 // A NavigationRequest should have been generated.
1767 EXPECT_TRUE(main_request != NULL);
1768 RenderFrameHostImpl* rfh = main_test_rfh();
1769
1770 // Now commit the same url.
1771 NavigationBeforeCommitInfo commit_info;
1772 commit_info.navigation_url = kUrl;
1773 commit_info.navigation_request_id = main_request->navigation_request_id();
1774 render_manager->CommitNavigation(commit_info);
1775 main_request = GetNavigationRequestForRenderFrameManager(render_manager);
1776
1777 // The main RFH should not have been changed, and the renderer should have
1778 // been initialized.
1779 EXPECT_EQ(rfh, main_test_rfh());
1780 EXPECT_TRUE(main_test_rfh()->render_view_host()->IsRenderViewLive());
1781 }
1782
1783 // PlzNavigate: Test that a new RenderFrameHost is created when doing a cross
1784 // site navigation.
TEST_F(RenderFrameHostManagerTest,BrowserSideNavigationCrossSiteNavigation)1785 TEST_F(RenderFrameHostManagerTest,
1786 BrowserSideNavigationCrossSiteNavigation) {
1787 const GURL kUrl1("http://www.chromium.org/");
1788 const GURL kUrl2("http://www.google.com/");
1789
1790 // TODO(clamy): we should be enabling browser side navigations here
1791 // when CommitNavigation is properly implemented.
1792 // Navigate to the first page.
1793 contents()->NavigateAndCommit(kUrl1);
1794 TestRenderViewHost* rvh1 = test_rvh();
1795 EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state());
1796 RenderFrameHostImpl* rfh = main_test_rfh();
1797 RenderFrameHostManager* render_manager =
1798 main_test_rfh()->frame_tree_node()->render_manager();
1799
1800 EnableBrowserSideNavigation();
1801 // Navigate to a different site.
1802 main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
1803 NavigationRequest* main_request =
1804 GetNavigationRequestForRenderFrameManager(render_manager);
1805 ASSERT_TRUE(main_request);
1806
1807 NavigationBeforeCommitInfo commit_info;
1808 commit_info.navigation_url = kUrl2;
1809 commit_info.navigation_request_id = main_request->navigation_request_id();
1810 render_manager->CommitNavigation(commit_info);
1811 EXPECT_NE(main_test_rfh(), rfh);
1812 EXPECT_TRUE(main_test_rfh()->render_view_host()->IsRenderViewLive());
1813 }
1814
1815 // PlzNavigate: Test that a navigation commit is ignored if another request has
1816 // been issued in the meantime.
1817 // TODO(carlosk): add checks to assert that the cancel call was sent to
1818 // ResourceDispatcherHost in the IO thread by extending
1819 // ResourceDispatcherHostDelegate (like in cross_site_transfer_browsertest.cc
1820 // and plugin_browsertest.cc).
TEST_F(RenderFrameHostManagerTest,BrowserSideNavigationIgnoreStaleNavigationCommit)1821 TEST_F(RenderFrameHostManagerTest,
1822 BrowserSideNavigationIgnoreStaleNavigationCommit) {
1823 const GURL kUrl0("http://www.wikipedia.org/");
1824 const GURL kUrl0_site = SiteInstance::GetSiteForURL(browser_context(), kUrl0);
1825 const GURL kUrl1("http://www.chromium.org/");
1826 const GURL kUrl2("http://www.google.com/");
1827 const GURL kUrl2_site = SiteInstance::GetSiteForURL(browser_context(), kUrl2);
1828
1829 // Initialization.
1830 contents()->NavigateAndCommit(kUrl0);
1831 RenderFrameHostManager* render_manager =
1832 main_test_rfh()->frame_tree_node()->render_manager();
1833 EnableBrowserSideNavigation();
1834 EXPECT_EQ(kUrl0_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
1835
1836 // Request navigation to the 1st URL and gather data.
1837 main_test_rfh()->SendBeginNavigationWithURL(kUrl1);
1838 NavigationRequest* request1 =
1839 GetNavigationRequestForRenderFrameManager(render_manager);
1840 ASSERT_TRUE(request1);
1841 int64 request_id1 = request1->navigation_request_id();
1842
1843 // Request navigation to the 2nd URL and gather more data.
1844 main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
1845 NavigationRequest* request2 =
1846 GetNavigationRequestForRenderFrameManager(render_manager);
1847 ASSERT_TRUE(request2);
1848 int64 request_id2 = request2->navigation_request_id();
1849 EXPECT_NE(request_id1, request_id2);
1850
1851 // Confirms that a stale commit is ignored by the RHFM.
1852 NavigationBeforeCommitInfo nbc_info;
1853 nbc_info.navigation_url = kUrl1;
1854 nbc_info.navigation_request_id = request_id1;
1855 render_manager->CommitNavigation(nbc_info);
1856 EXPECT_EQ(kUrl0_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
1857
1858 // Confirms that a valid, request-matching commit is correctly processed.
1859 nbc_info.navigation_url = kUrl2;
1860 nbc_info.navigation_request_id = request_id2;
1861 render_manager->CommitNavigation(nbc_info);
1862 EXPECT_EQ(kUrl2_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
1863 }
1864
1865 } // namespace content
1866