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 <stdint.h>
6
7 #include <tuple>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/check.h"
13 #include "base/command_line.h"
14 #include "base/macros.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/test/bind.h"
18 #include "base/test/scoped_feature_list.h"
19 #include "build/build_config.h"
20 #include "components/download/public/common/download_url_parameters.h"
21 #include "content/browser/child_process_security_policy_impl.h"
22 #include "content/browser/media/audio_stream_monitor.h"
23 #include "content/browser/media/media_web_contents_observer.h"
24 #include "content/browser/renderer_host/navigation_entry_impl.h"
25 #include "content/browser/renderer_host/navigator.h"
26 #include "content/browser/renderer_host/render_frame_host_impl.h"
27 #include "content/browser/renderer_host/render_frame_proxy_host.h"
28 #include "content/browser/renderer_host/render_view_host_impl.h"
29 #include "content/browser/site_instance_impl.h"
30 #include "content/browser/webui/content_web_ui_controller_factory.h"
31 #include "content/browser/webui/web_ui_controller_factory_registry.h"
32 #include "content/common/content_navigation_policy.h"
33 #include "content/common/frame.mojom.h"
34 #include "content/common/frame_messages.h"
35 #include "content/public/browser/child_process_security_policy.h"
36 #include "content/public/browser/content_browser_client.h"
37 #include "content/public/browser/context_menu_params.h"
38 #include "content/public/browser/global_request_id.h"
39 #include "content/public/browser/javascript_dialog_manager.h"
40 #include "content/public/browser/navigation_details.h"
41 #include "content/public/browser/notification_details.h"
42 #include "content/public/browser/notification_source.h"
43 #include "content/public/browser/render_widget_host_view.h"
44 #include "content/public/browser/ssl_host_state_delegate.h"
45 #include "content/public/browser/storage_partition.h"
46 #include "content/public/browser/web_contents_delegate.h"
47 #include "content/public/browser/web_contents_observer.h"
48 #include "content/public/browser/web_ui_controller.h"
49 #include "content/public/common/bindings_policy.h"
50 #include "content/public/common/content_constants.h"
51 #include "content/public/common/content_features.h"
52 #include "content/public/common/url_constants.h"
53 #include "content/public/common/url_utils.h"
54 #include "content/public/test/fake_local_frame.h"
55 #include "content/public/test/mock_render_process_host.h"
56 #include "content/public/test/navigation_simulator.h"
57 #include "content/public/test/test_browser_context.h"
58 #include "content/public/test/test_utils.h"
59 #include "content/test/navigation_simulator_impl.h"
60 #include "content/test/test_content_browser_client.h"
61 #include "content/test/test_content_client.h"
62 #include "content/test/test_render_frame_host.h"
63 #include "content/test/test_render_view_host.h"
64 #include "content/test/test_web_contents.h"
65 #include "net/test/cert_test_util.h"
66 #include "net/test/test_data_directory.h"
67 #include "services/network/public/cpp/web_sandbox_flags.h"
68 #include "skia/ext/skia_utils_base.h"
69 #include "testing/gmock/include/gmock/gmock.h"
70 #include "testing/gtest/include/gtest/gtest.h"
71 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
72 #include "third_party/blink/public/common/security/protocol_handler_security_level.h"
73 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
74 #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
75 #include "third_party/blink/public/mojom/page/page_visibility_state.mojom.h"
76 #include "third_party/skia/include/core/SkColor.h"
77 #include "ui/gfx/skia_util.h"
78 #include "url/url_constants.h"
79
80 namespace content {
81 namespace {
82 class WebContentsImplTestBrowserClient : public TestContentBrowserClient {
83 public:
WebContentsImplTestBrowserClient()84 WebContentsImplTestBrowserClient()
85 : original_browser_client_(SetBrowserClientForTesting(this)) {}
86
~WebContentsImplTestBrowserClient()87 ~WebContentsImplTestBrowserClient() override {
88 SetBrowserClientForTesting(original_browser_client_);
89 }
90
ShouldAssignSiteForURL(const GURL & url)91 bool ShouldAssignSiteForURL(const GURL& url) override {
92 if (site_assignment_for_url_.find(url) != site_assignment_for_url_.end()) {
93 return site_assignment_for_url_[url];
94 }
95
96 return true;
97 }
98
set_assign_site_for_url(bool assign,const GURL & url)99 void set_assign_site_for_url(bool assign, const GURL& url) {
100 DCHECK(url.is_valid());
101 site_assignment_for_url_[url] = assign;
102 }
103
104 private:
105 std::map<GURL, bool> site_assignment_for_url_;
106 ContentBrowserClient* original_browser_client_;
107 };
108
109 class WebContentsImplTest : public RenderViewHostImplTestHarness {
110 public:
SetUp()111 void SetUp() override {
112 RenderViewHostImplTestHarness::SetUp();
113 WebUIControllerFactory::RegisterFactory(
114 ContentWebUIControllerFactory::GetInstance());
115
116 if (AreDefaultSiteInstancesEnabled()) {
117 // Isolate |isolated_cross_site_url()| so we can't get a default
118 // SiteInstance for it.
119 ChildProcessSecurityPolicyImpl::GetInstance()->AddIsolatedOrigins(
120 {url::Origin::Create(isolated_cross_site_url())},
121 ChildProcessSecurityPolicy::IsolatedOriginSource::TEST,
122 browser_context());
123
124 // Reset the WebContents so the isolated origin will be honored by
125 // all BrowsingInstances used in the test.
126 SetContents(CreateTestWebContents());
127 }
128 }
129
TearDown()130 void TearDown() override {
131 WebUIControllerFactory::UnregisterFactoryForTesting(
132 ContentWebUIControllerFactory::GetInstance());
133 RenderViewHostImplTestHarness::TearDown();
134 }
135
has_audio_wake_lock()136 bool has_audio_wake_lock() {
137 return contents()
138 ->media_web_contents_observer()
139 ->has_audio_wake_lock_for_testing();
140 }
141
isolated_cross_site_url() const142 GURL isolated_cross_site_url() const {
143 return GURL("http://isolated-cross-site.com");
144 }
145 };
146
147 class TestWebContentsObserver : public WebContentsObserver {
148 public:
TestWebContentsObserver(WebContents * contents)149 explicit TestWebContentsObserver(WebContents* contents)
150 : WebContentsObserver(contents) {}
~TestWebContentsObserver()151 ~TestWebContentsObserver() override {}
152
DidFinishLoad(RenderFrameHost * render_frame_host,const GURL & validated_url)153 void DidFinishLoad(RenderFrameHost* render_frame_host,
154 const GURL& validated_url) override {
155 last_url_ = validated_url;
156 }
DidFailLoad(RenderFrameHost * render_frame_host,const GURL & validated_url,int error_code)157 void DidFailLoad(RenderFrameHost* render_frame_host,
158 const GURL& validated_url,
159 int error_code) override {
160 last_url_ = validated_url;
161 }
162
DidFirstVisuallyNonEmptyPaint()163 void DidFirstVisuallyNonEmptyPaint() override {
164 observed_did_first_visually_non_empty_paint_ = true;
165 EXPECT_TRUE(web_contents()->CompletedFirstVisuallyNonEmptyPaint());
166 }
167
DidChangeThemeColor()168 void DidChangeThemeColor() override { ++theme_color_change_calls_; }
169
DidChangeVerticalScrollDirection(viz::VerticalScrollDirection scroll_direction)170 void DidChangeVerticalScrollDirection(
171 viz::VerticalScrollDirection scroll_direction) override {
172 last_vertical_scroll_direction_ = scroll_direction;
173 }
174
OnIsConnectedToBluetoothDeviceChanged(bool is_connected_to_bluetooth_device)175 void OnIsConnectedToBluetoothDeviceChanged(
176 bool is_connected_to_bluetooth_device) override {
177 ++num_is_connected_to_bluetooth_device_changed_;
178 last_is_connected_to_bluetooth_device_ = is_connected_to_bluetooth_device;
179 }
180
last_url() const181 const GURL& last_url() const { return last_url_; }
theme_color_change_calls() const182 int theme_color_change_calls() const { return theme_color_change_calls_; }
last_vertical_scroll_direction() const183 base::Optional<viz::VerticalScrollDirection> last_vertical_scroll_direction()
184 const {
185 return last_vertical_scroll_direction_;
186 }
observed_did_first_visually_non_empty_paint() const187 bool observed_did_first_visually_non_empty_paint() const {
188 return observed_did_first_visually_non_empty_paint_;
189 }
num_is_connected_to_bluetooth_device_changed() const190 int num_is_connected_to_bluetooth_device_changed() const {
191 return num_is_connected_to_bluetooth_device_changed_;
192 }
last_is_connected_to_bluetooth_device() const193 bool last_is_connected_to_bluetooth_device() const {
194 return last_is_connected_to_bluetooth_device_;
195 }
196
197 private:
198 GURL last_url_;
199 int theme_color_change_calls_ = 0;
200 base::Optional<viz::VerticalScrollDirection> last_vertical_scroll_direction_;
201 bool observed_did_first_visually_non_empty_paint_ = false;
202 int num_is_connected_to_bluetooth_device_changed_ = 0;
203 bool last_is_connected_to_bluetooth_device_ = false;
204
205 DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
206 };
207
208 // Pretends to be a normal browser that receives toggles and transitions to/from
209 // a fullscreened state.
210 class FakeFullscreenDelegate : public WebContentsDelegate {
211 public:
FakeFullscreenDelegate()212 FakeFullscreenDelegate() : fullscreened_contents_(nullptr) {}
~FakeFullscreenDelegate()213 ~FakeFullscreenDelegate() override {}
214
EnterFullscreenModeForTab(RenderFrameHost * requesting_frame,const blink::mojom::FullscreenOptions & options)215 void EnterFullscreenModeForTab(
216 RenderFrameHost* requesting_frame,
217 const blink::mojom::FullscreenOptions& options) override {
218 fullscreened_contents_ = WebContents::FromRenderFrameHost(requesting_frame);
219 }
220
ExitFullscreenModeForTab(WebContents * web_contents)221 void ExitFullscreenModeForTab(WebContents* web_contents) override {
222 fullscreened_contents_ = nullptr;
223 }
224
IsFullscreenForTabOrPending(const WebContents * web_contents)225 bool IsFullscreenForTabOrPending(const WebContents* web_contents) override {
226 return fullscreened_contents_ && web_contents == fullscreened_contents_;
227 }
228
229 private:
230 WebContents* fullscreened_contents_;
231
232 DISALLOW_COPY_AND_ASSIGN(FakeFullscreenDelegate);
233 };
234
235 class FakeWebContentsDelegate : public WebContentsDelegate {
236 public:
FakeWebContentsDelegate()237 FakeWebContentsDelegate() : loading_state_changed_was_called_(false) {}
~FakeWebContentsDelegate()238 ~FakeWebContentsDelegate() override {}
239
LoadingStateChanged(WebContents * source,bool to_different_document)240 void LoadingStateChanged(WebContents* source,
241 bool to_different_document) override {
242 loading_state_changed_was_called_ = true;
243 }
244
loading_state_changed_was_called() const245 bool loading_state_changed_was_called() const {
246 return loading_state_changed_was_called_;
247 }
248
249 private:
250 bool loading_state_changed_was_called_;
251
252 DISALLOW_COPY_AND_ASSIGN(FakeWebContentsDelegate);
253 };
254
255 } // namespace
256
TEST_F(WebContentsImplTest,UpdateTitle)257 TEST_F(WebContentsImplTest, UpdateTitle) {
258 FakeWebContentsDelegate fake_delegate;
259 contents()->SetDelegate(&fake_delegate);
260
261 NavigationControllerImpl& cont =
262 static_cast<NavigationControllerImpl&>(controller());
263 cont.LoadURL(GURL(url::kAboutBlankURL), Referrer(), ui::PAGE_TRANSITION_TYPED,
264 std::string());
265
266 FrameHostMsg_DidCommitProvisionalLoad_Params params;
267 InitNavigateParams(¶ms, 0, true, GURL(url::kAboutBlankURL),
268 ui::PAGE_TRANSITION_TYPED);
269
270 main_test_rfh()->SendNavigateWithParams(¶ms,
271 false /* was_within_same_document */);
272
273 contents()->UpdateTitle(main_test_rfh(),
274 base::ASCIIToUTF16(" Lots O' Whitespace\n"),
275 base::i18n::LEFT_TO_RIGHT);
276 // Make sure that title updates get stripped of whitespace.
277 EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
278 EXPECT_FALSE(contents()->IsWaitingForResponse());
279 EXPECT_TRUE(fake_delegate.loading_state_changed_was_called());
280
281 contents()->SetDelegate(nullptr);
282 }
283
TEST_F(WebContentsImplTest,DownloadInvalidImage)284 TEST_F(WebContentsImplTest, DownloadInvalidImage) {
285 TestWebContents* test_contents = contents();
286
287 GURL url("about:blank");
288
289 SkBitmap result_bitmap;
290 WebContents::ImageDownloadCallback cb = base::BindLambdaForTesting(
291 [&](int id, int http_status_code, const GURL& image_url,
292 const std::vector<SkBitmap>& bitmaps,
293 const std::vector<gfx::Size>& sizes) {
294 ASSERT_EQ(bitmaps.size(), 1u);
295 result_bitmap = bitmaps[0];
296 });
297
298 // In production this would go to the renderer, but TestWebContents just
299 // waits for us to give a reply.
300 test_contents->DownloadImage(url,
301 /*is_favicon=*/false,
302 /*preferred_size=*/false,
303 /*max_bitmap_size=*/0,
304 /*bypass_cache=*/false, std::move(cb));
305
306 SkBitmap badbitmap;
307 badbitmap.allocPixels(
308 SkImageInfo::Make(1, 1, kARGB_4444_SkColorType, kPremul_SkAlphaType));
309 badbitmap.eraseColor(SK_ColorGREEN);
310
311 SkBitmap n32bitmap;
312 EXPECT_TRUE(skia::SkBitmapToN32OpaqueOrPremul(badbitmap, &n32bitmap));
313
314 // The result from the renderer is not an N32 32bpp bitmap, so it should
315 // be converted before being given to the rest of the browser process.
316 test_contents->TestDidDownloadImage(url, 400, {badbitmap},
317 {gfx::Size(1000, 2000)});
318 EXPECT_TRUE(gfx::BitmapsAreEqual(result_bitmap, n32bitmap));
319 }
320
TEST_F(WebContentsImplTest,UpdateTitleBeforeFirstNavigation)321 TEST_F(WebContentsImplTest, UpdateTitleBeforeFirstNavigation) {
322 ASSERT_TRUE(controller().IsInitialNavigation());
323 const base::string16 title = base::ASCIIToUTF16("Initial Entry Title");
324 contents()->UpdateTitle(main_test_rfh(), title, base::i18n::LEFT_TO_RIGHT);
325 EXPECT_EQ(title, contents()->GetTitle());
326 }
327
TEST_F(WebContentsImplTest,SetMainFrameMimeType)328 TEST_F(WebContentsImplTest, SetMainFrameMimeType) {
329 ASSERT_TRUE(controller().IsInitialNavigation());
330 std::string mime = "text/html";
331 RenderViewHostImpl* rvh =
332 static_cast<RenderViewHostImpl*>(main_test_rfh()->GetRenderViewHost());
333 rvh->SetContentsMimeType(mime);
334 EXPECT_EQ(mime, contents()->GetContentsMimeType());
335 }
336
TEST_F(WebContentsImplTest,DontUseTitleFromPendingEntry)337 TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
338 const GURL kGURL(GetWebUIURL("blah"));
339 controller().LoadURL(
340 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
341 EXPECT_EQ(base::string16(), contents()->GetTitle());
342
343 // Also test setting title while the first navigation is still pending.
344 const base::string16 title = base::ASCIIToUTF16("Initial Entry Title");
345 contents()->UpdateTitle(main_test_rfh(), title, base::i18n::LEFT_TO_RIGHT);
346 EXPECT_EQ(title, contents()->GetTitle());
347 }
348
TEST_F(WebContentsImplTest,UseTitleFromPendingEntryIfSet)349 TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
350 const GURL kGURL(GetWebUIURL("blah"));
351 const base::string16 title = base::ASCIIToUTF16("My Title");
352 controller().LoadURL(
353 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
354
355 NavigationEntry* entry = controller().GetVisibleEntry();
356 ASSERT_EQ(kGURL, entry->GetURL());
357 entry->SetTitle(title);
358
359 EXPECT_EQ(title, contents()->GetTitle());
360 }
361
362 // Stub out local frame mojo binding. Intercepts calls to EnableViewSourceMode
363 // and marks the message as received. This class attaches to the first
364 // RenderFrameHostImpl created.
365 class EnableViewSourceLocalFrame : public content::FakeLocalFrame,
366 public WebContentsObserver {
367 public:
EnableViewSourceLocalFrame(WebContents * web_contents)368 explicit EnableViewSourceLocalFrame(WebContents* web_contents)
369 : WebContentsObserver(web_contents) {}
370
RenderFrameCreated(RenderFrameHost * render_frame_host)371 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
372 if (!initialized_) {
373 initialized_ = true;
374 Init(render_frame_host->GetRemoteAssociatedInterfaces());
375 }
376 }
377
EnableViewSourceMode()378 void EnableViewSourceMode() final { enabled_view_source_ = true; }
379
IsViewSourceModeEnabled() const380 bool IsViewSourceModeEnabled() const { return enabled_view_source_; }
381
382 private:
383 bool enabled_view_source_ = false;
384 bool initialized_ = false;
385 };
386
387 // Browser initiated navigations to view-source URLs of WebUI pages should work.
TEST_F(WebContentsImplTest,DirectNavigationToViewSourceWebUI)388 TEST_F(WebContentsImplTest, DirectNavigationToViewSourceWebUI) {
389 const GURL kGURL("view-source:" + GetWebUIURLString("blah/"));
390 // NavigationControllerImpl rewrites view-source URLs, simulating that here.
391 const GURL kRewrittenURL(GetWebUIURL("blah"));
392
393 EnableViewSourceLocalFrame local_frame(contents());
394 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kGURL);
395
396 // Did we get the expected message?
397 base::RunLoop().RunUntilIdle();
398 EXPECT_TRUE(local_frame.IsViewSourceModeEnabled());
399
400 // This is the virtual URL.
401 EXPECT_EQ(
402 kGURL,
403 contents()->GetController().GetLastCommittedEntry()->GetVirtualURL());
404
405 // The actual URL navigated to.
406 EXPECT_EQ(kRewrittenURL,
407 contents()->GetController().GetLastCommittedEntry()->GetURL());
408 }
409
410 // Test simple same-SiteInstance navigation.
TEST_F(WebContentsImplTest,SimpleNavigation)411 TEST_F(WebContentsImplTest, SimpleNavigation) {
412 TestRenderFrameHost* orig_rfh = main_test_rfh();
413 SiteInstance* instance1 = contents()->GetSiteInstance();
414 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
415
416 // Navigate until ready to commit.
417 const GURL url("http://www.google.com");
418 auto navigation =
419 NavigationSimulator::CreateBrowserInitiated(url, contents());
420 navigation->ReadyToCommit();
421 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
422 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
423 // Controller's pending entry will have a null site instance until we assign
424 // it in Commit.
425 EXPECT_EQ(
426 nullptr,
427 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
428 site_instance());
429
430 navigation->Commit();
431 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
432 EXPECT_EQ(orig_rfh, main_test_rfh());
433 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
434 // Controller's entry should now have the SiteInstance, or else we won't be
435 // able to find it later.
436 EXPECT_EQ(
437 instance1,
438 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
439 site_instance());
440 }
441
442 // Test that we reject NavigateToEntry if the url is over kMaxURLChars.
TEST_F(WebContentsImplTest,NavigateToExcessivelyLongURL)443 TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
444 // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
445 const GURL url(std::string("http://example.org/").append(
446 url::kMaxURLChars + 1, 'a'));
447
448 controller().LoadURL(
449 url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
450 EXPECT_EQ(nullptr, controller().GetPendingEntry());
451 }
452
453 // Test that we reject NavigateToEntry if the url is invalid.
TEST_F(WebContentsImplTest,NavigateToInvalidURL)454 TEST_F(WebContentsImplTest, NavigateToInvalidURL) {
455 // Invalid URLs should not trigger a navigation.
456 const GURL invalid_url("view-source:http://example.org/%00");
457 controller().LoadURL(
458 invalid_url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
459 EXPECT_EQ(nullptr, controller().GetPendingEntry());
460
461 // Empty URLs are supported and should start a navigation.
462 controller().LoadURL(
463 GURL(), Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
464 EXPECT_NE(nullptr, controller().GetPendingEntry());
465 }
466
467 // Test that we reject NavigateToEntry if the url is a renderer debug URL
468 // inside a view-source: URL. This verifies that the navigation is not allowed
469 // to proceed after the view-source: URL rewriting logic has run.
TEST_F(WebContentsImplTest,NavigateToViewSourceRendererDebugURL)470 TEST_F(WebContentsImplTest, NavigateToViewSourceRendererDebugURL) {
471 const GURL renderer_debug_url(kChromeUIKillURL);
472 const GURL view_source_debug_url("view-source:" + renderer_debug_url.spec());
473 EXPECT_TRUE(IsRendererDebugURL(renderer_debug_url));
474 EXPECT_FALSE(IsRendererDebugURL(view_source_debug_url));
475 controller().LoadURL(view_source_debug_url, Referrer(),
476 ui::PAGE_TRANSITION_GENERATED, std::string());
477 EXPECT_EQ(nullptr, controller().GetPendingEntry());
478 }
479
480 // Test that navigating across a site boundary creates a new RenderViewHost
481 // with a new SiteInstance. Going back should do the same.
TEST_F(WebContentsImplTest,CrossSiteBoundaries)482 TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
483 // This test assumes no interaction with the back forward cache.
484 // Similar coverage when BFCache is on can be found in
485 // BackForwardCacheBrowserTest.NavigateBackForwardRepeatedly.
486 contents()->GetController().GetBackForwardCache().DisableForTesting(
487 BackForwardCache::TEST_ASSUMES_NO_CACHING);
488
489 TestRenderFrameHost* orig_rfh = main_test_rfh();
490 int orig_rvh_delete_count = 0;
491 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
492 SiteInstance* instance1 = contents()->GetSiteInstance();
493
494 // Navigate to URL. First URL should use first RenderViewHost.
495 const GURL url("http://www.google.com");
496 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
497
498 // Keep the number of active frames in orig_rfh's SiteInstance non-zero so
499 // that orig_rfh doesn't get deleted when it gets swapped out.
500 orig_rfh->GetSiteInstance()->IncrementActiveFrameCount();
501
502 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
503 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
504 EXPECT_EQ(url, contents()->GetLastCommittedURL());
505 EXPECT_EQ(url, contents()->GetVisibleURL());
506
507 // Navigate to new site
508 const GURL url2("http://www.yahoo.com");
509 auto new_site_navigation =
510 NavigationSimulator::CreateBrowserInitiated(url2, contents());
511 new_site_navigation->ReadyToCommit();
512 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
513 EXPECT_EQ(url, contents()->GetLastCommittedURL());
514 EXPECT_EQ(url2, contents()->GetVisibleURL());
515 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
516 EXPECT_TRUE(pending_rfh->GetLastCommittedURL().is_empty());
517 int pending_rvh_delete_count = 0;
518 pending_rfh->GetRenderViewHost()->set_delete_counter(
519 &pending_rvh_delete_count);
520
521 // DidNavigate from the pending page.
522 new_site_navigation->Commit();
523 SiteInstance* instance2 = contents()->GetSiteInstance();
524
525 // Keep the number of active frames in pending_rfh's SiteInstance
526 // non-zero so that orig_rfh doesn't get deleted when it gets
527 // swapped out.
528 pending_rfh->GetSiteInstance()->IncrementActiveFrameCount();
529
530 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
531 EXPECT_EQ(pending_rfh, main_test_rfh());
532 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
533 EXPECT_EQ(url2, contents()->GetVisibleURL());
534 EXPECT_NE(instance1, instance2);
535 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
536 // We keep a proxy for the original RFH's SiteInstance.
537 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost(
538 instance1));
539 EXPECT_EQ(orig_rvh_delete_count, 0);
540
541 // Going back should switch SiteInstances again. The first SiteInstance is
542 // stored in the NavigationEntry, so it should be the same as at the start.
543 // We should use the same RFH as before, swapping it back in.
544 auto back_navigation =
545 NavigationSimulator::CreateHistoryNavigation(-1, contents());
546 back_navigation->ReadyToCommit();
547 TestRenderFrameHost* goback_rfh = contents()->GetPendingMainFrame();
548 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
549
550 // DidNavigate from the back action.
551 back_navigation->Commit();
552 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
553 EXPECT_EQ(goback_rfh, main_test_rfh());
554 EXPECT_EQ(url, contents()->GetLastCommittedURL());
555 EXPECT_EQ(url, contents()->GetVisibleURL());
556 EXPECT_EQ(instance1, contents()->GetSiteInstance());
557 // There should be a proxy for the pending RFH SiteInstance.
558 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost(
559 instance2));
560 EXPECT_EQ(pending_rvh_delete_count, 0);
561
562 // Close contents and ensure RVHs are deleted.
563 DeleteContents();
564 EXPECT_EQ(orig_rvh_delete_count, 1);
565 EXPECT_EQ(pending_rvh_delete_count, 1);
566 }
567
568 // Test that navigating across a site boundary after a crash creates a new
569 // RFH without requiring a cross-site transition (i.e., PENDING state).
TEST_F(WebContentsImplTest,CrossSiteBoundariesAfterCrash)570 TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
571 // Ensure that the cross-site transition will also be cross-process on
572 // Android.
573 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
574
575 TestRenderFrameHost* orig_rfh = main_test_rfh();
576
577 int orig_rvh_delete_count = 0;
578 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
579 SiteInstance* instance1 = contents()->GetSiteInstance();
580
581 // Navigate to URL. First URL should use first RenderViewHost.
582 const GURL url("http://www.google.com");
583 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
584 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
585 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
586
587 // Simulate a renderer crash.
588 EXPECT_TRUE(orig_rfh->IsRenderFrameLive());
589 orig_rfh->GetProcess()->SimulateCrash();
590 EXPECT_FALSE(orig_rfh->IsRenderFrameLive());
591
592 // Start navigating to a new site. We should not go into PENDING.
593 const GURL url2("http://www.yahoo.com");
594 auto navigation_to_url2 =
595 NavigationSimulator::CreateBrowserInitiated(url2, contents());
596 navigation_to_url2->ReadyToCommit();
597
598 TestRenderFrameHost* new_rfh = main_test_rfh();
599 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
600 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
601 EXPECT_NE(orig_rfh, new_rfh);
602 EXPECT_EQ(orig_rvh_delete_count, 1);
603
604 navigation_to_url2->Commit();
605 SiteInstance* instance2 = contents()->GetSiteInstance();
606
607 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
608 EXPECT_EQ(new_rfh, main_rfh());
609 EXPECT_NE(instance1, instance2);
610 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
611
612 // Close contents and ensure RVHs are deleted.
613 DeleteContents();
614 EXPECT_EQ(orig_rvh_delete_count, 1);
615 }
616
617 // Test that opening a new contents in the same SiteInstance and then navigating
618 // both contentses to a new site will place both contentses in a single
619 // SiteInstance.
TEST_F(WebContentsImplTest,NavigateTwoTabsCrossSite)620 TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
621 SiteInstance* instance1 = contents()->GetSiteInstance();
622
623 // Navigate to URL. First URL should use first RenderViewHost.
624 const GURL url("http://www.google.com");
625 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
626
627 // Open a new contents with the same SiteInstance, navigated to the same site.
628 std::unique_ptr<TestWebContents> contents2(
629 TestWebContents::Create(browser_context(), instance1));
630 NavigationSimulator::NavigateAndCommitFromBrowser(contents2.get(), url);
631 EXPECT_EQ(instance1, contents2->GetSiteInstance());
632
633 // Navigate first contents to a new site.
634 const GURL url2a = isolated_cross_site_url();
635 auto navigation1 =
636 NavigationSimulator::CreateBrowserInitiated(url2a, contents());
637 navigation1->SetTransition(ui::PAGE_TRANSITION_LINK);
638 navigation1->ReadyToCommit();
639 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
640 navigation1->Commit();
641 SiteInstance* instance2a = contents()->GetSiteInstance();
642 EXPECT_NE(instance1, instance2a);
643
644 // Navigate second contents to the same site as the first tab.
645 const GURL url2b = isolated_cross_site_url().Resolve("/foo");
646 auto navigation2 =
647 NavigationSimulator::CreateBrowserInitiated(url2b, contents2.get());
648 navigation2->SetTransition(ui::PAGE_TRANSITION_LINK);
649 navigation2->ReadyToCommit();
650 EXPECT_TRUE(contents2->CrossProcessNavigationPending());
651
652 // NOTE(creis): We used to be in danger of showing a crash page here if the
653 // second contents hadn't navigated somewhere first (bug 1145430). That case
654 // is now covered by the CrossSiteBoundariesAfterCrash test.
655 navigation2->Commit();
656 SiteInstance* instance2b = contents2->GetSiteInstance();
657 EXPECT_NE(instance1, instance2b);
658
659 // Both contentses should now be in the same SiteInstance.
660 EXPECT_EQ(instance2a, instance2b);
661 }
662
663 // The embedder can request sites for certain urls not be be assigned to the
664 // SiteInstance through ShouldAssignSiteForURL() in content browser client,
665 // allowing to reuse the renderer backing certain chrome urls for subsequent
666 // navigation. The test verifies that the override is honored.
TEST_F(WebContentsImplTest,NavigateFromSitelessUrl)667 TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) {
668 WebContentsImplTestBrowserClient browser_client;
669 SetBrowserClientForTesting(&browser_client);
670
671 TestRenderFrameHost* orig_rfh = main_test_rfh();
672 int orig_rvh_delete_count = 0;
673 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
674 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
675
676 // Navigate to an URL that will not assign a new SiteInstance.
677 const GURL native_url("non-site-url://stuffandthings");
678 browser_client.set_assign_site_for_url(false, native_url);
679 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), native_url);
680
681 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
682 EXPECT_EQ(orig_rfh, main_test_rfh());
683 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
684 EXPECT_EQ(native_url, contents()->GetVisibleURL());
685 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
686 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
687 EXPECT_FALSE(orig_instance->HasSite());
688
689 // Navigate to new site (should keep same site instance).
690 const GURL url("http://www.google.com");
691 browser_client.set_assign_site_for_url(true, url);
692 auto navigation1 =
693 NavigationSimulator::CreateBrowserInitiated(url, contents());
694 navigation1->ReadyToCommit();
695 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
696 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
697 EXPECT_EQ(url, contents()->GetVisibleURL());
698 EXPECT_FALSE(contents()->GetPendingMainFrame());
699 navigation1->Commit();
700
701 // The first entry's SiteInstance should be reset to a new, related one. This
702 // prevents wrongly detecting a SiteInstance mismatch when returning to it
703 // later.
704 SiteInstanceImpl* prev_entry_instance = contents()
705 ->GetController()
706 .GetEntryAtIndex(0)
707 ->root_node()
708 ->frame_entry->site_instance();
709 EXPECT_NE(prev_entry_instance, orig_instance);
710 EXPECT_TRUE(orig_instance->IsRelatedSiteInstance(prev_entry_instance));
711 EXPECT_FALSE(prev_entry_instance->HasSite());
712
713 SiteInstanceImpl* curr_entry_instance = contents()
714 ->GetController()
715 .GetEntryAtIndex(1)
716 ->root_node()
717 ->frame_entry->site_instance();
718 EXPECT_EQ(curr_entry_instance, orig_instance);
719 // Keep the number of active frames in orig_rfh's SiteInstance
720 // non-zero so that orig_rfh doesn't get deleted when it gets
721 // swapped out.
722 orig_rfh->GetSiteInstance()->IncrementActiveFrameCount();
723
724 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
725 if (AreDefaultSiteInstancesEnabled()) {
726 // Verify that the empty SiteInstance gets converted into a default
727 // SiteInstance because |url| does not require a dedicated process.
728 EXPECT_TRUE(contents()->GetSiteInstance()->IsDefaultSiteInstance());
729 } else {
730 EXPECT_TRUE(
731 contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com"));
732 }
733 EXPECT_EQ(url, contents()->GetLastCommittedURL());
734
735 // Navigate to another new site (should create a new site instance).
736 const GURL url2 = isolated_cross_site_url();
737 auto navigation2 =
738 NavigationSimulator::CreateBrowserInitiated(url2, contents());
739 navigation2->ReadyToCommit();
740 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
741 EXPECT_EQ(url, contents()->GetLastCommittedURL());
742 EXPECT_EQ(url2, contents()->GetVisibleURL());
743 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
744 int pending_rvh_delete_count = 0;
745 pending_rfh->GetRenderViewHost()->set_delete_counter(
746 &pending_rvh_delete_count);
747
748 // DidNavigate from the pending page.
749 navigation2->Commit();
750 SiteInstance* new_instance = contents()->GetSiteInstance();
751
752 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
753 EXPECT_EQ(pending_rfh, main_test_rfh());
754 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
755 EXPECT_EQ(url2, contents()->GetVisibleURL());
756 EXPECT_NE(new_instance, orig_instance);
757 EXPECT_FALSE(contents()->GetPendingMainFrame());
758 EXPECT_EQ(orig_rvh_delete_count, 0);
759
760 // Close contents and ensure RVHs are deleted.
761 DeleteContents();
762 EXPECT_EQ(orig_rvh_delete_count, 1);
763 EXPECT_EQ(pending_rvh_delete_count, 1);
764 }
765
766 // Regression test for http://crbug.com/386542 - variation of
767 // NavigateFromSitelessUrl in which the original navigation is a session
768 // restore.
TEST_F(WebContentsImplTest,NavigateFromRestoredSitelessUrl)769 TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) {
770 WebContentsImplTestBrowserClient browser_client;
771 SetBrowserClientForTesting(&browser_client);
772 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
773 TestRenderFrameHost* orig_rfh = main_test_rfh();
774
775 // Restore a navigation entry for URL that should not assign site to the
776 // SiteInstance.
777 const GURL native_url("non-site-url://stuffandthings");
778 browser_client.set_assign_site_for_url(false, native_url);
779 std::vector<std::unique_ptr<NavigationEntry>> entries;
780 std::unique_ptr<NavigationEntry> new_entry =
781 NavigationController::CreateNavigationEntry(
782 native_url, Referrer(), base::nullopt, ui::PAGE_TRANSITION_LINK,
783 false, std::string(), browser_context(),
784 nullptr /* blob_url_loader_factory */);
785 entries.push_back(std::move(new_entry));
786 controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries);
787 ASSERT_EQ(0u, entries.size());
788 ASSERT_EQ(1, controller().GetEntryCount());
789
790 EXPECT_TRUE(controller().NeedsReload());
791 controller().LoadIfNecessary();
792 NavigationEntry* entry = controller().GetPendingEntry();
793 orig_rfh->PrepareForCommit();
794 contents()->TestDidNavigate(orig_rfh, entry->GetUniqueID(), false,
795 native_url, ui::PAGE_TRANSITION_RELOAD);
796 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
797 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
798 EXPECT_FALSE(orig_instance->HasSite());
799
800 // Navigate to a regular site and verify that the SiteInstance was kept.
801 const GURL url("http://www.google.com");
802 browser_client.set_assign_site_for_url(true, url);
803 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
804 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
805
806 // Cleanup.
807 DeleteContents();
808 }
809
810 // Complement for NavigateFromRestoredSitelessUrl, verifying that when a regular
811 // tab is restored, the SiteInstance will change upon navigation.
TEST_F(WebContentsImplTest,NavigateFromRestoredRegularUrl)812 TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) {
813 WebContentsImplTestBrowserClient browser_client;
814 SetBrowserClientForTesting(&browser_client);
815 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
816 TestRenderFrameHost* orig_rfh = main_test_rfh();
817
818 // Restore a navigation entry for a regular URL ensuring that the embedder
819 // ShouldAssignSiteForUrl override is disabled (i.e. returns true).
820 const GURL regular_url("http://www.yahoo.com");
821 browser_client.set_assign_site_for_url(true, regular_url);
822 std::vector<std::unique_ptr<NavigationEntry>> entries;
823 std::unique_ptr<NavigationEntry> new_entry =
824 NavigationController::CreateNavigationEntry(
825 regular_url, Referrer(), base::nullopt, ui::PAGE_TRANSITION_LINK,
826 false, std::string(), browser_context(),
827 nullptr /* blob_url_loader_factory */);
828 entries.push_back(std::move(new_entry));
829 controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries);
830 ASSERT_EQ(0u, entries.size());
831
832 ASSERT_EQ(1, controller().GetEntryCount());
833 EXPECT_TRUE(controller().NeedsReload());
834 controller().LoadIfNecessary();
835 NavigationEntry* entry = controller().GetPendingEntry();
836 orig_rfh->PrepareForCommit();
837 contents()->TestDidNavigate(orig_rfh, entry->GetUniqueID(), false,
838 regular_url, ui::PAGE_TRANSITION_RELOAD);
839 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
840 EXPECT_TRUE(orig_instance->HasSite());
841 EXPECT_EQ(AreDefaultSiteInstancesEnabled(),
842 orig_instance->IsDefaultSiteInstance());
843
844 // Navigate to another site and verify that a new SiteInstance was created.
845 const GURL url("http://www.google.com");
846 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
847 if (AreDefaultSiteInstancesEnabled()) {
848 // Verify this remains the default SiteInstance since |url| does
849 // not require a dedicated process.
850 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
851
852 // Navigate to a URL that does require a dedicated process and verify that
853 // the SiteInstance changes.
854 NavigationSimulator::NavigateAndCommitFromBrowser(
855 contents(), isolated_cross_site_url());
856 EXPECT_NE(orig_instance, contents()->GetSiteInstance());
857 } else {
858 EXPECT_NE(orig_instance, contents()->GetSiteInstance());
859 }
860
861 // Cleanup.
862 DeleteContents();
863 }
864
865 // Test that we can find an opener RVH even if it's pending.
866 // http://crbug.com/176252.
TEST_F(WebContentsImplTest,FindOpenerRVHWhenPending)867 TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
868
869 // Navigate to a URL.
870 const GURL url("http://www.google.com");
871 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
872
873 // Start to navigate first tab to a new site, so that it has a pending RVH.
874 const GURL url2("http://www.yahoo.com");
875 auto navigation =
876 NavigationSimulator::CreateBrowserInitiated(url2, contents());
877 navigation->ReadyToCommit();
878 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
879 SiteInstance* instance = pending_rfh->GetSiteInstance();
880
881 // While it is still pending, simulate opening a new tab with the first tab
882 // as its opener. This will call CreateOpenerProxies on the opener to ensure
883 // that an RVH exists.
884 std::unique_ptr<TestWebContents> popup(
885 TestWebContents::Create(browser_context(), instance));
886 popup->SetOpener(contents());
887 contents()->GetRenderManager()->CreateOpenerProxies(instance, nullptr);
888
889 // If swapped out is forbidden, a new proxy should be created for the opener
890 // in |instance|, and we should ensure that its routing ID is returned here.
891 // Otherwise, we should find the pending RFH and not create a new proxy.
892 auto opener_frame_token =
893 popup->GetRenderManager()->GetOpenerFrameToken(instance);
894 RenderFrameProxyHost* proxy =
895 contents()->GetRenderManager()->GetRenderFrameProxyHost(instance);
896 EXPECT_TRUE(proxy);
897 EXPECT_EQ(*opener_frame_token, proxy->GetFrameToken());
898
899 // Ensure that committing the navigation removes the proxy.
900 navigation->Commit();
901 EXPECT_FALSE(
902 contents()->GetRenderManager()->GetRenderFrameProxyHost(instance));
903 }
904
905 // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
906 // to determine whether a navigation is cross-site.
TEST_F(WebContentsImplTest,CrossSiteComparesAgainstCurrentPage)907 TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
908 // The assumptions this test makes aren't valid with --site-per-process. For
909 // example, a cross-site URL won't ever commit in the old RFH. The test also
910 // assumes that default SiteInstances are enabled, and that aggressive
911 // BrowsingInstance swapping (even on renderer-initiated navigations) is
912 // disabled.
913 if (AreAllSitesIsolatedForTesting() || !AreDefaultSiteInstancesEnabled() ||
914 CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
915 return;
916 }
917
918 TestRenderFrameHost* orig_rfh = main_test_rfh();
919 SiteInstanceImpl* instance1 = contents()->GetSiteInstance();
920
921 const GURL url("http://www.google.com");
922
923 // Navigate to URL.
924 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
925
926 // Open a related contents to a second site.
927 std::unique_ptr<TestWebContents> contents2(
928 TestWebContents::Create(browser_context(), instance1));
929 const GURL url2("http://www.yahoo.com");
930 auto navigation =
931 NavigationSimulator::CreateBrowserInitiated(url2, contents2.get());
932 navigation->ReadyToCommit();
933
934 // The first RVH in contents2 isn't live yet, so we shortcut the cross site
935 // pending.
936 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
937 navigation->Commit();
938 SiteInstance* instance2 = contents2->GetSiteInstance();
939 // With default SiteInstances, navigations in both tabs should
940 // share the same default SiteInstance, since neither requires a dedicated
941 // process.
942 EXPECT_EQ(instance1, instance2);
943 EXPECT_TRUE(instance1->IsDefaultSiteInstance());
944 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
945
946 // Simulate a link click in first contents to second site. This doesn't
947 // switch SiteInstances and stays in the default SiteInstance.
948 NavigationSimulator::NavigateAndCommitFromDocument(url2, orig_rfh);
949 SiteInstance* instance3 = contents()->GetSiteInstance();
950 EXPECT_EQ(instance1, instance3);
951 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
952
953 // Navigate same-site. This also stays in the default SiteInstance.
954 const GURL url3("http://mail.yahoo.com");
955 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url3);
956 SiteInstance* instance4 = contents()->GetSiteInstance();
957 EXPECT_EQ(instance1, instance4);
958 }
959
960 // Test that the onbeforeunload and onunload handlers run when navigating
961 // across site boundaries.
TEST_F(WebContentsImplTest,CrossSiteUnloadHandlers)962 TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
963 TestRenderFrameHost* orig_rfh = main_test_rfh();
964 SiteInstance* instance1 = contents()->GetSiteInstance();
965
966 // Navigate to URL. First URL should use first RenderViewHost.
967 const GURL url("http://www.google.com");
968 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
969 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
970 EXPECT_EQ(orig_rfh, main_test_rfh());
971
972 // Navigate to new site, but simulate an onbeforeunload denial.
973 const GURL url2("http://www.yahoo.com");
974 controller().LoadURL(
975 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
976 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
977 orig_rfh->SimulateBeforeUnloadCompleted(false);
978 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
979 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
980 EXPECT_EQ(orig_rfh, main_test_rfh());
981
982 // Navigate again, but simulate an onbeforeunload approval.
983 controller().LoadURL(
984 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
985 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
986 auto navigation = NavigationSimulator::CreateFromPending(contents());
987 navigation->ReadyToCommit();
988 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
989 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
990 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
991
992 // DidNavigate from the pending page.
993 navigation->Commit();
994 SiteInstance* instance2 = contents()->GetSiteInstance();
995 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
996 EXPECT_EQ(pending_rfh, main_test_rfh());
997 EXPECT_NE(instance1, instance2);
998 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
999 }
1000
1001 // Test that during a slow cross-site navigation, the original renderer can
1002 // navigate to a different URL and have it displayed, canceling the slow
1003 // navigation.
TEST_F(WebContentsImplTest,CrossSiteNavigationPreempted)1004 TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
1005 TestRenderFrameHost* orig_rfh = main_test_rfh();
1006 SiteInstance* instance1 = contents()->GetSiteInstance();
1007
1008 // Navigate to URL. First URL should use first RenderFrameHost.
1009 const GURL url("http://www.google.com");
1010 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1011 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1012 EXPECT_EQ(orig_rfh, main_test_rfh());
1013
1014 // Navigate to new site.
1015 const GURL url2("http://www.yahoo.com");
1016 controller().LoadURL(
1017 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1018 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1019 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1020
1021 // Suppose the original renderer navigates before the new one is ready.
1022 const GURL url3("http://www.google.com/foo");
1023 NavigationSimulator::NavigateAndCommitFromDocument(url3, orig_rfh);
1024
1025 // Verify that the pending navigation is cancelled.
1026 SiteInstance* instance2 = contents()->GetSiteInstance();
1027 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
1028 // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
1029 // is enabled, the RFH should change.
1030 EXPECT_NE(orig_rfh, main_test_rfh());
1031 } else {
1032 EXPECT_EQ(orig_rfh, main_test_rfh());
1033 }
1034 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
1035 // When ProactivelySwapBrowsingInstance is enabled on same-site navigations,
1036 // the SiteInstance will change.
1037 EXPECT_NE(instance1, instance2);
1038 } else {
1039 EXPECT_EQ(instance1, instance2);
1040 }
1041 EXPECT_FALSE(main_test_rfh()->is_waiting_for_beforeunload_completion());
1042 EXPECT_EQ(main_test_rfh()->GetLastCommittedURL(), url3);
1043 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1044 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
1045 }
1046
1047 // Tests that if we go back twice (same-site then cross-site), and the same-site
1048 // RFH commits first, the cross-site RFH's navigation is canceled. If the
1049 // same-site navigation is a cross-RFH navigation, however, the same-site
1050 // navigation will get canceled instead and we are left with the newer
1051 // cross-site navigation.
1052 // TODO(avi,creis): Consider changing this behavior to better match the user's
1053 // intent.
TEST_F(WebContentsImplTest,CrossSiteNavigationBackPreempted)1054 TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
1055 // Start with a web ui page, which gets a new RVH with WebUI bindings.
1056 GURL url1(std::string(kChromeUIScheme) + "://" +
1057 std::string(kChromeUIGpuHost));
1058 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1059 TestRenderFrameHost* webui_rfh = main_test_rfh();
1060 NavigationEntry* entry1 = controller().GetLastCommittedEntry();
1061 SiteInstance* instance1 = contents()->GetSiteInstance();
1062
1063 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1064 EXPECT_EQ(url1, entry1->GetURL());
1065 EXPECT_EQ(instance1,
1066 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1067 EXPECT_TRUE(webui_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1068
1069 // Navigate to new site.
1070 const GURL url2("http://www.google.com");
1071 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1072 TestRenderFrameHost* google_rfh = main_test_rfh();
1073 NavigationEntry* entry2 = controller().GetLastCommittedEntry();
1074 SiteInstance* instance2 = contents()->GetSiteInstance();
1075
1076 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1077 EXPECT_NE(instance1, instance2);
1078 EXPECT_FALSE(contents()->GetPendingMainFrame());
1079 EXPECT_EQ(url2, entry2->GetURL());
1080 EXPECT_EQ(instance2,
1081 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1082 EXPECT_FALSE(google_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1083
1084 // Navigate to third page on same site.
1085 const GURL url3("http://news.google.com");
1086 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url3);
1087 NavigationEntry* entry3 = controller().GetLastCommittedEntry();
1088 SiteInstance* instance3 = contents()->GetSiteInstance();
1089
1090 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1091 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
1092 // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
1093 // is enabled, the RFH should change.
1094 EXPECT_NE(google_rfh, main_test_rfh());
1095 } else {
1096 EXPECT_EQ(google_rfh, main_test_rfh());
1097 }
1098 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
1099 // When ProactivelySwapBrowsingInstance is enabled on same-site navigations,
1100 // the SiteInstance will change.
1101 EXPECT_NE(instance2, instance3);
1102 } else {
1103 EXPECT_EQ(instance2, instance3);
1104 }
1105 EXPECT_FALSE(contents()->GetPendingMainFrame());
1106 EXPECT_EQ(url3, entry3->GetURL());
1107 EXPECT_EQ(instance3,
1108 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1109
1110 // Go back within the site.
1111 auto back_navigation1 =
1112 NavigationSimulatorImpl::CreateHistoryNavigation(-1, contents());
1113 back_navigation1->Start();
1114
1115 auto* first_pending_rfh = contents()->GetPendingMainFrame();
1116 GlobalFrameRoutingId first_pending_rfh_id;
1117 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
1118 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1119 EXPECT_TRUE(first_pending_rfh);
1120 first_pending_rfh_id = first_pending_rfh->GetGlobalFrameRoutingId();
1121 } else {
1122 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1123 EXPECT_FALSE(first_pending_rfh);
1124 }
1125 EXPECT_EQ(entry2, controller().GetPendingEntry());
1126
1127 // Before that commits, go back again.
1128 back_navigation1->ReadyToCommit();
1129 auto back_navigation2 =
1130 NavigationSimulatorImpl::CreateHistoryNavigation(-1, contents());
1131 back_navigation2->Start();
1132 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1133 EXPECT_TRUE(contents()->GetPendingMainFrame());
1134 EXPECT_EQ(entry1, controller().GetPendingEntry());
1135 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
1136 // When ProactivelySwapBrowsingInstance or RenderDocument is enabled on
1137 // same-site main frame navigation, the first back navigation will create a
1138 // speculative RFH even though it's a same-site navigation, and the
1139 // speculative RFH will be overwritten by the second back-navigation that
1140 // will also create a speculative RFH.
1141 EXPECT_NE(first_pending_rfh_id,
1142 contents()->GetPendingMainFrame()->GetGlobalFrameRoutingId());
1143 // Calling Commit() on the first back navigation below will cause a DCHECK
1144 // failure because we've already called DidFinishNavigaition on it, so we
1145 // will call it on the second back navigation instead.
1146 back_navigation2->Commit();
1147 } else {
1148 // DidNavigate from the first back. This aborts the second back's
1149 // speculative RFH.
1150 back_navigation1->Commit();
1151 }
1152
1153 // We have committed this navigation and forgot about the second back if
1154 // CanSameSiteMainFrameNavigationsChangeRenderFrameHosts() is false, or the
1155 // first back if it's true.
1156 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1157 EXPECT_FALSE(controller().GetPendingEntry());
1158 EXPECT_EQ(google_rfh, main_test_rfh());
1159 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
1160 // We committed the second back navigation and landed on the first page.
1161 EXPECT_EQ(url1, controller().GetLastCommittedEntry()->GetURL());
1162 } else {
1163 // We committed the second back navigation and landed on the second page.
1164 EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
1165 }
1166
1167 // We should not have corrupted the NTP entry.
1168 EXPECT_EQ(instance3,
1169 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1170 EXPECT_EQ(instance2,
1171 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1172 EXPECT_EQ(instance1,
1173 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1174 EXPECT_EQ(url1, entry1->GetURL());
1175 }
1176
1177 // Tests that if we go back twice (same-site then cross-site), and the cross-
1178 // site RFH commits first, we ignore the now-swapped-out RFH's commit.
TEST_F(WebContentsImplTest,CrossSiteNavigationBackOldNavigationIgnored)1179 TEST_F(WebContentsImplTest, CrossSiteNavigationBackOldNavigationIgnored) {
1180 // Start with a web ui page, which gets a new RFH with WebUI bindings.
1181 GURL url1(std::string(kChromeUIScheme) + "://" +
1182 std::string(kChromeUIGpuHost));
1183 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1184 TestRenderFrameHost* webui_rfh = main_test_rfh();
1185 NavigationEntry* entry1 = controller().GetLastCommittedEntry();
1186 SiteInstance* instance1 = contents()->GetSiteInstance();
1187
1188 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1189 EXPECT_EQ(url1, entry1->GetURL());
1190 EXPECT_EQ(instance1,
1191 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1192 EXPECT_TRUE(webui_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1193
1194 // Navigate to new site.
1195 const GURL url2("http://www.google.com");
1196 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1197 TestRenderFrameHost* google_rfh = main_test_rfh();
1198 NavigationEntry* entry2 = controller().GetLastCommittedEntry();
1199 SiteInstance* instance2 = contents()->GetSiteInstance();
1200
1201 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1202 EXPECT_NE(instance1, instance2);
1203 EXPECT_FALSE(contents()->GetPendingMainFrame());
1204 EXPECT_EQ(url2, entry2->GetURL());
1205 EXPECT_EQ(instance2,
1206 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1207 EXPECT_FALSE(google_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1208
1209 // Navigate to third page on same site.
1210 const GURL url3("http://google.com/foo");
1211 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url3);
1212 NavigationEntry* entry3 = controller().GetLastCommittedEntry();
1213 SiteInstance* instance3 = contents()->GetSiteInstance();
1214
1215 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1216 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
1217 // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
1218 // is enabled, the RFH should change.
1219 EXPECT_NE(google_rfh, main_test_rfh());
1220 google_rfh = main_test_rfh();
1221 } else {
1222 EXPECT_EQ(google_rfh, main_test_rfh());
1223 }
1224 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
1225 // When ProactivelySwapBrowsingInstance is enabled on same-site navigations,
1226 // the SiteInstance will change.
1227 EXPECT_NE(instance2, instance3);
1228 } else {
1229 EXPECT_EQ(instance2, instance3);
1230 }
1231 EXPECT_FALSE(contents()->GetPendingMainFrame());
1232 EXPECT_EQ(url3, entry3->GetURL());
1233 EXPECT_EQ(instance3,
1234 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1235
1236 // Go back within the site.
1237 auto back_navigation1 =
1238 NavigationSimulator::CreateHistoryNavigation(-1, contents());
1239 back_navigation1->ReadyToCommit();
1240 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
1241 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1242 } else {
1243 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1244 }
1245 EXPECT_EQ(entry2, controller().GetPendingEntry());
1246
1247 // Before that commits, go back again.
1248 auto back_navigation2 =
1249 NavigationSimulatorImpl::CreateHistoryNavigation(-1, contents());
1250 back_navigation2->set_drop_unload_ack(true);
1251 back_navigation2->ReadyToCommit();
1252 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1253 EXPECT_TRUE(contents()->GetPendingMainFrame());
1254 EXPECT_EQ(entry1, controller().GetPendingEntry());
1255 webui_rfh = contents()->GetPendingMainFrame();
1256
1257 // DidNavigate from the second back.
1258 // Note that the process in instance1 is gone at this point, but we will still
1259 // use instance1 and entry1 because IsSuitableForURL will return true when
1260 // there is no process and the site URL matches.
1261 back_navigation2->Commit();
1262
1263 // That should have landed us on the first entry.
1264 EXPECT_EQ(entry1, controller().GetLastCommittedEntry());
1265
1266 // When the second back commits, it should be ignored.
1267 contents()->TestDidNavigate(google_rfh, entry2->GetUniqueID(), false, url2,
1268 ui::PAGE_TRANSITION_TYPED);
1269 EXPECT_EQ(entry1, controller().GetLastCommittedEntry());
1270
1271 // The newly created process for url1 should be locked to chrome://gpu.
1272 RenderProcessHost* new_process = contents()->GetMainFrame()->GetProcess();
1273 auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
1274 EXPECT_TRUE(policy->CanAccessDataForOrigin(new_process->GetID(), url1));
1275 EXPECT_FALSE(policy->CanAccessDataForOrigin(new_process->GetID(), url2));
1276 }
1277
1278 // Test that during a slow cross-site navigation, a sub-frame navigation in the
1279 // original renderer will not cancel the slow navigation (bug 42029).
TEST_F(WebContentsImplTest,CrossSiteNavigationNotPreemptedByFrame)1280 TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
1281 TestRenderFrameHost* orig_rfh = main_test_rfh();
1282
1283 // Navigate to URL. First URL should use the original RenderFrameHost.
1284 const GURL url("http://www.google.com");
1285 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1286 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1287 EXPECT_EQ(orig_rfh, main_test_rfh());
1288
1289 // Start navigating to new site.
1290 const GURL url2("http://www.yahoo.com");
1291 controller().LoadURL(
1292 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1293
1294 // Simulate a sub-frame navigation arriving and ensure the RVH is still
1295 // waiting for a before unload response.
1296 TestRenderFrameHost* child_rfh = orig_rfh->AppendChild("subframe");
1297 child_rfh->SendNavigateWithTransition(0, false,
1298 GURL("http://google.com/frame"),
1299 ui::PAGE_TRANSITION_AUTO_SUBFRAME);
1300 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1301
1302 // Now simulate the onbeforeunload approval and verify the navigation is
1303 // not canceled.
1304 orig_rfh->PrepareForCommit();
1305 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
1306 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1307 }
1308
1309 // Test that a cross-site navigation is not preempted if the previous
1310 // renderer sends a FrameNavigate message just before being told to stop.
1311 // We should only preempt the cross-site navigation if the previous renderer
1312 // has started a new navigation. See http://crbug.com/79176.
TEST_F(WebContentsImplTest,CrossSiteNotPreemptedDuringBeforeUnload)1313 TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
1314 const GURL kUrl("http://foo");
1315 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kUrl);
1316
1317 // First, make a non-user initiated same-site navigation.
1318 const GURL kSameSiteUrl("http://foo/1");
1319 TestRenderFrameHost* orig_rfh = main_test_rfh();
1320 // When ProactivelySwapBrowsingInstance or RenderDocument is enabled on
1321 // same-site main frame navigations, the same-site navigation below will
1322 // create a speculative RFH that will be overwritten when the cross-site
1323 // navigation starts, finishing the same-site navigation, so the scenario in
1324 // this test cannot be tested. We should disable same-site proactive
1325 // BrowsingInstance for |orig_rfh| before continuing.
1326 // Note: this will not disable RenderDocument.
1327 // TODO(crbug.com/936696): Skip this test when main-frame RenderDocument is
1328 // enabled.
1329 DisableProactiveBrowsingInstanceSwapFor(orig_rfh);
1330 auto same_site_navigation = NavigationSimulator::CreateRendererInitiated(
1331 kSameSiteUrl, main_test_rfh());
1332 same_site_navigation->SetHasUserGesture(false);
1333 same_site_navigation->ReadyToCommit();
1334 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1335
1336 // Navigate to a new site, with the beforeunload request in flight.
1337 const GURL kCrossSiteUrl("http://www.yahoo.com");
1338 auto cross_site_navigation = NavigationSimulatorImpl::CreateBrowserInitiated(
1339 kCrossSiteUrl, contents());
1340 cross_site_navigation->set_block_invoking_before_unload_completed_callback(
1341 true);
1342 cross_site_navigation->Start();
1343 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
1344 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1345 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1346 EXPECT_NE(orig_rfh, pending_rfh);
1347
1348 // Suppose the first navigation tries to commit now, with a
1349 // blink::mojom::LocalFrame::StopLoading() in flight. This should not cancel
1350 // the pending navigation, but it should act as if the beforeunload completion
1351 // callback had been invoked.
1352 same_site_navigation->Commit();
1353 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1354 EXPECT_EQ(orig_rfh, main_test_rfh());
1355 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
1356 // It should commit.
1357 ASSERT_EQ(2, controller().GetEntryCount());
1358 EXPECT_EQ(kSameSiteUrl, controller().GetLastCommittedEntry()->GetURL());
1359
1360 // The pending navigation should be able to commit successfully.
1361 cross_site_navigation->Commit();
1362 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1363 EXPECT_EQ(pending_rfh, main_test_rfh());
1364 EXPECT_EQ(3, controller().GetEntryCount());
1365 }
1366
1367 // Test that NavigationEntries have the correct page state after going
1368 // forward and back. Prevents regression for bug 1116137.
TEST_F(WebContentsImplTest,NavigationEntryContentState)1369 TEST_F(WebContentsImplTest, NavigationEntryContentState) {
1370
1371 // Navigate to URL. There should be no committed entry yet.
1372 const GURL url("http://www.google.com");
1373 auto navigation =
1374 NavigationSimulator::CreateBrowserInitiated(url, contents());
1375 navigation->ReadyToCommit();
1376 NavigationEntry* entry = controller().GetLastCommittedEntry();
1377 EXPECT_EQ(nullptr, entry);
1378
1379 // Committed entry should have page state.
1380 navigation->Commit();
1381 entry = controller().GetLastCommittedEntry();
1382 EXPECT_TRUE(entry->GetPageState().IsValid());
1383
1384 // Navigate to same site.
1385 const GURL url2("http://images.google.com");
1386 auto navigation2 =
1387 NavigationSimulator::CreateBrowserInitiated(url2, contents());
1388 navigation2->ReadyToCommit();
1389 entry = controller().GetLastCommittedEntry();
1390 EXPECT_TRUE(entry->GetPageState().IsValid());
1391
1392 // Committed entry should have page state.
1393 navigation2->Commit();
1394 entry = controller().GetLastCommittedEntry();
1395 EXPECT_TRUE(entry->GetPageState().IsValid());
1396
1397 // Now go back. Committed entry should still have page state.
1398 NavigationSimulator::GoBack(contents());
1399 entry = controller().GetLastCommittedEntry();
1400 EXPECT_TRUE(entry->GetPageState().IsValid());
1401 }
1402
1403 // Test that NavigationEntries have the correct page state and SiteInstance
1404 // state after opening a new window to about:blank. Prevents regression for
1405 // bugs b/1116137 and http://crbug.com/111975.
TEST_F(WebContentsImplTest,NavigationEntryContentStateNewWindow)1406 TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
1407 TestRenderFrameHost* orig_rfh = main_test_rfh();
1408
1409 // Navigate to about:blank.
1410 const GURL url(url::kAboutBlankURL);
1411 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1412
1413 // Should have a page state here.
1414 NavigationEntry* entry = controller().GetLastCommittedEntry();
1415 EXPECT_TRUE(entry->GetPageState().IsValid());
1416
1417 // The SiteInstance should be available for other navigations to use.
1418 NavigationEntryImpl* entry_impl =
1419 NavigationEntryImpl::FromNavigationEntry(entry);
1420 EXPECT_FALSE(entry_impl->site_instance()->HasSite());
1421 int32_t site_instance_id = entry_impl->site_instance()->GetId();
1422
1423 // Navigating to a normal page should not cause a process swap.
1424 const GURL new_url("http://www.google.com");
1425 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), new_url);
1426
1427 EXPECT_EQ(orig_rfh, main_test_rfh());
1428 NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry(
1429 controller().GetLastCommittedEntry());
1430 EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId());
1431 EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
1432 }
1433
1434 namespace {
1435
ExpectTrue(bool value)1436 void ExpectTrue(bool value) {
1437 DCHECK(value);
1438 }
1439
ExpectFalse(bool value)1440 void ExpectFalse(bool value) {
1441 DCHECK(!value);
1442 }
1443
1444 } // namespace
1445
1446 // Tests that fullscreen is exited throughout the object hierarchy when
1447 // navigating to a new page.
TEST_F(WebContentsImplTest,NavigationExitsFullscreen)1448 TEST_F(WebContentsImplTest, NavigationExitsFullscreen) {
1449 FakeFullscreenDelegate fake_delegate;
1450 contents()->SetDelegate(&fake_delegate);
1451 TestRenderFrameHost* orig_rfh = main_test_rfh();
1452
1453 // Navigate to a site.
1454 const GURL url("http://www.google.com");
1455 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1456 EXPECT_EQ(orig_rfh, main_test_rfh());
1457
1458 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1459 EXPECT_FALSE(contents()->IsFullscreen());
1460 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1461 main_test_rfh()->frame_tree_node()->UpdateUserActivationState(
1462 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1463 blink::mojom::UserActivationNotificationType::kTest);
1464 orig_rfh->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1465 base::BindOnce(&ExpectTrue));
1466 EXPECT_TRUE(contents()->IsFullscreen());
1467 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1468
1469 // Navigate to a new site.
1470 const GURL url2("http://www.yahoo.com");
1471 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1472
1473 // Confirm fullscreen has exited.
1474 EXPECT_FALSE(contents()->IsFullscreen());
1475 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1476
1477 contents()->SetDelegate(nullptr);
1478 }
1479
1480 // Tests that fullscreen is exited throughout the object hierarchy when
1481 // instructing NavigationController to GoBack() or GoForward().
TEST_F(WebContentsImplTest,HistoryNavigationExitsFullscreen)1482 TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) {
1483 FakeFullscreenDelegate fake_delegate;
1484 contents()->SetDelegate(&fake_delegate);
1485 TestRenderFrameHost* orig_rfh = main_test_rfh();
1486
1487 // Navigate to a site.
1488 const GURL url("http://www.google.com");
1489 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1490 EXPECT_EQ(orig_rfh, main_test_rfh());
1491
1492 // Now, navigate to another page on the same site.
1493 const GURL url2("http://www.google.com/search?q=kittens");
1494 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1495 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
1496 // If ProactivelySwapBrowsingInstance is enabled on same-site navigations,
1497 // the same-site navigation above will use a new RFH.
1498 EXPECT_NE(orig_rfh, main_test_rfh());
1499 } else {
1500 EXPECT_EQ(orig_rfh, main_test_rfh());
1501 }
1502
1503 // Sanity-check: Confirm we're not starting out in fullscreen mode.
1504 EXPECT_FALSE(contents()->IsFullscreen());
1505 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1506
1507 for (int i = 0; i < 2; ++i) {
1508 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1509 main_test_rfh()->frame_tree_node()->UpdateUserActivationState(
1510 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1511 blink::mojom::UserActivationNotificationType::kTest);
1512 main_test_rfh()->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1513 base::BindOnce(&ExpectTrue));
1514 EXPECT_TRUE(contents()->IsFullscreen());
1515 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1516
1517 // Navigate backward (or forward).
1518 if (i == 0)
1519 NavigationSimulator::GoBack(contents());
1520 else
1521 NavigationSimulator::GoForward(contents());
1522
1523 // Confirm fullscreen has exited.
1524 EXPECT_FALSE(contents()->IsFullscreen());
1525 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1526 }
1527
1528 contents()->SetDelegate(nullptr);
1529 }
1530
1531 // Tests that fullscreen is exited throughout the object hierarchy on a renderer
1532 // crash.
TEST_F(WebContentsImplTest,CrashExitsFullscreen)1533 TEST_F(WebContentsImplTest, CrashExitsFullscreen) {
1534 FakeFullscreenDelegate fake_delegate;
1535 contents()->SetDelegate(&fake_delegate);
1536
1537 // Navigate to a site.
1538 const GURL url("http://www.google.com");
1539 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1540
1541 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1542 EXPECT_FALSE(contents()->IsFullscreen());
1543 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1544 main_test_rfh()->frame_tree_node()->UpdateUserActivationState(
1545 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1546 blink::mojom::UserActivationNotificationType::kTest);
1547 main_test_rfh()->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1548 base::BindOnce(&ExpectTrue));
1549 EXPECT_TRUE(contents()->IsFullscreen());
1550 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1551
1552 // Crash the renderer.
1553 main_test_rfh()->GetProcess()->SimulateCrash();
1554
1555 // Confirm fullscreen has exited.
1556 EXPECT_FALSE(contents()->IsFullscreen());
1557 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1558
1559 contents()->SetDelegate(nullptr);
1560 }
1561
TEST_F(WebContentsImplTest,FailEnterFullscreenWhenNoUserActivationNoOrientationChange)1562 TEST_F(WebContentsImplTest,
1563 FailEnterFullscreenWhenNoUserActivationNoOrientationChange) {
1564 FakeFullscreenDelegate fake_delegate;
1565 contents()->SetDelegate(&fake_delegate);
1566
1567 // Navigate to a site.
1568 const GURL url("http://www.google.com");
1569 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1570
1571 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1572 EXPECT_FALSE(contents()->IsFullscreen());
1573 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1574
1575 // When there is no user activation and no orientation change, entering
1576 // fullscreen will fail.
1577 main_test_rfh()->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1578 base::BindOnce(&ExpectFalse));
1579 EXPECT_FALSE(contents()->HasSeenRecentScreenOrientationChange());
1580 EXPECT_FALSE(
1581 main_test_rfh()->frame_tree_node()->HasTransientUserActivation());
1582 EXPECT_FALSE(contents()->IsFullscreen());
1583 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1584
1585 contents()->SetDelegate(nullptr);
1586 }
1587
1588 // Regression test for http://crbug.com/168611 - the URLs passed by the
1589 // DidFinishLoad and DidFailLoadWithError IPCs should get filtered.
TEST_F(WebContentsImplTest,FilterURLs)1590 TEST_F(WebContentsImplTest, FilterURLs) {
1591 TestWebContentsObserver observer(contents());
1592
1593 // A navigation to about:whatever should always look like a navigation to
1594 // about:blank
1595 GURL url_normalized(url::kAboutBlankURL);
1596 GURL url_from_ipc("about:whatever");
1597 GURL url_blocked(kBlockedURL);
1598
1599 // We navigate the test WebContents to about:blank, since NavigateAndCommit
1600 // will use the given URL to create the NavigationEntry as well, and that
1601 // entry should contain the filtered URL.
1602 contents()->NavigateAndCommit(url_normalized);
1603
1604 // Check that an IPC with about:whatever is correctly normalized.
1605 contents()->TestDidFinishLoad(url_from_ipc);
1606
1607 EXPECT_EQ(url_blocked, observer.last_url());
1608
1609 // Create and navigate another WebContents.
1610 std::unique_ptr<TestWebContents> other_contents(
1611 static_cast<TestWebContents*>(CreateTestWebContents().release()));
1612 TestWebContentsObserver other_observer(other_contents.get());
1613 other_contents->NavigateAndCommit(url_normalized);
1614
1615 // Check that an IPC with about:whatever is correctly normalized.
1616 other_contents->GetMainFrame()->DidFailLoadWithError(url_from_ipc, 1);
1617 EXPECT_EQ(url_blocked, other_observer.last_url());
1618 }
1619
1620 // Test that if a pending contents is deleted before it is shown, we don't
1621 // crash.
TEST_F(WebContentsImplTest,PendingContentsDestroyed)1622 TEST_F(WebContentsImplTest, PendingContentsDestroyed) {
1623 auto other_contents = base::WrapUnique(
1624 static_cast<TestWebContents*>(CreateTestWebContents().release()));
1625 content::TestWebContents* test_web_contents = other_contents.get();
1626 contents()->AddPendingContents(std::move(other_contents), GURL());
1627 RenderWidgetHost* widget =
1628 test_web_contents->GetMainFrame()->GetRenderWidgetHost();
1629 int process_id = widget->GetProcess()->GetID();
1630 int widget_id = widget->GetRoutingID();
1631
1632 // TODO(erikchen): Fix ownership semantics of WebContents. Nothing should be
1633 // able to delete it beside from the owner. https://crbug.com/832879.
1634 delete test_web_contents;
1635 EXPECT_FALSE(contents()->GetCreatedWindow(process_id, widget_id).has_value());
1636 }
1637
TEST_F(WebContentsImplTest,PendingContentsShown)1638 TEST_F(WebContentsImplTest, PendingContentsShown) {
1639 GURL url("http://example.com");
1640 auto other_contents = base::WrapUnique(
1641 static_cast<TestWebContents*>(CreateTestWebContents().release()));
1642 content::TestWebContents* test_web_contents = other_contents.get();
1643 contents()->AddPendingContents(std::move(other_contents), url);
1644
1645 RenderWidgetHost* widget =
1646 test_web_contents->GetMainFrame()->GetRenderWidgetHost();
1647 int process_id = widget->GetProcess()->GetID();
1648 int widget_id = widget->GetRoutingID();
1649
1650 // The first call to GetCreatedWindow pops it off the pending list.
1651 base::Optional<CreatedWindow> created_window =
1652 contents()->GetCreatedWindow(process_id, widget_id);
1653 EXPECT_TRUE(created_window.has_value());
1654 EXPECT_EQ(test_web_contents, created_window->contents.get());
1655 // Validate target_url.
1656 EXPECT_EQ(url, created_window->target_url);
1657
1658 // A second call should return nullopt, verifying that it's been forgotten.
1659 EXPECT_FALSE(contents()->GetCreatedWindow(process_id, widget_id).has_value());
1660 }
1661
TEST_F(WebContentsImplTest,CapturerOverridesPreferredSize)1662 TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) {
1663 const gfx::Size original_preferred_size(1024, 768);
1664 contents()->UpdatePreferredSize(original_preferred_size);
1665
1666 // With no capturers, expect the preferred size to be the one propagated into
1667 // WebContentsImpl via the RenderViewHostDelegate interface.
1668 EXPECT_FALSE(contents()->IsBeingCaptured());
1669 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
1670
1671 // Increment capturer count, but without specifying a capture size. Expect
1672 // a "not set" preferred size.
1673 contents()->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
1674 EXPECT_TRUE(contents()->IsBeingCaptured());
1675 EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize());
1676
1677 // Increment capturer count again, but with an overriding capture size.
1678 // Expect preferred size to now be overridden to the capture size.
1679 const gfx::Size capture_size(1280, 720);
1680 contents()->IncrementCapturerCount(capture_size, /* stay_hidden */ false);
1681 EXPECT_TRUE(contents()->IsBeingCaptured());
1682 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
1683
1684 // Increment capturer count a third time, but the expect that the preferred
1685 // size is still the first capture size.
1686 const gfx::Size another_capture_size(720, 480);
1687 contents()->IncrementCapturerCount(another_capture_size,
1688 /* stay_hidden */ false);
1689 EXPECT_TRUE(contents()->IsBeingCaptured());
1690 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
1691
1692 // Decrement capturer count twice, but expect the preferred size to still be
1693 // overridden.
1694 contents()->DecrementCapturerCount(/* stay_hidden */ false);
1695 contents()->DecrementCapturerCount(/* stay_hidden */ false);
1696 EXPECT_TRUE(contents()->IsBeingCaptured());
1697 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
1698
1699 // Decrement capturer count, and since the count has dropped to zero, the
1700 // original preferred size should be restored.
1701 contents()->DecrementCapturerCount(/* stay_hidden */ false);
1702 EXPECT_FALSE(contents()->IsBeingCaptured());
1703 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
1704 }
1705
TEST_F(WebContentsImplTest,UpdateWebContentsVisibility)1706 TEST_F(WebContentsImplTest, UpdateWebContentsVisibility) {
1707 base::test::ScopedFeatureList scoped_feature_list;
1708 scoped_feature_list.InitAndEnableFeature(features::kWebContentsOcclusion);
1709
1710 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
1711 main_test_rfh()->GetRenderViewHost()->GetWidget()->GetView());
1712 TestWebContentsObserver observer(contents());
1713
1714 EXPECT_FALSE(view->is_showing());
1715 EXPECT_FALSE(view->is_occluded());
1716
1717 // WebContents must be made visible once before it can be hidden.
1718 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
1719 EXPECT_FALSE(view->is_showing());
1720 EXPECT_FALSE(view->is_occluded());
1721 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
1722
1723 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
1724 EXPECT_TRUE(view->is_showing());
1725 EXPECT_FALSE(view->is_occluded());
1726 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
1727
1728 // Hiding/occluding/showing the WebContents should hide and show |view|.
1729 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
1730 EXPECT_FALSE(view->is_showing());
1731 EXPECT_FALSE(view->is_occluded());
1732 EXPECT_EQ(Visibility::HIDDEN, contents()->GetVisibility());
1733
1734 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
1735 EXPECT_TRUE(view->is_showing());
1736 EXPECT_FALSE(view->is_occluded());
1737 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
1738
1739 contents()->UpdateWebContentsVisibility(Visibility::OCCLUDED);
1740 EXPECT_TRUE(view->is_showing());
1741 EXPECT_TRUE(view->is_occluded());
1742 EXPECT_EQ(Visibility::OCCLUDED, contents()->GetVisibility());
1743
1744 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
1745 EXPECT_TRUE(view->is_showing());
1746 EXPECT_FALSE(view->is_occluded());
1747 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
1748
1749 contents()->UpdateWebContentsVisibility(Visibility::OCCLUDED);
1750 EXPECT_TRUE(view->is_showing());
1751 EXPECT_TRUE(view->is_occluded());
1752 EXPECT_EQ(Visibility::OCCLUDED, contents()->GetVisibility());
1753
1754 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
1755 EXPECT_FALSE(view->is_showing());
1756 EXPECT_EQ(Visibility::HIDDEN, contents()->GetVisibility());
1757 }
1758
1759 namespace {
1760
HideOrOccludeWithCapturerTest(WebContentsImpl * contents,Visibility hidden_or_occluded)1761 void HideOrOccludeWithCapturerTest(WebContentsImpl* contents,
1762 Visibility hidden_or_occluded) {
1763 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
1764 contents->GetRenderWidgetHostView());
1765
1766 EXPECT_FALSE(view->is_showing());
1767
1768 // WebContents must be made visible once before it can be hidden.
1769 contents->UpdateWebContentsVisibility(Visibility::VISIBLE);
1770 EXPECT_TRUE(view->is_showing());
1771 EXPECT_FALSE(view->is_occluded());
1772 EXPECT_EQ(Visibility::VISIBLE, contents->GetVisibility());
1773
1774 // Add a capturer when the contents is visible and then hide the contents.
1775 // |view| should remain visible.
1776 contents->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
1777 contents->UpdateWebContentsVisibility(hidden_or_occluded);
1778 EXPECT_TRUE(view->is_showing());
1779 EXPECT_FALSE(view->is_occluded());
1780 EXPECT_EQ(hidden_or_occluded, contents->GetVisibility());
1781
1782 // Remove the capturer when the contents is hidden/occluded. |view| should be
1783 // hidden/occluded.
1784 contents->DecrementCapturerCount(/* stay_hidden */ false);
1785 if (hidden_or_occluded == Visibility::HIDDEN) {
1786 EXPECT_FALSE(view->is_showing());
1787 } else {
1788 EXPECT_TRUE(view->is_showing());
1789 EXPECT_TRUE(view->is_occluded());
1790 }
1791
1792 // Add a capturer when the contents is hidden. |view| should be unoccluded.
1793 contents->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
1794 EXPECT_FALSE(view->is_occluded());
1795
1796 // Show the contents. The view should be visible.
1797 contents->UpdateWebContentsVisibility(Visibility::VISIBLE);
1798 EXPECT_TRUE(view->is_showing());
1799 EXPECT_FALSE(view->is_occluded());
1800 EXPECT_EQ(Visibility::VISIBLE, contents->GetVisibility());
1801
1802 // Remove the capturer when the contents is visible. The view should remain
1803 // visible.
1804 contents->DecrementCapturerCount(/* stay_hidden */ false);
1805 EXPECT_TRUE(view->is_showing());
1806 EXPECT_FALSE(view->is_occluded());
1807 }
1808
1809 } // namespace
1810
TEST_F(WebContentsImplTest,HideWithCapturer)1811 TEST_F(WebContentsImplTest, HideWithCapturer) {
1812 HideOrOccludeWithCapturerTest(contents(), Visibility::HIDDEN);
1813 }
1814
TEST_F(WebContentsImplTest,OccludeWithCapturer)1815 TEST_F(WebContentsImplTest, OccludeWithCapturer) {
1816 base::test::ScopedFeatureList scoped_feature_list;
1817 scoped_feature_list.InitAndEnableFeature(features::kWebContentsOcclusion);
1818 HideOrOccludeWithCapturerTest(contents(), Visibility::OCCLUDED);
1819 }
1820
TEST_F(WebContentsImplTest,HiddenCapture)1821 TEST_F(WebContentsImplTest, HiddenCapture) {
1822 TestRenderWidgetHostView* rwhv = static_cast<TestRenderWidgetHostView*>(
1823 contents()->GetRenderWidgetHostView());
1824
1825 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
1826 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
1827 EXPECT_EQ(Visibility::HIDDEN, contents()->GetVisibility());
1828
1829 contents()->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ true);
1830 EXPECT_TRUE(rwhv->is_showing());
1831
1832 contents()->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
1833 EXPECT_TRUE(rwhv->is_showing());
1834
1835 contents()->DecrementCapturerCount(/* stay_hidden */ true);
1836 EXPECT_TRUE(rwhv->is_showing());
1837
1838 contents()->DecrementCapturerCount(/* stay_hidden */ false);
1839 EXPECT_FALSE(rwhv->is_showing());
1840 }
1841
1842 // Tests that GetLastActiveTime starts with a real, non-zero time and updates
1843 // on activity.
TEST_F(WebContentsImplTest,GetLastActiveTime)1844 TEST_F(WebContentsImplTest, GetLastActiveTime) {
1845 // The WebContents starts with a valid creation time.
1846 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
1847
1848 // Reset the last active time to a known-bad value.
1849 contents()->last_active_time_ = base::TimeTicks();
1850 ASSERT_TRUE(contents()->GetLastActiveTime().is_null());
1851
1852 // Simulate activating the WebContents. The active time should update.
1853 contents()->WasShown();
1854 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
1855 }
1856
1857 class ContentsZoomChangedDelegate : public WebContentsDelegate {
1858 public:
ContentsZoomChangedDelegate()1859 ContentsZoomChangedDelegate() :
1860 contents_zoom_changed_call_count_(0),
1861 last_zoom_in_(false) {
1862 }
1863
GetAndResetContentsZoomChangedCallCount()1864 int GetAndResetContentsZoomChangedCallCount() {
1865 int count = contents_zoom_changed_call_count_;
1866 contents_zoom_changed_call_count_ = 0;
1867 return count;
1868 }
1869
last_zoom_in() const1870 bool last_zoom_in() const {
1871 return last_zoom_in_;
1872 }
1873
1874 // WebContentsDelegate:
ContentsZoomChange(bool zoom_in)1875 void ContentsZoomChange(bool zoom_in) override {
1876 contents_zoom_changed_call_count_++;
1877 last_zoom_in_ = zoom_in;
1878 }
1879
1880 private:
1881 int contents_zoom_changed_call_count_;
1882 bool last_zoom_in_;
1883
1884 DISALLOW_COPY_AND_ASSIGN(ContentsZoomChangedDelegate);
1885 };
1886
1887 // Tests that some mouseehweel events get turned into browser zoom requests.
TEST_F(WebContentsImplTest,HandleWheelEvent)1888 TEST_F(WebContentsImplTest, HandleWheelEvent) {
1889 using blink::WebInputEvent;
1890
1891 std::unique_ptr<ContentsZoomChangedDelegate> delegate(
1892 new ContentsZoomChangedDelegate());
1893 contents()->SetDelegate(delegate.get());
1894
1895 int modifiers = 0;
1896 // Verify that normal mouse wheel events do nothing to change the zoom level.
1897 blink::WebMouseWheelEvent event =
1898 blink::SyntheticWebMouseWheelEventBuilder::Build(
1899 0, 0, 0, 1, modifiers, ui::ScrollGranularity::kScrollByPixel);
1900 EXPECT_FALSE(contents()->HandleWheelEvent(event));
1901 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
1902
1903 // But whenever the ctrl modifier is applied zoom can be increased or
1904 // decreased. Except on MacOS where we never want to adjust zoom
1905 // with mousewheel.
1906 modifiers = WebInputEvent::kControlKey;
1907 event = blink::SyntheticWebMouseWheelEventBuilder::Build(
1908 0, 0, 0, 1, modifiers, ui::ScrollGranularity::kScrollByPixel);
1909 bool handled = contents()->HandleWheelEvent(event);
1910 #if defined(USE_AURA)
1911 EXPECT_TRUE(handled);
1912 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
1913 EXPECT_TRUE(delegate->last_zoom_in());
1914 #else
1915 EXPECT_FALSE(handled);
1916 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
1917 #endif
1918
1919 modifiers = WebInputEvent::kControlKey | WebInputEvent::kShiftKey |
1920 WebInputEvent::kAltKey;
1921 event = blink::SyntheticWebMouseWheelEventBuilder::Build(
1922 0, 0, 2, -5, modifiers, ui::ScrollGranularity::kScrollByPixel);
1923 handled = contents()->HandleWheelEvent(event);
1924 #if defined(USE_AURA)
1925 EXPECT_TRUE(handled);
1926 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
1927 EXPECT_FALSE(delegate->last_zoom_in());
1928 #else
1929 EXPECT_FALSE(handled);
1930 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
1931 #endif
1932
1933 // Unless there is no vertical movement.
1934 event = blink::SyntheticWebMouseWheelEventBuilder::Build(
1935 0, 0, 2, 0, modifiers, ui::ScrollGranularity::kScrollByPixel);
1936 EXPECT_FALSE(contents()->HandleWheelEvent(event));
1937 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
1938
1939 // Events containing precise scrolling deltas also shouldn't result in the
1940 // zoom being adjusted, to avoid accidental adjustments caused by
1941 // two-finger-scrolling on a touchpad.
1942 modifiers = WebInputEvent::kControlKey;
1943 event = blink::SyntheticWebMouseWheelEventBuilder::Build(
1944 0, 0, 0, 5, modifiers, ui::ScrollGranularity::kScrollByPrecisePixel);
1945 EXPECT_FALSE(contents()->HandleWheelEvent(event));
1946 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
1947
1948 // Ensure pointers to the delegate aren't kept beyond its lifetime.
1949 contents()->SetDelegate(nullptr);
1950 }
1951
1952 // Tests that GetRelatedActiveContentsCount is shared between related
1953 // SiteInstances and includes WebContents that have not navigated yet.
TEST_F(WebContentsImplTest,ActiveContentsCountBasic)1954 TEST_F(WebContentsImplTest, ActiveContentsCountBasic) {
1955 scoped_refptr<SiteInstance> instance1(
1956 SiteInstance::CreateForURL(browser_context(), GURL("http://a.com")));
1957 scoped_refptr<SiteInstance> instance2(
1958 instance1->GetRelatedSiteInstance(GURL("http://b.com")));
1959
1960 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
1961 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
1962
1963 std::unique_ptr<TestWebContents> contents1(
1964 TestWebContents::Create(browser_context(), instance1.get()));
1965 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
1966 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
1967
1968 std::unique_ptr<TestWebContents> contents2(
1969 TestWebContents::Create(browser_context(), instance1.get()));
1970 EXPECT_EQ(2u, instance1->GetRelatedActiveContentsCount());
1971 EXPECT_EQ(2u, instance2->GetRelatedActiveContentsCount());
1972
1973 contents1.reset();
1974 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
1975 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
1976
1977 contents2.reset();
1978 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
1979 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
1980 }
1981
1982 // Tests that GetRelatedActiveContentsCount is preserved correctly across
1983 // same-site and cross-site navigations.
TEST_F(WebContentsImplTest,ActiveContentsCountNavigate)1984 TEST_F(WebContentsImplTest, ActiveContentsCountNavigate) {
1985 scoped_refptr<SiteInstance> instance(
1986 SiteInstance::Create(browser_context()));
1987
1988 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
1989
1990 std::unique_ptr<TestWebContents> contents(
1991 TestWebContents::Create(browser_context(), instance.get()));
1992 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
1993
1994 // Navigate to a URL.
1995 auto navigation1 = NavigationSimulator::CreateBrowserInitiated(
1996 GURL("http://a.com/1"), contents.get());
1997 navigation1->Start();
1998 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
1999 navigation1->Commit();
2000 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2001
2002 // Navigate to a URL in the same site.
2003 auto navigation2 = NavigationSimulator::CreateBrowserInitiated(
2004 GURL("http://a.com/2"), contents.get());
2005 navigation2->Start();
2006 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2007 navigation2->Commit();
2008 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
2009 // When ProactivelySwapBrowsingInstance turned on for same-site navigations,
2010 // the BrowsingInstance will change on same-site navigations.
2011 EXPECT_NE(instance, contents->GetSiteInstance());
2012 // Check the previous instance's count.
2013 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2014 // Update the current instance.
2015 instance = contents->GetSiteInstance();
2016 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2017 }
2018
2019 // Navigate to a URL in a different site in the same BrowsingInstance.
2020 const GURL kUrl2("http://b.com");
2021 auto navigation3 = NavigationSimulator::CreateRendererInitiated(
2022 kUrl2, contents->GetMainFrame());
2023 navigation3->ReadyToCommit();
2024 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2025 if (AreAllSitesIsolatedForTesting() ||
2026 CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
2027 EXPECT_TRUE(contents->CrossProcessNavigationPending());
2028 } else {
2029 EXPECT_FALSE(contents->CrossProcessNavigationPending());
2030 }
2031 navigation3->Commit();
2032 if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
2033 // When ProactivelySwapBrowsingInstance turned on, the BrowsingInstance will
2034 // change on cross-site navigations.
2035 EXPECT_NE(instance, contents->GetSiteInstance());
2036 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2037 // Update the current instance.
2038 instance = contents->GetSiteInstance();
2039 }
2040 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2041
2042 // Navigate to a URL in a different site and different BrowsingInstance, by
2043 // using a TYPED page transition instead of LINK.
2044 const GURL kUrl3("http://c.com");
2045 auto navigation4 =
2046 NavigationSimulator::CreateBrowserInitiated(kUrl3, contents.get());
2047 navigation4->SetTransition(ui::PAGE_TRANSITION_TYPED);
2048 navigation4->ReadyToCommit();
2049 EXPECT_TRUE(contents->CrossProcessNavigationPending());
2050 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2051 scoped_refptr<SiteInstance> new_instance =
2052 contents->GetPendingMainFrame()->GetSiteInstance();
2053 navigation4->Commit();
2054 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2055 EXPECT_EQ(1u, new_instance->GetRelatedActiveContentsCount());
2056 EXPECT_FALSE(new_instance->IsRelatedSiteInstance(instance.get()));
2057
2058 contents.reset();
2059 EXPECT_EQ(0u, new_instance->GetRelatedActiveContentsCount());
2060 }
2061
2062 // Tests that GetRelatedActiveContentsCount tracks BrowsingInstance changes
2063 // from WebUI.
TEST_F(WebContentsImplTest,ActiveContentsCountChangeBrowsingInstance)2064 TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) {
2065 scoped_refptr<SiteInstance> instance(
2066 SiteInstance::Create(browser_context()));
2067
2068 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2069
2070 std::unique_ptr<TestWebContents> contents(
2071 TestWebContents::Create(browser_context(), instance.get()));
2072 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2073
2074 // Navigate to a URL.
2075 contents->NavigateAndCommit(GURL("http://a.com"));
2076 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2077
2078 // Navigate to a URL which sort of looks like a chrome:// url.
2079 contents->NavigateAndCommit(GURL("http://gpu"));
2080 if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
2081 // The navigation from "a.com" to "gpu" is using a new BrowsingInstance.
2082 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2083 // The rest of the test expects |instance| to match the one in the main
2084 // frame.
2085 instance = contents->GetMainFrame()->GetSiteInstance();
2086 }
2087 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2088
2089 // Navigate to a URL with WebUI. This will change BrowsingInstances.
2090 const GURL kWebUIUrl = GURL(GetWebUIURL(kChromeUIGpuHost));
2091 auto web_ui_navigation =
2092 NavigationSimulator::CreateBrowserInitiated(kWebUIUrl, contents.get());
2093 web_ui_navigation->Start();
2094 EXPECT_TRUE(contents->CrossProcessNavigationPending());
2095 scoped_refptr<SiteInstance> instance_webui(
2096 contents->GetPendingMainFrame()->GetSiteInstance());
2097 EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get()));
2098
2099 // At this point, contents still counts for the old BrowsingInstance.
2100 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2101 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2102
2103 // Commit and contents counts for the new one.
2104 web_ui_navigation->Commit();
2105 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2106 EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount());
2107
2108 contents.reset();
2109 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2110 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2111 }
2112
2113 class LoadingWebContentsObserver : public WebContentsObserver {
2114 public:
LoadingWebContentsObserver(WebContents * contents)2115 explicit LoadingWebContentsObserver(WebContents* contents)
2116 : WebContentsObserver(contents),
2117 is_loading_(false),
2118 did_receive_response_(false) {}
~LoadingWebContentsObserver()2119 ~LoadingWebContentsObserver() override {}
2120
2121 // The assertions on these messages ensure that they are received in order.
DidStartLoading()2122 void DidStartLoading() override {
2123 ASSERT_FALSE(did_receive_response_);
2124 ASSERT_FALSE(is_loading_);
2125 is_loading_ = true;
2126 }
DidReceiveResponse()2127 void DidReceiveResponse() override {
2128 ASSERT_TRUE(is_loading_);
2129 did_receive_response_ = true;
2130 }
DidStopLoading()2131 void DidStopLoading() override {
2132 ASSERT_TRUE(is_loading_);
2133 is_loading_ = false;
2134 did_receive_response_ = false;
2135 }
2136
is_loading() const2137 bool is_loading() const { return is_loading_; }
did_receive_response() const2138 bool did_receive_response() const { return did_receive_response_; }
2139
2140 private:
2141 bool is_loading_;
2142 bool did_receive_response_;
2143
2144 DISALLOW_COPY_AND_ASSIGN(LoadingWebContentsObserver);
2145 };
2146
2147 // Subclass of WebContentsImplTest for cases that need out-of-process iframes.
2148 class WebContentsImplTestWithSiteIsolation : public WebContentsImplTest {
2149 public:
WebContentsImplTestWithSiteIsolation()2150 WebContentsImplTestWithSiteIsolation() {
2151 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2152 }
2153 };
2154
2155 // Ensure that DidStartLoading/DidStopLoading events balance out properly with
2156 // interleaving cross-process navigations in multiple subframes.
2157 // See https://crbug.com/448601 for details of the underlying issue. The
2158 // sequence of events that reproduce it are as follows:
2159 // * Navigate top-level frame with one subframe.
2160 // * Subframe navigates more than once before the top-level frame has had a
2161 // chance to complete the load.
2162 // The subframe navigations cause the loading_frames_in_progress_ to drop down
2163 // to 0, while the loading_progresses_ map is not reset.
TEST_F(WebContentsImplTestWithSiteIsolation,StartStopEventsBalance)2164 TEST_F(WebContentsImplTestWithSiteIsolation, StartStopEventsBalance) {
2165 // The bug manifests itself in regular mode as well, but browser-initiated
2166 // navigation of subframes is only possible in --site-per-process mode within
2167 // unit tests.
2168 const GURL initial_url("about:blank");
2169 const GURL main_url("http://www.chromium.org");
2170 const GURL foo_url("http://foo.chromium.org");
2171 const GURL bar_url("http://bar.chromium.org");
2172 TestRenderFrameHost* orig_rfh = main_test_rfh();
2173
2174 // Use a WebContentsObserver to observe the behavior of the tab's spinner.
2175 LoadingWebContentsObserver observer(contents());
2176
2177 // Navigate the main RenderFrame and commit. The frame should still be
2178 // loading.
2179 auto main_frame_navigation =
2180 NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
2181 main_frame_navigation->SetKeepLoading(true);
2182 main_frame_navigation->Commit();
2183 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2184 EXPECT_EQ(orig_rfh, main_test_rfh());
2185 EXPECT_TRUE(contents()->IsLoading());
2186
2187 // The Observer callback implementations contain assertions to ensure that the
2188 // events arrive in the correct order.
2189 EXPECT_TRUE(observer.is_loading());
2190 EXPECT_TRUE(observer.did_receive_response());
2191
2192 // Create a child frame to navigate multiple times.
2193 TestRenderFrameHost* subframe = orig_rfh->AppendChild("subframe");
2194
2195 // Navigate the child frame to about:blank, which will send DidStopLoading
2196 // message.
2197 NavigationSimulator::NavigateAndCommitFromDocument(initial_url, subframe);
2198
2199 // Navigate the frame to another URL, which will send again
2200 // DidStartLoading and DidStopLoading messages.
2201 NavigationSimulator::NavigateAndCommitFromDocument(foo_url, subframe);
2202
2203 // Since the main frame hasn't sent any DidStopLoading messages, it is
2204 // expected that the WebContents is still in loading state.
2205 EXPECT_TRUE(contents()->IsLoading());
2206 EXPECT_TRUE(observer.is_loading());
2207 EXPECT_TRUE(observer.did_receive_response());
2208
2209 // After navigation, the RenderFrameHost may change.
2210 subframe = static_cast<TestRenderFrameHost*>(
2211 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host());
2212 // Navigate the frame again, this time using LoadURLWithParams. This causes
2213 // RenderFrameHost to call into WebContents::DidStartLoading, which starts
2214 // the spinner.
2215 {
2216 auto navigation =
2217 NavigationSimulatorImpl::CreateBrowserInitiated(bar_url, contents());
2218
2219 NavigationController::LoadURLParams load_params(bar_url);
2220 load_params.referrer = Referrer(GURL("http://referrer"),
2221 network::mojom::ReferrerPolicy::kDefault);
2222 load_params.transition_type = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2223 load_params.extra_headers = "content-type: text/plain";
2224 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
2225 load_params.is_renderer_initiated = false;
2226 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
2227 load_params.frame_tree_node_id =
2228 subframe->frame_tree_node()->frame_tree_node_id();
2229 navigation->SetLoadURLParams(&load_params);
2230
2231 navigation->Commit();
2232 subframe = static_cast<TestRenderFrameHost*>(
2233 navigation->GetFinalRenderFrameHost());
2234 }
2235
2236 // At this point the status should still be loading, since the main frame
2237 // hasn't sent the DidstopLoading message yet.
2238 EXPECT_TRUE(contents()->IsLoading());
2239 EXPECT_TRUE(observer.is_loading());
2240 EXPECT_TRUE(observer.did_receive_response());
2241
2242 // Send the DidStopLoading for the main frame and ensure it isn't loading
2243 // anymore.
2244 main_frame_navigation->StopLoading();
2245 EXPECT_FALSE(contents()->IsLoading());
2246 EXPECT_FALSE(observer.is_loading());
2247 EXPECT_FALSE(observer.did_receive_response());
2248 }
2249
2250 // Tests that WebContentsImpl::IsLoadingToDifferentDocument only reports main
2251 // frame loads. Browser-initiated navigation of subframes is only possible in
2252 // --site-per-process mode within unit tests.
TEST_F(WebContentsImplTestWithSiteIsolation,IsLoadingToDifferentDocument)2253 TEST_F(WebContentsImplTestWithSiteIsolation, IsLoadingToDifferentDocument) {
2254 const GURL main_url("http://www.chromium.org");
2255 TestRenderFrameHost* orig_rfh = main_test_rfh();
2256
2257 // Navigate the main RenderFrame and commit. The frame should still be
2258 // loading.
2259 auto navigation =
2260 NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
2261 navigation->SetKeepLoading(true);
2262 navigation->Commit();
2263 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2264 EXPECT_EQ(orig_rfh, main_test_rfh());
2265 EXPECT_TRUE(contents()->IsLoading());
2266 EXPECT_TRUE(contents()->IsLoadingToDifferentDocument());
2267
2268 // Send the DidStopLoading for the main frame and ensure it isn't loading
2269 // anymore.
2270 navigation->StopLoading();
2271 EXPECT_FALSE(contents()->IsLoading());
2272 EXPECT_FALSE(contents()->IsLoadingToDifferentDocument());
2273
2274 // Create a child frame to navigate.
2275 TestRenderFrameHost* subframe = orig_rfh->AppendChild("subframe");
2276
2277 // Navigate the child frame to about:blank, make sure the web contents is
2278 // marked as "loading" but not "loading to different document".
2279 subframe->SendNavigateWithTransition(0, false, GURL("about:blank"),
2280 ui::PAGE_TRANSITION_AUTO_SUBFRAME);
2281 EXPECT_TRUE(contents()->IsLoading());
2282 EXPECT_FALSE(contents()->IsLoadingToDifferentDocument());
2283 static_cast<mojom::FrameHost*>(subframe)->DidStopLoading();
2284 EXPECT_FALSE(contents()->IsLoading());
2285 }
2286
2287 // Ensure that WebContentsImpl does not stop loading too early when there still
2288 // is a pending renderer. This can happen if a same-process non user-initiated
2289 // navigation completes while there is an ongoing cross-process navigation.
2290 // TODO(clamy): Rewrite that test when the renderer-initiated non-user-initiated
2291 // navigation no longer kills the speculative RenderFrameHost. See
2292 // https://crbug.com/889039.
TEST_F(WebContentsImplTest,DISABLED_NoEarlyStop)2293 TEST_F(WebContentsImplTest, DISABLED_NoEarlyStop) {
2294 const GURL kUrl1("http://www.chromium.org");
2295 const GURL kUrl2("http://www.google.com");
2296 const GURL kUrl3("http://www.chromium.org/foo");
2297
2298 contents()->NavigateAndCommit(kUrl1);
2299
2300 TestRenderFrameHost* current_rfh = main_test_rfh();
2301
2302 // Start a browser-initiated cross-process navigation to |kUrl2|. The
2303 // WebContents should be loading.
2304 auto cross_process_navigation =
2305 NavigationSimulator::CreateBrowserInitiated(kUrl2, contents());
2306 cross_process_navigation->ReadyToCommit();
2307 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
2308 EXPECT_TRUE(contents()->IsLoading());
2309
2310 // The current RenderFrameHost starts a non user-initiated render-initiated
2311 // navigation. The WebContents should still be loading.
2312 auto same_process_navigation =
2313 NavigationSimulator::CreateRendererInitiated(kUrl3, current_rfh);
2314 same_process_navigation->SetHasUserGesture(false);
2315 same_process_navigation->Start();
2316 EXPECT_TRUE(contents()->IsLoading());
2317
2318 // Simulate the commit and DidStopLoading from the renderer-initiated
2319 // navigation in the current RenderFrameHost. There should still be a pending
2320 // RenderFrameHost and the WebContents should still be loading.
2321 same_process_navigation->Commit();
2322 static_cast<mojom::FrameHost*>(current_rfh)->DidStopLoading();
2323 EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh);
2324 EXPECT_TRUE(contents()->IsLoading());
2325
2326 // The same-process navigation should have committed.
2327 ASSERT_EQ(2, controller().GetEntryCount());
2328 EXPECT_EQ(kUrl3, controller().GetLastCommittedEntry()->GetURL());
2329
2330 // Commit the cross-process navigation. The formerly pending RenderFrameHost
2331 // should now be the current RenderFrameHost and the WebContents should still
2332 // be loading.
2333 cross_process_navigation->Commit();
2334 EXPECT_FALSE(contents()->GetPendingMainFrame());
2335 TestRenderFrameHost* new_current_rfh = main_test_rfh();
2336 EXPECT_EQ(new_current_rfh, pending_rfh);
2337 EXPECT_TRUE(contents()->IsLoading());
2338 EXPECT_EQ(3, controller().GetEntryCount());
2339
2340 // Simulate the new current RenderFrameHost DidStopLoading. The WebContents
2341 // should now have stopped loading.
2342 static_cast<mojom::FrameHost*>(new_current_rfh)->DidStopLoading();
2343 EXPECT_EQ(main_test_rfh(), new_current_rfh);
2344 EXPECT_FALSE(contents()->IsLoading());
2345 }
2346
TEST_F(WebContentsImplTest,MediaWakeLock)2347 TEST_F(WebContentsImplTest, MediaWakeLock) {
2348 EXPECT_FALSE(has_audio_wake_lock());
2349
2350 AudioStreamMonitor* monitor = contents()->audio_stream_monitor();
2351
2352 // Ensure RenderFrame is initialized before simulating events coming from it.
2353 main_test_rfh()->InitializeRenderFrameIfNeeded();
2354
2355 // Send a fake audio stream monitor notification. The audio wake lock
2356 // should be created.
2357 monitor->set_was_recently_audible_for_testing(true);
2358 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_AUDIO);
2359 EXPECT_TRUE(has_audio_wake_lock());
2360
2361 // Send another fake notification, this time when WasRecentlyAudible() will
2362 // be false. The wake lock should be released.
2363 monitor->set_was_recently_audible_for_testing(false);
2364 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_AUDIO);
2365 EXPECT_FALSE(has_audio_wake_lock());
2366
2367 main_test_rfh()->GetProcess()->SimulateCrash();
2368
2369 // Verify that all the wake locks have been released.
2370 EXPECT_FALSE(has_audio_wake_lock());
2371 }
2372
TEST_F(WebContentsImplTest,ThemeColorChangeDependingOnFirstVisiblePaint)2373 TEST_F(WebContentsImplTest, ThemeColorChangeDependingOnFirstVisiblePaint) {
2374 TestWebContentsObserver observer(contents());
2375 TestRenderFrameHost* rfh = main_test_rfh();
2376 rfh->InitializeRenderFrameIfNeeded();
2377
2378 EXPECT_EQ(base::nullopt, contents()->GetThemeColor());
2379 EXPECT_EQ(0, observer.theme_color_change_calls());
2380
2381 // Theme color changes should not propagate past the WebContentsImpl before
2382 // the first visually non-empty paint has occurred.
2383 rfh->DidChangeThemeColor(SK_ColorRED);
2384
2385 EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor());
2386 EXPECT_EQ(0, observer.theme_color_change_calls());
2387
2388 // Simulate that the first visually non-empty paint has occurred. This will
2389 // propagate the current theme color to the delegates.
2390 RenderViewHostTester::SimulateFirstPaint(test_rvh());
2391
2392 EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor());
2393 EXPECT_EQ(1, observer.theme_color_change_calls());
2394
2395 // Additional changes made by the web contents should propagate as well.
2396 rfh->DidChangeThemeColor(SK_ColorGREEN);
2397
2398 EXPECT_EQ(SK_ColorGREEN, contents()->GetThemeColor());
2399 EXPECT_EQ(2, observer.theme_color_change_calls());
2400 }
2401
TEST_F(WebContentsImplTest,ParseDownloadHeaders)2402 TEST_F(WebContentsImplTest, ParseDownloadHeaders) {
2403 download::DownloadUrlParameters::RequestHeadersType request_headers =
2404 WebContentsImpl::ParseDownloadHeaders("A: 1\r\nB: 2\r\nC: 3\r\n\r\n");
2405 ASSERT_EQ(3u, request_headers.size());
2406 EXPECT_EQ("A", request_headers[0].first);
2407 EXPECT_EQ("1", request_headers[0].second);
2408 EXPECT_EQ("B", request_headers[1].first);
2409 EXPECT_EQ("2", request_headers[1].second);
2410 EXPECT_EQ("C", request_headers[2].first);
2411 EXPECT_EQ("3", request_headers[2].second);
2412
2413 request_headers = WebContentsImpl::ParseDownloadHeaders("A:1\r\nA:2\r\n");
2414 ASSERT_EQ(2u, request_headers.size());
2415 EXPECT_EQ("A", request_headers[0].first);
2416 EXPECT_EQ("1", request_headers[0].second);
2417 EXPECT_EQ("A", request_headers[1].first);
2418 EXPECT_EQ("2", request_headers[1].second);
2419
2420 request_headers = WebContentsImpl::ParseDownloadHeaders("A 1\r\nA: 2");
2421 ASSERT_EQ(1u, request_headers.size());
2422 EXPECT_EQ("A", request_headers[0].first);
2423 EXPECT_EQ("2", request_headers[0].second);
2424
2425 request_headers = WebContentsImpl::ParseDownloadHeaders("A: 1");
2426 ASSERT_EQ(1u, request_headers.size());
2427 EXPECT_EQ("A", request_headers[0].first);
2428 EXPECT_EQ("1", request_headers[0].second);
2429
2430 request_headers = WebContentsImpl::ParseDownloadHeaders("A 1");
2431 ASSERT_EQ(0u, request_headers.size());
2432 }
2433
2434 namespace {
2435
2436 class TestJavaScriptDialogManager : public JavaScriptDialogManager {
2437 public:
TestJavaScriptDialogManager()2438 TestJavaScriptDialogManager() {}
~TestJavaScriptDialogManager()2439 ~TestJavaScriptDialogManager() override {}
2440
reset_count()2441 size_t reset_count() { return reset_count_; }
2442
2443 // JavaScriptDialogManager
2444
RunJavaScriptDialog(WebContents * web_contents,RenderFrameHost * render_frame_host,JavaScriptDialogType dialog_type,const base::string16 & message_text,const base::string16 & default_prompt_text,DialogClosedCallback callback,bool * did_suppress_message)2445 void RunJavaScriptDialog(WebContents* web_contents,
2446 RenderFrameHost* render_frame_host,
2447 JavaScriptDialogType dialog_type,
2448 const base::string16& message_text,
2449 const base::string16& default_prompt_text,
2450 DialogClosedCallback callback,
2451 bool* did_suppress_message) override {
2452 *did_suppress_message = true;
2453 }
2454
RunBeforeUnloadDialog(WebContents * web_contents,RenderFrameHost * render_frame_host,bool is_reload,DialogClosedCallback callback)2455 void RunBeforeUnloadDialog(WebContents* web_contents,
2456 RenderFrameHost* render_frame_host,
2457 bool is_reload,
2458 DialogClosedCallback callback) override {}
2459
HandleJavaScriptDialog(WebContents * web_contents,bool accept,const base::string16 * prompt_override)2460 bool HandleJavaScriptDialog(WebContents* web_contents,
2461 bool accept,
2462 const base::string16* prompt_override) override {
2463 return true;
2464 }
2465
CancelDialogs(WebContents * web_contents,bool reset_state)2466 void CancelDialogs(WebContents* web_contents,
2467 bool reset_state) override {
2468 if (reset_state)
2469 ++reset_count_;
2470 }
2471
2472 private:
2473 size_t reset_count_ = 0;
2474
2475 DISALLOW_COPY_AND_ASSIGN(TestJavaScriptDialogManager);
2476 };
2477
2478 } // namespace
2479
TEST_F(WebContentsImplTest,ResetJavaScriptDialogOnUserNavigate)2480 TEST_F(WebContentsImplTest, ResetJavaScriptDialogOnUserNavigate) {
2481 const GURL kUrl("http://www.google.com");
2482 const GURL kUrl2("http://www.google.com/sub");
2483 TestJavaScriptDialogManager dialog_manager;
2484 contents()->SetJavaScriptDialogManagerForTesting(&dialog_manager);
2485
2486 // A user-initiated navigation.
2487 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kUrl);
2488 EXPECT_EQ(1u, dialog_manager.reset_count());
2489
2490 // An automatic navigation.
2491 auto navigation =
2492 NavigationSimulator::CreateRendererInitiated(kUrl2, main_test_rfh());
2493 navigation->SetHasUserGesture(false);
2494 navigation->Commit();
2495 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
2496 // If we changed RenderFrameHost on a renderer-initiated navigation above,
2497 // we would trigger RenderFrameHostManager::UnloadOldFrame, similar to the
2498 // first (user/browser-initiated) navigation, which will trigger dialog
2499 // cancellations and increment the reset_count to 2.
2500 EXPECT_EQ(2u, dialog_manager.reset_count());
2501 } else {
2502 EXPECT_EQ(1u, dialog_manager.reset_count());
2503 }
2504
2505 contents()->SetJavaScriptDialogManagerForTesting(nullptr);
2506 }
2507
TEST_F(WebContentsImplTest,StartingSandboxFlags)2508 TEST_F(WebContentsImplTest, StartingSandboxFlags) {
2509 WebContents::CreateParams params(browser_context());
2510 network::mojom::WebSandboxFlags expected_flags =
2511 network::mojom::WebSandboxFlags::kPopups |
2512 network::mojom::WebSandboxFlags::kModals |
2513 network::mojom::WebSandboxFlags::kTopNavigation;
2514 params.starting_sandbox_flags = expected_flags;
2515 std::unique_ptr<WebContentsImpl> new_contents(
2516 WebContentsImpl::CreateWithOpener(params, nullptr));
2517 FrameTreeNode* root = new_contents->GetFrameTree()->root();
2518 network::mojom::WebSandboxFlags pending_flags =
2519 root->pending_frame_policy().sandbox_flags;
2520 EXPECT_EQ(pending_flags, expected_flags);
2521 network::mojom::WebSandboxFlags effective_flags =
2522 root->effective_frame_policy().sandbox_flags;
2523 EXPECT_EQ(effective_flags, expected_flags);
2524 }
2525
TEST_F(WebContentsImplTest,DidFirstVisuallyNonEmptyPaint)2526 TEST_F(WebContentsImplTest, DidFirstVisuallyNonEmptyPaint) {
2527 TestWebContentsObserver observer(contents());
2528
2529 RenderWidgetHostOwnerDelegate* rwhod = test_rvh();
2530 rwhod->RenderWidgetDidFirstVisuallyNonEmptyPaint();
2531
2532 EXPECT_TRUE(observer.observed_did_first_visually_non_empty_paint());
2533 }
2534
TEST_F(WebContentsImplTest,DidChangeVerticalScrollDirection)2535 TEST_F(WebContentsImplTest, DidChangeVerticalScrollDirection) {
2536 TestWebContentsObserver observer(contents());
2537
2538 EXPECT_FALSE(observer.last_vertical_scroll_direction().has_value());
2539
2540 contents()->OnVerticalScrollDirectionChanged(
2541 viz::VerticalScrollDirection::kUp);
2542
2543 EXPECT_EQ(viz::VerticalScrollDirection::kUp,
2544 observer.last_vertical_scroll_direction().value());
2545 }
2546
2547 namespace {
2548
2549 class MockWebContentsDelegate : public WebContentsDelegate {
2550 public:
MockWebContentsDelegate(blink::ProtocolHandlerSecurityLevel security_level=blink::ProtocolHandlerSecurityLevel::kStrict)2551 explicit MockWebContentsDelegate(
2552 blink::ProtocolHandlerSecurityLevel security_level =
2553 blink::ProtocolHandlerSecurityLevel::kStrict)
2554 : security_level_(security_level) {}
2555 MOCK_METHOD2(HandleContextMenu,
2556 bool(RenderFrameHost*, const ContextMenuParams&));
2557 MOCK_METHOD4(RegisterProtocolHandler,
2558 void(RenderFrameHost*, const std::string&, const GURL&, bool));
2559
GetProtocolHandlerSecurityLevel(RenderFrameHost *)2560 blink::ProtocolHandlerSecurityLevel GetProtocolHandlerSecurityLevel(
2561 RenderFrameHost*) override {
2562 return security_level_;
2563 }
2564
2565 private:
2566 blink::ProtocolHandlerSecurityLevel security_level_;
2567 };
2568
2569 } // namespace
2570
TEST_F(WebContentsImplTest,HandleContextMenuDelegate)2571 TEST_F(WebContentsImplTest, HandleContextMenuDelegate) {
2572 MockWebContentsDelegate delegate;
2573 contents()->SetDelegate(&delegate);
2574
2575 RenderFrameHost* rfh = main_test_rfh();
2576 EXPECT_CALL(delegate, HandleContextMenu(rfh, ::testing::_))
2577 .WillOnce(::testing::Return(true));
2578
2579 ContextMenuParams params;
2580 contents()->ShowContextMenu(rfh, params);
2581
2582 contents()->SetDelegate(nullptr);
2583 }
2584
TEST_F(WebContentsImplTest,RegisterProtocolHandlerDifferentOrigin)2585 TEST_F(WebContentsImplTest, RegisterProtocolHandlerDifferentOrigin) {
2586 MockWebContentsDelegate delegate;
2587 contents()->SetDelegate(&delegate);
2588
2589 GURL url("https://www.google.com");
2590 GURL handler_url1("https://www.google.com/handler/%s");
2591 GURL handler_url2("https://www.example.com/handler/%s");
2592
2593 contents()->NavigateAndCommit(url);
2594
2595 // Only the first call to RegisterProtocolHandler should register because the
2596 // other call has a handler from a different origin.
2597 EXPECT_CALL(delegate, RegisterProtocolHandler(main_test_rfh(), "mailto",
2598 handler_url1, true))
2599 .Times(1);
2600 EXPECT_CALL(delegate, RegisterProtocolHandler(main_test_rfh(), "mailto",
2601 handler_url2, true))
2602 .Times(0);
2603
2604 {
2605 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url1,
2606 /*user_gesture=*/true);
2607 }
2608
2609 {
2610 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url2,
2611 /*user_gesture=*/true);
2612 }
2613
2614 // Check behavior for RegisterProtocolHandler::kUntrustedOrigins.
2615 MockWebContentsDelegate unrestrictive_delegate(
2616 blink::ProtocolHandlerSecurityLevel::kUntrustedOrigins);
2617 contents()->SetDelegate(&unrestrictive_delegate);
2618 EXPECT_CALL(
2619 unrestrictive_delegate,
2620 RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url1, true))
2621 .Times(1);
2622 EXPECT_CALL(
2623 unrestrictive_delegate,
2624 RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url2, true))
2625 .Times(1);
2626
2627 {
2628 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url1,
2629 /*user_gesture=*/true);
2630 }
2631
2632 {
2633 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url2,
2634 /*user_gesture=*/true);
2635 }
2636
2637 contents()->SetDelegate(nullptr);
2638 }
2639
TEST_F(WebContentsImplTest,RegisterProtocolHandlerDataURL)2640 TEST_F(WebContentsImplTest, RegisterProtocolHandlerDataURL) {
2641 MockWebContentsDelegate delegate;
2642 contents()->SetDelegate(&delegate);
2643
2644 GURL data("data:text/html,<html><body><b>hello world</b></body></html>");
2645 GURL data_handler(data.spec() + "%s");
2646
2647 contents()->NavigateAndCommit(data);
2648
2649 // Data URLs should fail.
2650 EXPECT_CALL(delegate, RegisterProtocolHandler(contents()->GetMainFrame(),
2651 "mailto", data_handler, true))
2652 .Times(0);
2653
2654 {
2655 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", data_handler,
2656 /*user_gesture=*/true);
2657 }
2658
2659 contents()->SetDelegate(nullptr);
2660 }
2661
TEST_F(WebContentsImplTest,Bluetooth)2662 TEST_F(WebContentsImplTest, Bluetooth) {
2663 TestWebContentsObserver observer(contents());
2664 EXPECT_EQ(observer.num_is_connected_to_bluetooth_device_changed(), 0);
2665 EXPECT_FALSE(contents()->IsConnectedToBluetoothDevice());
2666
2667 contents()->TestIncrementBluetoothConnectedDeviceCount();
2668 EXPECT_EQ(observer.num_is_connected_to_bluetooth_device_changed(), 1);
2669 EXPECT_TRUE(observer.last_is_connected_to_bluetooth_device());
2670 EXPECT_TRUE(contents()->IsConnectedToBluetoothDevice());
2671
2672 contents()->TestDecrementBluetoothConnectedDeviceCount();
2673 EXPECT_EQ(observer.num_is_connected_to_bluetooth_device_changed(), 2);
2674 EXPECT_FALSE(observer.last_is_connected_to_bluetooth_device());
2675 EXPECT_FALSE(contents()->IsConnectedToBluetoothDevice());
2676 }
2677
TEST_F(WebContentsImplTest,FaviconURLsSet)2678 TEST_F(WebContentsImplTest, FaviconURLsSet) {
2679 std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
2680 const auto kFavicon =
2681 blink::mojom::FaviconURL(GURL("https://example.com/favicon.ico"),
2682 blink::mojom::FaviconIconType::kFavicon, {});
2683
2684 contents()->NavigateAndCommit(GURL("https://example.com"));
2685 EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
2686
2687 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
2688 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
2689 std::move(favicon_urls));
2690 EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
2691
2692 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
2693 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
2694 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
2695 std::move(favicon_urls));
2696 EXPECT_EQ(2u, contents()->GetFaviconURLs().size());
2697
2698 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
2699 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
2700 std::move(favicon_urls));
2701 EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
2702 }
2703
TEST_F(WebContentsImplTest,FaviconURLsResetWithNavigation)2704 TEST_F(WebContentsImplTest, FaviconURLsResetWithNavigation) {
2705 std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
2706 favicon_urls.push_back(blink::mojom::FaviconURL::New(
2707 GURL("https://example.com/favicon.ico"),
2708 blink::mojom::FaviconIconType::kFavicon, std::vector<gfx::Size>()));
2709
2710 contents()->NavigateAndCommit(GURL("https://example.com"));
2711 EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
2712
2713 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
2714 std::move(favicon_urls));
2715 EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
2716
2717 contents()->NavigateAndCommit(GURL("https://example.com/navigation.html"));
2718 EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
2719 }
2720
2721 } // namespace content
2722