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 |