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(&params, 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(&params, 0, true, GURL(url::kAboutBlankURL),
431                      ui::PAGE_TRANSITION_TYPED);
432 
433   main_test_rfh()->SendNavigateWithParams(&params,
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, &params));
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