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/bind_helpers.h"
12 #include "base/command_line.h"
13 #include "base/logging.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/scoped_feature_list.h"
18 #include "build/build_config.h"
19 #include "components/download/public/common/download_url_parameters.h"
20 #include "content/browser/child_process_security_policy_impl.h"
21 #include "content/browser/frame_host/interstitial_page_impl.h"
22 #include "content/browser/frame_host/navigation_entry_impl.h"
23 #include "content/browser/frame_host/navigator.h"
24 #include "content/browser/frame_host/render_frame_host_impl.h"
25 #include "content/browser/frame_host/render_frame_proxy_host.h"
26 #include "content/browser/media/audio_stream_monitor.h"
27 #include "content/browser/media/media_web_contents_observer.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_messages.h"
34 #include "content/common/input/synthetic_web_input_event_builders.h"
35 #include "content/common/page_messages.h"
36 #include "content/common/view_messages.h"
37 #include "content/public/browser/child_process_security_policy.h"
38 #include "content/public/browser/content_browser_client.h"
39 #include "content/public/browser/context_menu_params.h"
40 #include "content/public/browser/global_request_id.h"
41 #include "content/public/browser/interstitial_page_delegate.h"
42 #include "content/public/browser/javascript_dialog_manager.h"
43 #include "content/public/browser/navigation_details.h"
44 #include "content/public/browser/notification_details.h"
45 #include "content/public/browser/notification_source.h"
46 #include "content/public/browser/render_widget_host_view.h"
47 #include "content/public/browser/ssl_host_state_delegate.h"
48 #include "content/public/browser/storage_partition.h"
49 #include "content/public/browser/web_contents_delegate.h"
50 #include "content/public/browser/web_contents_observer.h"
51 #include "content/public/browser/web_ui_controller.h"
52 #include "content/public/common/bindings_policy.h"
53 #include "content/public/common/content_constants.h"
54 #include "content/public/common/content_features.h"
55 #include "content/public/common/url_constants.h"
56 #include "content/public/test/fake_local_frame.h"
57 #include "content/public/test/mock_render_process_host.h"
58 #include "content/public/test/navigation_simulator.h"
59 #include "content/public/test/test_browser_context.h"
60 #include "content/public/test/test_utils.h"
61 #include "content/test/navigation_simulator_impl.h"
62 #include "content/test/test_content_browser_client.h"
63 #include "content/test/test_content_client.h"
64 #include "content/test/test_render_frame_host.h"
65 #include "content/test/test_render_view_host.h"
66 #include "content/test/test_web_contents.h"
67 #include "net/test/cert_test_util.h"
68 #include "net/test/test_data_directory.h"
69 #include "testing/gmock/include/gmock/gmock.h"
70 #include "testing/gtest/include/gtest/gtest.h"
71 #include "third_party/blink/public/common/frame/sandbox_flags.h"
72 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
73 #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
74 #include "third_party/skia/include/core/SkColor.h"
75 #include "url/url_constants.h"
76
77 namespace content {
78 namespace {
79
80 class TestInterstitialPage;
81
82 class TestInterstitialPageDelegate : public InterstitialPageDelegate {
83 public:
TestInterstitialPageDelegate(TestInterstitialPage * interstitial_page)84 explicit TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page)
85 : interstitial_page_(interstitial_page) {}
86 void CommandReceived(const std::string& command) override;
GetHTMLContents()87 std::string GetHTMLContents() override { return std::string(); }
88 void OnDontProceed() override;
89 void OnProceed() override;
90
91 private:
92 TestInterstitialPage* interstitial_page_;
93 };
94
95 class TestInterstitialPage : public InterstitialPageImpl {
96 public:
97 enum InterstitialState {
98 INVALID = 0, // Hasn't yet been initialized.
99 UNDECIDED, // Initialized, but no decision taken yet.
100 OKED, // Proceed was called.
101 CANCELED // DontProceed was called.
102 };
103
104 class Delegate {
105 public:
106 virtual void TestInterstitialPageDeleted(
107 TestInterstitialPage* interstitial) = 0;
108
109 protected:
~Delegate()110 virtual ~Delegate() {}
111 };
112
113 // IMPORTANT NOTE: if you pass stack allocated values for |state| and
114 // |deleted| (like all interstitial related tests do at this point), make sure
115 // to create an instance of the TestInterstitialPageStateGuard class on the
116 // stack in your test. This will ensure that the TestInterstitialPage states
117 // are cleared when the test finishes.
118 // Not doing so will cause stack trashing if your test does not hide the
119 // interstitial, as in such a case it will be destroyed in the test TearDown
120 // method and will dereference the |deleted| local variable which by then is
121 // out of scope.
TestInterstitialPage(WebContentsImpl * contents,bool new_navigation,const GURL & url,InterstitialState * state,bool * deleted)122 TestInterstitialPage(WebContentsImpl* contents,
123 bool new_navigation,
124 const GURL& url,
125 InterstitialState* state,
126 bool* deleted)
127 : InterstitialPageImpl(
128 contents,
129 static_cast<RenderWidgetHostDelegate*>(contents),
130 new_navigation, url, new TestInterstitialPageDelegate(this)),
131 state_(state),
132 deleted_(deleted),
133 command_received_count_(0),
134 delegate_(nullptr) {
135 *state_ = UNDECIDED;
136 *deleted_ = false;
137 }
138
~TestInterstitialPage()139 ~TestInterstitialPage() override {
140 if (deleted_)
141 *deleted_ = true;
142 if (delegate_)
143 delegate_->TestInterstitialPageDeleted(this);
144 }
145
OnDontProceed()146 void OnDontProceed() {
147 if (state_)
148 *state_ = CANCELED;
149 }
OnProceed()150 void OnProceed() {
151 if (state_)
152 *state_ = OKED;
153 }
154
command_received_count() const155 int command_received_count() const {
156 return command_received_count_;
157 }
158
TestDomOperationResponse(const std::string & json_string)159 void TestDomOperationResponse(const std::string& json_string) {
160 if (enabled())
161 CommandReceived();
162 }
163
TestDidNavigate(int nav_entry_id,bool did_create_new_entry,const GURL & url)164 void TestDidNavigate(int nav_entry_id,
165 bool did_create_new_entry,
166 const GURL& url) {
167 FrameHostMsg_DidCommitProvisionalLoad_Params params;
168 InitNavigateParams(¶ms, nav_entry_id, did_create_new_entry,
169 url, ui::PAGE_TRANSITION_TYPED);
170 DidNavigate(GetMainFrame()->GetRenderViewHost(), params);
171 }
172
TestRenderViewTerminated(base::TerminationStatus status,int error_code)173 void TestRenderViewTerminated(base::TerminationStatus status,
174 int error_code) {
175 RenderViewTerminated(GetMainFrame()->GetRenderViewHost(), status,
176 error_code);
177 }
178
is_showing()179 bool is_showing() {
180 return static_cast<TestRenderWidgetHostView*>(
181 GetMainFrame()->GetRenderViewHost()->GetWidget()->GetView())
182 ->is_showing();
183 }
184
ClearStates()185 void ClearStates() {
186 state_ = nullptr;
187 deleted_ = nullptr;
188 delegate_ = nullptr;
189 }
190
CommandReceived()191 void CommandReceived() {
192 command_received_count_++;
193 }
194
set_delegate(Delegate * delegate)195 void set_delegate(Delegate* delegate) {
196 delegate_ = delegate;
197 }
198
199 protected:
CreateWebContentsView()200 WebContentsView* CreateWebContentsView() override { return nullptr; }
201
202 private:
203 InterstitialState* state_;
204 bool* deleted_;
205 int command_received_count_;
206 Delegate* delegate_;
207 };
208
CommandReceived(const std::string & command)209 void TestInterstitialPageDelegate::CommandReceived(const std::string& command) {
210 interstitial_page_->CommandReceived();
211 }
212
OnDontProceed()213 void TestInterstitialPageDelegate::OnDontProceed() {
214 interstitial_page_->OnDontProceed();
215 }
216
OnProceed()217 void TestInterstitialPageDelegate::OnProceed() {
218 interstitial_page_->OnProceed();
219 }
220
221 class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
222 public:
TestInterstitialPageStateGuard(TestInterstitialPage * interstitial_page)223 explicit TestInterstitialPageStateGuard(
224 TestInterstitialPage* interstitial_page)
225 : interstitial_page_(interstitial_page) {
226 DCHECK(interstitial_page_);
227 interstitial_page_->set_delegate(this);
228 }
~TestInterstitialPageStateGuard()229 ~TestInterstitialPageStateGuard() override {
230 if (interstitial_page_)
231 interstitial_page_->ClearStates();
232 }
233
TestInterstitialPageDeleted(TestInterstitialPage * interstitial)234 void TestInterstitialPageDeleted(
235 TestInterstitialPage* interstitial) override {
236 DCHECK(interstitial_page_ == interstitial);
237 interstitial_page_ = nullptr;
238 }
239
240 private:
241 TestInterstitialPage* interstitial_page_;
242 };
243
244 class WebContentsImplTestBrowserClient : public TestContentBrowserClient {
245 public:
WebContentsImplTestBrowserClient()246 WebContentsImplTestBrowserClient()
247 : original_browser_client_(SetBrowserClientForTesting(this)) {}
248
~WebContentsImplTestBrowserClient()249 ~WebContentsImplTestBrowserClient() override {
250 SetBrowserClientForTesting(original_browser_client_);
251 }
252
ShouldAssignSiteForURL(const GURL & url)253 bool ShouldAssignSiteForURL(const GURL& url) override {
254 if (site_assignment_for_url_.find(url) != site_assignment_for_url_.end()) {
255 return site_assignment_for_url_[url];
256 }
257
258 return true;
259 }
260
set_assign_site_for_url(bool assign,const GURL & url)261 void set_assign_site_for_url(bool assign, const GURL& url) {
262 DCHECK(url.is_valid());
263 site_assignment_for_url_[url] = assign;
264 }
265
266 private:
267 std::map<GURL, bool> site_assignment_for_url_;
268 ContentBrowserClient* original_browser_client_;
269 };
270
271 class WebContentsImplTest : public RenderViewHostImplTestHarness {
272 public:
SetUp()273 void SetUp() override {
274 RenderViewHostImplTestHarness::SetUp();
275 WebUIControllerFactory::RegisterFactory(
276 ContentWebUIControllerFactory::GetInstance());
277
278 if (AreDefaultSiteInstancesEnabled()) {
279 // Isolate |isolated_cross_site_url()| so we can't get a default
280 // SiteInstance for it.
281 ChildProcessSecurityPolicyImpl::GetInstance()->AddIsolatedOrigins(
282 {url::Origin::Create(isolated_cross_site_url())},
283 ChildProcessSecurityPolicy::IsolatedOriginSource::TEST,
284 browser_context());
285
286 // Reset the WebContents so the isolated origin will be honored by
287 // all BrowsingInstances used in the test.
288 SetContents(CreateTestWebContents());
289 }
290 }
291
TearDown()292 void TearDown() override {
293 WebUIControllerFactory::UnregisterFactoryForTesting(
294 ContentWebUIControllerFactory::GetInstance());
295 RenderViewHostImplTestHarness::TearDown();
296 }
297
has_audio_wake_lock()298 bool has_audio_wake_lock() {
299 return contents()
300 ->media_web_contents_observer()
301 ->has_audio_wake_lock_for_testing();
302 }
303
isolated_cross_site_url() const304 GURL isolated_cross_site_url() const {
305 return GURL("http://isolated-cross-site.com");
306 }
307 };
308
309 class TestWebContentsObserver : public WebContentsObserver {
310 public:
TestWebContentsObserver(WebContents * contents)311 explicit TestWebContentsObserver(WebContents* contents)
312 : WebContentsObserver(contents) {}
~TestWebContentsObserver()313 ~TestWebContentsObserver() override {}
314
DidFinishLoad(RenderFrameHost * render_frame_host,const GURL & validated_url)315 void DidFinishLoad(RenderFrameHost* render_frame_host,
316 const GURL& validated_url) override {
317 last_url_ = validated_url;
318 }
DidFailLoad(RenderFrameHost * render_frame_host,const GURL & validated_url,int error_code)319 void DidFailLoad(RenderFrameHost* render_frame_host,
320 const GURL& validated_url,
321 int error_code) override {
322 last_url_ = validated_url;
323 }
324
DidFirstVisuallyNonEmptyPaint()325 void DidFirstVisuallyNonEmptyPaint() override {
326 observed_did_first_visually_non_empty_paint_ = true;
327 EXPECT_TRUE(web_contents()->CompletedFirstVisuallyNonEmptyPaint());
328 }
329
DidChangeThemeColor()330 void DidChangeThemeColor() override { ++theme_color_change_calls_; }
331
DidChangeVerticalScrollDirection(viz::VerticalScrollDirection scroll_direction)332 void DidChangeVerticalScrollDirection(
333 viz::VerticalScrollDirection scroll_direction) override {
334 last_vertical_scroll_direction_ = scroll_direction;
335 }
336
OnIsConnectedToBluetoothDeviceChanged(bool is_connected_to_bluetooth_device)337 void OnIsConnectedToBluetoothDeviceChanged(
338 bool is_connected_to_bluetooth_device) override {
339 ++num_is_connected_to_bluetooth_device_changed_;
340 last_is_connected_to_bluetooth_device_ = is_connected_to_bluetooth_device;
341 }
342
last_url() const343 const GURL& last_url() const { return last_url_; }
theme_color_change_calls() const344 int theme_color_change_calls() const { return theme_color_change_calls_; }
last_vertical_scroll_direction() const345 base::Optional<viz::VerticalScrollDirection> last_vertical_scroll_direction()
346 const {
347 return last_vertical_scroll_direction_;
348 }
observed_did_first_visually_non_empty_paint() const349 bool observed_did_first_visually_non_empty_paint() const {
350 return observed_did_first_visually_non_empty_paint_;
351 }
num_is_connected_to_bluetooth_device_changed() const352 int num_is_connected_to_bluetooth_device_changed() const {
353 return num_is_connected_to_bluetooth_device_changed_;
354 }
last_is_connected_to_bluetooth_device() const355 bool last_is_connected_to_bluetooth_device() const {
356 return last_is_connected_to_bluetooth_device_;
357 }
358
359 private:
360 GURL last_url_;
361 int theme_color_change_calls_ = 0;
362 base::Optional<viz::VerticalScrollDirection> last_vertical_scroll_direction_;
363 bool observed_did_first_visually_non_empty_paint_ = false;
364 int num_is_connected_to_bluetooth_device_changed_ = 0;
365 bool last_is_connected_to_bluetooth_device_ = false;
366
367 DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
368 };
369
370 // Pretends to be a normal browser that receives toggles and transitions to/from
371 // a fullscreened state.
372 class FakeFullscreenDelegate : public WebContentsDelegate {
373 public:
FakeFullscreenDelegate()374 FakeFullscreenDelegate() : fullscreened_contents_(nullptr) {}
~FakeFullscreenDelegate()375 ~FakeFullscreenDelegate() override {}
376
EnterFullscreenModeForTab(WebContents * web_contents,const GURL & origin,const blink::mojom::FullscreenOptions & options)377 void EnterFullscreenModeForTab(
378 WebContents* web_contents,
379 const GURL& origin,
380 const blink::mojom::FullscreenOptions& options) override {
381 fullscreened_contents_ = web_contents;
382 }
383
ExitFullscreenModeForTab(WebContents * web_contents)384 void ExitFullscreenModeForTab(WebContents* web_contents) override {
385 fullscreened_contents_ = nullptr;
386 }
387
IsFullscreenForTabOrPending(const WebContents * web_contents)388 bool IsFullscreenForTabOrPending(const WebContents* web_contents) override {
389 return fullscreened_contents_ && web_contents == fullscreened_contents_;
390 }
391
392 private:
393 WebContents* fullscreened_contents_;
394
395 DISALLOW_COPY_AND_ASSIGN(FakeFullscreenDelegate);
396 };
397
398 class FakeWebContentsDelegate : public WebContentsDelegate {
399 public:
FakeWebContentsDelegate()400 FakeWebContentsDelegate() : loading_state_changed_was_called_(false) {}
~FakeWebContentsDelegate()401 ~FakeWebContentsDelegate() override {}
402
LoadingStateChanged(WebContents * source,bool to_different_document)403 void LoadingStateChanged(WebContents* source,
404 bool to_different_document) override {
405 loading_state_changed_was_called_ = true;
406 }
407
loading_state_changed_was_called() const408 bool loading_state_changed_was_called() const {
409 return loading_state_changed_was_called_;
410 }
411
412 private:
413 bool loading_state_changed_was_called_;
414
415 DISALLOW_COPY_AND_ASSIGN(FakeWebContentsDelegate);
416 };
417
418 } // namespace
419
TEST_F(WebContentsImplTest,UpdateTitle)420 TEST_F(WebContentsImplTest, UpdateTitle) {
421 FakeWebContentsDelegate fake_delegate;
422 contents()->SetDelegate(&fake_delegate);
423
424 NavigationControllerImpl& cont =
425 static_cast<NavigationControllerImpl&>(controller());
426 cont.LoadURL(GURL(url::kAboutBlankURL), Referrer(), ui::PAGE_TRANSITION_TYPED,
427 std::string());
428
429 FrameHostMsg_DidCommitProvisionalLoad_Params params;
430 InitNavigateParams(¶ms, 0, true, GURL(url::kAboutBlankURL),
431 ui::PAGE_TRANSITION_TYPED);
432
433 main_test_rfh()->SendNavigateWithParams(¶ms,
434 false /* was_within_same_document */);
435
436 contents()->UpdateTitle(main_test_rfh(),
437 base::ASCIIToUTF16(" Lots O' Whitespace\n"),
438 base::i18n::LEFT_TO_RIGHT);
439 // Make sure that title updates get stripped of whitespace.
440 EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
441 EXPECT_FALSE(contents()->IsWaitingForResponse());
442 EXPECT_TRUE(fake_delegate.loading_state_changed_was_called());
443
444 contents()->SetDelegate(nullptr);
445 }
446
TEST_F(WebContentsImplTest,UpdateTitleBeforeFirstNavigation)447 TEST_F(WebContentsImplTest, UpdateTitleBeforeFirstNavigation) {
448 ASSERT_TRUE(controller().IsInitialNavigation());
449 const base::string16 title = base::ASCIIToUTF16("Initial Entry Title");
450 contents()->UpdateTitle(main_test_rfh(), title, base::i18n::LEFT_TO_RIGHT);
451 EXPECT_EQ(title, contents()->GetTitle());
452 }
453
TEST_F(WebContentsImplTest,DontUseTitleFromPendingEntry)454 TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
455 const GURL kGURL(GetWebUIURL("blah"));
456 controller().LoadURL(
457 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
458 EXPECT_EQ(base::string16(), contents()->GetTitle());
459
460 // Also test setting title while the first navigation is still pending.
461 const base::string16 title = base::ASCIIToUTF16("Initial Entry Title");
462 contents()->UpdateTitle(main_test_rfh(), title, base::i18n::LEFT_TO_RIGHT);
463 EXPECT_EQ(title, contents()->GetTitle());
464 }
465
TEST_F(WebContentsImplTest,UseTitleFromPendingEntryIfSet)466 TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
467 const GURL kGURL(GetWebUIURL("blah"));
468 const base::string16 title = base::ASCIIToUTF16("My Title");
469 controller().LoadURL(
470 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
471
472 NavigationEntry* entry = controller().GetVisibleEntry();
473 ASSERT_EQ(kGURL, entry->GetURL());
474 entry->SetTitle(title);
475
476 EXPECT_EQ(title, contents()->GetTitle());
477 }
478
479 // Stub out local frame mojo binding. Intercepts calls to EnableViewSourceMode
480 // and marks the message as received. This class attaches to the first
481 // RenderFrameHostImpl created.
482 class EnableViewSourceLocalFrame : public content::FakeLocalFrame,
483 public WebContentsObserver {
484 public:
EnableViewSourceLocalFrame(WebContents * web_contents)485 explicit EnableViewSourceLocalFrame(WebContents* web_contents)
486 : WebContentsObserver(web_contents) {}
487
RenderFrameCreated(RenderFrameHost * render_frame_host)488 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
489 if (!initialized_) {
490 initialized_ = true;
491 Init(render_frame_host->GetRemoteAssociatedInterfaces());
492 }
493 }
494
EnableViewSourceMode()495 void EnableViewSourceMode() final { enabled_view_source_ = true; }
496
IsViewSourceModeEnabled() const497 bool IsViewSourceModeEnabled() const { return enabled_view_source_; }
498
499 private:
500 bool enabled_view_source_ = false;
501 bool initialized_ = false;
502 };
503
504 // Browser initiated navigations to view-source URLs of WebUI pages should work.
TEST_F(WebContentsImplTest,DirectNavigationToViewSourceWebUI)505 TEST_F(WebContentsImplTest, DirectNavigationToViewSourceWebUI) {
506 const GURL kGURL("view-source:" + GetWebUIURLString("blah/"));
507 // NavigationControllerImpl rewrites view-source URLs, simulating that here.
508 const GURL kRewrittenURL(GetWebUIURL("blah"));
509
510 EnableViewSourceLocalFrame local_frame(contents());
511 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kGURL);
512
513 // Did we get the expected message?
514 base::RunLoop().RunUntilIdle();
515 EXPECT_TRUE(local_frame.IsViewSourceModeEnabled());
516
517 // This is the virtual URL.
518 EXPECT_EQ(
519 kGURL,
520 contents()->GetController().GetLastCommittedEntry()->GetVirtualURL());
521
522 // The actual URL navigated to.
523 EXPECT_EQ(kRewrittenURL,
524 contents()->GetController().GetLastCommittedEntry()->GetURL());
525 }
526
527 // Test simple same-SiteInstance navigation.
TEST_F(WebContentsImplTest,SimpleNavigation)528 TEST_F(WebContentsImplTest, SimpleNavigation) {
529 TestRenderFrameHost* orig_rfh = main_test_rfh();
530 SiteInstance* instance1 = contents()->GetSiteInstance();
531 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
532
533 // Navigate until ready to commit.
534 const GURL url("http://www.google.com");
535 auto navigation =
536 NavigationSimulator::CreateBrowserInitiated(url, contents());
537 navigation->ReadyToCommit();
538 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
539 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
540 // Controller's pending entry will have a null site instance until we assign
541 // it in Commit.
542 EXPECT_EQ(
543 nullptr,
544 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
545 site_instance());
546
547 navigation->Commit();
548 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
549 EXPECT_EQ(orig_rfh, main_test_rfh());
550 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
551 // Controller's entry should now have the SiteInstance, or else we won't be
552 // able to find it later.
553 EXPECT_EQ(
554 instance1,
555 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
556 site_instance());
557 }
558
559 // Test that we reject NavigateToEntry if the url is over kMaxURLChars.
TEST_F(WebContentsImplTest,NavigateToExcessivelyLongURL)560 TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
561 // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
562 const GURL url(std::string("http://example.org/").append(
563 url::kMaxURLChars + 1, 'a'));
564
565 controller().LoadURL(
566 url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
567 EXPECT_EQ(nullptr, controller().GetPendingEntry());
568 }
569
570 // Test that we reject NavigateToEntry if the url is invalid.
TEST_F(WebContentsImplTest,NavigateToInvalidURL)571 TEST_F(WebContentsImplTest, NavigateToInvalidURL) {
572 // Invalid URLs should not trigger a navigation.
573 const GURL invalid_url("view-source:http://example.org/%00");
574 controller().LoadURL(
575 invalid_url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
576 EXPECT_EQ(nullptr, controller().GetPendingEntry());
577
578 // Empty URLs are supported and should start a navigation.
579 controller().LoadURL(
580 GURL(), Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
581 EXPECT_NE(nullptr, controller().GetPendingEntry());
582 }
583
584 // Test that navigating across a site boundary creates a new RenderViewHost
585 // with a new SiteInstance. Going back should do the same.
TEST_F(WebContentsImplTest,CrossSiteBoundaries)586 TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
587 // This test assumes no interaction with the back forward cache.
588 // Similar coverage when BFCache is on can be found in
589 // BackForwardCacheBrowserTest.NavigateBackForwardRepeatedly.
590 contents()->GetController().GetBackForwardCache().DisableForTesting(
591 BackForwardCache::TEST_ASSUMES_NO_CACHING);
592
593 TestRenderFrameHost* orig_rfh = main_test_rfh();
594 int orig_rvh_delete_count = 0;
595 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
596 SiteInstance* instance1 = contents()->GetSiteInstance();
597
598 // Navigate to URL. First URL should use first RenderViewHost.
599 const GURL url("http://www.google.com");
600 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
601
602 // Keep the number of active frames in orig_rfh's SiteInstance non-zero so
603 // that orig_rfh doesn't get deleted when it gets swapped out.
604 orig_rfh->GetSiteInstance()->IncrementActiveFrameCount();
605
606 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
607 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
608 EXPECT_EQ(url, contents()->GetLastCommittedURL());
609 EXPECT_EQ(url, contents()->GetVisibleURL());
610
611 // Navigate to new site
612 const GURL url2("http://www.yahoo.com");
613 auto new_site_navigation =
614 NavigationSimulator::CreateBrowserInitiated(url2, contents());
615 new_site_navigation->ReadyToCommit();
616 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
617 EXPECT_EQ(url, contents()->GetLastCommittedURL());
618 EXPECT_EQ(url2, contents()->GetVisibleURL());
619 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
620 EXPECT_TRUE(pending_rfh->GetLastCommittedURL().is_empty());
621 int pending_rvh_delete_count = 0;
622 pending_rfh->GetRenderViewHost()->set_delete_counter(
623 &pending_rvh_delete_count);
624
625 // DidNavigate from the pending page.
626 new_site_navigation->Commit();
627 SiteInstance* instance2 = contents()->GetSiteInstance();
628
629 // Keep the number of active frames in pending_rfh's SiteInstance
630 // non-zero so that orig_rfh doesn't get deleted when it gets
631 // swapped out.
632 pending_rfh->GetSiteInstance()->IncrementActiveFrameCount();
633
634 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
635 EXPECT_EQ(pending_rfh, main_test_rfh());
636 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
637 EXPECT_EQ(url2, contents()->GetVisibleURL());
638 EXPECT_NE(instance1, instance2);
639 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
640 // We keep a proxy for the original RFH's SiteInstance.
641 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost(
642 instance1));
643 EXPECT_EQ(orig_rvh_delete_count, 0);
644
645 // Going back should switch SiteInstances again. The first SiteInstance is
646 // stored in the NavigationEntry, so it should be the same as at the start.
647 // We should use the same RFH as before, swapping it back in.
648 auto back_navigation =
649 NavigationSimulator::CreateHistoryNavigation(-1, contents());
650 back_navigation->ReadyToCommit();
651 TestRenderFrameHost* goback_rfh = contents()->GetPendingMainFrame();
652 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
653
654 // DidNavigate from the back action.
655 back_navigation->Commit();
656 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
657 EXPECT_EQ(goback_rfh, main_test_rfh());
658 EXPECT_EQ(url, contents()->GetLastCommittedURL());
659 EXPECT_EQ(url, contents()->GetVisibleURL());
660 EXPECT_EQ(instance1, contents()->GetSiteInstance());
661 // There should be a proxy for the pending RFH SiteInstance.
662 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost(
663 instance2));
664 EXPECT_EQ(pending_rvh_delete_count, 0);
665
666 // Close contents and ensure RVHs are deleted.
667 DeleteContents();
668 EXPECT_EQ(orig_rvh_delete_count, 1);
669 EXPECT_EQ(pending_rvh_delete_count, 1);
670 }
671
672 // Test that navigating across a site boundary after a crash creates a new
673 // RFH without requiring a cross-site transition (i.e., PENDING state).
TEST_F(WebContentsImplTest,CrossSiteBoundariesAfterCrash)674 TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
675 TestRenderFrameHost* orig_rfh = main_test_rfh();
676
677 int orig_rvh_delete_count = 0;
678 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
679 SiteInstance* instance1 = contents()->GetSiteInstance();
680
681 // Navigate to URL. First URL should use first RenderViewHost.
682 const GURL url("http://www.google.com");
683 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
684 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
685 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
686
687 // Simulate a renderer crash.
688 EXPECT_TRUE(orig_rfh->IsRenderFrameLive());
689 orig_rfh->GetProcess()->SimulateCrash();
690 EXPECT_FALSE(orig_rfh->IsRenderFrameLive());
691
692 // Start navigating to a new site. We should not go into PENDING.
693 const GURL url2("http://www.yahoo.com");
694 auto navigation_to_url2 =
695 NavigationSimulator::CreateBrowserInitiated(url2, contents());
696 navigation_to_url2->ReadyToCommit();
697
698 TestRenderFrameHost* new_rfh = main_test_rfh();
699 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
700 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
701 EXPECT_NE(orig_rfh, new_rfh);
702 EXPECT_EQ(orig_rvh_delete_count, 1);
703
704 navigation_to_url2->Commit();
705 SiteInstance* instance2 = contents()->GetSiteInstance();
706
707 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
708 EXPECT_EQ(new_rfh, main_rfh());
709 EXPECT_NE(instance1, instance2);
710 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
711
712 // Close contents and ensure RVHs are deleted.
713 DeleteContents();
714 EXPECT_EQ(orig_rvh_delete_count, 1);
715 }
716
717 // Test that opening a new contents in the same SiteInstance and then navigating
718 // both contentses to a new site will place both contentses in a single
719 // SiteInstance.
TEST_F(WebContentsImplTest,NavigateTwoTabsCrossSite)720 TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
721 SiteInstance* instance1 = contents()->GetSiteInstance();
722
723 // Navigate to URL. First URL should use first RenderViewHost.
724 const GURL url("http://www.google.com");
725 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
726
727 // Open a new contents with the same SiteInstance, navigated to the same site.
728 std::unique_ptr<TestWebContents> contents2(
729 TestWebContents::Create(browser_context(), instance1));
730 NavigationSimulator::NavigateAndCommitFromBrowser(contents2.get(), url);
731 EXPECT_EQ(instance1, contents2->GetSiteInstance());
732
733 // Navigate first contents to a new site.
734 const GURL url2a = isolated_cross_site_url();
735 auto navigation1 =
736 NavigationSimulator::CreateBrowserInitiated(url2a, contents());
737 navigation1->SetTransition(ui::PAGE_TRANSITION_LINK);
738 navigation1->ReadyToCommit();
739 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
740 navigation1->Commit();
741 SiteInstance* instance2a = contents()->GetSiteInstance();
742 EXPECT_NE(instance1, instance2a);
743
744 // Navigate second contents to the same site as the first tab.
745 const GURL url2b = isolated_cross_site_url().Resolve("/foo");
746 auto navigation2 =
747 NavigationSimulator::CreateBrowserInitiated(url2b, contents2.get());
748 navigation2->SetTransition(ui::PAGE_TRANSITION_LINK);
749 navigation2->ReadyToCommit();
750 EXPECT_TRUE(contents2->CrossProcessNavigationPending());
751
752 // NOTE(creis): We used to be in danger of showing a crash page here if the
753 // second contents hadn't navigated somewhere first (bug 1145430). That case
754 // is now covered by the CrossSiteBoundariesAfterCrash test.
755 navigation2->Commit();
756 SiteInstance* instance2b = contents2->GetSiteInstance();
757 EXPECT_NE(instance1, instance2b);
758
759 // Both contentses should now be in the same SiteInstance.
760 EXPECT_EQ(instance2a, instance2b);
761 }
762
763 // The embedder can request sites for certain urls not be be assigned to the
764 // SiteInstance through ShouldAssignSiteForURL() in content browser client,
765 // allowing to reuse the renderer backing certain chrome urls for subsequent
766 // navigation. The test verifies that the override is honored.
TEST_F(WebContentsImplTest,NavigateFromSitelessUrl)767 TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) {
768 WebContentsImplTestBrowserClient browser_client;
769 SetBrowserClientForTesting(&browser_client);
770
771 TestRenderFrameHost* orig_rfh = main_test_rfh();
772 int orig_rvh_delete_count = 0;
773 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
774 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
775
776 // Navigate to an URL that will not assign a new SiteInstance.
777 const GURL native_url("non-site-url://stuffandthings");
778 browser_client.set_assign_site_for_url(false, native_url);
779 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), native_url);
780
781 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
782 EXPECT_EQ(orig_rfh, main_test_rfh());
783 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
784 EXPECT_EQ(native_url, contents()->GetVisibleURL());
785 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
786 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
787 EXPECT_FALSE(orig_instance->HasSite());
788
789 // Navigate to new site (should keep same site instance).
790 const GURL url("http://www.google.com");
791 browser_client.set_assign_site_for_url(true, url);
792 auto navigation1 =
793 NavigationSimulator::CreateBrowserInitiated(url, contents());
794 navigation1->ReadyToCommit();
795 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
796 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
797 EXPECT_EQ(url, contents()->GetVisibleURL());
798 EXPECT_FALSE(contents()->GetPendingMainFrame());
799 navigation1->Commit();
800
801 // The first entry's SiteInstance should be reset to a new, related one. This
802 // prevents wrongly detecting a SiteInstance mismatch when returning to it
803 // later.
804 SiteInstanceImpl* prev_entry_instance = contents()
805 ->GetController()
806 .GetEntryAtIndex(0)
807 ->root_node()
808 ->frame_entry->site_instance();
809 EXPECT_NE(prev_entry_instance, orig_instance);
810 EXPECT_TRUE(orig_instance->IsRelatedSiteInstance(prev_entry_instance));
811 EXPECT_FALSE(prev_entry_instance->HasSite());
812
813 SiteInstanceImpl* curr_entry_instance = contents()
814 ->GetController()
815 .GetEntryAtIndex(1)
816 ->root_node()
817 ->frame_entry->site_instance();
818 EXPECT_EQ(curr_entry_instance, orig_instance);
819 // Keep the number of active frames in orig_rfh's SiteInstance
820 // non-zero so that orig_rfh doesn't get deleted when it gets
821 // swapped out.
822 orig_rfh->GetSiteInstance()->IncrementActiveFrameCount();
823
824 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
825 if (AreDefaultSiteInstancesEnabled()) {
826 // Verify that the empty SiteInstance gets converted into a default
827 // SiteInstance because |url| does not require a dedicated process.
828 EXPECT_TRUE(contents()->GetSiteInstance()->IsDefaultSiteInstance());
829 } else {
830 EXPECT_TRUE(
831 contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com"));
832 }
833 EXPECT_EQ(url, contents()->GetLastCommittedURL());
834
835 // Navigate to another new site (should create a new site instance).
836 const GURL url2 = isolated_cross_site_url();
837 auto navigation2 =
838 NavigationSimulator::CreateBrowserInitiated(url2, contents());
839 navigation2->ReadyToCommit();
840 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
841 EXPECT_EQ(url, contents()->GetLastCommittedURL());
842 EXPECT_EQ(url2, contents()->GetVisibleURL());
843 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
844 int pending_rvh_delete_count = 0;
845 pending_rfh->GetRenderViewHost()->set_delete_counter(
846 &pending_rvh_delete_count);
847
848 // DidNavigate from the pending page.
849 navigation2->Commit();
850 SiteInstance* new_instance = contents()->GetSiteInstance();
851
852 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
853 EXPECT_EQ(pending_rfh, main_test_rfh());
854 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
855 EXPECT_EQ(url2, contents()->GetVisibleURL());
856 EXPECT_NE(new_instance, orig_instance);
857 EXPECT_FALSE(contents()->GetPendingMainFrame());
858 EXPECT_EQ(orig_rvh_delete_count, 0);
859
860 // Close contents and ensure RVHs are deleted.
861 DeleteContents();
862 EXPECT_EQ(orig_rvh_delete_count, 1);
863 EXPECT_EQ(pending_rvh_delete_count, 1);
864 }
865
866 // Regression test for http://crbug.com/386542 - variation of
867 // NavigateFromSitelessUrl in which the original navigation is a session
868 // restore.
TEST_F(WebContentsImplTest,NavigateFromRestoredSitelessUrl)869 TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) {
870 WebContentsImplTestBrowserClient browser_client;
871 SetBrowserClientForTesting(&browser_client);
872 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
873 TestRenderFrameHost* orig_rfh = main_test_rfh();
874
875 // Restore a navigation entry for URL that should not assign site to the
876 // SiteInstance.
877 const GURL native_url("non-site-url://stuffandthings");
878 browser_client.set_assign_site_for_url(false, native_url);
879 std::vector<std::unique_ptr<NavigationEntry>> entries;
880 std::unique_ptr<NavigationEntry> new_entry =
881 NavigationController::CreateNavigationEntry(
882 native_url, Referrer(), base::nullopt, ui::PAGE_TRANSITION_LINK,
883 false, std::string(), browser_context(),
884 nullptr /* blob_url_loader_factory */);
885 entries.push_back(std::move(new_entry));
886 controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries);
887 ASSERT_EQ(0u, entries.size());
888 ASSERT_EQ(1, controller().GetEntryCount());
889
890 EXPECT_TRUE(controller().NeedsReload());
891 controller().LoadIfNecessary();
892 NavigationEntry* entry = controller().GetPendingEntry();
893 orig_rfh->PrepareForCommit();
894 contents()->TestDidNavigate(orig_rfh, entry->GetUniqueID(), false,
895 native_url, ui::PAGE_TRANSITION_RELOAD);
896 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
897 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
898 EXPECT_FALSE(orig_instance->HasSite());
899
900 // Navigate to a regular site and verify that the SiteInstance was kept.
901 const GURL url("http://www.google.com");
902 browser_client.set_assign_site_for_url(true, url);
903 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
904 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
905
906 // Cleanup.
907 DeleteContents();
908 }
909
910 // Complement for NavigateFromRestoredSitelessUrl, verifying that when a regular
911 // tab is restored, the SiteInstance will change upon navigation.
TEST_F(WebContentsImplTest,NavigateFromRestoredRegularUrl)912 TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) {
913 WebContentsImplTestBrowserClient browser_client;
914 SetBrowserClientForTesting(&browser_client);
915 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
916 TestRenderFrameHost* orig_rfh = main_test_rfh();
917
918 // Restore a navigation entry for a regular URL ensuring that the embedder
919 // ShouldAssignSiteForUrl override is disabled (i.e. returns true).
920 const GURL regular_url("http://www.yahoo.com");
921 browser_client.set_assign_site_for_url(true, regular_url);
922 std::vector<std::unique_ptr<NavigationEntry>> entries;
923 std::unique_ptr<NavigationEntry> new_entry =
924 NavigationController::CreateNavigationEntry(
925 regular_url, Referrer(), base::nullopt, ui::PAGE_TRANSITION_LINK,
926 false, std::string(), browser_context(),
927 nullptr /* blob_url_loader_factory */);
928 entries.push_back(std::move(new_entry));
929 controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries);
930 ASSERT_EQ(0u, entries.size());
931
932 ASSERT_EQ(1, controller().GetEntryCount());
933 EXPECT_TRUE(controller().NeedsReload());
934 controller().LoadIfNecessary();
935 NavigationEntry* entry = controller().GetPendingEntry();
936 orig_rfh->PrepareForCommit();
937 contents()->TestDidNavigate(orig_rfh, entry->GetUniqueID(), false,
938 regular_url, ui::PAGE_TRANSITION_RELOAD);
939 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
940 EXPECT_TRUE(orig_instance->HasSite());
941 EXPECT_EQ(AreDefaultSiteInstancesEnabled(),
942 orig_instance->IsDefaultSiteInstance());
943
944 // Navigate to another site and verify that a new SiteInstance was created.
945 const GURL url("http://www.google.com");
946 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
947 if (AreDefaultSiteInstancesEnabled()) {
948 // Verify this remains the default SiteInstance since |url| does
949 // not require a dedicated process.
950 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
951
952 // Navigate to a URL that does require a dedicated process and verify that
953 // the SiteInstance changes.
954 NavigationSimulator::NavigateAndCommitFromBrowser(
955 contents(), isolated_cross_site_url());
956 EXPECT_NE(orig_instance, contents()->GetSiteInstance());
957 } else {
958 EXPECT_NE(orig_instance, contents()->GetSiteInstance());
959 }
960
961 // Cleanup.
962 DeleteContents();
963 }
964
965 // Test that we can find an opener RVH even if it's pending.
966 // http://crbug.com/176252.
TEST_F(WebContentsImplTest,FindOpenerRVHWhenPending)967 TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
968
969 // Navigate to a URL.
970 const GURL url("http://www.google.com");
971 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
972
973 // Start to navigate first tab to a new site, so that it has a pending RVH.
974 const GURL url2("http://www.yahoo.com");
975 auto navigation =
976 NavigationSimulator::CreateBrowserInitiated(url2, contents());
977 navigation->ReadyToCommit();
978 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
979 SiteInstance* instance = pending_rfh->GetSiteInstance();
980
981 // While it is still pending, simulate opening a new tab with the first tab
982 // as its opener. This will call CreateOpenerProxies on the opener to ensure
983 // that an RVH exists.
984 std::unique_ptr<TestWebContents> popup(
985 TestWebContents::Create(browser_context(), instance));
986 popup->SetOpener(contents());
987 contents()->GetRenderManager()->CreateOpenerProxies(instance, nullptr);
988
989 // If swapped out is forbidden, a new proxy should be created for the opener
990 // in |instance|, and we should ensure that its routing ID is returned here.
991 // Otherwise, we should find the pending RFH and not create a new proxy.
992 int opener_frame_routing_id =
993 popup->GetRenderManager()->GetOpenerRoutingID(instance);
994 RenderFrameProxyHost* proxy =
995 contents()->GetRenderManager()->GetRenderFrameProxyHost(instance);
996 EXPECT_TRUE(proxy);
997 EXPECT_EQ(proxy->GetRoutingID(), opener_frame_routing_id);
998
999 // Ensure that committing the navigation removes the proxy.
1000 navigation->Commit();
1001 EXPECT_FALSE(
1002 contents()->GetRenderManager()->GetRenderFrameProxyHost(instance));
1003 }
1004
1005 // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
1006 // to determine whether a navigation is cross-site.
TEST_F(WebContentsImplTest,CrossSiteComparesAgainstCurrentPage)1007 TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
1008 // The assumptions this test makes aren't valid with --site-per-process. For
1009 // example, a cross-site URL won't ever commit in the old RFH. The test also
1010 // assumes that default SiteInstances are enabled, and that aggressive
1011 // BrowsingInstance swapping (even on renderer-initiated navigations) is
1012 // disabled.
1013 if (AreAllSitesIsolatedForTesting() || !AreDefaultSiteInstancesEnabled() ||
1014 IsProactivelySwapBrowsingInstanceEnabled()) {
1015 return;
1016 }
1017
1018 TestRenderFrameHost* orig_rfh = main_test_rfh();
1019 SiteInstanceImpl* instance1 = contents()->GetSiteInstance();
1020
1021 const GURL url("http://www.google.com");
1022
1023 // Navigate to URL.
1024 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1025
1026 // Open a related contents to a second site.
1027 std::unique_ptr<TestWebContents> contents2(
1028 TestWebContents::Create(browser_context(), instance1));
1029 const GURL url2("http://www.yahoo.com");
1030 auto navigation =
1031 NavigationSimulator::CreateBrowserInitiated(url2, contents2.get());
1032 navigation->ReadyToCommit();
1033
1034 // The first RVH in contents2 isn't live yet, so we shortcut the cross site
1035 // pending.
1036 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
1037 navigation->Commit();
1038 SiteInstance* instance2 = contents2->GetSiteInstance();
1039 // With default SiteInstances, navigations in both tabs should
1040 // share the same default SiteInstance, since neither requires a dedicated
1041 // process.
1042 EXPECT_EQ(instance1, instance2);
1043 EXPECT_TRUE(instance1->IsDefaultSiteInstance());
1044 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
1045
1046 // Simulate a link click in first contents to second site. This doesn't
1047 // switch SiteInstances and stays in the default SiteInstance.
1048 NavigationSimulator::NavigateAndCommitFromDocument(url2, orig_rfh);
1049 SiteInstance* instance3 = contents()->GetSiteInstance();
1050 EXPECT_EQ(instance1, instance3);
1051 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1052
1053 // Navigate same-site. This also stays in the default SiteInstance.
1054 const GURL url3("http://mail.yahoo.com");
1055 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url3);
1056 SiteInstance* instance4 = contents()->GetSiteInstance();
1057 EXPECT_EQ(instance1, instance4);
1058 }
1059
1060 // Test that the onbeforeunload and onunload handlers run when navigating
1061 // across site boundaries.
TEST_F(WebContentsImplTest,CrossSiteUnloadHandlers)1062 TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
1063 TestRenderFrameHost* orig_rfh = main_test_rfh();
1064 SiteInstance* instance1 = contents()->GetSiteInstance();
1065
1066 // Navigate to URL. First URL should use first RenderViewHost.
1067 const GURL url("http://www.google.com");
1068 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1069 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1070 EXPECT_EQ(orig_rfh, main_test_rfh());
1071
1072 // Navigate to new site, but simulate an onbeforeunload denial.
1073 const GURL url2("http://www.yahoo.com");
1074 controller().LoadURL(
1075 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1076 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1077 orig_rfh->SimulateBeforeUnloadCompleted(false);
1078 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
1079 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1080 EXPECT_EQ(orig_rfh, main_test_rfh());
1081
1082 // Navigate again, but simulate an onbeforeunload approval.
1083 controller().LoadURL(
1084 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1085 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1086 auto navigation = NavigationSimulator::CreateFromPending(contents());
1087 navigation->ReadyToCommit();
1088 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
1089 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1090 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
1091
1092 // DidNavigate from the pending page.
1093 navigation->Commit();
1094 SiteInstance* instance2 = contents()->GetSiteInstance();
1095 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1096 EXPECT_EQ(pending_rfh, main_test_rfh());
1097 EXPECT_NE(instance1, instance2);
1098 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
1099 }
1100
1101 // Test that during a slow cross-site navigation, the original renderer can
1102 // navigate to a different URL and have it displayed, canceling the slow
1103 // navigation.
TEST_F(WebContentsImplTest,CrossSiteNavigationPreempted)1104 TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
1105 TestRenderFrameHost* orig_rfh = main_test_rfh();
1106 SiteInstance* instance1 = contents()->GetSiteInstance();
1107
1108 // Navigate to URL. First URL should use first RenderFrameHost.
1109 const GURL url("http://www.google.com");
1110 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1111 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1112 EXPECT_EQ(orig_rfh, main_test_rfh());
1113
1114 // Navigate to new site.
1115 const GURL url2("http://www.yahoo.com");
1116 controller().LoadURL(
1117 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1118 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1119 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1120
1121 // Suppose the original renderer navigates before the new one is ready.
1122 NavigationSimulator::NavigateAndCommitFromDocument(
1123 GURL("http://www.google.com/foo"), orig_rfh);
1124
1125 // Verify that the pending navigation is cancelled.
1126 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
1127 SiteInstance* instance2 = contents()->GetSiteInstance();
1128 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1129 EXPECT_EQ(orig_rfh, main_test_rfh());
1130 EXPECT_EQ(instance1, instance2);
1131 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
1132 }
1133
1134 // Tests that if we go back twice (same-site then cross-site), and the same-site
1135 // RFH commits first, the cross-site RFH's navigation is canceled.
1136 // TODO(avi,creis): Consider changing this behavior to better match the user's
1137 // intent.
TEST_F(WebContentsImplTest,CrossSiteNavigationBackPreempted)1138 TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
1139 // Start with a web ui page, which gets a new RVH with WebUI bindings.
1140 GURL url1(std::string(kChromeUIScheme) + "://" +
1141 std::string(kChromeUIGpuHost));
1142 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1143 TestRenderFrameHost* webui_rfh = main_test_rfh();
1144 NavigationEntry* entry1 = controller().GetLastCommittedEntry();
1145 SiteInstance* instance1 = contents()->GetSiteInstance();
1146
1147 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1148 EXPECT_EQ(url1, entry1->GetURL());
1149 EXPECT_EQ(instance1,
1150 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1151 EXPECT_TRUE(webui_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1152
1153 // Navigate to new site.
1154 const GURL url2("http://www.google.com");
1155 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1156 TestRenderFrameHost* google_rfh = main_test_rfh();
1157 NavigationEntry* entry2 = controller().GetLastCommittedEntry();
1158 SiteInstance* instance2 = contents()->GetSiteInstance();
1159
1160 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1161 EXPECT_NE(instance1, instance2);
1162 EXPECT_FALSE(contents()->GetPendingMainFrame());
1163 EXPECT_EQ(url2, entry2->GetURL());
1164 EXPECT_EQ(instance2,
1165 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1166 EXPECT_FALSE(google_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1167
1168 // Navigate to third page on same site.
1169 const GURL url3("http://news.google.com");
1170 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url3);
1171 NavigationEntry* entry3 = controller().GetLastCommittedEntry();
1172 SiteInstance* instance3 = contents()->GetSiteInstance();
1173
1174 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1175 EXPECT_EQ(google_rfh, main_test_rfh());
1176 EXPECT_EQ(instance2, instance3);
1177 EXPECT_FALSE(contents()->GetPendingMainFrame());
1178 EXPECT_EQ(url3, entry3->GetURL());
1179 EXPECT_EQ(instance3,
1180 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1181
1182 // Go back within the site.
1183 auto back_navigation1 =
1184 NavigationSimulator::CreateHistoryNavigation(-1, contents());
1185 back_navigation1->Start();
1186 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1187 EXPECT_EQ(entry2, controller().GetPendingEntry());
1188
1189 // Before that commits, go back again.
1190 back_navigation1->ReadyToCommit();
1191 auto back_navigation2 =
1192 NavigationSimulator::CreateHistoryNavigation(-1, contents());
1193 back_navigation2->Start();
1194 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1195 EXPECT_TRUE(contents()->GetPendingMainFrame());
1196 EXPECT_EQ(entry1, controller().GetPendingEntry());
1197
1198 // DidNavigate from the first back. This aborts the second back's pending RFH.
1199 back_navigation1->Commit();
1200
1201 // We should commit this page and forget about the second back.
1202 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1203 EXPECT_FALSE(controller().GetPendingEntry());
1204 EXPECT_EQ(google_rfh, main_test_rfh());
1205 EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
1206
1207 // We should not have corrupted the NTP entry.
1208 EXPECT_EQ(instance3,
1209 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1210 EXPECT_EQ(instance2,
1211 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1212 EXPECT_EQ(instance1,
1213 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1214 EXPECT_EQ(url1, entry1->GetURL());
1215 }
1216
1217 // Tests that if we go back twice (same-site then cross-site), and the cross-
1218 // site RFH commits first, we ignore the now-swapped-out RFH's commit.
TEST_F(WebContentsImplTest,CrossSiteNavigationBackOldNavigationIgnored)1219 TEST_F(WebContentsImplTest, CrossSiteNavigationBackOldNavigationIgnored) {
1220 // Start with a web ui page, which gets a new RFH with WebUI bindings.
1221 GURL url1(std::string(kChromeUIScheme) + "://" +
1222 std::string(kChromeUIGpuHost));
1223 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1224 TestRenderFrameHost* webui_rfh = main_test_rfh();
1225 NavigationEntry* entry1 = controller().GetLastCommittedEntry();
1226 SiteInstance* instance1 = contents()->GetSiteInstance();
1227
1228 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1229 EXPECT_EQ(url1, entry1->GetURL());
1230 EXPECT_EQ(instance1,
1231 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1232 EXPECT_TRUE(webui_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1233
1234 // Navigate to new site.
1235 const GURL url2("http://www.google.com");
1236 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1237 TestRenderFrameHost* google_rfh = main_test_rfh();
1238 NavigationEntry* entry2 = controller().GetLastCommittedEntry();
1239 SiteInstance* instance2 = contents()->GetSiteInstance();
1240
1241 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1242 EXPECT_NE(instance1, instance2);
1243 EXPECT_FALSE(contents()->GetPendingMainFrame());
1244 EXPECT_EQ(url2, entry2->GetURL());
1245 EXPECT_EQ(instance2,
1246 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1247 EXPECT_FALSE(google_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
1248
1249 // Navigate to third page on same site.
1250 const GURL url3("http://google.com/foo");
1251 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url3);
1252 NavigationEntry* entry3 = controller().GetLastCommittedEntry();
1253 SiteInstance* instance3 = contents()->GetSiteInstance();
1254
1255 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1256 EXPECT_EQ(google_rfh, main_test_rfh());
1257 EXPECT_EQ(instance2, instance3);
1258 EXPECT_FALSE(contents()->GetPendingMainFrame());
1259 EXPECT_EQ(url3, entry3->GetURL());
1260 EXPECT_EQ(instance3,
1261 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1262
1263 // Go back within the site.
1264 auto back_navigation1 =
1265 NavigationSimulator::CreateHistoryNavigation(-1, contents());
1266 back_navigation1->ReadyToCommit();
1267 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1268 EXPECT_EQ(entry2, controller().GetPendingEntry());
1269
1270 // Before that commits, go back again.
1271 auto back_navigation2 =
1272 NavigationSimulatorImpl::CreateHistoryNavigation(-1, contents());
1273 back_navigation2->set_drop_unload_ack(true);
1274 back_navigation2->ReadyToCommit();
1275 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1276 EXPECT_TRUE(contents()->GetPendingMainFrame());
1277 EXPECT_EQ(entry1, controller().GetPendingEntry());
1278 webui_rfh = contents()->GetPendingMainFrame();
1279
1280 // DidNavigate from the second back.
1281 // Note that the process in instance1 is gone at this point, but we will still
1282 // use instance1 and entry1 because IsSuitableForURL will return true when
1283 // there is no process and the site URL matches.
1284 back_navigation2->Commit();
1285
1286 // That should have landed us on the first entry.
1287 EXPECT_EQ(entry1, controller().GetLastCommittedEntry());
1288
1289 // When the second back commits, it should be ignored.
1290 contents()->TestDidNavigate(google_rfh, entry2->GetUniqueID(), false, url2,
1291 ui::PAGE_TRANSITION_TYPED);
1292 EXPECT_EQ(entry1, controller().GetLastCommittedEntry());
1293
1294 // The newly created process for url1 should be locked to chrome://gpu.
1295 RenderProcessHost* new_process = contents()->GetMainFrame()->GetProcess();
1296 auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
1297 EXPECT_TRUE(policy->CanAccessDataForOrigin(new_process->GetID(), url1));
1298 EXPECT_FALSE(policy->CanAccessDataForOrigin(new_process->GetID(), url2));
1299 }
1300
1301 // Test that during a slow cross-site navigation, a sub-frame navigation in the
1302 // original renderer will not cancel the slow navigation (bug 42029).
TEST_F(WebContentsImplTest,CrossSiteNavigationNotPreemptedByFrame)1303 TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
1304 TestRenderFrameHost* orig_rfh = main_test_rfh();
1305
1306 // Navigate to URL. First URL should use the original RenderFrameHost.
1307 const GURL url("http://www.google.com");
1308 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1309 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1310 EXPECT_EQ(orig_rfh, main_test_rfh());
1311
1312 // Start navigating to new site.
1313 const GURL url2("http://www.yahoo.com");
1314 controller().LoadURL(
1315 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1316
1317 // Simulate a sub-frame navigation arriving and ensure the RVH is still
1318 // waiting for a before unload response.
1319 TestRenderFrameHost* child_rfh = orig_rfh->AppendChild("subframe");
1320 child_rfh->SendNavigateWithTransition(0, false,
1321 GURL("http://google.com/frame"),
1322 ui::PAGE_TRANSITION_AUTO_SUBFRAME);
1323 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1324
1325 // Now simulate the onbeforeunload approval and verify the navigation is
1326 // not canceled.
1327 orig_rfh->PrepareForCommit();
1328 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
1329 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1330 }
1331
1332 // Test that a cross-site navigation is not preempted if the previous
1333 // renderer sends a FrameNavigate message just before being told to stop.
1334 // We should only preempt the cross-site navigation if the previous renderer
1335 // has started a new navigation. See http://crbug.com/79176.
TEST_F(WebContentsImplTest,CrossSiteNotPreemptedDuringBeforeUnload)1336 TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
1337 const GURL kUrl("http://foo");
1338 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kUrl);
1339
1340 // First, make a non-user initiated same-site navigation.
1341 const GURL kSameSiteUrl("http://foo/1");
1342 TestRenderFrameHost* orig_rfh = main_test_rfh();
1343 auto same_site_navigation = NavigationSimulator::CreateRendererInitiated(
1344 kSameSiteUrl, main_test_rfh());
1345 same_site_navigation->SetHasUserGesture(false);
1346 same_site_navigation->ReadyToCommit();
1347 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1348
1349 // Navigate to a new site, with the beforeunload request in flight.
1350 const GURL kCrossSiteUrl("http://www.yahoo.com");
1351 auto cross_site_navigation = NavigationSimulatorImpl::CreateBrowserInitiated(
1352 kCrossSiteUrl, contents());
1353 cross_site_navigation->set_block_invoking_before_unload_completed_callback(
1354 true);
1355 cross_site_navigation->Start();
1356 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
1357 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1358 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_completion());
1359 EXPECT_NE(orig_rfh, pending_rfh);
1360
1361 // Suppose the first navigation tries to commit now, with a
1362 // FrameMsg_Stop in flight. This should not cancel the pending navigation,
1363 // but it should act as if the beforeunload completion callback had been
1364 // invoked.
1365 same_site_navigation->Commit();
1366 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1367 EXPECT_EQ(orig_rfh, main_test_rfh());
1368 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_completion());
1369 // It should commit.
1370 ASSERT_EQ(2, controller().GetEntryCount());
1371 EXPECT_EQ(kSameSiteUrl, controller().GetLastCommittedEntry()->GetURL());
1372
1373 // The pending navigation should be able to commit successfully.
1374 cross_site_navigation->Commit();
1375 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1376 EXPECT_EQ(pending_rfh, main_test_rfh());
1377 EXPECT_EQ(3, controller().GetEntryCount());
1378 }
1379
1380 // Test that NavigationEntries have the correct page state after going
1381 // forward and back. Prevents regression for bug 1116137.
TEST_F(WebContentsImplTest,NavigationEntryContentState)1382 TEST_F(WebContentsImplTest, NavigationEntryContentState) {
1383
1384 // Navigate to URL. There should be no committed entry yet.
1385 const GURL url("http://www.google.com");
1386 auto navigation =
1387 NavigationSimulator::CreateBrowserInitiated(url, contents());
1388 navigation->ReadyToCommit();
1389 NavigationEntry* entry = controller().GetLastCommittedEntry();
1390 EXPECT_EQ(nullptr, entry);
1391
1392 // Committed entry should have page state.
1393 navigation->Commit();
1394 entry = controller().GetLastCommittedEntry();
1395 EXPECT_TRUE(entry->GetPageState().IsValid());
1396
1397 // Navigate to same site.
1398 const GURL url2("http://images.google.com");
1399 auto navigation2 =
1400 NavigationSimulator::CreateBrowserInitiated(url2, contents());
1401 navigation2->ReadyToCommit();
1402 entry = controller().GetLastCommittedEntry();
1403 EXPECT_TRUE(entry->GetPageState().IsValid());
1404
1405 // Committed entry should have page state.
1406 navigation2->Commit();
1407 entry = controller().GetLastCommittedEntry();
1408 EXPECT_TRUE(entry->GetPageState().IsValid());
1409
1410 // Now go back. Committed entry should still have page state.
1411 NavigationSimulator::GoBack(contents());
1412 entry = controller().GetLastCommittedEntry();
1413 EXPECT_TRUE(entry->GetPageState().IsValid());
1414 }
1415
1416 // Test that NavigationEntries have the correct page state and SiteInstance
1417 // state after opening a new window to about:blank. Prevents regression for
1418 // bugs b/1116137 and http://crbug.com/111975.
TEST_F(WebContentsImplTest,NavigationEntryContentStateNewWindow)1419 TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
1420 TestRenderFrameHost* orig_rfh = main_test_rfh();
1421
1422 // Navigate to about:blank.
1423 const GURL url(url::kAboutBlankURL);
1424 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1425
1426 // Should have a page state here.
1427 NavigationEntry* entry = controller().GetLastCommittedEntry();
1428 EXPECT_TRUE(entry->GetPageState().IsValid());
1429
1430 // The SiteInstance should be available for other navigations to use.
1431 NavigationEntryImpl* entry_impl =
1432 NavigationEntryImpl::FromNavigationEntry(entry);
1433 EXPECT_FALSE(entry_impl->site_instance()->HasSite());
1434 int32_t site_instance_id = entry_impl->site_instance()->GetId();
1435
1436 // Navigating to a normal page should not cause a process swap.
1437 const GURL new_url("http://www.google.com");
1438 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), new_url);
1439
1440 EXPECT_EQ(orig_rfh, main_test_rfh());
1441 NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry(
1442 controller().GetLastCommittedEntry());
1443 EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId());
1444 EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
1445 }
1446
1447 namespace {
1448
ExpectTrue(bool value)1449 void ExpectTrue(bool value) {
1450 DCHECK(value);
1451 }
1452
ExpectFalse(bool value)1453 void ExpectFalse(bool value) {
1454 DCHECK(!value);
1455 }
1456
1457 } // namespace
1458
1459 // Tests that fullscreen is exited throughout the object hierarchy when
1460 // navigating to a new page.
TEST_F(WebContentsImplTest,NavigationExitsFullscreen)1461 TEST_F(WebContentsImplTest, NavigationExitsFullscreen) {
1462 FakeFullscreenDelegate fake_delegate;
1463 contents()->SetDelegate(&fake_delegate);
1464 TestRenderFrameHost* orig_rfh = main_test_rfh();
1465
1466 // Navigate to a site.
1467 const GURL url("http://www.google.com");
1468 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1469 EXPECT_EQ(orig_rfh, main_test_rfh());
1470
1471 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1472 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1473 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1474 main_test_rfh()->frame_tree_node()->UpdateUserActivationState(
1475 blink::mojom::UserActivationUpdateType::kNotifyActivation);
1476 orig_rfh->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1477 base::BindOnce(&ExpectTrue));
1478 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1479 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1480
1481 // Navigate to a new site.
1482 const GURL url2("http://www.yahoo.com");
1483 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1484
1485 // Confirm fullscreen has exited.
1486 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1487 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1488
1489 contents()->SetDelegate(nullptr);
1490 }
1491
1492 // Tests that fullscreen is exited throughout the object hierarchy when
1493 // instructing NavigationController to GoBack() or GoForward().
TEST_F(WebContentsImplTest,HistoryNavigationExitsFullscreen)1494 TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) {
1495 FakeFullscreenDelegate fake_delegate;
1496 contents()->SetDelegate(&fake_delegate);
1497 TestRenderFrameHost* orig_rfh = main_test_rfh();
1498
1499 // Navigate to a site.
1500 const GURL url("http://www.google.com");
1501 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1502 EXPECT_EQ(orig_rfh, main_test_rfh());
1503
1504 // Now, navigate to another page on the same site.
1505 const GURL url2("http://www.google.com/search?q=kittens");
1506 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1507 EXPECT_EQ(orig_rfh, main_test_rfh());
1508
1509 // Sanity-check: Confirm we're not starting out in fullscreen mode.
1510 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1511 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1512
1513 for (int i = 0; i < 2; ++i) {
1514 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1515 main_test_rfh()->frame_tree_node()->UpdateUserActivationState(
1516 blink::mojom::UserActivationUpdateType::kNotifyActivation);
1517 orig_rfh->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1518 base::BindOnce(&ExpectTrue));
1519 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1520 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1521
1522 // Navigate backward (or forward).
1523 if (i == 0)
1524 NavigationSimulator::GoBack(contents());
1525 else
1526 NavigationSimulator::GoForward(contents());
1527
1528 // Confirm fullscreen has exited.
1529 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1530 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1531 }
1532
1533 contents()->SetDelegate(nullptr);
1534 }
1535
1536 // Tests that fullscreen is exited throughout the object hierarchy on a renderer
1537 // crash.
TEST_F(WebContentsImplTest,CrashExitsFullscreen)1538 TEST_F(WebContentsImplTest, CrashExitsFullscreen) {
1539 FakeFullscreenDelegate fake_delegate;
1540 contents()->SetDelegate(&fake_delegate);
1541
1542 // Navigate to a site.
1543 const GURL url("http://www.google.com");
1544 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1545
1546 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1547 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1548 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1549 main_test_rfh()->frame_tree_node()->UpdateUserActivationState(
1550 blink::mojom::UserActivationUpdateType::kNotifyActivation);
1551 main_test_rfh()->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1552 base::BindOnce(&ExpectTrue));
1553 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1554 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1555
1556 // Crash the renderer.
1557 main_test_rfh()->GetProcess()->SimulateCrash();
1558
1559 // Confirm fullscreen has exited.
1560 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1561 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1562
1563 contents()->SetDelegate(nullptr);
1564 }
1565
TEST_F(WebContentsImplTest,FailEnterFullscreenWhenNoUserActivationNoOrientationChange)1566 TEST_F(WebContentsImplTest,
1567 FailEnterFullscreenWhenNoUserActivationNoOrientationChange) {
1568 FakeFullscreenDelegate fake_delegate;
1569 contents()->SetDelegate(&fake_delegate);
1570
1571 // Navigate to a site.
1572 const GURL url("http://www.google.com");
1573 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url);
1574
1575 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1576 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1577 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1578
1579 // When there is no user activation and no orientation change, entering
1580 // fullscreen will fail.
1581 main_test_rfh()->EnterFullscreen(blink::mojom::FullscreenOptions::New(),
1582 base::BindOnce(&ExpectFalse));
1583 EXPECT_FALSE(contents()->HasSeenRecentScreenOrientationChange());
1584 EXPECT_FALSE(
1585 main_test_rfh()->frame_tree_node()->HasTransientUserActivation());
1586 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1587 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1588
1589 contents()->SetDelegate(nullptr);
1590 }
1591
1592 ////////////////////////////////////////////////////////////////////////////////
1593 // Interstitial Tests
1594 ////////////////////////////////////////////////////////////////////////////////
1595
1596 // Test navigating to a page (with the navigation initiated from the browser,
1597 // as when a URL is typed in the location bar) that shows an interstitial and
1598 // creates a new navigation entry, then hiding it without proceeding.
TEST_F(WebContentsImplTest,ShowInterstitialFromBrowserWithNewNavigationDontProceed)1599 TEST_F(WebContentsImplTest,
1600 ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
1601 // Navigate to a page.
1602 GURL url1("http://www.google.com");
1603 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1604 EXPECT_EQ(1, controller().GetEntryCount());
1605
1606 // Initiate a browser navigation that will trigger the interstitial.
1607 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1608 ui::PAGE_TRANSITION_TYPED, std::string());
1609 NavigationEntry* entry = controller().GetPendingEntry();
1610
1611 // Show an interstitial.
1612 TestInterstitialPage::InterstitialState state =
1613 TestInterstitialPage::INVALID;
1614 bool deleted = false;
1615 GURL url2("http://interstitial");
1616 TestInterstitialPage* interstitial =
1617 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1618 TestInterstitialPageStateGuard state_guard(interstitial);
1619 interstitial->Show();
1620 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1621 // The interstitial should not show until its navigation has committed.
1622 EXPECT_FALSE(interstitial->is_showing());
1623 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1624 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1625 // Let's commit the interstitial navigation.
1626 interstitial->TestDidNavigate(interstitial_entry_id, true, url2);
1627 EXPECT_TRUE(interstitial->is_showing());
1628 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1629 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1630 entry = controller().GetVisibleEntry();
1631 ASSERT_NE(nullptr, entry);
1632 EXPECT_TRUE(entry->GetURL() == url2);
1633
1634 // Now don't proceed.
1635 interstitial->DontProceed();
1636 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1637 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1638 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1639 entry = controller().GetVisibleEntry();
1640 ASSERT_NE(nullptr, entry);
1641 EXPECT_TRUE(entry->GetURL() == url1);
1642 EXPECT_EQ(1, controller().GetEntryCount());
1643
1644 RunAllPendingInMessageLoop();
1645 EXPECT_TRUE(deleted);
1646 }
1647
1648 // Test navigating to a page (with the navigation initiated from the renderer,
1649 // as when clicking on a link in the page) that shows an interstitial and
1650 // creates a new navigation entry, then hiding it without proceeding.
TEST_F(WebContentsImplTest,ShowInterstitialFromRendererWithNewNavigationDontProceed)1651 TEST_F(WebContentsImplTest,
1652 ShowInterstitialFromRendererWithNewNavigationDontProceed) {
1653 // Navigate to a page.
1654 GURL url1("http://www.google.com");
1655 NavigationSimulator::NavigateAndCommitFromDocument(url1, main_test_rfh());
1656 EXPECT_EQ(1, controller().GetEntryCount());
1657
1658 // Show an interstitial (no pending entry, the interstitial would have been
1659 // triggered by clicking on a link).
1660 TestInterstitialPage::InterstitialState state =
1661 TestInterstitialPage::INVALID;
1662 bool deleted = false;
1663 GURL url2("http://interstitial");
1664 TestInterstitialPage* interstitial =
1665 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1666 TestInterstitialPageStateGuard state_guard(interstitial);
1667 interstitial->Show();
1668 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1669 // The interstitial should not show until its navigation has committed.
1670 EXPECT_FALSE(interstitial->is_showing());
1671 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1672 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1673 // Let's commit the interstitial navigation.
1674 interstitial->TestDidNavigate(interstitial_entry_id, true, url2);
1675 EXPECT_TRUE(interstitial->is_showing());
1676 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1677 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1678 NavigationEntry* entry = controller().GetVisibleEntry();
1679 ASSERT_NE(nullptr, entry);
1680 EXPECT_TRUE(entry->GetURL() == url2);
1681
1682 // Now don't proceed.
1683 interstitial->DontProceed();
1684 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1685 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1686 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1687 entry = controller().GetVisibleEntry();
1688 ASSERT_NE(nullptr, entry);
1689 EXPECT_TRUE(entry->GetURL() == url1);
1690 EXPECT_EQ(1, controller().GetEntryCount());
1691
1692 RunAllPendingInMessageLoop();
1693 EXPECT_TRUE(deleted);
1694 }
1695
1696 // Test navigating to a page that shows an interstitial without creating a new
1697 // navigation entry (this happens when the interstitial is triggered by a
1698 // sub-resource in the page), then hiding it without proceeding.
TEST_F(WebContentsImplTest,ShowInterstitialNoNewNavigationDontProceed)1699 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) {
1700 // Navigate to a page.
1701 GURL url1("http://www.google.com");
1702 NavigationSimulator::NavigateAndCommitFromDocument(url1, main_test_rfh());
1703 EXPECT_EQ(1, controller().GetEntryCount());
1704
1705 // Show an interstitial.
1706 TestInterstitialPage::InterstitialState state =
1707 TestInterstitialPage::INVALID;
1708 bool deleted = false;
1709 GURL url2("http://interstitial");
1710 TestInterstitialPage* interstitial =
1711 new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1712 TestInterstitialPageStateGuard state_guard(interstitial);
1713 interstitial->Show();
1714 // The interstitial should not show until its navigation has committed.
1715 EXPECT_FALSE(interstitial->is_showing());
1716 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1717 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1718 // Let's commit the interstitial navigation.
1719 interstitial->TestDidNavigate(0, true, url2);
1720 EXPECT_TRUE(interstitial->is_showing());
1721 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1722 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1723 NavigationEntry* entry = controller().GetVisibleEntry();
1724 ASSERT_NE(nullptr, entry);
1725 // The URL specified to the interstitial should have been ignored.
1726 EXPECT_TRUE(entry->GetURL() == url1);
1727
1728 // Now don't proceed.
1729 interstitial->DontProceed();
1730 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1731 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1732 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1733 entry = controller().GetVisibleEntry();
1734 ASSERT_NE(nullptr, entry);
1735 EXPECT_TRUE(entry->GetURL() == url1);
1736 EXPECT_EQ(1, controller().GetEntryCount());
1737
1738 RunAllPendingInMessageLoop();
1739 EXPECT_TRUE(deleted);
1740 }
1741
1742 // Test navigating to a page (with the navigation initiated from the browser,
1743 // as when a URL is typed in the location bar) that shows an interstitial and
1744 // creates a new navigation entry, then proceeding.
TEST_F(WebContentsImplTest,ShowInterstitialFromBrowserNewNavigationProceed)1745 TEST_F(WebContentsImplTest,
1746 ShowInterstitialFromBrowserNewNavigationProceed) {
1747 // Navigate to a page.
1748 GURL url1("http://www.thepage.com/one");
1749 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1750 EXPECT_EQ(1, controller().GetEntryCount());
1751
1752 // Initiate a browser navigation that will trigger the interstitial
1753 GURL evil_url = GURL("http://www.evil.com");
1754 auto navigation =
1755 NavigationSimulator::CreateBrowserInitiated(evil_url, contents());
1756 navigation->Start();
1757
1758 // Show an interstitial.
1759 TestInterstitialPage::InterstitialState state =
1760 TestInterstitialPage::INVALID;
1761 bool deleted = false;
1762 GURL url2("http://interstitial");
1763 TestInterstitialPage* interstitial =
1764 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1765 TestInterstitialPageStateGuard state_guard(interstitial);
1766 interstitial->Show();
1767 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1768 // The interstitial should not show until its navigation has committed.
1769 EXPECT_FALSE(interstitial->is_showing());
1770 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1771 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1772 // Let's commit the interstitial navigation.
1773 interstitial->TestDidNavigate(interstitial_entry_id, true, url2);
1774 EXPECT_TRUE(interstitial->is_showing());
1775 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1776 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1777 NavigationEntry* entry = controller().GetVisibleEntry();
1778 ASSERT_NE(nullptr, entry);
1779 EXPECT_TRUE(entry->GetURL() == url2);
1780
1781 // Then proceed.
1782 interstitial->Proceed();
1783 // The interstitial should show until the new navigation commits.
1784 RunAllPendingInMessageLoop();
1785 ASSERT_FALSE(deleted);
1786 EXPECT_EQ(TestInterstitialPage::OKED, state);
1787 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1788 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1789
1790 // Simulate the navigation to the page, that's when the interstitial gets
1791 // hidden.
1792 navigation->Commit();
1793
1794 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1795 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1796 entry = controller().GetVisibleEntry();
1797 ASSERT_NE(nullptr, entry);
1798 EXPECT_EQ(evil_url, entry->GetURL());
1799
1800 EXPECT_EQ(2, controller().GetEntryCount());
1801
1802 RunAllPendingInMessageLoop();
1803 EXPECT_TRUE(deleted);
1804 }
1805
1806 // Test navigating to a page (with the navigation initiated from the renderer,
1807 // as when clicking on a link in the page) that shows an interstitial and
1808 // creates a new navigation entry, then proceeding.
TEST_F(WebContentsImplTest,ShowInterstitialFromRendererNewNavigationProceed)1809 TEST_F(WebContentsImplTest,
1810 ShowInterstitialFromRendererNewNavigationProceed) {
1811 // Navigate to a page.
1812 GURL url1("http://www.google.com");
1813 NavigationSimulator::NavigateAndCommitFromDocument(url1, main_test_rfh());
1814 EXPECT_EQ(1, controller().GetEntryCount());
1815
1816 // Show an interstitial.
1817 TestInterstitialPage::InterstitialState state =
1818 TestInterstitialPage::INVALID;
1819 bool deleted = false;
1820 GURL url2("http://interstitial");
1821 TestInterstitialPage* interstitial =
1822 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1823 TestInterstitialPageStateGuard state_guard(interstitial);
1824 interstitial->Show();
1825 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1826 // The interstitial should not show until its navigation has committed.
1827 EXPECT_FALSE(interstitial->is_showing());
1828 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1829 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1830 // Let's commit the interstitial navigation.
1831 interstitial->TestDidNavigate(interstitial_entry_id, true, url2);
1832 EXPECT_TRUE(interstitial->is_showing());
1833 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1834 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1835 NavigationEntry* entry = controller().GetVisibleEntry();
1836 ASSERT_NE(nullptr, entry);
1837 EXPECT_TRUE(entry->GetURL() == url2);
1838
1839 // Then proceed.
1840 interstitial->Proceed();
1841 // The interstitial should show until the new navigation commits.
1842 RunAllPendingInMessageLoop();
1843 ASSERT_FALSE(deleted);
1844 EXPECT_EQ(TestInterstitialPage::OKED, state);
1845 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1846 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1847
1848 // Simulate the navigation to the page, that's when the interstitial gets
1849 // hidden.
1850 GURL url3("http://www.thepage.com");
1851 NavigationSimulator::NavigateAndCommitFromDocument(url3, main_test_rfh());
1852
1853 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1854 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1855 entry = controller().GetVisibleEntry();
1856 ASSERT_NE(nullptr, entry);
1857 EXPECT_TRUE(entry->GetURL() == url3);
1858
1859 EXPECT_EQ(2, controller().GetEntryCount());
1860
1861 RunAllPendingInMessageLoop();
1862 EXPECT_TRUE(deleted);
1863 }
1864
1865 // Test navigating to a page that shows an interstitial without creating a new
1866 // navigation entry (this happens when the interstitial is triggered by a
1867 // sub-resource in the page), then proceeding.
TEST_F(WebContentsImplTest,ShowInterstitialNoNewNavigationProceed)1868 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) {
1869 // Navigate to a page so we have a navigation entry in the controller.
1870 GURL url1("http://www.google.com");
1871 NavigationSimulator::NavigateAndCommitFromDocument(url1, main_test_rfh());
1872 EXPECT_EQ(1, controller().GetEntryCount());
1873
1874 // Show an interstitial.
1875 TestInterstitialPage::InterstitialState state =
1876 TestInterstitialPage::INVALID;
1877 bool deleted = false;
1878 GURL url2("http://interstitial");
1879 TestInterstitialPage* interstitial =
1880 new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1881 TestInterstitialPageStateGuard state_guard(interstitial);
1882 interstitial->Show();
1883 // The interstitial should not show until its navigation has committed.
1884 EXPECT_FALSE(interstitial->is_showing());
1885 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1886 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1887 // Let's commit the interstitial navigation.
1888 interstitial->TestDidNavigate(0, true, url2);
1889 EXPECT_TRUE(interstitial->is_showing());
1890 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1891 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1892 NavigationEntry* entry = controller().GetVisibleEntry();
1893 ASSERT_NE(nullptr, entry);
1894 // The URL specified to the interstitial should have been ignored.
1895 EXPECT_TRUE(entry->GetURL() == url1);
1896
1897 // Then proceed.
1898 interstitial->Proceed();
1899 // Since this is not a new navigation, the previous page is dismissed right
1900 // away and shows the original page.
1901 EXPECT_EQ(TestInterstitialPage::OKED, state);
1902 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1903 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1904 entry = controller().GetVisibleEntry();
1905 ASSERT_NE(nullptr, entry);
1906 EXPECT_TRUE(entry->GetURL() == url1);
1907
1908 EXPECT_EQ(1, controller().GetEntryCount());
1909
1910 RunAllPendingInMessageLoop();
1911 EXPECT_TRUE(deleted);
1912 }
1913
1914 // Test navigating to a page that shows an interstitial, then navigating away.
TEST_F(WebContentsImplTest,ShowInterstitialThenNavigate)1915 TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) {
1916 // Show interstitial.
1917 TestInterstitialPage::InterstitialState state =
1918 TestInterstitialPage::INVALID;
1919 bool deleted = false;
1920 GURL url("http://interstitial");
1921 TestInterstitialPage* interstitial =
1922 new TestInterstitialPage(contents(), true, url, &state, &deleted);
1923 TestInterstitialPageStateGuard state_guard(interstitial);
1924 interstitial->Show();
1925 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1926 interstitial->TestDidNavigate(interstitial_entry_id, true, url);
1927
1928 // While interstitial showing, navigate to a new URL.
1929 const GURL url2("http://www.yahoo.com");
1930 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
1931
1932 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1933
1934 RunAllPendingInMessageLoop();
1935 EXPECT_TRUE(deleted);
1936 }
1937
1938 // Test navigating to a page that shows an interstitial, then going back.
TEST_F(WebContentsImplTest,ShowInterstitialThenGoBack)1939 TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) {
1940 // Navigate to a page so we have a navigation entry in the controller.
1941 GURL url1("http://www.google.com");
1942 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1943 EXPECT_EQ(1, controller().GetEntryCount());
1944
1945 // Show interstitial.
1946 TestInterstitialPage::InterstitialState state =
1947 TestInterstitialPage::INVALID;
1948 bool deleted = false;
1949 GURL interstitial_url("http://interstitial");
1950 TestInterstitialPage* interstitial =
1951 new TestInterstitialPage(contents(), true, interstitial_url,
1952 &state, &deleted);
1953 TestInterstitialPageStateGuard state_guard(interstitial);
1954 interstitial->Show();
1955 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1956 interstitial->TestDidNavigate(interstitial_entry_id, true,
1957 interstitial_url);
1958 EXPECT_EQ(2, controller().GetEntryCount());
1959
1960 // While the interstitial is showing, go back. This will dismiss the
1961 // interstitial and not initiate a navigation, but just show the existing
1962 // RenderFrameHost.
1963 controller().GoBack();
1964
1965 // Make sure we are back to the original page and that the interstitial is
1966 // gone.
1967 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1968 NavigationEntry* entry = controller().GetVisibleEntry();
1969 ASSERT_TRUE(entry);
1970 EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1971 EXPECT_EQ(1, controller().GetEntryCount());
1972
1973 RunAllPendingInMessageLoop();
1974 EXPECT_TRUE(deleted);
1975 }
1976
1977 // Test navigating to a page that shows an interstitial, has a renderer crash,
1978 // and then goes back.
TEST_F(WebContentsImplTest,ShowInterstitialCrashRendererThenGoBack)1979 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) {
1980 // Navigate to a page so we have a navigation entry in the controller.
1981 GURL url1("http://www.google.com");
1982 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
1983 EXPECT_EQ(1, controller().GetEntryCount());
1984 NavigationEntry* entry = controller().GetLastCommittedEntry();
1985
1986 // Show interstitial.
1987 TestInterstitialPage::InterstitialState state =
1988 TestInterstitialPage::INVALID;
1989 bool deleted = false;
1990 GURL interstitial_url("http://interstitial");
1991 TestInterstitialPage* interstitial =
1992 new TestInterstitialPage(contents(), true, interstitial_url,
1993 &state, &deleted);
1994 TestInterstitialPageStateGuard state_guard(interstitial);
1995 interstitial->Show();
1996 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1997 interstitial->TestDidNavigate(interstitial_entry_id, true,
1998 interstitial_url);
1999
2000 // Crash the renderer
2001 main_test_rfh()->GetProcess()->SimulateCrash();
2002
2003 // While the interstitial is showing, go back. This will dismiss the
2004 // interstitial and not initiate a navigation, but just show the existing
2005 // RenderFrameHost.
2006 controller().GoBack();
2007
2008 // Make sure we are back to the original page and that the interstitial is
2009 // gone.
2010 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2011 entry = controller().GetVisibleEntry();
2012 ASSERT_TRUE(entry);
2013 EXPECT_EQ(url1.spec(), entry->GetURL().spec());
2014
2015 RunAllPendingInMessageLoop();
2016 EXPECT_TRUE(deleted);
2017 }
2018
2019 // Test navigating to a page that shows an interstitial, has the renderer crash,
2020 // and then navigates to the interstitial.
TEST_F(WebContentsImplTest,ShowInterstitialCrashRendererThenNavigate)2021 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) {
2022 // Navigate to a page so we have a navigation entry in the controller.
2023 GURL url1("http://www.google.com");
2024 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
2025 EXPECT_EQ(1, controller().GetEntryCount());
2026
2027 // Show interstitial.
2028 TestInterstitialPage::InterstitialState state =
2029 TestInterstitialPage::INVALID;
2030 bool deleted = false;
2031 GURL interstitial_url("http://interstitial");
2032 TestInterstitialPage* interstitial =
2033 new TestInterstitialPage(contents(), true, interstitial_url,
2034 &state, &deleted);
2035 TestInterstitialPageStateGuard state_guard(interstitial);
2036 interstitial->Show();
2037 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2038
2039 // Crash the renderer
2040 main_test_rfh()->GetProcess()->SimulateCrash();
2041
2042 interstitial->TestDidNavigate(interstitial_entry_id, true,
2043 interstitial_url);
2044 }
2045
2046 // Test navigating to a page that shows an interstitial, then close the
2047 // contents.
TEST_F(WebContentsImplTest,ShowInterstitialThenCloseTab)2048 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) {
2049 // Show interstitial.
2050 TestInterstitialPage::InterstitialState state =
2051 TestInterstitialPage::INVALID;
2052 bool deleted = false;
2053 GURL url("http://interstitial");
2054 TestInterstitialPage* interstitial =
2055 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2056 TestInterstitialPageStateGuard state_guard(interstitial);
2057 interstitial->Show();
2058 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2059 interstitial->TestDidNavigate(interstitial_entry_id, true, url);
2060
2061 // Now close the contents.
2062 DeleteContents();
2063 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2064
2065 RunAllPendingInMessageLoop();
2066 EXPECT_TRUE(deleted);
2067 }
2068
2069 // Test navigating to a page that shows an interstitial, then close the
2070 // contents.
TEST_F(WebContentsImplTest,ShowInterstitialThenCloseAndShutdown)2071 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) {
2072 // Show interstitial.
2073 TestInterstitialPage::InterstitialState state =
2074 TestInterstitialPage::INVALID;
2075 bool deleted = false;
2076 GURL url("http://interstitial");
2077 TestInterstitialPage* interstitial =
2078 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2079 TestInterstitialPageStateGuard state_guard(interstitial);
2080 interstitial->Show();
2081 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2082 interstitial->TestDidNavigate(interstitial_entry_id, true, url);
2083 TestRenderFrameHost* rfh =
2084 static_cast<TestRenderFrameHost*>(interstitial->GetMainFrame());
2085
2086 // Now close the contents.
2087 DeleteContents();
2088 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2089
2090 // Before the interstitial has a chance to process its shutdown task,
2091 // simulate quitting the browser. This goes through all processes and
2092 // tells them to destruct.
2093 rfh->GetProcess()->SimulateCrash();
2094
2095 RunAllPendingInMessageLoop();
2096 EXPECT_TRUE(deleted);
2097 }
2098
2099 // Test for https://crbug.com/730592, where deleting a WebContents while its
2100 // interstitial is navigating could lead to a crash.
TEST_F(WebContentsImplTest,CreateInterstitialForClosingTab)2101 TEST_F(WebContentsImplTest, CreateInterstitialForClosingTab) {
2102 // Navigate to a page.
2103 GURL url1("http://www.google.com");
2104 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
2105 EXPECT_EQ(1, controller().GetEntryCount());
2106
2107 // Initiate a browser navigation that will trigger an interstitial.
2108 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
2109 ui::PAGE_TRANSITION_TYPED, std::string());
2110
2111 // Show an interstitial.
2112 TestInterstitialPage::InterstitialState state = TestInterstitialPage::INVALID;
2113 bool deleted = false;
2114 GURL url2("http://interstitial");
2115 TestInterstitialPage* interstitial =
2116 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2117 TestInterstitialPageStateGuard state_guard(interstitial);
2118 interstitial->Show();
2119 TestRenderFrameHost* interstitial_rfh =
2120 static_cast<TestRenderFrameHost*>(interstitial->GetMainFrame());
2121
2122 // Ensure the InterfaceProvider for the initial empty document is bound.
2123 interstitial_rfh->InitializeRenderFrameIfNeeded();
2124
2125 // The interstitial should not show until its navigation has committed.
2126 EXPECT_FALSE(interstitial->is_showing());
2127 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2128 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2129
2130 // Close the tab before the interstitial commits.
2131 DeleteContents();
2132 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2133
2134 // Simulate a commit in the interstitial page, which should not crash.
2135 interstitial_rfh->SimulateNavigationCommit(url2);
2136
2137 RunAllPendingInMessageLoop();
2138 EXPECT_TRUE(deleted);
2139 }
2140
2141 // Test for https://crbug.com/703655, where navigating a tab and showing an
2142 // interstitial could race.
TEST_F(WebContentsImplTest,TabNavigationDoesntRaceInterstitial)2143 TEST_F(WebContentsImplTest, TabNavigationDoesntRaceInterstitial) {
2144 // Navigate to a page.
2145 GURL url1("http://www.google.com");
2146 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
2147 EXPECT_EQ(1, controller().GetEntryCount());
2148
2149 // Initiate a browser navigation that will trigger an interstitial.
2150 GURL evil_url("http://www.evil.com");
2151 controller().LoadURL(evil_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
2152 std::string());
2153 NavigationEntry* entry = contents()->GetController().GetPendingEntry();
2154 ASSERT_TRUE(entry);
2155 EXPECT_EQ(evil_url, entry->GetURL());
2156
2157 // Show an interstitial.
2158 TestInterstitialPage::InterstitialState state = TestInterstitialPage::INVALID;
2159 bool deleted = false;
2160 GURL url2("http://interstitial");
2161 TestInterstitialPage* interstitial =
2162 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2163 TestInterstitialPageStateGuard state_guard(interstitial);
2164 interstitial->Show();
2165 // The interstitial should not show until its navigation has committed.
2166 EXPECT_FALSE(interstitial->is_showing());
2167 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2168 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2169
2170 // At this point, there is an interstitial that has been instructed to show
2171 // but has not yet committed its own navigation. This is a window; navigate
2172 // back one page within this window.
2173 //
2174 // Because the page with the interstitial did not commit, this invokes an
2175 // early return in NavigationControllerImpl::NavigateToPendingEntry which just
2176 // drops the pending entry, so no committing is required.
2177 controller().GoBack();
2178 entry = contents()->GetController().GetPendingEntry();
2179 ASSERT_FALSE(entry);
2180
2181 // The interstitial should be gone.
2182 RunAllPendingInMessageLoop();
2183 EXPECT_TRUE(deleted);
2184 }
2185
2186 // Test that after Proceed is called and an interstitial is still shown, no more
2187 // commands get executed.
TEST_F(WebContentsImplTest,ShowInterstitialProceedMultipleCommands)2188 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
2189 // Navigate to a page so we have a navigation entry in the controller.
2190 GURL url1("http://www.google.com");
2191 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
2192 EXPECT_EQ(1, controller().GetEntryCount());
2193
2194 // Show an interstitial.
2195 TestInterstitialPage::InterstitialState state =
2196 TestInterstitialPage::INVALID;
2197 bool deleted = false;
2198 GURL url2("http://interstitial");
2199 TestInterstitialPage* interstitial =
2200 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2201 TestInterstitialPageStateGuard state_guard(interstitial);
2202 interstitial->Show();
2203 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2204 interstitial->TestDidNavigate(interstitial_entry_id, true, url2);
2205
2206 // Run a command.
2207 EXPECT_EQ(0, interstitial->command_received_count());
2208 interstitial->TestDomOperationResponse("toto");
2209 EXPECT_EQ(1, interstitial->command_received_count());
2210
2211 // Then proceed.
2212 interstitial->Proceed();
2213 RunAllPendingInMessageLoop();
2214 ASSERT_FALSE(deleted);
2215
2216 // While the navigation to the new page is pending, send other commands, they
2217 // should be ignored.
2218 interstitial->TestDomOperationResponse("hello");
2219 interstitial->TestDomOperationResponse("hi");
2220 EXPECT_EQ(1, interstitial->command_received_count());
2221 }
2222
2223 // Test showing an interstitial while another interstitial is already showing.
TEST_F(WebContentsImplTest,ShowInterstitialOnInterstitial)2224 TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) {
2225 // Navigate to a page so we have a navigation entry in the controller.
2226 GURL start_url("http://www.thepage.com/one");
2227 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), start_url);
2228 EXPECT_EQ(1, controller().GetEntryCount());
2229
2230 // Show an interstitial.
2231 TestInterstitialPage::InterstitialState state1 =
2232 TestInterstitialPage::INVALID;
2233 bool deleted1 = false;
2234 GURL url1("http://interstitial1");
2235 TestInterstitialPage* interstitial1 =
2236 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
2237 TestInterstitialPageStateGuard state_guard1(interstitial1);
2238 interstitial1->Show();
2239 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2240 interstitial1->TestDidNavigate(interstitial_entry_id, true, url1);
2241
2242 // Now show another interstitial.
2243 TestInterstitialPage::InterstitialState state2 =
2244 TestInterstitialPage::INVALID;
2245 bool deleted2 = false;
2246 GURL url2("http://interstitial2");
2247 TestInterstitialPage* interstitial2 =
2248 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
2249 TestInterstitialPageStateGuard state_guard2(interstitial2);
2250 interstitial2->Show();
2251 interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2252 interstitial2->TestDidNavigate(interstitial_entry_id, true, url2);
2253
2254 // Showing interstitial2 should have caused interstitial1 to go away.
2255 EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
2256 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2257
2258 RunAllPendingInMessageLoop();
2259 EXPECT_TRUE(deleted1);
2260 ASSERT_FALSE(deleted2);
2261
2262 // Let's make sure interstitial2 is working as intended.
2263 interstitial2->Proceed();
2264 GURL landing_url("http://www.thepage.com/two");
2265 NavigationSimulator::NavigateAndCommitFromDocument(landing_url,
2266 main_test_rfh());
2267
2268 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2269 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2270 NavigationEntry* entry = controller().GetVisibleEntry();
2271 ASSERT_NE(nullptr, entry);
2272 EXPECT_TRUE(entry->GetURL() == landing_url);
2273 EXPECT_EQ(2, controller().GetEntryCount());
2274 RunAllPendingInMessageLoop();
2275 EXPECT_TRUE(deleted2);
2276 }
2277
2278 // Test showing an interstitial, proceeding and then navigating to another
2279 // interstitial.
TEST_F(WebContentsImplTest,ShowInterstitialProceedShowInterstitial)2280 TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) {
2281 // Navigate to a page so we have a navigation entry in the controller.
2282 GURL start_url("http://www.thepage.com/one");
2283 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), start_url);
2284 EXPECT_EQ(1, controller().GetEntryCount());
2285
2286 // Show an interstitial.
2287 TestInterstitialPage::InterstitialState state1 =
2288 TestInterstitialPage::INVALID;
2289 bool deleted1 = false;
2290 GURL url1("http://interstitial1");
2291 TestInterstitialPage* interstitial1 =
2292 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
2293 TestInterstitialPageStateGuard state_guard1(interstitial1);
2294 interstitial1->Show();
2295 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2296 interstitial1->TestDidNavigate(interstitial_entry_id, true, url1);
2297
2298 // Take action. The interstitial won't be hidden until the navigation is
2299 // committed.
2300 interstitial1->Proceed();
2301 EXPECT_EQ(TestInterstitialPage::OKED, state1);
2302
2303 // Now show another interstitial (simulating the navigation causing another
2304 // interstitial).
2305 TestInterstitialPage::InterstitialState state2 =
2306 TestInterstitialPage::INVALID;
2307 bool deleted2 = false;
2308 GURL url2("http://interstitial2");
2309 TestInterstitialPage* interstitial2 =
2310 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
2311 TestInterstitialPageStateGuard state_guard2(interstitial2);
2312 interstitial2->Show();
2313 interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2314 interstitial2->TestDidNavigate(interstitial_entry_id, true, url2);
2315
2316 // Showing interstitial2 should have caused interstitial1 to go away.
2317 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2318 RunAllPendingInMessageLoop();
2319 EXPECT_TRUE(deleted1);
2320 ASSERT_FALSE(deleted2);
2321
2322 // Let's make sure interstitial2 is working as intended.
2323 interstitial2->Proceed();
2324 GURL landing_url("http://www.thepage.com/two");
2325 NavigationSimulator::NavigateAndCommitFromDocument(landing_url,
2326 main_test_rfh());
2327
2328 RunAllPendingInMessageLoop();
2329 EXPECT_TRUE(deleted2);
2330 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2331 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2332 NavigationEntry* entry = controller().GetVisibleEntry();
2333 ASSERT_NE(nullptr, entry);
2334 EXPECT_TRUE(entry->GetURL() == landing_url);
2335 EXPECT_EQ(2, controller().GetEntryCount());
2336 }
2337
2338 // Test that navigating away from an interstitial while it's loading cause it
2339 // not to show.
TEST_F(WebContentsImplTest,NavigateBeforeInterstitialShows)2340 TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) {
2341 // Show an interstitial.
2342 TestInterstitialPage::InterstitialState state =
2343 TestInterstitialPage::INVALID;
2344 bool deleted = false;
2345 GURL interstitial_url("http://interstitial");
2346 TestInterstitialPage* interstitial =
2347 new TestInterstitialPage(contents(), true, interstitial_url,
2348 &state, &deleted);
2349 TestInterstitialPageStateGuard state_guard(interstitial);
2350 interstitial->Show();
2351 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2352
2353 // Let's simulate a navigation initiated from the browser before the
2354 // interstitial finishes loading.
2355 const GURL url("http://www.google.com");
2356 controller().LoadURL(
2357 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2358 EXPECT_FALSE(interstitial->is_showing());
2359 RunAllPendingInMessageLoop();
2360 ASSERT_FALSE(deleted);
2361
2362 // Now let's make the interstitial navigation commit.
2363 interstitial->TestDidNavigate(interstitial_entry_id, true,
2364 interstitial_url);
2365
2366 // After it loaded the interstitial should be gone.
2367 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2368
2369 RunAllPendingInMessageLoop();
2370 EXPECT_TRUE(deleted);
2371 }
2372
2373 // Test that a new request to show an interstitial while an interstitial is
2374 // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442.
TEST_F(WebContentsImplTest,TwoQuickInterstitials)2375 TEST_F(WebContentsImplTest, TwoQuickInterstitials) {
2376 GURL interstitial_url("http://interstitial");
2377
2378 // Show a first interstitial.
2379 TestInterstitialPage::InterstitialState state1 =
2380 TestInterstitialPage::INVALID;
2381 bool deleted1 = false;
2382 TestInterstitialPage* interstitial1 =
2383 new TestInterstitialPage(contents(), true, interstitial_url,
2384 &state1, &deleted1);
2385 TestInterstitialPageStateGuard state_guard1(interstitial1);
2386 interstitial1->Show();
2387
2388 // Show another interstitial on that same contents before the first one had
2389 // time to load.
2390 TestInterstitialPage::InterstitialState state2 =
2391 TestInterstitialPage::INVALID;
2392 bool deleted2 = false;
2393 TestInterstitialPage* interstitial2 =
2394 new TestInterstitialPage(contents(), true, interstitial_url,
2395 &state2, &deleted2);
2396 TestInterstitialPageStateGuard state_guard2(interstitial2);
2397 interstitial2->Show();
2398 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2399
2400 // The first interstitial should have been closed and deleted.
2401 EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
2402 // The 2nd one should still be OK.
2403 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2404
2405 RunAllPendingInMessageLoop();
2406 EXPECT_TRUE(deleted1);
2407 ASSERT_FALSE(deleted2);
2408
2409 // Make the interstitial navigation commit it should be showing.
2410 interstitial2->TestDidNavigate(interstitial_entry_id, true,
2411 interstitial_url);
2412 EXPECT_EQ(interstitial2, contents()->GetInterstitialPage());
2413 }
2414
2415 // Test showing an interstitial and have its renderer crash.
TEST_F(WebContentsImplTest,InterstitialCrasher)2416 TEST_F(WebContentsImplTest, InterstitialCrasher) {
2417 // Show an interstitial.
2418 TestInterstitialPage::InterstitialState state =
2419 TestInterstitialPage::INVALID;
2420 bool deleted = false;
2421 GURL url("http://interstitial");
2422 TestInterstitialPage* interstitial =
2423 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2424 TestInterstitialPageStateGuard state_guard(interstitial);
2425 interstitial->Show();
2426 // Simulate a renderer crash before the interstitial is shown.
2427 interstitial->TestRenderViewTerminated(
2428 base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2429 // The interstitial should have been dismissed.
2430 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2431 RunAllPendingInMessageLoop();
2432 EXPECT_TRUE(deleted);
2433
2434 // Now try again but this time crash the interstitial after it was shown.
2435 interstitial =
2436 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2437 interstitial->Show();
2438 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2439 interstitial->TestDidNavigate(interstitial_entry_id, true, url);
2440 // Simulate a renderer crash.
2441 interstitial->TestRenderViewTerminated(
2442 base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2443 // The interstitial should have been dismissed.
2444 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2445 RunAllPendingInMessageLoop();
2446 EXPECT_TRUE(deleted);
2447 }
2448
2449 // Tests that showing an interstitial as a result of a browser initiated
2450 // navigation while an interstitial is showing does not remove the pending
2451 // entry (see http://crbug.com/9791).
TEST_F(WebContentsImplTest,NewInterstitialDoesNotCancelPendingEntry)2452 TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) {
2453 const char kUrl[] = "http://www.badguys.com/";
2454 const GURL kGURL(kUrl);
2455
2456 // Start a navigation to a page
2457 contents()->GetController().LoadURL(
2458 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2459
2460 // Simulate that navigation triggering an interstitial.
2461 TestInterstitialPage::InterstitialState state =
2462 TestInterstitialPage::INVALID;
2463 bool deleted = false;
2464 TestInterstitialPage* interstitial =
2465 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2466 TestInterstitialPageStateGuard state_guard(interstitial);
2467 interstitial->Show();
2468 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2469 interstitial->TestDidNavigate(interstitial_entry_id, true, kGURL);
2470
2471 // Initiate a new navigation from the browser that also triggers an
2472 // interstitial.
2473 contents()->GetController().LoadURL(
2474 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2475 TestInterstitialPage::InterstitialState state2 =
2476 TestInterstitialPage::INVALID;
2477 bool deleted2 = false;
2478 TestInterstitialPage* interstitial2 =
2479 new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2);
2480 TestInterstitialPageStateGuard state_guard2(interstitial2);
2481 interstitial2->Show();
2482 interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2483 interstitial2->TestDidNavigate(interstitial_entry_id, true, kGURL);
2484
2485 // Make sure we still have an entry.
2486 NavigationEntry* entry = contents()->GetController().GetPendingEntry();
2487 ASSERT_TRUE(entry);
2488 EXPECT_EQ(kUrl, entry->GetURL().spec());
2489
2490 // And that the first interstitial is gone, but not the second.
2491 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2492 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2493 RunAllPendingInMessageLoop();
2494 EXPECT_TRUE(deleted);
2495 EXPECT_FALSE(deleted2);
2496 }
2497
2498 // Tests that Javascript messages are not shown while an interstitial is
2499 // showing.
TEST_F(WebContentsImplTest,NoJSMessageOnInterstitials)2500 TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) {
2501 const char kUrl[] = "http://www.badguys.com/";
2502 const GURL kGURL(kUrl);
2503
2504 // Start a navigation to a page
2505 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kGURL);
2506
2507 // Simulate showing an interstitial while the page is showing.
2508 TestInterstitialPage::InterstitialState state =
2509 TestInterstitialPage::INVALID;
2510 bool deleted = false;
2511 TestInterstitialPage* interstitial =
2512 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2513 TestInterstitialPageStateGuard state_guard(interstitial);
2514 interstitial->Show();
2515 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2516 interstitial->TestDidNavigate(interstitial_entry_id, true, kGURL);
2517
2518 // While the interstitial is showing, let's simulate the hidden page
2519 // attempting to show a JS message.
2520 contents()->RunJavaScriptDialog(
2521 main_test_rfh(), base::ASCIIToUTF16("This is an informative message"),
2522 base::ASCIIToUTF16("OK"), JAVASCRIPT_DIALOG_TYPE_ALERT,
2523 base::DoNothing());
2524 EXPECT_TRUE(contents()->last_dialog_suppressed_);
2525 }
2526
2527 // Makes sure that if the source passed to CopyStateFromAndPrune has an
2528 // interstitial it isn't copied over to the destination.
TEST_F(WebContentsImplTest,CopyStateFromAndPruneSourceInterstitial)2529 TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) {
2530 // Navigate to a page.
2531 GURL url1("http://www.google.com");
2532 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
2533 EXPECT_EQ(1, controller().GetEntryCount());
2534
2535 // Initiate a browser navigation that will trigger the interstitial
2536 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
2537 ui::PAGE_TRANSITION_TYPED, std::string());
2538
2539 // Show an interstitial.
2540 TestInterstitialPage::InterstitialState state =
2541 TestInterstitialPage::INVALID;
2542 bool deleted = false;
2543 GURL url2("http://interstitial");
2544 TestInterstitialPage* interstitial =
2545 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2546 TestInterstitialPageStateGuard state_guard(interstitial);
2547 interstitial->Show();
2548 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2549 interstitial->TestDidNavigate(interstitial_entry_id, true, url2);
2550 EXPECT_TRUE(interstitial->is_showing());
2551 EXPECT_EQ(2, controller().GetEntryCount());
2552
2553 // Create another NavigationController.
2554 GURL url3("http://foo2");
2555 std::unique_ptr<TestWebContents> other_contents(
2556 static_cast<TestWebContents*>(CreateTestWebContents().release()));
2557 NavigationControllerImpl& other_controller = other_contents->GetController();
2558 other_contents->NavigateAndCommit(url3);
2559 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
2560 other_controller.CopyStateFromAndPrune(&controller(), false);
2561
2562 // The merged controller should only have two entries: url1 and url2.
2563 ASSERT_EQ(2, other_controller.GetEntryCount());
2564 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
2565 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
2566 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
2567
2568 // And the merged controller shouldn't be showing an interstitial.
2569 EXPECT_FALSE(other_contents->ShowingInterstitialPage());
2570 }
2571
2572 // Makes sure that CopyStateFromAndPrune cannot be called if the target is
2573 // showing an interstitial.
TEST_F(WebContentsImplTest,CopyStateFromAndPruneTargetInterstitial)2574 TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) {
2575 // Navigate to a page.
2576 GURL url1("http://www.google.com");
2577 contents()->NavigateAndCommit(url1);
2578
2579 // Create another NavigationController.
2580 std::unique_ptr<TestWebContents> other_contents(
2581 static_cast<TestWebContents*>(CreateTestWebContents().release()));
2582 NavigationControllerImpl& other_controller = other_contents->GetController();
2583
2584 // Navigate it to url2.
2585 GURL url2("http://foo2");
2586 other_contents->NavigateAndCommit(url2);
2587
2588 // Show an interstitial.
2589 TestInterstitialPage::InterstitialState state =
2590 TestInterstitialPage::INVALID;
2591 bool deleted = false;
2592 GURL url3("http://interstitial");
2593 TestInterstitialPage* interstitial =
2594 new TestInterstitialPage(other_contents.get(), true, url3, &state,
2595 &deleted);
2596 TestInterstitialPageStateGuard state_guard(interstitial);
2597 interstitial->Show();
2598 int interstitial_entry_id =
2599 other_controller.GetTransientEntry()->GetUniqueID();
2600 interstitial->TestDidNavigate(interstitial_entry_id, true, url3);
2601 EXPECT_TRUE(interstitial->is_showing());
2602 EXPECT_EQ(2, other_controller.GetEntryCount());
2603
2604 // Ensure that we do not allow calling CopyStateFromAndPrune when an
2605 // interstitial is showing in the target.
2606 EXPECT_FALSE(other_controller.CanPruneAllButLastCommitted());
2607 }
2608
2609 // Regression test for http://crbug.com/168611 - the URLs passed by the
2610 // DidFinishLoad and DidFailLoadWithError IPCs should get filtered.
TEST_F(WebContentsImplTest,FilterURLs)2611 TEST_F(WebContentsImplTest, FilterURLs) {
2612 TestWebContentsObserver observer(contents());
2613
2614 // A navigation to about:whatever should always look like a navigation to
2615 // about:blank
2616 GURL url_normalized(url::kAboutBlankURL);
2617 GURL url_from_ipc("about:whatever");
2618 GURL url_blocked(kBlockedURL);
2619
2620 // We navigate the test WebContents to about:blank, since NavigateAndCommit
2621 // will use the given URL to create the NavigationEntry as well, and that
2622 // entry should contain the filtered URL.
2623 contents()->NavigateAndCommit(url_normalized);
2624
2625 // Check that an IPC with about:whatever is correctly normalized.
2626 contents()->TestDidFinishLoad(url_from_ipc);
2627
2628 EXPECT_EQ(url_blocked, observer.last_url());
2629
2630 // Create and navigate another WebContents.
2631 std::unique_ptr<TestWebContents> other_contents(
2632 static_cast<TestWebContents*>(CreateTestWebContents().release()));
2633 TestWebContentsObserver other_observer(other_contents.get());
2634 other_contents->NavigateAndCommit(url_normalized);
2635
2636 // Check that an IPC with about:whatever is correctly normalized.
2637 other_contents->GetMainFrame()->DidFailLoadWithError(url_from_ipc, 1);
2638 EXPECT_EQ(url_blocked, other_observer.last_url());
2639 }
2640
2641 // Test that if a pending contents is deleted before it is shown, we don't
2642 // crash.
TEST_F(WebContentsImplTest,PendingContentsDestroyed)2643 TEST_F(WebContentsImplTest, PendingContentsDestroyed) {
2644 auto other_contents = base::WrapUnique(
2645 static_cast<TestWebContents*>(CreateTestWebContents().release()));
2646 content::TestWebContents* test_web_contents = other_contents.get();
2647 contents()->AddPendingContents(std::move(other_contents));
2648 RenderWidgetHost* widget =
2649 test_web_contents->GetMainFrame()->GetRenderWidgetHost();
2650 int process_id = widget->GetProcess()->GetID();
2651 int widget_id = widget->GetRoutingID();
2652
2653 // TODO(erikchen): Fix ownership semantics of WebContents. Nothing should be
2654 // able to delete it beside from the owner. https://crbug.com/832879.
2655 delete test_web_contents;
2656 EXPECT_EQ(nullptr, contents()->GetCreatedWindow(process_id, widget_id));
2657 }
2658
TEST_F(WebContentsImplTest,PendingContentsShown)2659 TEST_F(WebContentsImplTest, PendingContentsShown) {
2660 auto other_contents = base::WrapUnique(
2661 static_cast<TestWebContents*>(CreateTestWebContents().release()));
2662 content::TestWebContents* test_web_contents = other_contents.get();
2663 contents()->AddPendingContents(std::move(other_contents));
2664
2665 RenderWidgetHost* widget =
2666 test_web_contents->GetMainFrame()->GetRenderWidgetHost();
2667 int process_id = widget->GetProcess()->GetID();
2668 int widget_id = widget->GetRoutingID();
2669
2670 // The first call to GetCreatedWindow pops it off the pending list.
2671 EXPECT_EQ(test_web_contents,
2672 contents()->GetCreatedWindow(process_id, widget_id).get());
2673 // A second call should return nullptr, verifying that it's been forgotten.
2674 EXPECT_EQ(nullptr, contents()->GetCreatedWindow(process_id, widget_id));
2675 }
2676
TEST_F(WebContentsImplTest,CapturerOverridesPreferredSize)2677 TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) {
2678 const gfx::Size original_preferred_size(1024, 768);
2679 contents()->UpdatePreferredSize(original_preferred_size);
2680
2681 // With no capturers, expect the preferred size to be the one propagated into
2682 // WebContentsImpl via the RenderViewHostDelegate interface.
2683 EXPECT_FALSE(contents()->IsBeingCaptured());
2684 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2685
2686 // Increment capturer count, but without specifying a capture size. Expect
2687 // a "not set" preferred size.
2688 contents()->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
2689 EXPECT_TRUE(contents()->IsBeingCaptured());
2690 EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize());
2691
2692 // Increment capturer count again, but with an overriding capture size.
2693 // Expect preferred size to now be overridden to the capture size.
2694 const gfx::Size capture_size(1280, 720);
2695 contents()->IncrementCapturerCount(capture_size, /* stay_hidden */ false);
2696 EXPECT_TRUE(contents()->IsBeingCaptured());
2697 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2698
2699 // Increment capturer count a third time, but the expect that the preferred
2700 // size is still the first capture size.
2701 const gfx::Size another_capture_size(720, 480);
2702 contents()->IncrementCapturerCount(another_capture_size,
2703 /* stay_hidden */ false);
2704 EXPECT_TRUE(contents()->IsBeingCaptured());
2705 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2706
2707 // Decrement capturer count twice, but expect the preferred size to still be
2708 // overridden.
2709 contents()->DecrementCapturerCount(/* stay_hidden */ false);
2710 contents()->DecrementCapturerCount(/* stay_hidden */ false);
2711 EXPECT_TRUE(contents()->IsBeingCaptured());
2712 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2713
2714 // Decrement capturer count, and since the count has dropped to zero, the
2715 // original preferred size should be restored.
2716 contents()->DecrementCapturerCount(/* stay_hidden */ false);
2717 EXPECT_FALSE(contents()->IsBeingCaptured());
2718 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2719 }
2720
TEST_F(WebContentsImplTest,UpdateWebContentsVisibility)2721 TEST_F(WebContentsImplTest, UpdateWebContentsVisibility) {
2722 base::test::ScopedFeatureList scoped_feature_list;
2723 scoped_feature_list.InitAndEnableFeature(features::kWebContentsOcclusion);
2724
2725 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
2726 main_test_rfh()->GetRenderViewHost()->GetWidget()->GetView());
2727 TestWebContentsObserver observer(contents());
2728
2729 EXPECT_FALSE(view->is_showing());
2730 EXPECT_FALSE(view->is_occluded());
2731
2732 // WebContents must be made visible once before it can be hidden.
2733 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
2734 EXPECT_FALSE(view->is_showing());
2735 EXPECT_FALSE(view->is_occluded());
2736 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
2737
2738 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
2739 EXPECT_TRUE(view->is_showing());
2740 EXPECT_FALSE(view->is_occluded());
2741 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
2742
2743 // Hiding/occluding/showing the WebContents should hide and show |view|.
2744 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
2745 EXPECT_FALSE(view->is_showing());
2746 EXPECT_FALSE(view->is_occluded());
2747 EXPECT_EQ(Visibility::HIDDEN, contents()->GetVisibility());
2748
2749 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
2750 EXPECT_TRUE(view->is_showing());
2751 EXPECT_FALSE(view->is_occluded());
2752 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
2753
2754 contents()->UpdateWebContentsVisibility(Visibility::OCCLUDED);
2755 EXPECT_TRUE(view->is_showing());
2756 EXPECT_TRUE(view->is_occluded());
2757 EXPECT_EQ(Visibility::OCCLUDED, contents()->GetVisibility());
2758
2759 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
2760 EXPECT_TRUE(view->is_showing());
2761 EXPECT_FALSE(view->is_occluded());
2762 EXPECT_EQ(Visibility::VISIBLE, contents()->GetVisibility());
2763
2764 contents()->UpdateWebContentsVisibility(Visibility::OCCLUDED);
2765 EXPECT_TRUE(view->is_showing());
2766 EXPECT_TRUE(view->is_occluded());
2767 EXPECT_EQ(Visibility::OCCLUDED, contents()->GetVisibility());
2768
2769 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
2770 EXPECT_FALSE(view->is_showing());
2771 EXPECT_EQ(Visibility::HIDDEN, contents()->GetVisibility());
2772 }
2773
2774 namespace {
2775
HideOrOccludeWithCapturerTest(WebContentsImpl * contents,Visibility hidden_or_occluded)2776 void HideOrOccludeWithCapturerTest(WebContentsImpl* contents,
2777 Visibility hidden_or_occluded) {
2778 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
2779 contents->GetRenderWidgetHostView());
2780
2781 EXPECT_FALSE(view->is_showing());
2782
2783 // WebContents must be made visible once before it can be hidden.
2784 contents->UpdateWebContentsVisibility(Visibility::VISIBLE);
2785 EXPECT_TRUE(view->is_showing());
2786 EXPECT_FALSE(view->is_occluded());
2787 EXPECT_EQ(Visibility::VISIBLE, contents->GetVisibility());
2788
2789 // Add a capturer when the contents is visible and then hide the contents.
2790 // |view| should remain visible.
2791 contents->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
2792 contents->UpdateWebContentsVisibility(hidden_or_occluded);
2793 EXPECT_TRUE(view->is_showing());
2794 EXPECT_FALSE(view->is_occluded());
2795 EXPECT_EQ(hidden_or_occluded, contents->GetVisibility());
2796
2797 // Remove the capturer when the contents is hidden/occluded. |view| should be
2798 // hidden/occluded.
2799 contents->DecrementCapturerCount(/* stay_hidden */ false);
2800 if (hidden_or_occluded == Visibility::HIDDEN) {
2801 EXPECT_FALSE(view->is_showing());
2802 } else {
2803 EXPECT_TRUE(view->is_showing());
2804 EXPECT_TRUE(view->is_occluded());
2805 }
2806
2807 // Add a capturer when the contents is hidden. |view| should be unoccluded.
2808 contents->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
2809 EXPECT_FALSE(view->is_occluded());
2810
2811 // Show the contents. The view should be visible.
2812 contents->UpdateWebContentsVisibility(Visibility::VISIBLE);
2813 EXPECT_TRUE(view->is_showing());
2814 EXPECT_FALSE(view->is_occluded());
2815 EXPECT_EQ(Visibility::VISIBLE, contents->GetVisibility());
2816
2817 // Remove the capturer when the contents is visible. The view should remain
2818 // visible.
2819 contents->DecrementCapturerCount(/* stay_hidden */ false);
2820 EXPECT_TRUE(view->is_showing());
2821 EXPECT_FALSE(view->is_occluded());
2822 }
2823
2824 } // namespace
2825
TEST_F(WebContentsImplTest,HideWithCapturer)2826 TEST_F(WebContentsImplTest, HideWithCapturer) {
2827 HideOrOccludeWithCapturerTest(contents(), Visibility::HIDDEN);
2828 }
2829
TEST_F(WebContentsImplTest,OccludeWithCapturer)2830 TEST_F(WebContentsImplTest, OccludeWithCapturer) {
2831 base::test::ScopedFeatureList scoped_feature_list;
2832 scoped_feature_list.InitAndEnableFeature(features::kWebContentsOcclusion);
2833 HideOrOccludeWithCapturerTest(contents(), Visibility::OCCLUDED);
2834 }
2835
2836 namespace {
2837
CheckVisibilityMessage(const IPC::Message * message,PageVisibilityState expected_state)2838 void CheckVisibilityMessage(const IPC::Message* message,
2839 PageVisibilityState expected_state) {
2840 ASSERT_TRUE(message);
2841 std::tuple<content::PageVisibilityState> params;
2842 ASSERT_TRUE(PageMsg_VisibilityChanged::Read(message, ¶ms));
2843 EXPECT_EQ(expected_state, std::get<0>(params));
2844 }
2845
2846 } // namespace
2847
TEST_F(WebContentsImplTest,HiddenCapture)2848 TEST_F(WebContentsImplTest, HiddenCapture) {
2849 TestRenderViewHost* const rvh =
2850 static_cast<TestRenderViewHost*>(contents()->GetRenderViewHost());
2851 TestRenderWidgetHostView* rwhv = static_cast<TestRenderWidgetHostView*>(
2852 contents()->GetRenderWidgetHostView());
2853 MockRenderProcessHost* const rph = rvh->GetProcess();
2854 IPC::TestSink* const sink = &rph->sink();
2855
2856 contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
2857 contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
2858 EXPECT_EQ(Visibility::HIDDEN, contents()->GetVisibility());
2859
2860 sink->ClearMessages();
2861 contents()->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ true);
2862 const IPC::Message* visibility_message =
2863 sink->GetUniqueMessageMatching(PageMsg_VisibilityChanged::ID);
2864 CheckVisibilityMessage(visibility_message,
2865 PageVisibilityState::kHiddenButPainting);
2866 EXPECT_TRUE(rwhv->is_showing());
2867
2868 sink->ClearMessages();
2869 contents()->IncrementCapturerCount(gfx::Size(), /* stay_hidden */ false);
2870 visibility_message =
2871 sink->GetUniqueMessageMatching(PageMsg_VisibilityChanged::ID);
2872 CheckVisibilityMessage(visibility_message, PageVisibilityState::kVisible);
2873 EXPECT_TRUE(rwhv->is_showing());
2874
2875 sink->ClearMessages();
2876 contents()->DecrementCapturerCount(/* stay_hidden */ true);
2877 visibility_message =
2878 sink->GetUniqueMessageMatching(PageMsg_VisibilityChanged::ID);
2879 CheckVisibilityMessage(visibility_message, PageVisibilityState::kVisible);
2880 EXPECT_TRUE(rwhv->is_showing());
2881
2882 sink->ClearMessages();
2883 contents()->DecrementCapturerCount(/* stay_hidden */ false);
2884 visibility_message =
2885 sink->GetUniqueMessageMatching(PageMsg_VisibilityChanged::ID);
2886 CheckVisibilityMessage(visibility_message, PageVisibilityState::kHidden);
2887 EXPECT_FALSE(rwhv->is_showing());
2888 }
2889
2890 // Tests that GetLastActiveTime starts with a real, non-zero time and updates
2891 // on activity.
TEST_F(WebContentsImplTest,GetLastActiveTime)2892 TEST_F(WebContentsImplTest, GetLastActiveTime) {
2893 // The WebContents starts with a valid creation time.
2894 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2895
2896 // Reset the last active time to a known-bad value.
2897 contents()->last_active_time_ = base::TimeTicks();
2898 ASSERT_TRUE(contents()->GetLastActiveTime().is_null());
2899
2900 // Simulate activating the WebContents. The active time should update.
2901 contents()->WasShown();
2902 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2903 }
2904
2905 class ContentsZoomChangedDelegate : public WebContentsDelegate {
2906 public:
ContentsZoomChangedDelegate()2907 ContentsZoomChangedDelegate() :
2908 contents_zoom_changed_call_count_(0),
2909 last_zoom_in_(false) {
2910 }
2911
GetAndResetContentsZoomChangedCallCount()2912 int GetAndResetContentsZoomChangedCallCount() {
2913 int count = contents_zoom_changed_call_count_;
2914 contents_zoom_changed_call_count_ = 0;
2915 return count;
2916 }
2917
last_zoom_in() const2918 bool last_zoom_in() const {
2919 return last_zoom_in_;
2920 }
2921
2922 // WebContentsDelegate:
ContentsZoomChange(bool zoom_in)2923 void ContentsZoomChange(bool zoom_in) override {
2924 contents_zoom_changed_call_count_++;
2925 last_zoom_in_ = zoom_in;
2926 }
2927
2928 private:
2929 int contents_zoom_changed_call_count_;
2930 bool last_zoom_in_;
2931
2932 DISALLOW_COPY_AND_ASSIGN(ContentsZoomChangedDelegate);
2933 };
2934
2935 // Tests that some mouseehweel events get turned into browser zoom requests.
TEST_F(WebContentsImplTest,HandleWheelEvent)2936 TEST_F(WebContentsImplTest, HandleWheelEvent) {
2937 using blink::WebInputEvent;
2938
2939 std::unique_ptr<ContentsZoomChangedDelegate> delegate(
2940 new ContentsZoomChangedDelegate());
2941 contents()->SetDelegate(delegate.get());
2942
2943 int modifiers = 0;
2944 // Verify that normal mouse wheel events do nothing to change the zoom level.
2945 blink::WebMouseWheelEvent event = SyntheticWebMouseWheelEventBuilder::Build(
2946 0, 0, 0, 1, modifiers, ui::ScrollGranularity::kScrollByPixel);
2947 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2948 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2949
2950 // But whenever the ctrl modifier is applied zoom can be increased or
2951 // decreased. Except on MacOS where we never want to adjust zoom
2952 // with mousewheel.
2953 modifiers = WebInputEvent::kControlKey;
2954 event = SyntheticWebMouseWheelEventBuilder::Build(
2955 0, 0, 0, 1, modifiers, ui::ScrollGranularity::kScrollByPixel);
2956 bool handled = contents()->HandleWheelEvent(event);
2957 #if defined(USE_AURA)
2958 EXPECT_TRUE(handled);
2959 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2960 EXPECT_TRUE(delegate->last_zoom_in());
2961 #else
2962 EXPECT_FALSE(handled);
2963 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2964 #endif
2965
2966 modifiers = WebInputEvent::kControlKey | WebInputEvent::kShiftKey |
2967 WebInputEvent::kAltKey;
2968 event = SyntheticWebMouseWheelEventBuilder::Build(
2969 0, 0, 2, -5, modifiers, ui::ScrollGranularity::kScrollByPixel);
2970 handled = contents()->HandleWheelEvent(event);
2971 #if defined(USE_AURA)
2972 EXPECT_TRUE(handled);
2973 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2974 EXPECT_FALSE(delegate->last_zoom_in());
2975 #else
2976 EXPECT_FALSE(handled);
2977 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2978 #endif
2979
2980 // Unless there is no vertical movement.
2981 event = SyntheticWebMouseWheelEventBuilder::Build(
2982 0, 0, 2, 0, modifiers, ui::ScrollGranularity::kScrollByPixel);
2983 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2984 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2985
2986 // Events containing precise scrolling deltas also shouldn't result in the
2987 // zoom being adjusted, to avoid accidental adjustments caused by
2988 // two-finger-scrolling on a touchpad.
2989 modifiers = WebInputEvent::kControlKey;
2990 event = SyntheticWebMouseWheelEventBuilder::Build(
2991 0, 0, 0, 5, modifiers, ui::ScrollGranularity::kScrollByPrecisePixel);
2992 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2993 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2994
2995 // Ensure pointers to the delegate aren't kept beyond its lifetime.
2996 contents()->SetDelegate(nullptr);
2997 }
2998
2999 // Tests that GetRelatedActiveContentsCount is shared between related
3000 // SiteInstances and includes WebContents that have not navigated yet.
TEST_F(WebContentsImplTest,ActiveContentsCountBasic)3001 TEST_F(WebContentsImplTest, ActiveContentsCountBasic) {
3002 scoped_refptr<SiteInstance> instance1(
3003 SiteInstance::CreateForURL(browser_context(), GURL("http://a.com")));
3004 scoped_refptr<SiteInstance> instance2(
3005 instance1->GetRelatedSiteInstance(GURL("http://b.com")));
3006
3007 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
3008 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
3009
3010 std::unique_ptr<TestWebContents> contents1(
3011 TestWebContents::Create(browser_context(), instance1.get()));
3012 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
3013 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
3014
3015 std::unique_ptr<TestWebContents> contents2(
3016 TestWebContents::Create(browser_context(), instance1.get()));
3017 EXPECT_EQ(2u, instance1->GetRelatedActiveContentsCount());
3018 EXPECT_EQ(2u, instance2->GetRelatedActiveContentsCount());
3019
3020 contents1.reset();
3021 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
3022 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
3023
3024 contents2.reset();
3025 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
3026 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
3027 }
3028
3029 // Tests that GetRelatedActiveContentsCount is preserved correctly across
3030 // same-site and cross-site navigations.
TEST_F(WebContentsImplTest,ActiveContentsCountNavigate)3031 TEST_F(WebContentsImplTest, ActiveContentsCountNavigate) {
3032 scoped_refptr<SiteInstance> instance(
3033 SiteInstance::Create(browser_context()));
3034
3035 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
3036
3037 std::unique_ptr<TestWebContents> contents(
3038 TestWebContents::Create(browser_context(), instance.get()));
3039 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3040
3041 // Navigate to a URL.
3042 auto navigation1 = NavigationSimulator::CreateBrowserInitiated(
3043 GURL("http://a.com/1"), contents.get());
3044 navigation1->Start();
3045 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3046 navigation1->Commit();
3047 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3048
3049 // Navigate to a URL in the same site.
3050 auto navigation2 = NavigationSimulator::CreateBrowserInitiated(
3051 GURL("http://a.com/2"), contents.get());
3052 navigation2->Start();
3053 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3054 navigation2->Commit();
3055 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3056
3057 // Navigate to a URL in a different site in the same BrowsingInstance.
3058 const GURL kUrl2("http://b.com");
3059 auto navigation3 =
3060 NavigationSimulator::CreateRendererInitiated(kUrl2, main_test_rfh());
3061 navigation3->ReadyToCommit();
3062 EXPECT_FALSE(contents->CrossProcessNavigationPending());
3063 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3064 navigation3->Commit();
3065 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3066
3067 // Navigate to a URL in a different site and different BrowsingInstance, by
3068 // using a TYPED page transition instead of LINK.
3069 const GURL kUrl3("http://c.com");
3070 auto navigation4 =
3071 NavigationSimulator::CreateBrowserInitiated(kUrl3, contents.get());
3072 navigation4->SetTransition(ui::PAGE_TRANSITION_TYPED);
3073 navigation4->ReadyToCommit();
3074 EXPECT_TRUE(contents->CrossProcessNavigationPending());
3075 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3076 scoped_refptr<SiteInstance> new_instance =
3077 contents->GetPendingMainFrame()->GetSiteInstance();
3078 navigation4->Commit();
3079 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
3080 EXPECT_EQ(1u, new_instance->GetRelatedActiveContentsCount());
3081 EXPECT_FALSE(new_instance->IsRelatedSiteInstance(instance.get()));
3082
3083 contents.reset();
3084 EXPECT_EQ(0u, new_instance->GetRelatedActiveContentsCount());
3085 }
3086
3087 // Tests that GetRelatedActiveContentsCount tracks BrowsingInstance changes
3088 // from WebUI.
TEST_F(WebContentsImplTest,ActiveContentsCountChangeBrowsingInstance)3089 TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) {
3090 scoped_refptr<SiteInstance> instance(
3091 SiteInstance::Create(browser_context()));
3092
3093 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
3094
3095 std::unique_ptr<TestWebContents> contents(
3096 TestWebContents::Create(browser_context(), instance.get()));
3097 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3098
3099 // Navigate to a URL.
3100 contents->NavigateAndCommit(GURL("http://a.com"));
3101 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3102
3103 // Navigate to a URL which sort of looks like a chrome:// url.
3104 contents->NavigateAndCommit(GURL("http://gpu"));
3105 if (IsProactivelySwapBrowsingInstanceEnabled()) {
3106 // The navigation from "a.com" to "gpu" is using a new BrowsingInstance.
3107 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
3108 // The rest of the test expects |instance| to match the one in the main
3109 // frame.
3110 instance = contents->GetMainFrame()->GetSiteInstance();
3111 }
3112 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3113
3114 // Navigate to a URL with WebUI. This will change BrowsingInstances.
3115 const GURL kWebUIUrl = GURL(GetWebUIURL(kChromeUIGpuHost));
3116 auto web_ui_navigation =
3117 NavigationSimulator::CreateBrowserInitiated(kWebUIUrl, contents.get());
3118 web_ui_navigation->Start();
3119 EXPECT_TRUE(contents->CrossProcessNavigationPending());
3120 scoped_refptr<SiteInstance> instance_webui(
3121 contents->GetPendingMainFrame()->GetSiteInstance());
3122 EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get()));
3123
3124 // At this point, contents still counts for the old BrowsingInstance.
3125 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
3126 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
3127
3128 // Commit and contents counts for the new one.
3129 web_ui_navigation->Commit();
3130 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
3131 EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount());
3132
3133 contents.reset();
3134 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
3135 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
3136 }
3137
3138 class LoadingWebContentsObserver : public WebContentsObserver {
3139 public:
LoadingWebContentsObserver(WebContents * contents)3140 explicit LoadingWebContentsObserver(WebContents* contents)
3141 : WebContentsObserver(contents),
3142 is_loading_(false),
3143 did_receive_response_(false) {}
~LoadingWebContentsObserver()3144 ~LoadingWebContentsObserver() override {}
3145
3146 // The assertions on these messages ensure that they are received in order.
DidStartLoading()3147 void DidStartLoading() override {
3148 ASSERT_FALSE(did_receive_response_);
3149 ASSERT_FALSE(is_loading_);
3150 is_loading_ = true;
3151 }
DidReceiveResponse()3152 void DidReceiveResponse() override {
3153 ASSERT_TRUE(is_loading_);
3154 did_receive_response_ = true;
3155 }
DidStopLoading()3156 void DidStopLoading() override {
3157 ASSERT_TRUE(is_loading_);
3158 is_loading_ = false;
3159 did_receive_response_ = false;
3160 }
3161
is_loading() const3162 bool is_loading() const { return is_loading_; }
did_receive_response() const3163 bool did_receive_response() const { return did_receive_response_; }
3164
3165 private:
3166 bool is_loading_;
3167 bool did_receive_response_;
3168
3169 DISALLOW_COPY_AND_ASSIGN(LoadingWebContentsObserver);
3170 };
3171
3172 // Subclass of WebContentsImplTest for cases that need out-of-process iframes.
3173 class WebContentsImplTestWithSiteIsolation : public WebContentsImplTest {
3174 public:
WebContentsImplTestWithSiteIsolation()3175 WebContentsImplTestWithSiteIsolation() {
3176 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
3177 }
3178 };
3179
3180 // Ensure that DidStartLoading/DidStopLoading events balance out properly with
3181 // interleaving cross-process navigations in multiple subframes.
3182 // See https://crbug.com/448601 for details of the underlying issue. The
3183 // sequence of events that reproduce it are as follows:
3184 // * Navigate top-level frame with one subframe.
3185 // * Subframe navigates more than once before the top-level frame has had a
3186 // chance to complete the load.
3187 // The subframe navigations cause the loading_frames_in_progress_ to drop down
3188 // to 0, while the loading_progresses_ map is not reset.
TEST_F(WebContentsImplTestWithSiteIsolation,StartStopEventsBalance)3189 TEST_F(WebContentsImplTestWithSiteIsolation, StartStopEventsBalance) {
3190 // The bug manifests itself in regular mode as well, but browser-initiated
3191 // navigation of subframes is only possible in --site-per-process mode within
3192 // unit tests.
3193 const GURL initial_url("about:blank");
3194 const GURL main_url("http://www.chromium.org");
3195 const GURL foo_url("http://foo.chromium.org");
3196 const GURL bar_url("http://bar.chromium.org");
3197 TestRenderFrameHost* orig_rfh = main_test_rfh();
3198
3199 // Use a WebContentsObserver to observe the behavior of the tab's spinner.
3200 LoadingWebContentsObserver observer(contents());
3201
3202 // Navigate the main RenderFrame and commit. The frame should still be
3203 // loading.
3204 auto main_frame_navigation =
3205 NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
3206 main_frame_navigation->SetKeepLoading(true);
3207 main_frame_navigation->Commit();
3208 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
3209 EXPECT_EQ(orig_rfh, main_test_rfh());
3210 EXPECT_TRUE(contents()->IsLoading());
3211
3212 // The Observer callback implementations contain assertions to ensure that the
3213 // events arrive in the correct order.
3214 EXPECT_TRUE(observer.is_loading());
3215 EXPECT_TRUE(observer.did_receive_response());
3216
3217 // Create a child frame to navigate multiple times.
3218 TestRenderFrameHost* subframe = orig_rfh->AppendChild("subframe");
3219
3220 // Navigate the child frame to about:blank, which will send DidStopLoading
3221 // message.
3222 NavigationSimulator::NavigateAndCommitFromDocument(initial_url, subframe);
3223
3224 // Navigate the frame to another URL, which will send again
3225 // DidStartLoading and DidStopLoading messages.
3226 NavigationSimulator::NavigateAndCommitFromDocument(foo_url, subframe);
3227
3228 // Since the main frame hasn't sent any DidStopLoading messages, it is
3229 // expected that the WebContents is still in loading state.
3230 EXPECT_TRUE(contents()->IsLoading());
3231 EXPECT_TRUE(observer.is_loading());
3232 EXPECT_TRUE(observer.did_receive_response());
3233
3234 // Navigate the frame again, this time using LoadURLWithParams. This causes
3235 // RenderFrameHost to call into WebContents::DidStartLoading, which starts
3236 // the spinner.
3237 {
3238 auto navigation =
3239 NavigationSimulatorImpl::CreateBrowserInitiated(bar_url, contents());
3240
3241 NavigationController::LoadURLParams load_params(bar_url);
3242 load_params.referrer = Referrer(GURL("http://referrer"),
3243 network::mojom::ReferrerPolicy::kDefault);
3244 load_params.transition_type = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
3245 load_params.extra_headers = "content-type: text/plain";
3246 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
3247 load_params.is_renderer_initiated = false;
3248 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
3249 load_params.frame_tree_node_id =
3250 subframe->frame_tree_node()->frame_tree_node_id();
3251 navigation->SetLoadURLParams(&load_params);
3252
3253 navigation->Commit();
3254 subframe = static_cast<TestRenderFrameHost*>(
3255 navigation->GetFinalRenderFrameHost());
3256 }
3257
3258 // At this point the status should still be loading, since the main frame
3259 // hasn't sent the DidstopLoading message yet.
3260 EXPECT_TRUE(contents()->IsLoading());
3261 EXPECT_TRUE(observer.is_loading());
3262 EXPECT_TRUE(observer.did_receive_response());
3263
3264 // Send the DidStopLoading for the main frame and ensure it isn't loading
3265 // anymore.
3266 main_frame_navigation->StopLoading();
3267 EXPECT_FALSE(contents()->IsLoading());
3268 EXPECT_FALSE(observer.is_loading());
3269 EXPECT_FALSE(observer.did_receive_response());
3270 }
3271
3272 // Tests that WebContentsImpl::IsLoadingToDifferentDocument only reports main
3273 // frame loads. Browser-initiated navigation of subframes is only possible in
3274 // --site-per-process mode within unit tests.
TEST_F(WebContentsImplTestWithSiteIsolation,IsLoadingToDifferentDocument)3275 TEST_F(WebContentsImplTestWithSiteIsolation, IsLoadingToDifferentDocument) {
3276 const GURL main_url("http://www.chromium.org");
3277 TestRenderFrameHost* orig_rfh = main_test_rfh();
3278
3279 // Navigate the main RenderFrame and commit. The frame should still be
3280 // loading.
3281 auto navigation =
3282 NavigationSimulatorImpl::CreateBrowserInitiated(main_url, contents());
3283 navigation->SetKeepLoading(true);
3284 navigation->Commit();
3285 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
3286 EXPECT_EQ(orig_rfh, main_test_rfh());
3287 EXPECT_TRUE(contents()->IsLoading());
3288 EXPECT_TRUE(contents()->IsLoadingToDifferentDocument());
3289
3290 // Send the DidStopLoading for the main frame and ensure it isn't loading
3291 // anymore.
3292 navigation->StopLoading();
3293 EXPECT_FALSE(contents()->IsLoading());
3294 EXPECT_FALSE(contents()->IsLoadingToDifferentDocument());
3295
3296 // Create a child frame to navigate.
3297 TestRenderFrameHost* subframe = orig_rfh->AppendChild("subframe");
3298
3299 // Navigate the child frame to about:blank, make sure the web contents is
3300 // marked as "loading" but not "loading to different document".
3301 subframe->SendNavigateWithTransition(0, false, GURL("about:blank"),
3302 ui::PAGE_TRANSITION_AUTO_SUBFRAME);
3303 EXPECT_TRUE(contents()->IsLoading());
3304 EXPECT_FALSE(contents()->IsLoadingToDifferentDocument());
3305 subframe->OnMessageReceived(
3306 FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
3307 EXPECT_FALSE(contents()->IsLoading());
3308 }
3309
3310 // Ensure that WebContentsImpl does not stop loading too early when there still
3311 // is a pending renderer. This can happen if a same-process non user-initiated
3312 // navigation completes while there is an ongoing cross-process navigation.
3313 // TODO(clamy): Rewrite that test when the renderer-initiated non-user-initiated
3314 // navigation no longer kills the speculative RenderFrameHost. See
3315 // https://crbug.com/889039.
TEST_F(WebContentsImplTest,DISABLED_NoEarlyStop)3316 TEST_F(WebContentsImplTest, DISABLED_NoEarlyStop) {
3317 const GURL kUrl1("http://www.chromium.org");
3318 const GURL kUrl2("http://www.google.com");
3319 const GURL kUrl3("http://www.chromium.org/foo");
3320
3321 contents()->NavigateAndCommit(kUrl1);
3322
3323 TestRenderFrameHost* current_rfh = main_test_rfh();
3324
3325 // Start a browser-initiated cross-process navigation to |kUrl2|. The
3326 // WebContents should be loading.
3327 auto cross_process_navigation =
3328 NavigationSimulator::CreateBrowserInitiated(kUrl2, contents());
3329 cross_process_navigation->ReadyToCommit();
3330 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
3331 EXPECT_TRUE(contents()->IsLoading());
3332
3333 // The current RenderFrameHost starts a non user-initiated render-initiated
3334 // navigation. The WebContents should still be loading.
3335 auto same_process_navigation =
3336 NavigationSimulator::CreateRendererInitiated(kUrl3, current_rfh);
3337 same_process_navigation->SetHasUserGesture(false);
3338 same_process_navigation->Start();
3339 EXPECT_TRUE(contents()->IsLoading());
3340
3341 // Simulate the commit and DidStopLoading from the renderer-initiated
3342 // navigation in the current RenderFrameHost. There should still be a pending
3343 // RenderFrameHost and the WebContents should still be loading.
3344 same_process_navigation->Commit();
3345 current_rfh->OnMessageReceived(
3346 FrameHostMsg_DidStopLoading(current_rfh->GetRoutingID()));
3347 EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh);
3348 EXPECT_TRUE(contents()->IsLoading());
3349
3350 // The same-process navigation should have committed.
3351 ASSERT_EQ(2, controller().GetEntryCount());
3352 EXPECT_EQ(kUrl3, controller().GetLastCommittedEntry()->GetURL());
3353
3354 // Commit the cross-process navigation. The formerly pending RenderFrameHost
3355 // should now be the current RenderFrameHost and the WebContents should still
3356 // be loading.
3357 cross_process_navigation->Commit();
3358 EXPECT_FALSE(contents()->GetPendingMainFrame());
3359 TestRenderFrameHost* new_current_rfh = main_test_rfh();
3360 EXPECT_EQ(new_current_rfh, pending_rfh);
3361 EXPECT_TRUE(contents()->IsLoading());
3362 EXPECT_EQ(3, controller().GetEntryCount());
3363
3364 // Simulate the new current RenderFrameHost DidStopLoading. The WebContents
3365 // should now have stopped loading.
3366 new_current_rfh->OnMessageReceived(
3367 FrameHostMsg_DidStopLoading(new_current_rfh->GetRoutingID()));
3368 EXPECT_EQ(main_test_rfh(), new_current_rfh);
3369 EXPECT_FALSE(contents()->IsLoading());
3370 }
3371
TEST_F(WebContentsImplTest,MediaWakeLock)3372 TEST_F(WebContentsImplTest, MediaWakeLock) {
3373 EXPECT_FALSE(has_audio_wake_lock());
3374
3375 AudioStreamMonitor* monitor = contents()->audio_stream_monitor();
3376
3377 // Ensure RenderFrame is initialized before simulating events coming from it.
3378 main_test_rfh()->InitializeRenderFrameIfNeeded();
3379
3380 // Send a fake audio stream monitor notification. The audio wake lock
3381 // should be created.
3382 monitor->set_was_recently_audible_for_testing(true);
3383 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_AUDIO);
3384 EXPECT_TRUE(has_audio_wake_lock());
3385
3386 // Send another fake notification, this time when WasRecentlyAudible() will
3387 // be false. The wake lock should be released.
3388 monitor->set_was_recently_audible_for_testing(false);
3389 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_AUDIO);
3390 EXPECT_FALSE(has_audio_wake_lock());
3391
3392 main_test_rfh()->GetProcess()->SimulateCrash();
3393
3394 // Verify that all the wake locks have been released.
3395 EXPECT_FALSE(has_audio_wake_lock());
3396 }
3397
TEST_F(WebContentsImplTest,ThemeColorChangeDependingOnFirstVisiblePaint)3398 TEST_F(WebContentsImplTest, ThemeColorChangeDependingOnFirstVisiblePaint) {
3399 TestWebContentsObserver observer(contents());
3400 TestRenderFrameHost* rfh = main_test_rfh();
3401 rfh->InitializeRenderFrameIfNeeded();
3402
3403 EXPECT_EQ(base::nullopt, contents()->GetThemeColor());
3404 EXPECT_EQ(0, observer.theme_color_change_calls());
3405
3406 // Theme color changes should not propagate past the WebContentsImpl before
3407 // the first visually non-empty paint has occurred.
3408 rfh->DidChangeThemeColor(SK_ColorRED);
3409
3410 EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor());
3411 EXPECT_EQ(0, observer.theme_color_change_calls());
3412
3413 // Simulate that the first visually non-empty paint has occurred. This will
3414 // propagate the current theme color to the delegates.
3415 RenderViewHostTester::SimulateFirstPaint(test_rvh());
3416
3417 EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor());
3418 EXPECT_EQ(1, observer.theme_color_change_calls());
3419
3420 // Additional changes made by the web contents should propagate as well.
3421 rfh->DidChangeThemeColor(SK_ColorGREEN);
3422
3423 EXPECT_EQ(SK_ColorGREEN, contents()->GetThemeColor());
3424 EXPECT_EQ(2, observer.theme_color_change_calls());
3425 }
3426
TEST_F(WebContentsImplTest,ParseDownloadHeaders)3427 TEST_F(WebContentsImplTest, ParseDownloadHeaders) {
3428 download::DownloadUrlParameters::RequestHeadersType request_headers =
3429 WebContentsImpl::ParseDownloadHeaders("A: 1\r\nB: 2\r\nC: 3\r\n\r\n");
3430 ASSERT_EQ(3u, request_headers.size());
3431 EXPECT_EQ("A", request_headers[0].first);
3432 EXPECT_EQ("1", request_headers[0].second);
3433 EXPECT_EQ("B", request_headers[1].first);
3434 EXPECT_EQ("2", request_headers[1].second);
3435 EXPECT_EQ("C", request_headers[2].first);
3436 EXPECT_EQ("3", request_headers[2].second);
3437
3438 request_headers = WebContentsImpl::ParseDownloadHeaders("A:1\r\nA:2\r\n");
3439 ASSERT_EQ(2u, request_headers.size());
3440 EXPECT_EQ("A", request_headers[0].first);
3441 EXPECT_EQ("1", request_headers[0].second);
3442 EXPECT_EQ("A", request_headers[1].first);
3443 EXPECT_EQ("2", request_headers[1].second);
3444
3445 request_headers = WebContentsImpl::ParseDownloadHeaders("A 1\r\nA: 2");
3446 ASSERT_EQ(1u, request_headers.size());
3447 EXPECT_EQ("A", request_headers[0].first);
3448 EXPECT_EQ("2", request_headers[0].second);
3449
3450 request_headers = WebContentsImpl::ParseDownloadHeaders("A: 1");
3451 ASSERT_EQ(1u, request_headers.size());
3452 EXPECT_EQ("A", request_headers[0].first);
3453 EXPECT_EQ("1", request_headers[0].second);
3454
3455 request_headers = WebContentsImpl::ParseDownloadHeaders("A 1");
3456 ASSERT_EQ(0u, request_headers.size());
3457 }
3458
3459 namespace {
3460
3461 class TestJavaScriptDialogManager : public JavaScriptDialogManager {
3462 public:
TestJavaScriptDialogManager()3463 TestJavaScriptDialogManager() {}
~TestJavaScriptDialogManager()3464 ~TestJavaScriptDialogManager() override {}
3465
reset_count()3466 size_t reset_count() { return reset_count_; }
3467
3468 // JavaScriptDialogManager
3469
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)3470 void RunJavaScriptDialog(WebContents* web_contents,
3471 RenderFrameHost* render_frame_host,
3472 JavaScriptDialogType dialog_type,
3473 const base::string16& message_text,
3474 const base::string16& default_prompt_text,
3475 DialogClosedCallback callback,
3476 bool* did_suppress_message) override {
3477 *did_suppress_message = true;
3478 }
3479
RunBeforeUnloadDialog(WebContents * web_contents,RenderFrameHost * render_frame_host,bool is_reload,DialogClosedCallback callback)3480 void RunBeforeUnloadDialog(WebContents* web_contents,
3481 RenderFrameHost* render_frame_host,
3482 bool is_reload,
3483 DialogClosedCallback callback) override {}
3484
HandleJavaScriptDialog(WebContents * web_contents,bool accept,const base::string16 * prompt_override)3485 bool HandleJavaScriptDialog(WebContents* web_contents,
3486 bool accept,
3487 const base::string16* prompt_override) override {
3488 return true;
3489 }
3490
CancelDialogs(WebContents * web_contents,bool reset_state)3491 void CancelDialogs(WebContents* web_contents,
3492 bool reset_state) override {
3493 if (reset_state)
3494 ++reset_count_;
3495 }
3496
3497 private:
3498 size_t reset_count_ = 0;
3499
3500 DISALLOW_COPY_AND_ASSIGN(TestJavaScriptDialogManager);
3501 };
3502
3503 } // namespace
3504
TEST_F(WebContentsImplTest,ResetJavaScriptDialogOnUserNavigate)3505 TEST_F(WebContentsImplTest, ResetJavaScriptDialogOnUserNavigate) {
3506 const GURL kUrl("http://www.google.com");
3507 const GURL kUrl2("http://www.google.com/sub");
3508 TestJavaScriptDialogManager dialog_manager;
3509 contents()->SetJavaScriptDialogManagerForTesting(&dialog_manager);
3510
3511 // A user-initiated navigation.
3512 NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kUrl);
3513 EXPECT_EQ(1u, dialog_manager.reset_count());
3514
3515 // An automatic navigation.
3516 auto navigation =
3517 NavigationSimulator::CreateRendererInitiated(kUrl2, main_test_rfh());
3518 navigation->SetHasUserGesture(false);
3519 navigation->Commit();
3520 EXPECT_EQ(1u, dialog_manager.reset_count());
3521
3522 contents()->SetJavaScriptDialogManagerForTesting(nullptr);
3523 }
3524
TEST_F(WebContentsImplTest,StartingSandboxFlags)3525 TEST_F(WebContentsImplTest, StartingSandboxFlags) {
3526 WebContents::CreateParams params(browser_context());
3527 const blink::mojom::WebSandboxFlags expected_flags =
3528 blink::mojom::WebSandboxFlags::kPopups |
3529 blink::mojom::WebSandboxFlags::kModals |
3530 blink::mojom::WebSandboxFlags::kTopNavigation;
3531 params.starting_sandbox_flags = expected_flags;
3532 std::unique_ptr<WebContentsImpl> new_contents(
3533 WebContentsImpl::CreateWithOpener(params, nullptr));
3534 FrameTreeNode* root = new_contents->GetFrameTree()->root();
3535 blink::mojom::WebSandboxFlags pending_flags =
3536 root->pending_frame_policy().sandbox_flags;
3537 EXPECT_EQ(pending_flags, expected_flags);
3538 blink::mojom::WebSandboxFlags effective_flags =
3539 root->effective_frame_policy().sandbox_flags;
3540 EXPECT_EQ(effective_flags, expected_flags);
3541 }
3542
TEST_F(WebContentsImplTest,DidFirstVisuallyNonEmptyPaint)3543 TEST_F(WebContentsImplTest, DidFirstVisuallyNonEmptyPaint) {
3544 TestWebContentsObserver observer(contents());
3545
3546 RenderWidgetHostOwnerDelegate* rwhod = test_rvh();
3547 rwhod->RenderWidgetDidFirstVisuallyNonEmptyPaint();
3548
3549 EXPECT_TRUE(observer.observed_did_first_visually_non_empty_paint());
3550 }
3551
TEST_F(WebContentsImplTest,DidChangeVerticalScrollDirection)3552 TEST_F(WebContentsImplTest, DidChangeVerticalScrollDirection) {
3553 TestWebContentsObserver observer(contents());
3554
3555 EXPECT_FALSE(observer.last_vertical_scroll_direction().has_value());
3556
3557 contents()->OnVerticalScrollDirectionChanged(
3558 viz::VerticalScrollDirection::kUp);
3559
3560 EXPECT_EQ(viz::VerticalScrollDirection::kUp,
3561 observer.last_vertical_scroll_direction().value());
3562 }
3563
3564 namespace {
3565
3566 class MockWebContentsDelegate : public WebContentsDelegate {
3567 public:
3568 MOCK_METHOD2(HandleContextMenu,
3569 bool(RenderFrameHost*, const ContextMenuParams&));
3570 MOCK_METHOD4(RegisterProtocolHandler,
3571 void(WebContents*, const std::string&, const GURL&, bool));
3572 };
3573
3574 } // namespace
3575
TEST_F(WebContentsImplTest,HandleContextMenuDelegate)3576 TEST_F(WebContentsImplTest, HandleContextMenuDelegate) {
3577 MockWebContentsDelegate delegate;
3578 contents()->SetDelegate(&delegate);
3579
3580 RenderFrameHost* rfh = main_test_rfh();
3581 EXPECT_CALL(delegate, HandleContextMenu(rfh, ::testing::_))
3582 .WillOnce(::testing::Return(true));
3583
3584 ContextMenuParams params;
3585 contents()->ShowContextMenu(rfh, params);
3586
3587 contents()->SetDelegate(nullptr);
3588 }
3589
TEST_F(WebContentsImplTest,RegisterProtocolHandlerDifferentOrigin)3590 TEST_F(WebContentsImplTest, RegisterProtocolHandlerDifferentOrigin) {
3591 MockWebContentsDelegate delegate;
3592 contents()->SetDelegate(&delegate);
3593
3594 GURL url("https://www.google.com");
3595 GURL handler_url1("https://www.google.com/handler/%s");
3596 GURL handler_url2("https://www.example.com/handler/%s");
3597
3598 contents()->NavigateAndCommit(url);
3599
3600 // Only the first call to RegisterProtocolHandler should register because the
3601 // other call has a handler from a different origin.
3602 EXPECT_CALL(delegate,
3603 RegisterProtocolHandler(contents(), "mailto", handler_url1, true))
3604 .Times(1);
3605
3606 {
3607 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url1,
3608 base::string16(),
3609 /*user_gesture=*/true);
3610 }
3611
3612 {
3613 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", handler_url2,
3614 base::string16(),
3615 /*user_gesture=*/true);
3616 }
3617
3618 contents()->SetDelegate(nullptr);
3619 }
3620
TEST_F(WebContentsImplTest,RegisterProtocolHandlerDataURL)3621 TEST_F(WebContentsImplTest, RegisterProtocolHandlerDataURL) {
3622 MockWebContentsDelegate delegate;
3623 contents()->SetDelegate(&delegate);
3624
3625 GURL data("data:text/html,<html><body><b>hello world</b></body></html>");
3626 GURL data_handler(data.spec() + "%s");
3627
3628 contents()->NavigateAndCommit(data);
3629
3630 // Data URLs should fail.
3631 EXPECT_CALL(delegate,
3632 RegisterProtocolHandler(contents(), "mailto", data_handler, true))
3633 .Times(0);
3634
3635 {
3636 contents()->RegisterProtocolHandler(main_test_rfh(), "mailto", data_handler,
3637 base::string16(),
3638 /*user_gesture=*/true);
3639 }
3640
3641 contents()->SetDelegate(nullptr);
3642 }
3643
TEST_F(WebContentsImplTest,Bluetooth)3644 TEST_F(WebContentsImplTest, Bluetooth) {
3645 TestWebContentsObserver observer(contents());
3646 EXPECT_EQ(observer.num_is_connected_to_bluetooth_device_changed(), 0);
3647 EXPECT_FALSE(contents()->IsConnectedToBluetoothDevice());
3648
3649 contents()->TestIncrementBluetoothConnectedDeviceCount();
3650 EXPECT_EQ(observer.num_is_connected_to_bluetooth_device_changed(), 1);
3651 EXPECT_TRUE(observer.last_is_connected_to_bluetooth_device());
3652 EXPECT_TRUE(contents()->IsConnectedToBluetoothDevice());
3653
3654 contents()->TestDecrementBluetoothConnectedDeviceCount();
3655 EXPECT_EQ(observer.num_is_connected_to_bluetooth_device_changed(), 2);
3656 EXPECT_FALSE(observer.last_is_connected_to_bluetooth_device());
3657 EXPECT_FALSE(contents()->IsConnectedToBluetoothDevice());
3658 }
3659
TEST_F(WebContentsImplTest,FaviconURLsSet)3660 TEST_F(WebContentsImplTest, FaviconURLsSet) {
3661 std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
3662 const auto kFavicon =
3663 blink::mojom::FaviconURL(GURL("https://example.com/favicon.ico"),
3664 blink::mojom::FaviconIconType::kFavicon, {});
3665
3666 contents()->NavigateAndCommit(GURL("https://example.com"));
3667 EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
3668
3669 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
3670 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
3671 std::move(favicon_urls));
3672 EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
3673
3674 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
3675 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
3676 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
3677 std::move(favicon_urls));
3678 EXPECT_EQ(2u, contents()->GetFaviconURLs().size());
3679
3680 favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
3681 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
3682 std::move(favicon_urls));
3683 EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
3684 }
3685
TEST_F(WebContentsImplTest,FaviconURLsResetWithNavigation)3686 TEST_F(WebContentsImplTest, FaviconURLsResetWithNavigation) {
3687 std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
3688 favicon_urls.push_back(blink::mojom::FaviconURL::New(
3689 GURL("https://example.com/favicon.ico"),
3690 blink::mojom::FaviconIconType::kFavicon, std::vector<gfx::Size>()));
3691
3692 contents()->NavigateAndCommit(GURL("https://example.com"));
3693 EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
3694
3695 contents()->UpdateFaviconURL(contents()->GetMainFrame(),
3696 std::move(favicon_urls));
3697 EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
3698
3699 contents()->NavigateAndCommit(GURL("https://example.com/navigation.html"));
3700 EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
3701 }
3702
3703 } // namespace content
3704