| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/strings/utf_string_conversions.h" | |
| 6 #include "content/browser/renderer_host/test_render_view_host.h" | |
| 7 #include "content/browser/site_instance_impl.h" | |
| 8 #include "content/browser/web_contents/navigation_controller_impl.h" | |
| 9 #include "content/browser/web_contents/navigation_entry_impl.h" | |
| 10 #include "content/browser/web_contents/render_view_host_manager.h" | |
| 11 #include "content/browser/webui/web_ui_controller_factory_registry.h" | |
| 12 #include "content/common/view_messages.h" | |
| 13 #include "content/public/browser/notification_details.h" | |
| 14 #include "content/public/browser/notification_service.h" | |
| 15 #include "content/public/browser/notification_source.h" | |
| 16 #include "content/public/browser/notification_types.h" | |
| 17 #include "content/public/browser/render_process_host.h" | |
| 18 #include "content/public/browser/render_view_host_observer.h" | |
| 19 #include "content/public/browser/render_widget_host_iterator.h" | |
| 20 #include "content/public/browser/web_contents_delegate.h" | |
| 21 #include "content/public/browser/web_ui_controller.h" | |
| 22 #include "content/public/common/bindings_policy.h" | |
| 23 #include "content/public/common/javascript_message_type.h" | |
| 24 #include "content/public/common/page_transition_types.h" | |
| 25 #include "content/public/common/url_constants.h" | |
| 26 #include "content/public/common/url_utils.h" | |
| 27 #include "content/public/test/mock_render_process_host.h" | |
| 28 #include "content/public/test/test_notification_tracker.h" | |
| 29 #include "content/test/test_content_browser_client.h" | |
| 30 #include "content/test/test_content_client.h" | |
| 31 #include "content/test/test_web_contents.h" | |
| 32 #include "testing/gtest/include/gtest/gtest.h" | |
| 33 | |
| 34 namespace content { | |
| 35 namespace { | |
| 36 | |
| 37 class RenderViewHostManagerTestWebUIControllerFactory | |
| 38 : public WebUIControllerFactory { | |
| 39 public: | |
| 40 RenderViewHostManagerTestWebUIControllerFactory() | |
| 41 : should_create_webui_(false) { | |
| 42 } | |
| 43 virtual ~RenderViewHostManagerTestWebUIControllerFactory() {} | |
| 44 | |
| 45 void set_should_create_webui(bool should_create_webui) { | |
| 46 should_create_webui_ = should_create_webui; | |
| 47 } | |
| 48 | |
| 49 // WebUIFactory implementation. | |
| 50 virtual WebUIController* CreateWebUIControllerForURL( | |
| 51 WebUI* web_ui, const GURL& url) const OVERRIDE { | |
| 52 if (!(should_create_webui_ && HasWebUIScheme(url))) | |
| 53 return NULL; | |
| 54 return new WebUIController(web_ui); | |
| 55 } | |
| 56 | |
| 57 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, | |
| 58 const GURL& url) const OVERRIDE { | |
| 59 return WebUI::kNoWebUI; | |
| 60 } | |
| 61 | |
| 62 virtual bool UseWebUIForURL(BrowserContext* browser_context, | |
| 63 const GURL& url) const OVERRIDE { | |
| 64 return HasWebUIScheme(url); | |
| 65 } | |
| 66 | |
| 67 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context, | |
| 68 const GURL& url) const OVERRIDE { | |
| 69 return HasWebUIScheme(url); | |
| 70 } | |
| 71 | |
| 72 private: | |
| 73 bool should_create_webui_; | |
| 74 | |
| 75 DISALLOW_COPY_AND_ASSIGN(RenderViewHostManagerTestWebUIControllerFactory); | |
| 76 }; | |
| 77 | |
| 78 class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate { | |
| 79 public: | |
| 80 BeforeUnloadFiredWebContentsDelegate() {} | |
| 81 virtual ~BeforeUnloadFiredWebContentsDelegate() {} | |
| 82 | |
| 83 virtual void BeforeUnloadFired(WebContents* web_contents, | |
| 84 bool proceed, | |
| 85 bool* proceed_to_fire_unload) OVERRIDE { | |
| 86 *proceed_to_fire_unload = proceed; | |
| 87 } | |
| 88 | |
| 89 private: | |
| 90 DISALLOW_COPY_AND_ASSIGN(BeforeUnloadFiredWebContentsDelegate); | |
| 91 }; | |
| 92 | |
| 93 } // namespace | |
| 94 | |
| 95 class RenderViewHostManagerTest | |
| 96 : public RenderViewHostImplTestHarness { | |
| 97 public: | |
| 98 virtual void SetUp() OVERRIDE { | |
| 99 RenderViewHostImplTestHarness::SetUp(); | |
| 100 WebUIControllerFactory::RegisterFactory(&factory_); | |
| 101 } | |
| 102 | |
| 103 virtual void TearDown() OVERRIDE { | |
| 104 RenderViewHostImplTestHarness::TearDown(); | |
| 105 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_); | |
| 106 } | |
| 107 | |
| 108 void set_should_create_webui(bool should_create_webui) { | |
| 109 factory_.set_should_create_webui(should_create_webui); | |
| 110 } | |
| 111 | |
| 112 void NavigateActiveAndCommit(const GURL& url) { | |
| 113 // Note: we navigate the active RenderViewHost because previous navigations | |
| 114 // won't have committed yet, so NavigateAndCommit does the wrong thing | |
| 115 // for us. | |
| 116 controller().LoadURL(url, Referrer(), PAGE_TRANSITION_LINK, std::string()); | |
| 117 TestRenderViewHost* old_rvh = test_rvh(); | |
| 118 | |
| 119 // Simulate the ShouldClose_ACK that is received from the current renderer | |
| 120 // for a cross-site navigation. | |
| 121 if (old_rvh != active_rvh()) | |
| 122 old_rvh->SendShouldCloseACK(true); | |
| 123 | |
| 124 // Commit the navigation with a new page ID. | |
| 125 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance( | |
| 126 active_rvh()->GetSiteInstance()); | |
| 127 | |
| 128 // Simulate the SwapOut_ACK that fires if you commit a cross-site | |
| 129 // navigation. | |
| 130 if (old_rvh != active_rvh()) | |
| 131 old_rvh->OnSwappedOut(false); | |
| 132 | |
| 133 active_test_rvh()->SendNavigate(max_page_id + 1, url); | |
| 134 } | |
| 135 | |
| 136 bool ShouldSwapProcesses(RenderViewHostManager* manager, | |
| 137 const NavigationEntryImpl* cur_entry, | |
| 138 const NavigationEntryImpl* new_entry) const { | |
| 139 return manager->ShouldSwapProcessesForNavigation(cur_entry, new_entry); | |
| 140 } | |
| 141 | |
| 142 // Creates a test RenderViewHost that's swapped out. | |
| 143 TestRenderViewHost* CreateSwappedOutRenderViewHost() { | |
| 144 const GURL kChromeURL("chrome://foo"); | |
| 145 const GURL kDestUrl("http://www.google.com/"); | |
| 146 | |
| 147 // Navigate our first tab to a chrome url and then to the destination. | |
| 148 NavigateActiveAndCommit(kChromeURL); | |
| 149 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>( | |
| 150 contents()->GetRenderManagerForTesting()->current_host()); | |
| 151 | |
| 152 // Navigate to a cross-site URL. | |
| 153 contents()->GetController().LoadURL( | |
| 154 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); | |
| 155 EXPECT_TRUE(contents()->cross_navigation_pending()); | |
| 156 | |
| 157 // Manually increase the number of active views in the | |
| 158 // SiteInstance that ntp_rvh belongs to, to prevent it from being | |
| 159 // destroyed when it gets swapped out. | |
| 160 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())-> | |
| 161 increment_active_view_count(); | |
| 162 | |
| 163 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>( | |
| 164 contents()->GetRenderManagerForTesting()->pending_render_view_host()); | |
| 165 CHECK(dest_rvh); | |
| 166 EXPECT_NE(ntp_rvh, dest_rvh); | |
| 167 | |
| 168 // BeforeUnload finishes. | |
| 169 ntp_rvh->SendShouldCloseACK(true); | |
| 170 | |
| 171 // Assume SwapOutACK times out, so the dest_rvh proceeds and commits. | |
| 172 dest_rvh->SendNavigate(101, kDestUrl); | |
| 173 | |
| 174 EXPECT_TRUE(ntp_rvh->is_swapped_out()); | |
| 175 return ntp_rvh; | |
| 176 } | |
| 177 | |
| 178 private: | |
| 179 RenderViewHostManagerTestWebUIControllerFactory factory_; | |
| 180 }; | |
| 181 | |
| 182 // Tests that when you navigate from a chrome:// url to another page, and | |
| 183 // then do that same thing in another tab, that the two resulting pages have | |
| 184 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is | |
| 185 // a regression test for bug 9364. | |
| 186 TEST_F(RenderViewHostManagerTest, NewTabPageProcesses) { | |
| 187 set_should_create_webui(true); | |
| 188 const GURL kChromeUrl("chrome://foo"); | |
| 189 const GURL kDestUrl("http://www.google.com/"); | |
| 190 | |
| 191 // Navigate our first tab to the chrome url and then to the destination, | |
| 192 // ensuring we grant bindings to the chrome URL. | |
| 193 NavigateActiveAndCommit(kChromeUrl); | |
| 194 EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | |
| 195 NavigateActiveAndCommit(kDestUrl); | |
| 196 | |
| 197 // Make a second tab. | |
| 198 scoped_ptr<TestWebContents> contents2( | |
| 199 TestWebContents::Create(browser_context(), NULL)); | |
| 200 | |
| 201 // Load the two URLs in the second tab. Note that the first navigation creates | |
| 202 // a RVH that's not pending (since there is no cross-site transition), so | |
| 203 // we use the committed one. | |
| 204 contents2->GetController().LoadURL( | |
| 205 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); | |
| 206 TestRenderViewHost* ntp_rvh2 = static_cast<TestRenderViewHost*>( | |
| 207 contents2->GetRenderManagerForTesting()->current_host()); | |
| 208 EXPECT_FALSE(contents2->cross_navigation_pending()); | |
| 209 ntp_rvh2->SendNavigate(100, kChromeUrl); | |
| 210 | |
| 211 // The second one is the opposite, creating a cross-site transition and | |
| 212 // requiring a beforeunload ack. | |
| 213 contents2->GetController().LoadURL( | |
| 214 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); | |
| 215 EXPECT_TRUE(contents2->cross_navigation_pending()); | |
| 216 TestRenderViewHost* dest_rvh2 = static_cast<TestRenderViewHost*>( | |
| 217 contents2->GetRenderManagerForTesting()->pending_render_view_host()); | |
| 218 ASSERT_TRUE(dest_rvh2); | |
| 219 | |
| 220 ntp_rvh2->SendShouldCloseACK(true); | |
| 221 ntp_rvh2->OnSwappedOut(false); | |
| 222 dest_rvh2->SendNavigate(101, kDestUrl); | |
| 223 | |
| 224 // The two RVH's should be different in every way. | |
| 225 EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess()); | |
| 226 EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance()); | |
| 227 EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance( | |
| 228 dest_rvh2->GetSiteInstance())); | |
| 229 | |
| 230 // Navigate both to the new tab page, and verify that they share a | |
| 231 // RenderProcessHost (not a SiteInstance). | |
| 232 NavigateActiveAndCommit(kChromeUrl); | |
| 233 | |
| 234 contents2->GetController().LoadURL( | |
| 235 kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); | |
| 236 dest_rvh2->SendShouldCloseACK(true); | |
| 237 dest_rvh2->OnSwappedOut(false); | |
| 238 static_cast<TestRenderViewHost*>(contents2->GetRenderManagerForTesting()-> | |
| 239 pending_render_view_host())->SendNavigate(102, kChromeUrl); | |
| 240 | |
| 241 EXPECT_NE(active_rvh()->GetSiteInstance(), | |
| 242 contents2->GetRenderViewHost()->GetSiteInstance()); | |
| 243 EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(), | |
| 244 contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess()); | |
| 245 } | |
| 246 | |
| 247 // Ensure that the browser ignores most IPC messages that arrive from a | |
| 248 // RenderViewHost that has been swapped out. We do not want to take | |
| 249 // action on requests from a non-active renderer. The main exception is | |
| 250 // for synchronous messages, which cannot be ignored without leaving the | |
| 251 // renderer in a stuck state. See http://crbug.com/93427. | |
| 252 TEST_F(RenderViewHostManagerTest, FilterMessagesWhileSwappedOut) { | |
| 253 const GURL kChromeURL("chrome://foo"); | |
| 254 const GURL kDestUrl("http://www.google.com/"); | |
| 255 | |
| 256 // Navigate our first tab to a chrome url and then to the destination. | |
| 257 NavigateActiveAndCommit(kChromeURL); | |
| 258 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>( | |
| 259 contents()->GetRenderManagerForTesting()->current_host()); | |
| 260 | |
| 261 // Send an update title message and make sure it works. | |
| 262 const string16 ntp_title = ASCIIToUTF16("NTP Title"); | |
| 263 WebKit::WebTextDirection direction = WebKit::WebTextDirectionLeftToRight; | |
| 264 EXPECT_TRUE(ntp_rvh->OnMessageReceived( | |
| 265 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction))); | |
| 266 EXPECT_EQ(ntp_title, contents()->GetTitle()); | |
| 267 | |
| 268 // Navigate to a cross-site URL. | |
| 269 contents()->GetController().LoadURL( | |
| 270 kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); | |
| 271 EXPECT_TRUE(contents()->cross_navigation_pending()); | |
| 272 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>( | |
| 273 contents()->GetRenderManagerForTesting()->pending_render_view_host()); | |
| 274 ASSERT_TRUE(dest_rvh); | |
| 275 EXPECT_NE(ntp_rvh, dest_rvh); | |
| 276 | |
| 277 // Create one more view in the same SiteInstance where dest_rvh2 | |
| 278 // exists so that it doesn't get deleted on navigation to another | |
| 279 // site. | |
| 280 static_cast<SiteInstanceImpl*>(ntp_rvh->GetSiteInstance())-> | |
| 281 increment_active_view_count(); | |
| 282 | |
| 283 // BeforeUnload finishes. | |
| 284 ntp_rvh->SendShouldCloseACK(true); | |
| 285 | |
| 286 // Assume SwapOutACK times out, so the dest_rvh proceeds and commits. | |
| 287 dest_rvh->SendNavigate(101, kDestUrl); | |
| 288 | |
| 289 // The new RVH should be able to update its title. | |
| 290 const string16 dest_title = ASCIIToUTF16("Google"); | |
| 291 EXPECT_TRUE(dest_rvh->OnMessageReceived( | |
| 292 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 101, dest_title, | |
| 293 direction))); | |
| 294 EXPECT_EQ(dest_title, contents()->GetTitle()); | |
| 295 | |
| 296 // The old renderer, being slow, now updates the title. It should be filtered | |
| 297 // out and not take effect. | |
| 298 EXPECT_TRUE(ntp_rvh->is_swapped_out()); | |
| 299 EXPECT_TRUE(ntp_rvh->OnMessageReceived( | |
| 300 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction))); | |
| 301 EXPECT_EQ(dest_title, contents()->GetTitle()); | |
| 302 | |
| 303 // We cannot filter out synchronous IPC messages, because the renderer would | |
| 304 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example | |
| 305 // that can run easily within a unit test, and that needs to receive a reply | |
| 306 // without showing an actual dialog. | |
| 307 MockRenderProcessHost* ntp_process_host = | |
| 308 static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess()); | |
| 309 ntp_process_host->sink().ClearMessages(); | |
| 310 const string16 msg = ASCIIToUTF16("Message"); | |
| 311 bool result = false; | |
| 312 string16 unused; | |
| 313 ViewHostMsg_RunBeforeUnloadConfirm before_unload_msg( | |
| 314 rvh()->GetRoutingID(), kChromeURL, msg, false, &result, &unused); | |
| 315 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI. | |
| 316 before_unload_msg.EnableMessagePumping(); | |
| 317 EXPECT_TRUE(ntp_rvh->OnMessageReceived(before_unload_msg)); | |
| 318 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID)); | |
| 319 | |
| 320 // Also test RunJavaScriptMessage. | |
| 321 ntp_process_host->sink().ClearMessages(); | |
| 322 ViewHostMsg_RunJavaScriptMessage js_msg( | |
| 323 rvh()->GetRoutingID(), msg, msg, kChromeURL, | |
| 324 JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused); | |
| 325 js_msg.EnableMessagePumping(); | |
| 326 EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg)); | |
| 327 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID)); | |
| 328 } | |
| 329 | |
| 330 TEST_F(RenderViewHostManagerTest, WhiteListSwapCompositorFrame) { | |
| 331 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); | |
| 332 TestRenderWidgetHostView* swapped_out_rwhv = | |
| 333 static_cast<TestRenderWidgetHostView*>(swapped_out_rvh->GetView()); | |
| 334 EXPECT_FALSE(swapped_out_rwhv->did_swap_compositor_frame()); | |
| 335 | |
| 336 MockRenderProcessHost* process_host = | |
| 337 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess()); | |
| 338 process_host->sink().ClearMessages(); | |
| 339 | |
| 340 cc::CompositorFrame frame; | |
| 341 ViewHostMsg_SwapCompositorFrame msg(rvh()->GetRoutingID(), 0, frame); | |
| 342 | |
| 343 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg)); | |
| 344 EXPECT_TRUE(swapped_out_rwhv->did_swap_compositor_frame()); | |
| 345 } | |
| 346 | |
| 347 TEST_F(RenderViewHostManagerTest, WhiteListDidActivateAcceleratedCompositing) { | |
| 348 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); | |
| 349 | |
| 350 MockRenderProcessHost* process_host = | |
| 351 static_cast<MockRenderProcessHost*>(swapped_out_rvh->GetProcess()); | |
| 352 process_host->sink().ClearMessages(); | |
| 353 ViewHostMsg_DidActivateAcceleratedCompositing msg( | |
| 354 rvh()->GetRoutingID(), true); | |
| 355 EXPECT_TRUE(swapped_out_rvh->OnMessageReceived(msg)); | |
| 356 EXPECT_TRUE(swapped_out_rvh->is_accelerated_compositing_active()); | |
| 357 } | |
| 358 | |
| 359 // Test if RenderViewHost::GetRenderWidgetHosts() only returns active | |
| 360 // widgets. | |
| 361 TEST_F(RenderViewHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { | |
| 362 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); | |
| 363 EXPECT_TRUE(swapped_out_rvh->is_swapped_out()); | |
| 364 | |
| 365 scoped_ptr<RenderWidgetHostIterator> widgets( | |
| 366 RenderWidgetHost::GetRenderWidgetHosts()); | |
| 367 // We know that there is the only one active widget. Another view is | |
| 368 // now swapped out, so the swapped out view is not included in the | |
| 369 // list. | |
| 370 RenderWidgetHost* widget = widgets->GetNextHost(); | |
| 371 EXPECT_FALSE(widgets->GetNextHost()); | |
| 372 RenderViewHost* rvh = RenderViewHost::From(widget); | |
| 373 EXPECT_FALSE(static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out()); | |
| 374 } | |
| 375 | |
| 376 // Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of | |
| 377 // RenderViewHostImpl::GetAllRenderWidgetHosts(). | |
| 378 // RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but | |
| 379 // RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything | |
| 380 // including swapped out ones. | |
| 381 TEST_F(RenderViewHostManagerTest, | |
| 382 GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) { | |
| 383 TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); | |
| 384 EXPECT_TRUE(swapped_out_rvh->is_swapped_out()); | |
| 385 | |
| 386 scoped_ptr<RenderWidgetHostIterator> widgets( | |
| 387 RenderWidgetHost::GetRenderWidgetHosts()); | |
| 388 | |
| 389 while (RenderWidgetHost* w = widgets->GetNextHost()) { | |
| 390 bool found = false; | |
| 391 scoped_ptr<RenderWidgetHostIterator> all_widgets( | |
| 392 RenderWidgetHostImpl::GetAllRenderWidgetHosts()); | |
| 393 while (RenderWidgetHost* widget = all_widgets->GetNextHost()) { | |
| 394 if (w == widget) { | |
| 395 found = true; | |
| 396 break; | |
| 397 } | |
| 398 } | |
| 399 EXPECT_TRUE(found); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 // Test if SiteInstanceImpl::active_view_count() is correctly updated | |
| 404 // as views in a SiteInstance get swapped out and in. | |
| 405 TEST_F(RenderViewHostManagerTest, ActiveViewCountWhileSwappingInandOut) { | |
| 406 const GURL kUrl1("http://www.google.com/"); | |
| 407 const GURL kUrl2("http://www.chromium.org/"); | |
| 408 | |
| 409 // Navigate to an initial URL. | |
| 410 contents()->NavigateAndCommit(kUrl1); | |
| 411 TestRenderViewHost* rvh1 = test_rvh(); | |
| 412 | |
| 413 SiteInstanceImpl* instance1 = | |
| 414 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance()); | |
| 415 EXPECT_EQ(instance1->active_view_count(), 1U); | |
| 416 | |
| 417 // Create 2 new tabs and simulate them being the opener chain for the main | |
| 418 // tab. They should be in the same SiteInstance. | |
| 419 scoped_ptr<TestWebContents> opener1( | |
| 420 TestWebContents::Create(browser_context(), instance1)); | |
| 421 contents()->SetOpener(opener1.get()); | |
| 422 | |
| 423 scoped_ptr<TestWebContents> opener2( | |
| 424 TestWebContents::Create(browser_context(), instance1)); | |
| 425 opener1->SetOpener(opener2.get()); | |
| 426 | |
| 427 EXPECT_EQ(instance1->active_view_count(), 3U); | |
| 428 | |
| 429 // Navigate to a cross-site URL (different SiteInstance but same | |
| 430 // BrowsingInstance). | |
| 431 contents()->NavigateAndCommit(kUrl2); | |
| 432 TestRenderViewHost* rvh2 = test_rvh(); | |
| 433 SiteInstanceImpl* instance2 = | |
| 434 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance()); | |
| 435 | |
| 436 // rvh2 is on chromium.org which is different from google.com on | |
| 437 // which other tabs are. | |
| 438 EXPECT_EQ(instance2->active_view_count(), 1U); | |
| 439 | |
| 440 // There are two active views on google.com now. | |
| 441 EXPECT_EQ(instance1->active_view_count(), 2U); | |
| 442 | |
| 443 // Navigate to the original origin (google.com). | |
| 444 contents()->NavigateAndCommit(kUrl1); | |
| 445 | |
| 446 EXPECT_EQ(instance1->active_view_count(), 3U); | |
| 447 } | |
| 448 | |
| 449 // This deletes a WebContents when the given RVH is deleted. This is | |
| 450 // only for testing whether deleting an RVH does not cause any UaF in | |
| 451 // other parts of the system. For now, this class is only used for the | |
| 452 // next test cases to detect the bug mentioned at | |
| 453 // http://crbug.com/259859. | |
| 454 class RenderViewHostDestroyer : public content::RenderViewHostObserver { | |
| 455 public: | |
| 456 RenderViewHostDestroyer(RenderViewHost* render_view_host, | |
| 457 WebContents* web_contents) | |
| 458 : content::RenderViewHostObserver(render_view_host), | |
| 459 web_contents_(web_contents) {} | |
| 460 | |
| 461 virtual void RenderViewHostDestroyed(RenderViewHost* render_view_host) | |
| 462 OVERRIDE { | |
| 463 delete web_contents_; | |
| 464 } | |
| 465 | |
| 466 private: | |
| 467 WebContents* web_contents_; | |
| 468 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestroyer); | |
| 469 }; | |
| 470 | |
| 471 // Test if ShutdownRenderViewHostsInSiteInstance() does not touch any | |
| 472 // RenderWidget that has been freed while deleting a RenderViewHost in | |
| 473 // a previous iteration. This is a regression test for | |
| 474 // http://crbug.com/259859. | |
| 475 TEST_F(RenderViewHostManagerTest, | |
| 476 DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) { | |
| 477 const GURL kChromeURL("chrome://newtab"); | |
| 478 const GURL kUrl1("http://www.google.com"); | |
| 479 const GURL kUrl2("http://www.chromium.org"); | |
| 480 | |
| 481 // Navigate our first tab to a chrome url and then to the destination. | |
| 482 NavigateActiveAndCommit(kChromeURL); | |
| 483 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>( | |
| 484 contents()->GetRenderManagerForTesting()->current_host()); | |
| 485 | |
| 486 // Create one more tab and navigate to kUrl1. web_contents is not | |
| 487 // wrapped as scoped_ptr since it intentionally deleted by destroyer | |
| 488 // below as part of this test. | |
| 489 TestWebContents* web_contents = | |
| 490 TestWebContents::Create(browser_context(), ntp_rvh->GetSiteInstance()); | |
| 491 web_contents->NavigateAndCommit(kUrl1); | |
| 492 RenderViewHostDestroyer destroyer(ntp_rvh, web_contents); | |
| 493 | |
| 494 // This causes the first tab to navigate to kUrl2, which destroys | |
| 495 // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When | |
| 496 // ntp_rvh is destroyed, it also destroys the RVHs in web_contents | |
| 497 // too. This can test whether | |
| 498 // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can | |
| 499 // touch any object freed in this way or not while iterating through | |
| 500 // all widgets. | |
| 501 contents()->NavigateAndCommit(kUrl2); | |
| 502 } | |
| 503 | |
| 504 // When there is an error with the specified page, renderer exits view-source | |
| 505 // mode. See WebFrameImpl::DidFail(). We check by this test that | |
| 506 // EnableViewSourceMode message is sent on every navigation regardless | |
| 507 // RenderView is being newly created or reused. | |
| 508 TEST_F(RenderViewHostManagerTest, AlwaysSendEnableViewSourceMode) { | |
| 509 const GURL kChromeUrl("chrome://foo"); | |
| 510 const GURL kUrl("view-source:http://foo"); | |
| 511 | |
| 512 // We have to navigate to some page at first since without this, the first | |
| 513 // navigation will reuse the SiteInstance created by Init(), and the second | |
| 514 // one will create a new SiteInstance. Because current_instance and | |
| 515 // new_instance will be different, a new RenderViewHost will be created for | |
| 516 // the second navigation. We have to avoid this in order to exercise the | |
| 517 // target code patch. | |
| 518 NavigateActiveAndCommit(kChromeUrl); | |
| 519 | |
| 520 // Navigate. | |
| 521 controller().LoadURL( | |
| 522 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); | |
| 523 // Simulate response from RenderView for FirePageBeforeUnload. | |
| 524 base::TimeTicks now = base::TimeTicks::Now(); | |
| 525 test_rvh()->OnMessageReceived(ViewHostMsg_ShouldClose_ACK( | |
| 526 rvh()->GetRoutingID(), true, now, now)); | |
| 527 ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created. | |
| 528 RenderViewHost* last_rvh = pending_rvh(); | |
| 529 int32 new_id = contents()->GetMaxPageIDForSiteInstance( | |
| 530 active_rvh()->GetSiteInstance()) + 1; | |
| 531 pending_test_rvh()->SendNavigate(new_id, kUrl); | |
| 532 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1); | |
| 533 ASSERT_TRUE(controller().GetLastCommittedEntry()); | |
| 534 EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL()); | |
| 535 EXPECT_FALSE(controller().GetPendingEntry()); | |
| 536 // Because we're using TestWebContents and TestRenderViewHost in this | |
| 537 // unittest, no one calls WebContentsImpl::RenderViewCreated(). So, we see no | |
| 538 // EnableViewSourceMode message, here. | |
| 539 | |
| 540 // Clear queued messages before load. | |
| 541 process()->sink().ClearMessages(); | |
| 542 // Navigate, again. | |
| 543 controller().LoadURL( | |
| 544 kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); | |
| 545 // The same RenderViewHost should be reused. | |
| 546 EXPECT_FALSE(pending_rvh()); | |
| 547 EXPECT_TRUE(last_rvh == rvh()); | |
| 548 test_rvh()->SendNavigate(new_id, kUrl); // The same page_id returned. | |
| 549 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1); | |
| 550 EXPECT_FALSE(controller().GetPendingEntry()); | |
| 551 // New message should be sent out to make sure to enter view-source mode. | |
| 552 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching( | |
| 553 ViewMsg_EnableViewSourceMode::ID)); | |
| 554 } | |
| 555 | |
| 556 // Tests the Init function by checking the initial RenderViewHost. | |
| 557 TEST_F(RenderViewHostManagerTest, Init) { | |
| 558 // Using TestBrowserContext. | |
| 559 SiteInstanceImpl* instance = | |
| 560 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context())); | |
| 561 EXPECT_FALSE(instance->HasSite()); | |
| 562 | |
| 563 scoped_ptr<TestWebContents> web_contents( | |
| 564 TestWebContents::Create(browser_context(), instance)); | |
| 565 RenderViewHostManager manager(web_contents.get(), web_contents.get(), | |
| 566 web_contents.get()); | |
| 567 | |
| 568 manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 569 | |
| 570 RenderViewHost* host = manager.current_host(); | |
| 571 ASSERT_TRUE(host); | |
| 572 EXPECT_EQ(instance, host->GetSiteInstance()); | |
| 573 EXPECT_EQ(web_contents.get(), host->GetDelegate()); | |
| 574 EXPECT_TRUE(manager.GetRenderWidgetHostView()); | |
| 575 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 576 } | |
| 577 | |
| 578 // Tests the Navigate function. We navigate three sites consecutively and check | |
| 579 // how the pending/committed RenderViewHost are modified. | |
| 580 TEST_F(RenderViewHostManagerTest, Navigate) { | |
| 581 TestNotificationTracker notifications; | |
| 582 | |
| 583 SiteInstance* instance = SiteInstance::Create(browser_context()); | |
| 584 | |
| 585 scoped_ptr<TestWebContents> web_contents( | |
| 586 TestWebContents::Create(browser_context(), instance)); | |
| 587 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED, | |
| 588 Source<WebContents>(web_contents.get())); | |
| 589 | |
| 590 // Create. | |
| 591 RenderViewHostManager manager(web_contents.get(), web_contents.get(), | |
| 592 web_contents.get()); | |
| 593 | |
| 594 manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 595 | |
| 596 RenderViewHost* host; | |
| 597 | |
| 598 // 1) The first navigation. -------------------------- | |
| 599 const GURL kUrl1("http://www.google.com/"); | |
| 600 NavigationEntryImpl entry1( | |
| 601 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(), | |
| 602 string16() /* title */, PAGE_TRANSITION_TYPED, | |
| 603 false /* is_renderer_init */); | |
| 604 host = manager.Navigate(entry1); | |
| 605 | |
| 606 // The RenderViewHost created in Init will be reused. | |
| 607 EXPECT_TRUE(host == manager.current_host()); | |
| 608 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 609 | |
| 610 // Commit. | |
| 611 manager.DidNavigateMainFrame(host); | |
| 612 // Commit to SiteInstance should be delayed until RenderView commit. | |
| 613 EXPECT_TRUE(host == manager.current_host()); | |
| 614 ASSERT_TRUE(host); | |
| 615 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> | |
| 616 HasSite()); | |
| 617 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1); | |
| 618 | |
| 619 // 2) Navigate to next site. ------------------------- | |
| 620 const GURL kUrl2("http://www.google.com/foo"); | |
| 621 NavigationEntryImpl entry2( | |
| 622 NULL /* instance */, -1 /* page_id */, kUrl2, | |
| 623 Referrer(kUrl1, WebKit::WebReferrerPolicyDefault), | |
| 624 string16() /* title */, PAGE_TRANSITION_LINK, | |
| 625 true /* is_renderer_init */); | |
| 626 host = manager.Navigate(entry2); | |
| 627 | |
| 628 // The RenderViewHost created in Init will be reused. | |
| 629 EXPECT_TRUE(host == manager.current_host()); | |
| 630 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 631 | |
| 632 // Commit. | |
| 633 manager.DidNavigateMainFrame(host); | |
| 634 EXPECT_TRUE(host == manager.current_host()); | |
| 635 ASSERT_TRUE(host); | |
| 636 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> | |
| 637 HasSite()); | |
| 638 | |
| 639 // 3) Cross-site navigate to next site. -------------- | |
| 640 const GURL kUrl3("http://webkit.org/"); | |
| 641 NavigationEntryImpl entry3( | |
| 642 NULL /* instance */, -1 /* page_id */, kUrl3, | |
| 643 Referrer(kUrl2, WebKit::WebReferrerPolicyDefault), | |
| 644 string16() /* title */, PAGE_TRANSITION_LINK, | |
| 645 false /* is_renderer_init */); | |
| 646 host = manager.Navigate(entry3); | |
| 647 | |
| 648 // A new RenderViewHost should be created. | |
| 649 EXPECT_TRUE(manager.pending_render_view_host()); | |
| 650 ASSERT_EQ(host, manager.pending_render_view_host()); | |
| 651 | |
| 652 notifications.Reset(); | |
| 653 | |
| 654 // Commit. | |
| 655 manager.DidNavigateMainFrame(manager.pending_render_view_host()); | |
| 656 EXPECT_TRUE(host == manager.current_host()); | |
| 657 ASSERT_TRUE(host); | |
| 658 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> | |
| 659 HasSite()); | |
| 660 // Check the pending RenderViewHost has been committed. | |
| 661 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 662 | |
| 663 // We should observe a notification. | |
| 664 EXPECT_TRUE( | |
| 665 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); | |
| 666 } | |
| 667 | |
| 668 // Tests the Navigate function. In this unit test we verify that the Navigate | |
| 669 // function can handle a new navigation event before the previous navigation | |
| 670 // has been committed. This is also a regression test for | |
| 671 // http://crbug.com/104600. | |
| 672 TEST_F(RenderViewHostManagerTest, NavigateWithEarlyReNavigation) { | |
| 673 TestNotificationTracker notifications; | |
| 674 | |
| 675 SiteInstance* instance = SiteInstance::Create(browser_context()); | |
| 676 | |
| 677 scoped_ptr<TestWebContents> web_contents( | |
| 678 TestWebContents::Create(browser_context(), instance)); | |
| 679 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED, | |
| 680 Source<WebContents>(web_contents.get())); | |
| 681 | |
| 682 // Create. | |
| 683 RenderViewHostManager manager(web_contents.get(), web_contents.get(), | |
| 684 web_contents.get()); | |
| 685 | |
| 686 manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 687 | |
| 688 // 1) The first navigation. -------------------------- | |
| 689 const GURL kUrl1("http://www.google.com/"); | |
| 690 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1, | |
| 691 Referrer(), string16() /* title */, | |
| 692 PAGE_TRANSITION_TYPED, | |
| 693 false /* is_renderer_init */); | |
| 694 RenderViewHost* host = manager.Navigate(entry1); | |
| 695 | |
| 696 // The RenderViewHost created in Init will be reused. | |
| 697 EXPECT_TRUE(host == manager.current_host()); | |
| 698 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 699 | |
| 700 // We should observe a notification. | |
| 701 EXPECT_TRUE( | |
| 702 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); | |
| 703 notifications.Reset(); | |
| 704 | |
| 705 // Commit. | |
| 706 manager.DidNavigateMainFrame(host); | |
| 707 | |
| 708 // Commit to SiteInstance should be delayed until RenderView commit. | |
| 709 EXPECT_TRUE(host == manager.current_host()); | |
| 710 ASSERT_TRUE(host); | |
| 711 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> | |
| 712 HasSite()); | |
| 713 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1); | |
| 714 | |
| 715 // 2) Cross-site navigate to next site. ------------------------- | |
| 716 const GURL kUrl2("http://www.example.com"); | |
| 717 NavigationEntryImpl entry2( | |
| 718 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(), | |
| 719 string16() /* title */, PAGE_TRANSITION_TYPED, | |
| 720 false /* is_renderer_init */); | |
| 721 RenderViewHostImpl* host2 = static_cast<RenderViewHostImpl*>( | |
| 722 manager.Navigate(entry2)); | |
| 723 int host2_process_id = host2->GetProcess()->GetID(); | |
| 724 | |
| 725 // A new RenderViewHost should be created. | |
| 726 EXPECT_TRUE(manager.pending_render_view_host()); | |
| 727 ASSERT_EQ(host2, manager.pending_render_view_host()); | |
| 728 EXPECT_NE(host2, host); | |
| 729 | |
| 730 // Check that the navigation is still suspended because the old RVH | |
| 731 // is not swapped out, yet. | |
| 732 EXPECT_TRUE(host2->are_navigations_suspended()); | |
| 733 MockRenderProcessHost* test_process_host2 = | |
| 734 static_cast<MockRenderProcessHost*>(host2->GetProcess()); | |
| 735 test_process_host2->sink().ClearMessages(); | |
| 736 host2->NavigateToURL(kUrl2); | |
| 737 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching( | |
| 738 ViewMsg_Navigate::ID)); | |
| 739 | |
| 740 // Allow closing the current Render View (precondition for swapping out | |
| 741 // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by | |
| 742 // FirePageBeforeUnload. | |
| 743 TestRenderViewHost* test_host = static_cast<TestRenderViewHost*>(host); | |
| 744 MockRenderProcessHost* test_process_host = | |
| 745 static_cast<MockRenderProcessHost*>(test_host->GetProcess()); | |
| 746 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( | |
| 747 ViewMsg_ShouldClose::ID)); | |
| 748 test_host->SendShouldCloseACK(true); | |
| 749 | |
| 750 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a | |
| 751 // call of RenderViewHostManager::SwapOutOldPage before | |
| 752 // RenderViewHostManager::DidNavigateMainFrame is called. | |
| 753 // The RVH is not swapped out until the commit. | |
| 754 manager.SwapOutOldPage(); | |
| 755 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( | |
| 756 ViewMsg_SwapOut::ID)); | |
| 757 test_host->OnSwappedOut(false); | |
| 758 | |
| 759 EXPECT_EQ(host, manager.current_host()); | |
| 760 EXPECT_FALSE(static_cast<RenderViewHostImpl*>( | |
| 761 manager.current_host())->is_swapped_out()); | |
| 762 EXPECT_EQ(host2, manager.pending_render_view_host()); | |
| 763 // There should be still no navigation messages being sent. | |
| 764 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching( | |
| 765 ViewMsg_Navigate::ID)); | |
| 766 | |
| 767 // 3) Cross-site navigate to next site before 2) has committed. -------------- | |
| 768 const GURL kUrl3("http://webkit.org/"); | |
| 769 NavigationEntryImpl entry3(NULL /* instance */, -1 /* page_id */, kUrl3, | |
| 770 Referrer(), string16() /* title */, | |
| 771 PAGE_TRANSITION_TYPED, | |
| 772 false /* is_renderer_init */); | |
| 773 test_process_host->sink().ClearMessages(); | |
| 774 RenderViewHost* host3 = manager.Navigate(entry3); | |
| 775 | |
| 776 // A new RenderViewHost should be created. host2 is now deleted. | |
| 777 EXPECT_TRUE(manager.pending_render_view_host()); | |
| 778 ASSERT_EQ(host3, manager.pending_render_view_host()); | |
| 779 EXPECT_NE(host3, host); | |
| 780 EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id); | |
| 781 | |
| 782 // Navigations in the new RVH should be suspended, which is ok because the | |
| 783 // old RVH is not yet swapped out and can respond to a second beforeunload | |
| 784 // request. | |
| 785 EXPECT_TRUE(static_cast<RenderViewHostImpl*>( | |
| 786 host3)->are_navigations_suspended()); | |
| 787 EXPECT_EQ(host, manager.current_host()); | |
| 788 EXPECT_FALSE(static_cast<RenderViewHostImpl*>( | |
| 789 manager.current_host())->is_swapped_out()); | |
| 790 | |
| 791 // Simulate a response to the second beforeunload request. | |
| 792 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( | |
| 793 ViewMsg_ShouldClose::ID)); | |
| 794 test_host->SendShouldCloseACK(true); | |
| 795 | |
| 796 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a | |
| 797 // call of RenderViewHostManager::SwapOutOldPage before | |
| 798 // RenderViewHostManager::DidNavigateMainFrame is called. | |
| 799 // The RVH is not swapped out until the commit. | |
| 800 manager.SwapOutOldPage(); | |
| 801 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching( | |
| 802 ViewMsg_SwapOut::ID)); | |
| 803 test_host->OnSwappedOut(false); | |
| 804 | |
| 805 // Commit. | |
| 806 manager.DidNavigateMainFrame(host3); | |
| 807 EXPECT_TRUE(host3 == manager.current_host()); | |
| 808 ASSERT_TRUE(host3); | |
| 809 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())-> | |
| 810 HasSite()); | |
| 811 // Check the pending RenderViewHost has been committed. | |
| 812 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 813 | |
| 814 // We should observe a notification. | |
| 815 EXPECT_TRUE( | |
| 816 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); | |
| 817 } | |
| 818 | |
| 819 // Tests WebUI creation. | |
| 820 TEST_F(RenderViewHostManagerTest, WebUI) { | |
| 821 set_should_create_webui(true); | |
| 822 SiteInstance* instance = SiteInstance::Create(browser_context()); | |
| 823 | |
| 824 scoped_ptr<TestWebContents> web_contents( | |
| 825 TestWebContents::Create(browser_context(), instance)); | |
| 826 RenderViewHostManager manager(web_contents.get(), web_contents.get(), | |
| 827 web_contents.get()); | |
| 828 | |
| 829 manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 830 EXPECT_FALSE(manager.current_host()->IsRenderViewLive()); | |
| 831 | |
| 832 const GURL kUrl("chrome://foo"); | |
| 833 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl, | |
| 834 Referrer(), string16() /* title */, | |
| 835 PAGE_TRANSITION_TYPED, | |
| 836 false /* is_renderer_init */); | |
| 837 RenderViewHost* host = manager.Navigate(entry); | |
| 838 | |
| 839 // We commit the pending RenderViewHost immediately because the previous | |
| 840 // RenderViewHost was not live. We test a case where it is live in | |
| 841 // WebUIInNewTab. | |
| 842 EXPECT_TRUE(host); | |
| 843 EXPECT_EQ(host, manager.current_host()); | |
| 844 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 845 | |
| 846 // It's important that the site instance get set on the Web UI page as soon | |
| 847 // as the navigation starts, rather than lazily after it commits, so we don't | |
| 848 // try to re-use the SiteInstance/process for non Web UI things that may | |
| 849 // get loaded in between. | |
| 850 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> | |
| 851 HasSite()); | |
| 852 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL()); | |
| 853 | |
| 854 // The Web UI is committed immediately because the RenderViewHost has not been | |
| 855 // used yet. UpdateRendererStateForNavigate() took the short cut path. | |
| 856 EXPECT_FALSE(manager.pending_web_ui()); | |
| 857 EXPECT_TRUE(manager.web_ui()); | |
| 858 | |
| 859 // Commit. | |
| 860 manager.DidNavigateMainFrame(host); | |
| 861 EXPECT_TRUE(host->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | |
| 862 } | |
| 863 | |
| 864 // Tests that we can open a WebUI link in a new tab from a WebUI page and still | |
| 865 // grant the correct bindings. http://crbug.com/189101. | |
| 866 TEST_F(RenderViewHostManagerTest, WebUIInNewTab) { | |
| 867 set_should_create_webui(true); | |
| 868 SiteInstance* blank_instance = SiteInstance::Create(browser_context()); | |
| 869 | |
| 870 // Create a blank tab. | |
| 871 scoped_ptr<TestWebContents> web_contents1( | |
| 872 TestWebContents::Create(browser_context(), blank_instance)); | |
| 873 RenderViewHostManager manager1(web_contents1.get(), web_contents1.get(), | |
| 874 web_contents1.get()); | |
| 875 manager1.Init( | |
| 876 browser_context(), blank_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 877 // Test the case that new RVH is considered live. | |
| 878 manager1.current_host()->CreateRenderView(string16(), -1, -1); | |
| 879 | |
| 880 // Navigate to a WebUI page. | |
| 881 const GURL kUrl1("chrome://foo"); | |
| 882 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1, | |
| 883 Referrer(), string16() /* title */, | |
| 884 PAGE_TRANSITION_TYPED, | |
| 885 false /* is_renderer_init */); | |
| 886 RenderViewHost* host1 = manager1.Navigate(entry1); | |
| 887 | |
| 888 // We should have a pending navigation to the WebUI RenderViewHost. | |
| 889 // It should already have bindings. | |
| 890 EXPECT_EQ(host1, manager1.pending_render_view_host()); | |
| 891 EXPECT_NE(host1, manager1.current_host()); | |
| 892 EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | |
| 893 | |
| 894 // Commit and ensure we still have bindings. | |
| 895 manager1.DidNavigateMainFrame(host1); | |
| 896 SiteInstance* webui_instance = host1->GetSiteInstance(); | |
| 897 EXPECT_EQ(host1, manager1.current_host()); | |
| 898 EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | |
| 899 | |
| 900 // Now simulate clicking a link that opens in a new tab. | |
| 901 scoped_ptr<TestWebContents> web_contents2( | |
| 902 TestWebContents::Create(browser_context(), webui_instance)); | |
| 903 RenderViewHostManager manager2(web_contents2.get(), web_contents2.get(), | |
| 904 web_contents2.get()); | |
| 905 manager2.Init( | |
| 906 browser_context(), webui_instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 907 // Make sure the new RVH is considered live. This is usually done in | |
| 908 // RenderWidgetHost::Init when opening a new tab from a link. | |
| 909 manager2.current_host()->CreateRenderView(string16(), -1, -1); | |
| 910 | |
| 911 const GURL kUrl2("chrome://foo/bar"); | |
| 912 NavigationEntryImpl entry2(NULL /* instance */, -1 /* page_id */, kUrl2, | |
| 913 Referrer(), string16() /* title */, | |
| 914 PAGE_TRANSITION_LINK, | |
| 915 true /* is_renderer_init */); | |
| 916 RenderViewHost* host2 = manager2.Navigate(entry2); | |
| 917 | |
| 918 // No cross-process transition happens because we are already in the right | |
| 919 // SiteInstance. We should grant bindings immediately. | |
| 920 EXPECT_EQ(host2, manager2.current_host()); | |
| 921 EXPECT_TRUE(host2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | |
| 922 | |
| 923 manager2.DidNavigateMainFrame(host2); | |
| 924 } | |
| 925 | |
| 926 // Tests that we don't end up in an inconsistent state if a page does a back and | |
| 927 // then reload. http://crbug.com/51680 | |
| 928 TEST_F(RenderViewHostManagerTest, PageDoesBackAndReload) { | |
| 929 const GURL kUrl1("http://www.google.com/"); | |
| 930 const GURL kUrl2("http://www.evil-site.com/"); | |
| 931 | |
| 932 // Navigate to a safe site, then an evil site. | |
| 933 // This will switch RenderViewHosts. We cannot assert that the first and | |
| 934 // second RVHs are different, though, because the first one may be promptly | |
| 935 // deleted. | |
| 936 contents()->NavigateAndCommit(kUrl1); | |
| 937 contents()->NavigateAndCommit(kUrl2); | |
| 938 RenderViewHost* evil_rvh = contents()->GetRenderViewHost(); | |
| 939 | |
| 940 // Now let's simulate the evil page calling history.back(). | |
| 941 contents()->OnGoToEntryAtOffset(-1); | |
| 942 // We should have a new pending RVH. | |
| 943 // Note that in this case, the navigation has not committed, so evil_rvh will | |
| 944 // not be deleted yet. | |
| 945 EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()-> | |
| 946 pending_render_view_host()); | |
| 947 | |
| 948 // Before that RVH has committed, the evil page reloads itself. | |
| 949 ViewHostMsg_FrameNavigate_Params params; | |
| 950 params.page_id = 1; | |
| 951 params.url = kUrl2; | |
| 952 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT; | |
| 953 params.should_update_history = false; | |
| 954 params.gesture = NavigationGestureAuto; | |
| 955 params.was_within_same_page = false; | |
| 956 params.is_post = false; | |
| 957 params.page_state = PageState::CreateFromURL(kUrl2); | |
| 958 contents()->DidNavigate(evil_rvh, params); | |
| 959 | |
| 960 // That should have cancelled the pending RVH, and the evil RVH should be the | |
| 961 // current one. | |
| 962 EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> | |
| 963 pending_render_view_host() == NULL); | |
| 964 EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host()); | |
| 965 | |
| 966 // Also we should not have a pending navigation entry. | |
| 967 EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL); | |
| 968 NavigationEntry* entry = contents()->GetController().GetVisibleEntry(); | |
| 969 ASSERT_TRUE(entry != NULL); | |
| 970 EXPECT_EQ(kUrl2, entry->GetURL()); | |
| 971 } | |
| 972 | |
| 973 // Ensure that we can go back and forward even if a SwapOut ACK isn't received. | |
| 974 // See http://crbug.com/93427. | |
| 975 TEST_F(RenderViewHostManagerTest, NavigateAfterMissingSwapOutACK) { | |
| 976 const GURL kUrl1("http://www.google.com/"); | |
| 977 const GURL kUrl2("http://www.chromium.org/"); | |
| 978 | |
| 979 // Navigate to two pages. | |
| 980 contents()->NavigateAndCommit(kUrl1); | |
| 981 TestRenderViewHost* rvh1 = test_rvh(); | |
| 982 | |
| 983 // Keep active_view_count nonzero so that no swapped out views in | |
| 984 // this SiteInstance get forcefully deleted. | |
| 985 static_cast<SiteInstanceImpl*>(rvh1->GetSiteInstance())-> | |
| 986 increment_active_view_count(); | |
| 987 | |
| 988 contents()->NavigateAndCommit(kUrl2); | |
| 989 TestRenderViewHost* rvh2 = test_rvh(); | |
| 990 static_cast<SiteInstanceImpl*>(rvh2->GetSiteInstance())-> | |
| 991 increment_active_view_count(); | |
| 992 | |
| 993 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't | |
| 994 // happen, but we have seen it when going back quickly across many entries | |
| 995 // (http://crbug.com/93427). | |
| 996 contents()->GetController().GoBack(); | |
| 997 EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack()); | |
| 998 contents()->ProceedWithCrossSiteNavigation(); | |
| 999 EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack()); | |
| 1000 rvh2->SwapOut(); | |
| 1001 EXPECT_TRUE(rvh2->is_waiting_for_unload_ack()); | |
| 1002 | |
| 1003 // The back navigation commits. We should proactively clear the | |
| 1004 // is_waiting_for_unload_ack state to be safe. | |
| 1005 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry(); | |
| 1006 rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL()); | |
| 1007 EXPECT_TRUE(rvh2->is_swapped_out()); | |
| 1008 EXPECT_FALSE(rvh2->is_waiting_for_unload_ack()); | |
| 1009 | |
| 1010 // We should be able to navigate forward. | |
| 1011 contents()->GetController().GoForward(); | |
| 1012 contents()->ProceedWithCrossSiteNavigation(); | |
| 1013 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry(); | |
| 1014 rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL()); | |
| 1015 EXPECT_EQ(rvh2, rvh()); | |
| 1016 EXPECT_FALSE(rvh2->is_swapped_out()); | |
| 1017 EXPECT_TRUE(rvh1->is_swapped_out()); | |
| 1018 } | |
| 1019 | |
| 1020 // Test that we create swapped out RVHs for the opener chain when navigating an | |
| 1021 // opened tab cross-process. This allows us to support certain cross-process | |
| 1022 // JavaScript calls (http://crbug.com/99202). | |
| 1023 TEST_F(RenderViewHostManagerTest, CreateSwappedOutOpenerRVHs) { | |
| 1024 const GURL kUrl1("http://www.google.com/"); | |
| 1025 const GURL kUrl2("http://www.chromium.org/"); | |
| 1026 const GURL kChromeUrl("chrome://foo"); | |
| 1027 | |
| 1028 // Navigate to an initial URL. | |
| 1029 contents()->NavigateAndCommit(kUrl1); | |
| 1030 RenderViewHostManager* manager = contents()->GetRenderManagerForTesting(); | |
| 1031 TestRenderViewHost* rvh1 = test_rvh(); | |
| 1032 | |
| 1033 // Create 2 new tabs and simulate them being the opener chain for the main | |
| 1034 // tab. They should be in the same SiteInstance. | |
| 1035 scoped_ptr<TestWebContents> opener1( | |
| 1036 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); | |
| 1037 RenderViewHostManager* opener1_manager = | |
| 1038 opener1->GetRenderManagerForTesting(); | |
| 1039 contents()->SetOpener(opener1.get()); | |
| 1040 | |
| 1041 scoped_ptr<TestWebContents> opener2( | |
| 1042 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); | |
| 1043 RenderViewHostManager* opener2_manager = | |
| 1044 opener2->GetRenderManagerForTesting(); | |
| 1045 opener1->SetOpener(opener2.get()); | |
| 1046 | |
| 1047 // Navigate to a cross-site URL (different SiteInstance but same | |
| 1048 // BrowsingInstance). | |
| 1049 contents()->NavigateAndCommit(kUrl2); | |
| 1050 TestRenderViewHost* rvh2 = test_rvh(); | |
| 1051 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance()); | |
| 1052 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( | |
| 1053 rvh2->GetSiteInstance())); | |
| 1054 | |
| 1055 // Ensure rvh1 is placed on swapped out list of the current tab. | |
| 1056 EXPECT_TRUE(manager->IsOnSwappedOutList(rvh1)); | |
| 1057 EXPECT_EQ(rvh1, | |
| 1058 manager->GetSwappedOutRenderViewHost(rvh1->GetSiteInstance())); | |
| 1059 | |
| 1060 // Ensure a swapped out RVH is created in the first opener tab. | |
| 1061 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( | |
| 1062 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); | |
| 1063 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rvh)); | |
| 1064 EXPECT_TRUE(opener1_rvh->is_swapped_out()); | |
| 1065 | |
| 1066 // Ensure a swapped out RVH is created in the second opener tab. | |
| 1067 TestRenderViewHost* opener2_rvh = static_cast<TestRenderViewHost*>( | |
| 1068 opener2_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); | |
| 1069 EXPECT_TRUE(opener2_manager->IsOnSwappedOutList(opener2_rvh)); | |
| 1070 EXPECT_TRUE(opener2_rvh->is_swapped_out()); | |
| 1071 | |
| 1072 // Navigate to a cross-BrowsingInstance URL. | |
| 1073 contents()->NavigateAndCommit(kChromeUrl); | |
| 1074 TestRenderViewHost* rvh3 = test_rvh(); | |
| 1075 EXPECT_NE(rvh1->GetSiteInstance(), rvh3->GetSiteInstance()); | |
| 1076 EXPECT_FALSE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( | |
| 1077 rvh3->GetSiteInstance())); | |
| 1078 | |
| 1079 // No scripting is allowed across BrowsingInstances, so we should not create | |
| 1080 // swapped out RVHs for the opener chain in this case. | |
| 1081 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost( | |
| 1082 rvh3->GetSiteInstance())); | |
| 1083 EXPECT_FALSE(opener2_manager->GetSwappedOutRenderViewHost( | |
| 1084 rvh3->GetSiteInstance())); | |
| 1085 } | |
| 1086 | |
| 1087 // Test that we clean up swapped out RenderViewHosts when a process hosting | |
| 1088 // those associated RenderViews crashes. http://crbug.com/258993 | |
| 1089 TEST_F(RenderViewHostManagerTest, CleanUpSwappedOutRVHOnProcessCrash) { | |
| 1090 const GURL kUrl1("http://www.google.com/"); | |
| 1091 | |
| 1092 // Navigate to an initial URL. | |
| 1093 contents()->NavigateAndCommit(kUrl1); | |
| 1094 TestRenderViewHost* rvh1 = test_rvh(); | |
| 1095 | |
| 1096 // Create a new tab as an opener for the main tab. | |
| 1097 scoped_ptr<TestWebContents> opener1( | |
| 1098 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); | |
| 1099 RenderViewHostManager* opener1_manager = | |
| 1100 opener1->GetRenderManagerForTesting(); | |
| 1101 contents()->SetOpener(opener1.get()); | |
| 1102 | |
| 1103 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost( | |
| 1104 rvh1->GetSiteInstance())); | |
| 1105 opener1->CreateSwappedOutRenderView(rvh1->GetSiteInstance()); | |
| 1106 EXPECT_TRUE(opener1_manager->GetSwappedOutRenderViewHost( | |
| 1107 rvh1->GetSiteInstance())); | |
| 1108 | |
| 1109 // Fake a process crash. | |
| 1110 RenderProcessHost::RendererClosedDetails details( | |
| 1111 rvh1->GetProcess()->GetHandle(), | |
| 1112 base::TERMINATION_STATUS_PROCESS_CRASHED, | |
| 1113 0); | |
| 1114 NotificationService::current()->Notify( | |
| 1115 NOTIFICATION_RENDERER_PROCESS_CLOSED, | |
| 1116 Source<RenderProcessHost>(rvh1->GetProcess()), | |
| 1117 Details<RenderProcessHost::RendererClosedDetails>(&details)); | |
| 1118 rvh1->set_render_view_created(false); | |
| 1119 | |
| 1120 // Ensure that the swapped out RenderViewHost has been deleted. | |
| 1121 EXPECT_FALSE(opener1_manager->GetSwappedOutRenderViewHost( | |
| 1122 rvh1->GetSiteInstance())); | |
| 1123 | |
| 1124 // Reload the initial tab. This should recreate the opener. | |
| 1125 contents()->GetController().Reload(true); | |
| 1126 | |
| 1127 EXPECT_EQ(opener1_manager->current_host()->GetRoutingID(), | |
| 1128 test_rvh()->opener_route_id()); | |
| 1129 } | |
| 1130 | |
| 1131 // Test that RenderViewHosts created for WebUI navigations are properly | |
| 1132 // granted WebUI bindings even if an unprivileged swapped out RenderViewHost | |
| 1133 // is in the same process (http://crbug.com/79918). | |
| 1134 TEST_F(RenderViewHostManagerTest, EnableWebUIWithSwappedOutOpener) { | |
| 1135 set_should_create_webui(true); | |
| 1136 const GURL kSettingsUrl("chrome://chrome/settings"); | |
| 1137 const GURL kPluginUrl("chrome://plugins"); | |
| 1138 | |
| 1139 // Navigate to an initial WebUI URL. | |
| 1140 contents()->NavigateAndCommit(kSettingsUrl); | |
| 1141 | |
| 1142 // Ensure the RVH has WebUI bindings. | |
| 1143 TestRenderViewHost* rvh1 = test_rvh(); | |
| 1144 EXPECT_TRUE(rvh1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | |
| 1145 | |
| 1146 // Create a new tab and simulate it being the opener for the main | |
| 1147 // tab. It should be in the same SiteInstance. | |
| 1148 scoped_ptr<TestWebContents> opener1( | |
| 1149 TestWebContents::Create(browser_context(), rvh1->GetSiteInstance())); | |
| 1150 RenderViewHostManager* opener1_manager = | |
| 1151 opener1->GetRenderManagerForTesting(); | |
| 1152 contents()->SetOpener(opener1.get()); | |
| 1153 | |
| 1154 // Navigate to a different WebUI URL (different SiteInstance, same | |
| 1155 // BrowsingInstance). | |
| 1156 contents()->NavigateAndCommit(kPluginUrl); | |
| 1157 TestRenderViewHost* rvh2 = test_rvh(); | |
| 1158 EXPECT_NE(rvh1->GetSiteInstance(), rvh2->GetSiteInstance()); | |
| 1159 EXPECT_TRUE(rvh1->GetSiteInstance()->IsRelatedSiteInstance( | |
| 1160 rvh2->GetSiteInstance())); | |
| 1161 | |
| 1162 // Ensure a swapped out RVH is created in the first opener tab. | |
| 1163 TestRenderViewHost* opener1_rvh = static_cast<TestRenderViewHost*>( | |
| 1164 opener1_manager->GetSwappedOutRenderViewHost(rvh2->GetSiteInstance())); | |
| 1165 EXPECT_TRUE(opener1_manager->IsOnSwappedOutList(opener1_rvh)); | |
| 1166 EXPECT_TRUE(opener1_rvh->is_swapped_out()); | |
| 1167 | |
| 1168 // Ensure the new RVH has WebUI bindings. | |
| 1169 EXPECT_TRUE(rvh2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); | |
| 1170 } | |
| 1171 | |
| 1172 // Test that we reuse the same guest SiteInstance if we navigate across sites. | |
| 1173 TEST_F(RenderViewHostManagerTest, NoSwapOnGuestNavigations) { | |
| 1174 TestNotificationTracker notifications; | |
| 1175 | |
| 1176 GURL guest_url(std::string(kGuestScheme).append("://abc123")); | |
| 1177 SiteInstance* instance = | |
| 1178 SiteInstance::CreateForURL(browser_context(), guest_url); | |
| 1179 scoped_ptr<TestWebContents> web_contents( | |
| 1180 TestWebContents::Create(browser_context(), instance)); | |
| 1181 | |
| 1182 // Create. | |
| 1183 RenderViewHostManager manager(web_contents.get(), web_contents.get(), | |
| 1184 web_contents.get()); | |
| 1185 | |
| 1186 manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 1187 | |
| 1188 RenderViewHost* host; | |
| 1189 | |
| 1190 // 1) The first navigation. -------------------------- | |
| 1191 const GURL kUrl1("http://www.google.com/"); | |
| 1192 NavigationEntryImpl entry1( | |
| 1193 NULL /* instance */, -1 /* page_id */, kUrl1, Referrer(), | |
| 1194 string16() /* title */, PAGE_TRANSITION_TYPED, | |
| 1195 false /* is_renderer_init */); | |
| 1196 host = manager.Navigate(entry1); | |
| 1197 | |
| 1198 // The RenderViewHost created in Init will be reused. | |
| 1199 EXPECT_TRUE(host == manager.current_host()); | |
| 1200 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 1201 EXPECT_EQ(manager.current_host()->GetSiteInstance(), instance); | |
| 1202 | |
| 1203 // Commit. | |
| 1204 manager.DidNavigateMainFrame(host); | |
| 1205 // Commit to SiteInstance should be delayed until RenderView commit. | |
| 1206 EXPECT_EQ(host, manager.current_host()); | |
| 1207 ASSERT_TRUE(host); | |
| 1208 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> | |
| 1209 HasSite()); | |
| 1210 | |
| 1211 // 2) Navigate to a different domain. ------------------------- | |
| 1212 // Guests stay in the same process on navigation. | |
| 1213 const GURL kUrl2("http://www.chromium.org"); | |
| 1214 NavigationEntryImpl entry2( | |
| 1215 NULL /* instance */, -1 /* page_id */, kUrl2, | |
| 1216 Referrer(kUrl1, WebKit::WebReferrerPolicyDefault), | |
| 1217 string16() /* title */, PAGE_TRANSITION_LINK, | |
| 1218 true /* is_renderer_init */); | |
| 1219 host = manager.Navigate(entry2); | |
| 1220 | |
| 1221 // The RenderViewHost created in Init will be reused. | |
| 1222 EXPECT_EQ(host, manager.current_host()); | |
| 1223 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 1224 | |
| 1225 // Commit. | |
| 1226 manager.DidNavigateMainFrame(host); | |
| 1227 EXPECT_EQ(host, manager.current_host()); | |
| 1228 ASSERT_TRUE(host); | |
| 1229 EXPECT_EQ(static_cast<SiteInstanceImpl*>(host->GetSiteInstance()), | |
| 1230 instance); | |
| 1231 } | |
| 1232 | |
| 1233 // Test that we cancel a pending RVH if we close the tab while it's pending. | |
| 1234 // http://crbug.com/294697. | |
| 1235 TEST_F(RenderViewHostManagerTest, NavigateWithEarlyClose) { | |
| 1236 TestNotificationTracker notifications; | |
| 1237 | |
| 1238 SiteInstance* instance = SiteInstance::Create(browser_context()); | |
| 1239 | |
| 1240 BeforeUnloadFiredWebContentsDelegate delegate; | |
| 1241 scoped_ptr<TestWebContents> web_contents( | |
| 1242 TestWebContents::Create(browser_context(), instance)); | |
| 1243 web_contents->SetDelegate(&delegate); | |
| 1244 notifications.ListenFor(NOTIFICATION_RENDER_VIEW_HOST_CHANGED, | |
| 1245 Source<WebContents>(web_contents.get())); | |
| 1246 | |
| 1247 // Create. | |
| 1248 RenderViewHostManager manager(web_contents.get(), web_contents.get(), | |
| 1249 web_contents.get()); | |
| 1250 | |
| 1251 manager.Init(browser_context(), instance, MSG_ROUTING_NONE, MSG_ROUTING_NONE); | |
| 1252 | |
| 1253 // 1) The first navigation. -------------------------- | |
| 1254 const GURL kUrl1("http://www.google.com/"); | |
| 1255 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1, | |
| 1256 Referrer(), string16() /* title */, | |
| 1257 PAGE_TRANSITION_TYPED, | |
| 1258 false /* is_renderer_init */); | |
| 1259 RenderViewHost* host = manager.Navigate(entry1); | |
| 1260 | |
| 1261 // The RenderViewHost created in Init will be reused. | |
| 1262 EXPECT_EQ(host, manager.current_host()); | |
| 1263 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 1264 | |
| 1265 // We should observe a notification. | |
| 1266 EXPECT_TRUE( | |
| 1267 notifications.Check1AndReset(NOTIFICATION_RENDER_VIEW_HOST_CHANGED)); | |
| 1268 notifications.Reset(); | |
| 1269 | |
| 1270 // Commit. | |
| 1271 manager.DidNavigateMainFrame(host); | |
| 1272 | |
| 1273 // Commit to SiteInstance should be delayed until RenderView commit. | |
| 1274 EXPECT_EQ(host, manager.current_host()); | |
| 1275 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())-> | |
| 1276 HasSite()); | |
| 1277 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1); | |
| 1278 | |
| 1279 // 2) Cross-site navigate to next site. ------------------------- | |
| 1280 const GURL kUrl2("http://www.example.com"); | |
| 1281 NavigationEntryImpl entry2( | |
| 1282 NULL /* instance */, -1 /* page_id */, kUrl2, Referrer(), | |
| 1283 string16() /* title */, PAGE_TRANSITION_TYPED, | |
| 1284 false /* is_renderer_init */); | |
| 1285 RenderViewHostImpl* host2 = static_cast<RenderViewHostImpl*>( | |
| 1286 manager.Navigate(entry2)); | |
| 1287 | |
| 1288 // A new RenderViewHost should be created. | |
| 1289 ASSERT_EQ(host2, manager.pending_render_view_host()); | |
| 1290 EXPECT_NE(host2, host); | |
| 1291 | |
| 1292 EXPECT_EQ(host, manager.current_host()); | |
| 1293 EXPECT_FALSE(static_cast<RenderViewHostImpl*>( | |
| 1294 manager.current_host())->is_swapped_out()); | |
| 1295 EXPECT_EQ(host2, manager.pending_render_view_host()); | |
| 1296 | |
| 1297 // 3) Close the tab. ------------------------- | |
| 1298 notifications.ListenFor(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, | |
| 1299 Source<RenderWidgetHost>(host2)); | |
| 1300 manager.ShouldClosePage(false, true, base::TimeTicks()); | |
| 1301 | |
| 1302 EXPECT_TRUE( | |
| 1303 notifications.Check1AndReset(NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED)); | |
| 1304 EXPECT_FALSE(manager.pending_render_view_host()); | |
| 1305 EXPECT_EQ(host, manager.current_host()); | |
| 1306 } | |
| 1307 | |
| 1308 } // namespace content | |
| OLD | NEW |