e04b4410c69ec31a0ec84f84d039035ba49bb389
[platform/framework/web/crosswalk.git] / src / content / browser / web_contents / web_contents_impl_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/logging.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "content/browser/frame_host/interstitial_page_impl.h"
8 #include "content/browser/frame_host/navigation_entry_impl.h"
9 #include "content/browser/renderer_host/cross_site_transferring_request.h"
10 #include "content/browser/renderer_host/render_view_host_impl.h"
11 #include "content/browser/site_instance_impl.h"
12 #include "content/browser/webui/web_ui_controller_factory_registry.h"
13 #include "content/common/frame_messages.h"
14 #include "content/common/view_messages.h"
15 #include "content/public/browser/global_request_id.h"
16 #include "content/public/browser/interstitial_page_delegate.h"
17 #include "content/public/browser/navigation_details.h"
18 #include "content/public/browser/notification_details.h"
19 #include "content/public/browser/notification_source.h"
20 #include "content/public/browser/render_widget_host_view.h"
21 #include "content/public/browser/web_contents_observer.h"
22 #include "content/public/browser/web_ui_controller.h"
23 #include "content/public/common/bindings_policy.h"
24 #include "content/public/common/content_constants.h"
25 #include "content/public/common/url_constants.h"
26 #include "content/public/common/url_utils.h"
27 #include "content/public/test/mock_render_process_host.h"
28 #include "content/public/test/test_utils.h"
29 #include "content/test/test_content_browser_client.h"
30 #include "content/test/test_content_client.h"
31 #include "content/test/test_render_view_host.h"
32 #include "content/test/test_web_contents.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34
35 namespace content {
36 namespace {
37
38 const char kTestWebUIUrl[] = "chrome://blah";
39
40 class WebContentsImplTestWebUIControllerFactory
41     : public WebUIControllerFactory {
42  public:
43   virtual WebUIController* CreateWebUIControllerForURL(
44       WebUI* web_ui, const GURL& url) const OVERRIDE {
45     if (!UseWebUI(url))
46       return NULL;
47     return new WebUIController(web_ui);
48   }
49
50   virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
51       const GURL& url) const OVERRIDE {
52     return WebUI::kNoWebUI;
53   }
54
55   virtual bool UseWebUIForURL(BrowserContext* browser_context,
56                               const GURL& url) const OVERRIDE {
57     return UseWebUI(url);
58   }
59
60   virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
61                                       const GURL& url) const OVERRIDE {
62     return UseWebUI(url);
63   }
64
65  private:
66   bool UseWebUI(const GURL& url) const {
67     return url == GURL(kTestWebUIUrl);
68   }
69 };
70
71 class TestInterstitialPage;
72
73 class TestInterstitialPageDelegate : public InterstitialPageDelegate {
74  public:
75   explicit TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page)
76       : interstitial_page_(interstitial_page) {}
77   virtual void CommandReceived(const std::string& command) OVERRIDE;
78   virtual std::string GetHTMLContents() OVERRIDE { return std::string(); }
79   virtual void OnDontProceed() OVERRIDE;
80   virtual void OnProceed() OVERRIDE;
81  private:
82   TestInterstitialPage* interstitial_page_;
83 };
84
85 class TestInterstitialPage : public InterstitialPageImpl {
86  public:
87   enum InterstitialState {
88     INVALID = 0,    // Hasn't yet been initialized.
89     UNDECIDED,      // Initialized, but no decision taken yet.
90     OKED,           // Proceed was called.
91     CANCELED        // DontProceed was called.
92   };
93
94   class Delegate {
95    public:
96     virtual void TestInterstitialPageDeleted(
97         TestInterstitialPage* interstitial) = 0;
98
99    protected:
100     virtual ~Delegate() {}
101   };
102
103   // IMPORTANT NOTE: if you pass stack allocated values for |state| and
104   // |deleted| (like all interstitial related tests do at this point), make sure
105   // to create an instance of the TestInterstitialPageStateGuard class on the
106   // stack in your test.  This will ensure that the TestInterstitialPage states
107   // are cleared when the test finishes.
108   // Not doing so will cause stack trashing if your test does not hide the
109   // interstitial, as in such a case it will be destroyed in the test TearDown
110   // method and will dereference the |deleted| local variable which by then is
111   // out of scope.
112   TestInterstitialPage(WebContentsImpl* contents,
113                        bool new_navigation,
114                        const GURL& url,
115                        InterstitialState* state,
116                        bool* deleted)
117       : InterstitialPageImpl(
118             contents,
119             static_cast<RenderWidgetHostDelegate*>(contents),
120             new_navigation, url, new TestInterstitialPageDelegate(this)),
121         state_(state),
122         deleted_(deleted),
123         command_received_count_(0),
124         delegate_(NULL) {
125     *state_ = UNDECIDED;
126     *deleted_ = false;
127   }
128
129   virtual ~TestInterstitialPage() {
130     if (deleted_)
131       *deleted_ = true;
132     if (delegate_)
133       delegate_->TestInterstitialPageDeleted(this);
134   }
135
136   void OnDontProceed() {
137     if (state_)
138       *state_ = CANCELED;
139   }
140   void OnProceed() {
141     if (state_)
142       *state_ = OKED;
143   }
144
145   int command_received_count() const {
146     return command_received_count_;
147   }
148
149   void TestDomOperationResponse(const std::string& json_string) {
150     if (enabled())
151       CommandReceived();
152   }
153
154   void TestDidNavigate(int page_id, const GURL& url) {
155     FrameHostMsg_DidCommitProvisionalLoad_Params params;
156     InitNavigateParams(&params, page_id, url, PAGE_TRANSITION_TYPED);
157     DidNavigate(GetRenderViewHostForTesting(), params);
158   }
159
160   void TestRenderViewTerminated(base::TerminationStatus status,
161                                 int error_code) {
162     RenderViewTerminated(GetRenderViewHostForTesting(), status, error_code);
163   }
164
165   bool is_showing() const {
166     return static_cast<TestRenderWidgetHostView*>(
167         GetRenderViewHostForTesting()->GetView())->is_showing();
168   }
169
170   void ClearStates() {
171     state_ = NULL;
172     deleted_ = NULL;
173     delegate_ = NULL;
174   }
175
176   void CommandReceived() {
177     command_received_count_++;
178   }
179
180   void set_delegate(Delegate* delegate) {
181     delegate_ = delegate;
182   }
183
184  protected:
185   virtual WebContentsView* CreateWebContentsView() OVERRIDE {
186     return NULL;
187   }
188
189  private:
190   InterstitialState* state_;
191   bool* deleted_;
192   int command_received_count_;
193   Delegate* delegate_;
194 };
195
196 void TestInterstitialPageDelegate::CommandReceived(const std::string& command) {
197   interstitial_page_->CommandReceived();
198 }
199
200 void TestInterstitialPageDelegate::OnDontProceed() {
201   interstitial_page_->OnDontProceed();
202 }
203
204 void TestInterstitialPageDelegate::OnProceed() {
205   interstitial_page_->OnProceed();
206 }
207
208 class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
209  public:
210   explicit TestInterstitialPageStateGuard(
211       TestInterstitialPage* interstitial_page)
212       : interstitial_page_(interstitial_page) {
213     DCHECK(interstitial_page_);
214     interstitial_page_->set_delegate(this);
215   }
216   virtual ~TestInterstitialPageStateGuard() {
217     if (interstitial_page_)
218       interstitial_page_->ClearStates();
219   }
220
221   virtual void TestInterstitialPageDeleted(
222       TestInterstitialPage* interstitial) OVERRIDE {
223     DCHECK(interstitial_page_ == interstitial);
224     interstitial_page_ = NULL;
225   }
226
227  private:
228   TestInterstitialPage* interstitial_page_;
229 };
230
231 class WebContentsImplTestBrowserClient : public TestContentBrowserClient {
232  public:
233   WebContentsImplTestBrowserClient()
234       : assign_site_for_url_(false) {}
235
236   virtual ~WebContentsImplTestBrowserClient() {}
237
238   virtual bool ShouldAssignSiteForURL(const GURL& url) OVERRIDE {
239     return assign_site_for_url_;
240   }
241
242   void set_assign_site_for_url(bool assign) {
243     assign_site_for_url_ = assign;
244   }
245
246  private:
247   bool assign_site_for_url_;
248 };
249
250 class WebContentsImplTest : public RenderViewHostImplTestHarness {
251  public:
252   virtual void SetUp() {
253     RenderViewHostImplTestHarness::SetUp();
254     WebUIControllerFactory::RegisterFactory(&factory_);
255   }
256
257   virtual void TearDown() {
258     WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
259     RenderViewHostImplTestHarness::TearDown();
260   }
261
262  private:
263   WebContentsImplTestWebUIControllerFactory factory_;
264 };
265
266 class TestWebContentsObserver : public WebContentsObserver {
267  public:
268   explicit TestWebContentsObserver(WebContents* contents)
269       : WebContentsObserver(contents) {
270   }
271   virtual ~TestWebContentsObserver() {}
272
273   virtual void DidFinishLoad(int64 frame_id,
274                              const GURL& validated_url,
275                              bool is_main_frame,
276                              RenderViewHost* render_view_host) OVERRIDE {
277     last_url_ = validated_url;
278   }
279   virtual void DidFailLoad(int64 frame_id,
280                            const GURL& validated_url,
281                            bool is_main_frame,
282                            int error_code,
283                            const base::string16& error_description,
284                            RenderViewHost* render_view_host) OVERRIDE {
285     last_url_ = validated_url;
286   }
287
288   const GURL& last_url() const { return last_url_; }
289
290  private:
291   GURL last_url_;
292
293   DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
294 };
295
296 }  // namespace
297
298 // Test to make sure that title updates get stripped of whitespace.
299 TEST_F(WebContentsImplTest, UpdateTitle) {
300   NavigationControllerImpl& cont =
301       static_cast<NavigationControllerImpl&>(controller());
302   FrameHostMsg_DidCommitProvisionalLoad_Params params;
303   InitNavigateParams(&params, 0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED);
304
305   LoadCommittedDetails details;
306   cont.RendererDidNavigate(test_rvh(), params, &details);
307
308   contents()->UpdateTitle(rvh(), 0,
309                           base::ASCIIToUTF16("    Lots O' Whitespace\n"),
310                           base::i18n::LEFT_TO_RIGHT);
311   EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
312 }
313
314 TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
315   const GURL kGURL("chrome://blah");
316   controller().LoadURL(
317       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
318   EXPECT_EQ(base::string16(), contents()->GetTitle());
319 }
320
321 TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
322   const GURL kGURL("chrome://blah");
323   const base::string16 title = base::ASCIIToUTF16("My Title");
324   controller().LoadURL(
325       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
326
327   NavigationEntry* entry = controller().GetVisibleEntry();
328   ASSERT_EQ(kGURL, entry->GetURL());
329   entry->SetTitle(title);
330
331   EXPECT_EQ(title, contents()->GetTitle());
332 }
333
334 // Test view source mode for a webui page.
335 TEST_F(WebContentsImplTest, NTPViewSource) {
336   NavigationControllerImpl& cont =
337       static_cast<NavigationControllerImpl&>(controller());
338   const char kUrl[] = "view-source:chrome://blah";
339   const GURL kGURL(kUrl);
340
341   process()->sink().ClearMessages();
342
343   cont.LoadURL(
344       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
345   rvh()->GetDelegate()->RenderViewCreated(rvh());
346   // Did we get the expected message?
347   EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
348       ViewMsg_EnableViewSourceMode::ID));
349
350   FrameHostMsg_DidCommitProvisionalLoad_Params params;
351   InitNavigateParams(&params, 0, kGURL, PAGE_TRANSITION_TYPED);
352   LoadCommittedDetails details;
353   cont.RendererDidNavigate(test_rvh(), params, &details);
354   // Also check title and url.
355   EXPECT_EQ(base::ASCIIToUTF16(kUrl), contents()->GetTitle());
356 }
357
358 // Test to ensure UpdateMaxPageID is working properly.
359 TEST_F(WebContentsImplTest, UpdateMaxPageID) {
360   SiteInstance* instance1 = contents()->GetSiteInstance();
361   scoped_refptr<SiteInstance> instance2(SiteInstance::Create(NULL));
362
363   // Starts at -1.
364   EXPECT_EQ(-1, contents()->GetMaxPageID());
365   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1));
366   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
367
368   // Make sure max_page_id_ is monotonically increasing per SiteInstance.
369   contents()->UpdateMaxPageID(3);
370   contents()->UpdateMaxPageID(1);
371   EXPECT_EQ(3, contents()->GetMaxPageID());
372   EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
373   EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
374
375   contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7);
376   EXPECT_EQ(3, contents()->GetMaxPageID());
377   EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
378   EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
379 }
380
381 // Test simple same-SiteInstance navigation.
382 TEST_F(WebContentsImplTest, SimpleNavigation) {
383   TestRenderViewHost* orig_rvh = test_rvh();
384   SiteInstance* instance1 = contents()->GetSiteInstance();
385   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
386
387   // Navigate to URL
388   const GURL url("http://www.google.com");
389   controller().LoadURL(
390       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
391   EXPECT_FALSE(contents()->cross_navigation_pending());
392   EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
393   // Controller's pending entry will have a NULL site instance until we assign
394   // it in DidNavigate.
395   EXPECT_TRUE(
396       NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
397           site_instance() == NULL);
398
399   // DidNavigate from the page
400   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
401   EXPECT_FALSE(contents()->cross_navigation_pending());
402   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
403   EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
404   // Controller's entry should now have the SiteInstance, or else we won't be
405   // able to find it later.
406   EXPECT_EQ(
407       instance1,
408       NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
409           site_instance());
410 }
411
412 // Test that we reject NavigateToEntry if the url is over kMaxURLChars.
413 TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
414   // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
415   const GURL url(std::string("http://example.org/").append(
416       GetMaxURLChars() + 1, 'a'));
417
418   controller().LoadURL(
419       url, Referrer(), PAGE_TRANSITION_GENERATED, std::string());
420   EXPECT_TRUE(controller().GetVisibleEntry() == NULL);
421 }
422
423 // Test that navigating across a site boundary creates a new RenderViewHost
424 // with a new SiteInstance.  Going back should do the same.
425 TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
426   contents()->transition_cross_site = true;
427   TestRenderViewHost* orig_rvh = test_rvh();
428   RenderFrameHostImpl* orig_rfh =
429       contents()->GetFrameTree()->root()->current_frame_host();
430   int orig_rvh_delete_count = 0;
431   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
432   SiteInstance* instance1 = contents()->GetSiteInstance();
433
434   // Navigate to URL.  First URL should use first RenderViewHost.
435   const GURL url("http://www.google.com");
436   controller().LoadURL(
437       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
438   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
439
440   // Keep the number of active views in orig_rvh's SiteInstance
441   // non-zero so that orig_rvh doesn't get deleted when it gets
442   // swapped out.
443   static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())->
444       increment_active_view_count();
445
446   EXPECT_FALSE(contents()->cross_navigation_pending());
447   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
448   EXPECT_EQ(url, contents()->GetLastCommittedURL());
449   EXPECT_EQ(url, contents()->GetVisibleURL());
450
451   // Navigate to new site
452   const GURL url2("http://www.yahoo.com");
453   controller().LoadURL(
454       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
455   EXPECT_TRUE(contents()->cross_navigation_pending());
456   EXPECT_EQ(url, contents()->GetLastCommittedURL());
457   EXPECT_EQ(url2, contents()->GetVisibleURL());
458   TestRenderViewHost* pending_rvh =
459       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
460   int pending_rvh_delete_count = 0;
461   pending_rvh->set_delete_counter(&pending_rvh_delete_count);
462   RenderFrameHostImpl* pending_rfh = contents()->GetFrameTree()->root()->
463       render_manager()->pending_frame_host();
464
465   // Navigations should be suspended in pending_rvh until ShouldCloseACK.
466   EXPECT_TRUE(pending_rvh->are_navigations_suspended());
467   orig_rvh->SendShouldCloseACK(true);
468   EXPECT_FALSE(pending_rvh->are_navigations_suspended());
469
470   // DidNavigate from the pending page
471   contents()->TestDidNavigate(
472       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
473   SiteInstance* instance2 = contents()->GetSiteInstance();
474
475   // Keep the number of active views in pending_rvh's SiteInstance
476   // non-zero so that orig_rvh doesn't get deleted when it gets
477   // swapped out.
478   static_cast<SiteInstanceImpl*>(pending_rvh->GetSiteInstance())->
479       increment_active_view_count();
480
481   EXPECT_FALSE(contents()->cross_navigation_pending());
482   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
483   EXPECT_EQ(url2, contents()->GetLastCommittedURL());
484   EXPECT_EQ(url2, contents()->GetVisibleURL());
485   EXPECT_NE(instance1, instance2);
486   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
487   // We keep the original RFH around, swapped out.
488   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
489       orig_rfh));
490   EXPECT_EQ(orig_rvh_delete_count, 0);
491
492   // Going back should switch SiteInstances again.  The first SiteInstance is
493   // stored in the NavigationEntry, so it should be the same as at the start.
494   // We should use the same RVH as before, swapping it back in.
495   controller().GoBack();
496   TestRenderViewHost* goback_rvh =
497       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
498   EXPECT_EQ(orig_rvh, goback_rvh);
499   EXPECT_TRUE(contents()->cross_navigation_pending());
500
501   // Navigations should be suspended in goback_rvh until ShouldCloseACK.
502   EXPECT_TRUE(goback_rvh->are_navigations_suspended());
503   pending_rvh->SendShouldCloseACK(true);
504   EXPECT_FALSE(goback_rvh->are_navigations_suspended());
505
506   // DidNavigate from the back action
507   contents()->TestDidNavigate(
508       goback_rvh, 1, url2, PAGE_TRANSITION_TYPED);
509   EXPECT_FALSE(contents()->cross_navigation_pending());
510   EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost());
511   EXPECT_EQ(instance1, contents()->GetSiteInstance());
512   // The pending RFH should now be swapped out, not deleted.
513   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
514       IsOnSwappedOutList(pending_rfh));
515   EXPECT_EQ(pending_rvh_delete_count, 0);
516   pending_rvh->OnSwappedOut(false);
517
518   // Close contents and ensure RVHs are deleted.
519   DeleteContents();
520   EXPECT_EQ(orig_rvh_delete_count, 1);
521   EXPECT_EQ(pending_rvh_delete_count, 1);
522 }
523
524 // Test that navigating across a site boundary after a crash creates a new
525 // RVH without requiring a cross-site transition (i.e., PENDING state).
526 TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
527   contents()->transition_cross_site = true;
528   TestRenderViewHost* orig_rvh = test_rvh();
529   int orig_rvh_delete_count = 0;
530   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
531   SiteInstance* instance1 = contents()->GetSiteInstance();
532
533   // Navigate to URL.  First URL should use first RenderViewHost.
534   const GURL url("http://www.google.com");
535   controller().LoadURL(
536       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
537   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
538
539   EXPECT_FALSE(contents()->cross_navigation_pending());
540   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
541
542   // Crash the renderer.
543   orig_rvh->set_render_view_created(false);
544
545   // Navigate to new site.  We should not go into PENDING.
546   const GURL url2("http://www.yahoo.com");
547   controller().LoadURL(
548       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
549   RenderViewHost* new_rvh = rvh();
550   EXPECT_FALSE(contents()->cross_navigation_pending());
551   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
552   EXPECT_NE(orig_rvh, new_rvh);
553   EXPECT_EQ(orig_rvh_delete_count, 1);
554
555   // DidNavigate from the new page
556   contents()->TestDidNavigate(new_rvh, 1, url2, PAGE_TRANSITION_TYPED);
557   SiteInstance* instance2 = contents()->GetSiteInstance();
558
559   EXPECT_FALSE(contents()->cross_navigation_pending());
560   EXPECT_EQ(new_rvh, rvh());
561   EXPECT_NE(instance1, instance2);
562   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
563
564   // Close contents and ensure RVHs are deleted.
565   DeleteContents();
566   EXPECT_EQ(orig_rvh_delete_count, 1);
567 }
568
569 // Test that opening a new contents in the same SiteInstance and then navigating
570 // both contentses to a new site will place both contentses in a single
571 // SiteInstance.
572 TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
573   contents()->transition_cross_site = true;
574   TestRenderViewHost* orig_rvh = test_rvh();
575   SiteInstance* instance1 = contents()->GetSiteInstance();
576
577   // Navigate to URL.  First URL should use first RenderViewHost.
578   const GURL url("http://www.google.com");
579   controller().LoadURL(
580       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
581   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
582
583   // Open a new contents with the same SiteInstance, navigated to the same site.
584   scoped_ptr<TestWebContents> contents2(
585       TestWebContents::Create(browser_context(), instance1));
586   contents2->transition_cross_site = true;
587   contents2->GetController().LoadURL(url, Referrer(),
588                                     PAGE_TRANSITION_TYPED,
589                                     std::string());
590   // Need this page id to be 2 since the site instance is the same (which is the
591   // scope of page IDs) and we want to consider this a new page.
592   contents2->TestDidNavigate(
593       contents2->GetRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED);
594
595   // Navigate first contents to a new site.
596   const GURL url2a("http://www.yahoo.com");
597   controller().LoadURL(
598       url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string());
599   orig_rvh->SendShouldCloseACK(true);
600   TestRenderViewHost* pending_rvh_a =
601       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
602   contents()->TestDidNavigate(
603       pending_rvh_a, 1, url2a, PAGE_TRANSITION_TYPED);
604   SiteInstance* instance2a = contents()->GetSiteInstance();
605   EXPECT_NE(instance1, instance2a);
606
607   // Navigate second contents to the same site as the first tab.
608   const GURL url2b("http://mail.yahoo.com");
609   contents2->GetController().LoadURL(url2b, Referrer(),
610                                     PAGE_TRANSITION_TYPED,
611                                     std::string());
612   TestRenderViewHost* rvh2 =
613       static_cast<TestRenderViewHost*>(contents2->GetRenderViewHost());
614   rvh2->SendShouldCloseACK(true);
615   TestRenderViewHost* pending_rvh_b =
616       static_cast<TestRenderViewHost*>(contents2->GetPendingRenderViewHost());
617   EXPECT_TRUE(pending_rvh_b != NULL);
618   EXPECT_TRUE(contents2->cross_navigation_pending());
619
620   // NOTE(creis): We used to be in danger of showing a crash page here if the
621   // second contents hadn't navigated somewhere first (bug 1145430).  That case
622   // is now covered by the CrossSiteBoundariesAfterCrash test.
623   contents2->TestDidNavigate(
624       pending_rvh_b, 2, url2b, PAGE_TRANSITION_TYPED);
625   SiteInstance* instance2b = contents2->GetSiteInstance();
626   EXPECT_NE(instance1, instance2b);
627
628   // Both contentses should now be in the same SiteInstance.
629   EXPECT_EQ(instance2a, instance2b);
630 }
631
632 TEST_F(WebContentsImplTest, NavigateDoesNotUseUpSiteInstance) {
633   WebContentsImplTestBrowserClient browser_client;
634   SetBrowserClientForTesting(&browser_client);
635
636   contents()->transition_cross_site = true;
637   TestRenderViewHost* orig_rvh = test_rvh();
638   RenderFrameHostImpl* orig_rfh =
639       contents()->GetFrameTree()->root()->current_frame_host();
640   int orig_rvh_delete_count = 0;
641   orig_rvh->set_delete_counter(&orig_rvh_delete_count);
642   SiteInstanceImpl* orig_instance =
643       static_cast<SiteInstanceImpl*>(contents()->GetSiteInstance());
644
645   browser_client.set_assign_site_for_url(false);
646   // Navigate to an URL that will not assign a new SiteInstance.
647   const GURL native_url("non-site-url://stuffandthings");
648   controller().LoadURL(
649       native_url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
650   contents()->TestDidNavigate(orig_rvh, 1, native_url, PAGE_TRANSITION_TYPED);
651
652   EXPECT_FALSE(contents()->cross_navigation_pending());
653   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
654   EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
655   EXPECT_EQ(native_url, contents()->GetVisibleURL());
656   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
657   EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
658   EXPECT_FALSE(orig_instance->HasSite());
659
660   browser_client.set_assign_site_for_url(true);
661   // Navigate to new site (should keep same site instance).
662   const GURL url("http://www.google.com");
663   controller().LoadURL(
664       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
665   EXPECT_FALSE(contents()->cross_navigation_pending());
666   EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
667   EXPECT_EQ(url, contents()->GetVisibleURL());
668   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
669   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
670
671   // Keep the number of active views in orig_rvh's SiteInstance
672   // non-zero so that orig_rvh doesn't get deleted when it gets
673   // swapped out.
674   static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())->
675       increment_active_view_count();
676
677   EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
678   EXPECT_TRUE(
679       contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com"));
680   EXPECT_EQ(url, contents()->GetLastCommittedURL());
681
682   // Navigate to another new site (should create a new site instance).
683   const GURL url2("http://www.yahoo.com");
684   controller().LoadURL(
685       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
686   EXPECT_TRUE(contents()->cross_navigation_pending());
687   EXPECT_EQ(url, contents()->GetLastCommittedURL());
688   EXPECT_EQ(url2, contents()->GetVisibleURL());
689   TestRenderViewHost* pending_rvh =
690       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
691   int pending_rvh_delete_count = 0;
692   pending_rvh->set_delete_counter(&pending_rvh_delete_count);
693
694   // Navigations should be suspended in pending_rvh until ShouldCloseACK.
695   EXPECT_TRUE(pending_rvh->are_navigations_suspended());
696   orig_rvh->SendShouldCloseACK(true);
697   EXPECT_FALSE(pending_rvh->are_navigations_suspended());
698
699   // DidNavigate from the pending page.
700   contents()->TestDidNavigate(
701       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
702   SiteInstance* new_instance = contents()->GetSiteInstance();
703
704   EXPECT_FALSE(contents()->cross_navigation_pending());
705   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
706   EXPECT_EQ(url2, contents()->GetLastCommittedURL());
707   EXPECT_EQ(url2, contents()->GetVisibleURL());
708   EXPECT_NE(new_instance, orig_instance);
709   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
710   // We keep the original RFH around, swapped out.
711   EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
712       orig_rfh));
713   EXPECT_EQ(orig_rvh_delete_count, 0);
714   orig_rvh->OnSwappedOut(false);
715
716   // Close contents and ensure RVHs are deleted.
717   DeleteContents();
718   EXPECT_EQ(orig_rvh_delete_count, 1);
719   EXPECT_EQ(pending_rvh_delete_count, 1);
720 }
721
722 // Test that we can find an opener RVH even if it's pending.
723 // http://crbug.com/176252.
724 TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
725   contents()->transition_cross_site = true;
726   TestRenderViewHost* orig_rvh = test_rvh();
727
728   // Navigate to a URL.
729   const GURL url("http://www.google.com");
730   controller().LoadURL(
731       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
732   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
733
734   // Start to navigate first tab to a new site, so that it has a pending RVH.
735   const GURL url2("http://www.yahoo.com");
736   controller().LoadURL(
737       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
738   orig_rvh->SendShouldCloseACK(true);
739   TestRenderViewHost* pending_rvh =
740       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
741
742   // While it is still pending, simulate opening a new tab with the first tab
743   // as its opener.  This will call WebContentsImpl::CreateOpenerRenderViews
744   // on the opener to ensure that an RVH exists.
745   int opener_routing_id = contents()->CreateOpenerRenderViews(
746       pending_rvh->GetSiteInstance());
747
748   // We should find the pending RVH and not create a new one.
749   EXPECT_EQ(pending_rvh->GetRoutingID(), opener_routing_id);
750 }
751
752 // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
753 // to determine whether a navigation is cross-site.
754 TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
755   contents()->transition_cross_site = true;
756   RenderViewHost* orig_rvh = rvh();
757   SiteInstance* instance1 = contents()->GetSiteInstance();
758
759   // Navigate to URL.
760   const GURL url("http://www.google.com");
761   controller().LoadURL(
762       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
763   contents()->TestDidNavigate(
764       orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
765
766   // Open a related contents to a second site.
767   scoped_ptr<TestWebContents> contents2(
768       TestWebContents::Create(browser_context(), instance1));
769   contents2->transition_cross_site = true;
770   const GURL url2("http://www.yahoo.com");
771   contents2->GetController().LoadURL(url2, Referrer(),
772                                     PAGE_TRANSITION_TYPED,
773                                     std::string());
774   // The first RVH in contents2 isn't live yet, so we shortcut the cross site
775   // pending.
776   TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>(
777       contents2->GetRenderViewHost());
778   EXPECT_FALSE(contents2->cross_navigation_pending());
779   contents2->TestDidNavigate(rvh2, 2, url2, PAGE_TRANSITION_TYPED);
780   SiteInstance* instance2 = contents2->GetSiteInstance();
781   EXPECT_NE(instance1, instance2);
782   EXPECT_FALSE(contents2->cross_navigation_pending());
783
784   // Simulate a link click in first contents to second site.  Doesn't switch
785   // SiteInstances, because we don't intercept WebKit navigations.
786   contents()->TestDidNavigate(
787       orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
788   SiteInstance* instance3 = contents()->GetSiteInstance();
789   EXPECT_EQ(instance1, instance3);
790   EXPECT_FALSE(contents()->cross_navigation_pending());
791
792   // Navigate to the new site.  Doesn't switch SiteInstancees, because we
793   // compare against the current URL, not the SiteInstance's site.
794   const GURL url3("http://mail.yahoo.com");
795   controller().LoadURL(
796       url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
797   EXPECT_FALSE(contents()->cross_navigation_pending());
798   contents()->TestDidNavigate(
799       orig_rvh, 3, url3, PAGE_TRANSITION_TYPED);
800   SiteInstance* instance4 = contents()->GetSiteInstance();
801   EXPECT_EQ(instance1, instance4);
802 }
803
804 // Test that the onbeforeunload and onunload handlers run when navigating
805 // across site boundaries.
806 TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
807   contents()->transition_cross_site = true;
808   TestRenderViewHost* orig_rvh = test_rvh();
809   SiteInstance* instance1 = contents()->GetSiteInstance();
810
811   // Navigate to URL.  First URL should use first RenderViewHost.
812   const GURL url("http://www.google.com");
813   controller().LoadURL(
814       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
815   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
816   EXPECT_FALSE(contents()->cross_navigation_pending());
817   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
818
819   // Navigate to new site, but simulate an onbeforeunload denial.
820   const GURL url2("http://www.yahoo.com");
821   controller().LoadURL(
822       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
823   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
824   base::TimeTicks now = base::TimeTicks::Now();
825   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false, now, now));
826   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
827   EXPECT_FALSE(contents()->cross_navigation_pending());
828   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
829
830   // Navigate again, but simulate an onbeforeunload approval.
831   controller().LoadURL(
832       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
833   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
834   now = base::TimeTicks::Now();
835   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
836   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
837   EXPECT_TRUE(contents()->cross_navigation_pending());
838   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
839       contents()->GetPendingRenderViewHost());
840
841   // We won't hear DidNavigate until the onunload handler has finished running.
842
843   // DidNavigate from the pending page.
844   contents()->TestDidNavigate(
845       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
846   SiteInstance* instance2 = contents()->GetSiteInstance();
847   EXPECT_FALSE(contents()->cross_navigation_pending());
848   EXPECT_EQ(pending_rvh, rvh());
849   EXPECT_NE(instance1, instance2);
850   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
851 }
852
853 // Test that during a slow cross-site navigation, the original renderer can
854 // navigate to a different URL and have it displayed, canceling the slow
855 // navigation.
856 TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
857   contents()->transition_cross_site = true;
858   TestRenderViewHost* orig_rvh = test_rvh();
859   SiteInstance* instance1 = contents()->GetSiteInstance();
860
861   // Navigate to URL.  First URL should use first RenderViewHost.
862   const GURL url("http://www.google.com");
863   controller().LoadURL(
864       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
865   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
866   EXPECT_FALSE(contents()->cross_navigation_pending());
867   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
868
869   // Navigate to new site, simulating an onbeforeunload approval.
870   const GURL url2("http://www.yahoo.com");
871   controller().LoadURL(
872       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
873   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
874   base::TimeTicks now = base::TimeTicks::Now();
875   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
876   EXPECT_TRUE(contents()->cross_navigation_pending());
877
878   // Suppose the original renderer navigates before the new one is ready.
879   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
880
881   // Verify that the pending navigation is cancelled.
882   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
883   SiteInstance* instance2 = contents()->GetSiteInstance();
884   EXPECT_FALSE(contents()->cross_navigation_pending());
885   EXPECT_EQ(orig_rvh, rvh());
886   EXPECT_EQ(instance1, instance2);
887   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
888 }
889
890 TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
891   contents()->transition_cross_site = true;
892
893   // Start with a web ui page, which gets a new RVH with WebUI bindings.
894   const GURL url1("chrome://blah");
895   controller().LoadURL(
896       url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
897   TestRenderViewHost* ntp_rvh = test_rvh();
898   contents()->TestDidNavigate(ntp_rvh, 1, url1, PAGE_TRANSITION_TYPED);
899   NavigationEntry* entry1 = controller().GetLastCommittedEntry();
900   SiteInstance* instance1 = contents()->GetSiteInstance();
901
902   EXPECT_FALSE(contents()->cross_navigation_pending());
903   EXPECT_EQ(ntp_rvh, contents()->GetRenderViewHost());
904   EXPECT_EQ(url1, entry1->GetURL());
905   EXPECT_EQ(instance1,
906             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
907   EXPECT_TRUE(ntp_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
908
909   // Navigate to new site.
910   const GURL url2("http://www.google.com");
911   controller().LoadURL(
912       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
913   EXPECT_TRUE(contents()->cross_navigation_pending());
914   TestRenderViewHost* google_rvh =
915       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
916
917   // Simulate beforeunload approval.
918   EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack());
919   base::TimeTicks now = base::TimeTicks::Now();
920   ntp_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
921
922   // DidNavigate from the pending page.
923   contents()->TestDidNavigate(
924       google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
925   NavigationEntry* entry2 = controller().GetLastCommittedEntry();
926   SiteInstance* instance2 = contents()->GetSiteInstance();
927
928   EXPECT_FALSE(contents()->cross_navigation_pending());
929   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
930   EXPECT_NE(instance1, instance2);
931   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
932   EXPECT_EQ(url2, entry2->GetURL());
933   EXPECT_EQ(instance2,
934             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
935   EXPECT_FALSE(google_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
936
937   // Navigate to third page on same site.
938   const GURL url3("http://news.google.com");
939   controller().LoadURL(
940       url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
941   EXPECT_FALSE(contents()->cross_navigation_pending());
942   contents()->TestDidNavigate(
943       google_rvh, 2, url3, PAGE_TRANSITION_TYPED);
944   NavigationEntry* entry3 = controller().GetLastCommittedEntry();
945   SiteInstance* instance3 = contents()->GetSiteInstance();
946
947   EXPECT_FALSE(contents()->cross_navigation_pending());
948   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
949   EXPECT_EQ(instance2, instance3);
950   EXPECT_FALSE(contents()->GetPendingRenderViewHost());
951   EXPECT_EQ(url3, entry3->GetURL());
952   EXPECT_EQ(instance3,
953             NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
954
955   // Go back within the site.
956   controller().GoBack();
957   EXPECT_FALSE(contents()->cross_navigation_pending());
958   EXPECT_EQ(entry2, controller().GetPendingEntry());
959
960   // Before that commits, go back again.
961   controller().GoBack();
962   EXPECT_TRUE(contents()->cross_navigation_pending());
963   EXPECT_TRUE(contents()->GetPendingRenderViewHost());
964   EXPECT_EQ(entry1, controller().GetPendingEntry());
965
966   // Simulate beforeunload approval.
967   EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack());
968   now = base::TimeTicks::Now();
969   google_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
970
971   // DidNavigate from the first back. This aborts the second back's pending RVH.
972   contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
973
974   // We should commit this page and forget about the second back.
975   EXPECT_FALSE(contents()->cross_navigation_pending());
976   EXPECT_FALSE(controller().GetPendingEntry());
977   EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
978   EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
979
980   // We should not have corrupted the NTP entry.
981   EXPECT_EQ(instance3,
982             NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
983   EXPECT_EQ(instance2,
984             NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
985   EXPECT_EQ(instance1,
986             NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
987   EXPECT_EQ(url1, entry1->GetURL());
988 }
989
990 // Test that during a slow cross-site navigation, a sub-frame navigation in the
991 // original renderer will not cancel the slow navigation (bug 42029).
992 TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
993   contents()->transition_cross_site = true;
994   TestRenderViewHost* orig_rvh = test_rvh();
995
996   // Navigate to URL.  First URL should use first RenderViewHost.
997   const GURL url("http://www.google.com");
998   controller().LoadURL(
999       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1000   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1001   EXPECT_FALSE(contents()->cross_navigation_pending());
1002   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1003
1004   // Start navigating to new site.
1005   const GURL url2("http://www.yahoo.com");
1006   controller().LoadURL(
1007       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1008
1009   // Simulate a sub-frame navigation arriving and ensure the RVH is still
1010   // waiting for a before unload response.
1011   orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"),
1012                                        PAGE_TRANSITION_AUTO_SUBFRAME);
1013   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1014
1015   // Now simulate the onbeforeunload approval and verify the navigation is
1016   // not canceled.
1017   base::TimeTicks now = base::TimeTicks::Now();
1018   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1019   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1020   EXPECT_TRUE(contents()->cross_navigation_pending());
1021 }
1022
1023 // Test that a cross-site navigation is not preempted if the previous
1024 // renderer sends a FrameNavigate message just before being told to stop.
1025 // We should only preempt the cross-site navigation if the previous renderer
1026 // has started a new navigation.  See http://crbug.com/79176.
1027 TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
1028   contents()->transition_cross_site = true;
1029
1030   // Navigate to NTP URL.
1031   const GURL url("chrome://blah");
1032   controller().LoadURL(
1033       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1034   TestRenderViewHost* orig_rvh = test_rvh();
1035   EXPECT_FALSE(contents()->cross_navigation_pending());
1036
1037   // Navigate to new site, with the beforeunload request in flight.
1038   const GURL url2("http://www.yahoo.com");
1039   controller().LoadURL(
1040       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1041   TestRenderViewHost* pending_rvh =
1042       static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
1043   EXPECT_TRUE(contents()->cross_navigation_pending());
1044   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1045
1046   // Suppose the first navigation tries to commit now, with a
1047   // ViewMsg_Stop in flight.  This should not cancel the pending navigation,
1048   // but it should act as if the beforeunload ack arrived.
1049   orig_rvh->SendNavigate(1, GURL("chrome://blah"));
1050   EXPECT_TRUE(contents()->cross_navigation_pending());
1051   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1052   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1053
1054   // The pending navigation should be able to commit successfully.
1055   contents()->TestDidNavigate(pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1056   EXPECT_FALSE(contents()->cross_navigation_pending());
1057   EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
1058 }
1059
1060 // Test that the original renderer cannot preempt a cross-site navigation once
1061 // the unload request has been made.  At this point, the cross-site navigation
1062 // is almost ready to be displayed, and the original renderer is only given a
1063 // short chance to run an unload handler.  Prevents regression of bug 23942.
1064 TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
1065   contents()->transition_cross_site = true;
1066   TestRenderViewHost* orig_rvh = test_rvh();
1067   SiteInstance* instance1 = contents()->GetSiteInstance();
1068
1069   // Navigate to URL.  First URL should use first RenderViewHost.
1070   const GURL url("http://www.google.com");
1071   controller().LoadURL(
1072       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1073   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1074   EXPECT_FALSE(contents()->cross_navigation_pending());
1075   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1076
1077   // Navigate to new site, simulating an onbeforeunload approval.
1078   const GURL url2("http://www.yahoo.com");
1079   controller().LoadURL(
1080       url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1081   base::TimeTicks now = base::TimeTicks::Now();
1082   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1083   EXPECT_TRUE(contents()->cross_navigation_pending());
1084   TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
1085       contents()->GetPendingRenderViewHost());
1086
1087   // Simulate the pending renderer's response, which leads to an unload request
1088   // being sent to orig_rvh.
1089   std::vector<GURL> url_chain;
1090   url_chain.push_back(GURL());
1091   contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1092       pending_rvh, GlobalRequestID(0, 0),
1093       scoped_ptr<CrossSiteTransferringRequest>(), url_chain,
1094       Referrer(), PAGE_TRANSITION_TYPED, 1, false);
1095
1096   // Suppose the original renderer navigates now, while the unload request is in
1097   // flight.  We should ignore it, wait for the unload ack, and let the pending
1098   // request continue.  Otherwise, the contents may close spontaneously or stop
1099   // responding to navigation requests.  (See bug 23942.)
1100   FrameHostMsg_DidCommitProvisionalLoad_Params params1a;
1101   InitNavigateParams(&params1a, 2, GURL("http://www.google.com/foo"),
1102                      PAGE_TRANSITION_TYPED);
1103   orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
1104
1105   // Verify that the pending navigation is still in progress.
1106   EXPECT_TRUE(contents()->cross_navigation_pending());
1107   EXPECT_TRUE(contents()->GetPendingRenderViewHost() != NULL);
1108
1109   // DidNavigate from the pending page should commit it.
1110   contents()->TestDidNavigate(
1111       pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
1112   SiteInstance* instance2 = contents()->GetSiteInstance();
1113   EXPECT_FALSE(contents()->cross_navigation_pending());
1114   EXPECT_EQ(pending_rvh, rvh());
1115   EXPECT_NE(instance1, instance2);
1116   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1117 }
1118
1119 // Test that a cross-site navigation that doesn't commit after the unload
1120 // handler doesn't leave the contents in a stuck state.  http://crbug.com/88562
1121 TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) {
1122   contents()->transition_cross_site = true;
1123   TestRenderViewHost* orig_rvh = test_rvh();
1124   SiteInstance* instance1 = contents()->GetSiteInstance();
1125
1126   // Navigate to URL.  First URL should use first RenderViewHost.
1127   const GURL url("http://www.google.com");
1128   controller().LoadURL(
1129       url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1130   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1131   EXPECT_FALSE(contents()->cross_navigation_pending());
1132   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1133
1134   // Navigate to new site, simulating an onbeforeunload approval.
1135   const GURL url2("http://www.yahoo.com");
1136   controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1137   EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
1138   base::TimeTicks now = base::TimeTicks::Now();
1139   orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
1140   EXPECT_TRUE(contents()->cross_navigation_pending());
1141
1142   // Simulate swap out message when the response arrives.
1143   orig_rvh->OnSwappedOut(false);
1144
1145   // Suppose the navigation doesn't get a chance to commit, and the user
1146   // navigates in the current RVH's SiteInstance.
1147   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1148
1149   // Verify that the pending navigation is cancelled and the renderer is no
1150   // longer swapped out.
1151   EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
1152   SiteInstance* instance2 = contents()->GetSiteInstance();
1153   EXPECT_FALSE(contents()->cross_navigation_pending());
1154   EXPECT_EQ(orig_rvh, rvh());
1155   EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, orig_rvh->rvh_state());
1156   EXPECT_EQ(instance1, instance2);
1157   EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
1158 }
1159
1160 // Test that NavigationEntries have the correct page state after going
1161 // forward and back.  Prevents regression for bug 1116137.
1162 TEST_F(WebContentsImplTest, NavigationEntryContentState) {
1163   TestRenderViewHost* orig_rvh = test_rvh();
1164
1165   // Navigate to URL.  There should be no committed entry yet.
1166   const GURL url("http://www.google.com");
1167   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1168   NavigationEntry* entry = controller().GetLastCommittedEntry();
1169   EXPECT_TRUE(entry == NULL);
1170
1171   // Committed entry should have page state after DidNavigate.
1172   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1173   entry = controller().GetLastCommittedEntry();
1174   EXPECT_TRUE(entry->GetPageState().IsValid());
1175
1176   // Navigate to same site.
1177   const GURL url2("http://images.google.com");
1178   controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1179   entry = controller().GetLastCommittedEntry();
1180   EXPECT_TRUE(entry->GetPageState().IsValid());
1181
1182   // Committed entry should have page state after DidNavigate.
1183   contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
1184   entry = controller().GetLastCommittedEntry();
1185   EXPECT_TRUE(entry->GetPageState().IsValid());
1186
1187   // Now go back.  Committed entry should still have page state.
1188   controller().GoBack();
1189   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1190   entry = controller().GetLastCommittedEntry();
1191   EXPECT_TRUE(entry->GetPageState().IsValid());
1192 }
1193
1194 // Test that NavigationEntries have the correct page state and SiteInstance
1195 // state after opening a new window to about:blank.  Prevents regression for
1196 // bugs b/1116137 and http://crbug.com/111975.
1197 TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
1198   TestRenderViewHost* orig_rvh = test_rvh();
1199
1200   // When opening a new window, it is navigated to about:blank internally.
1201   // Currently, this results in two DidNavigate events.
1202   const GURL url(kAboutBlankURL);
1203   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1204   contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
1205
1206   // Should have a page state here.
1207   NavigationEntry* entry = controller().GetLastCommittedEntry();
1208   EXPECT_TRUE(entry->GetPageState().IsValid());
1209
1210   // The SiteInstance should be available for other navigations to use.
1211   NavigationEntryImpl* entry_impl =
1212       NavigationEntryImpl::FromNavigationEntry(entry);
1213   EXPECT_FALSE(entry_impl->site_instance()->HasSite());
1214   int32 site_instance_id = entry_impl->site_instance()->GetId();
1215
1216   // Navigating to a normal page should not cause a process swap.
1217   const GURL new_url("http://www.google.com");
1218   controller().LoadURL(new_url, Referrer(),
1219                        PAGE_TRANSITION_TYPED, std::string());
1220   EXPECT_FALSE(contents()->cross_navigation_pending());
1221   EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
1222   contents()->TestDidNavigate(orig_rvh, 1, new_url, PAGE_TRANSITION_TYPED);
1223   NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry(
1224       controller().GetLastCommittedEntry());
1225   EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId());
1226   EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
1227 }
1228
1229 ////////////////////////////////////////////////////////////////////////////////
1230 // Interstitial Tests
1231 ////////////////////////////////////////////////////////////////////////////////
1232
1233 // Test navigating to a page (with the navigation initiated from the browser,
1234 // as when a URL is typed in the location bar) that shows an interstitial and
1235 // creates a new navigation entry, then hiding it without proceeding.
1236 TEST_F(WebContentsImplTest,
1237        ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
1238   // Navigate to a page.
1239   GURL url1("http://www.google.com");
1240   test_rvh()->SendNavigate(1, url1);
1241   EXPECT_EQ(1, controller().GetEntryCount());
1242
1243   // Initiate a browser navigation that will trigger the interstitial
1244   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1245                         PAGE_TRANSITION_TYPED, std::string());
1246
1247   // Show an interstitial.
1248   TestInterstitialPage::InterstitialState state =
1249       TestInterstitialPage::INVALID;
1250   bool deleted = false;
1251   GURL url2("http://interstitial");
1252   TestInterstitialPage* interstitial =
1253       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1254   TestInterstitialPageStateGuard state_guard(interstitial);
1255   interstitial->Show();
1256   // The interstitial should not show until its navigation has committed.
1257   EXPECT_FALSE(interstitial->is_showing());
1258   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1259   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1260   // Let's commit the interstitial navigation.
1261   interstitial->TestDidNavigate(1, url2);
1262   EXPECT_TRUE(interstitial->is_showing());
1263   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1264   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1265   NavigationEntry* entry = controller().GetVisibleEntry();
1266   ASSERT_TRUE(entry != NULL);
1267   EXPECT_TRUE(entry->GetURL() == url2);
1268
1269   // Now don't proceed.
1270   interstitial->DontProceed();
1271   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1272   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1273   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1274   entry = controller().GetVisibleEntry();
1275   ASSERT_TRUE(entry != NULL);
1276   EXPECT_TRUE(entry->GetURL() == url1);
1277   EXPECT_EQ(1, controller().GetEntryCount());
1278
1279   RunAllPendingInMessageLoop();
1280   EXPECT_TRUE(deleted);
1281 }
1282
1283 // Test navigating to a page (with the navigation initiated from the renderer,
1284 // as when clicking on a link in the page) that shows an interstitial and
1285 // creates a new navigation entry, then hiding it without proceeding.
1286 TEST_F(WebContentsImplTest,
1287        ShowInterstitiaFromRendererlWithNewNavigationDontProceed) {
1288   // Navigate to a page.
1289   GURL url1("http://www.google.com");
1290   test_rvh()->SendNavigate(1, url1);
1291   EXPECT_EQ(1, controller().GetEntryCount());
1292
1293   // Show an interstitial (no pending entry, the interstitial would have been
1294   // triggered by clicking on a link).
1295   TestInterstitialPage::InterstitialState state =
1296       TestInterstitialPage::INVALID;
1297   bool deleted = false;
1298   GURL url2("http://interstitial");
1299   TestInterstitialPage* interstitial =
1300       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1301   TestInterstitialPageStateGuard state_guard(interstitial);
1302   interstitial->Show();
1303   // The interstitial should not show until its navigation has committed.
1304   EXPECT_FALSE(interstitial->is_showing());
1305   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1306   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1307   // Let's commit the interstitial navigation.
1308   interstitial->TestDidNavigate(1, url2);
1309   EXPECT_TRUE(interstitial->is_showing());
1310   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1311   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1312   NavigationEntry* entry = controller().GetVisibleEntry();
1313   ASSERT_TRUE(entry != NULL);
1314   EXPECT_TRUE(entry->GetURL() == url2);
1315
1316   // Now don't proceed.
1317   interstitial->DontProceed();
1318   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1319   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1320   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1321   entry = controller().GetVisibleEntry();
1322   ASSERT_TRUE(entry != NULL);
1323   EXPECT_TRUE(entry->GetURL() == url1);
1324   EXPECT_EQ(1, controller().GetEntryCount());
1325
1326   RunAllPendingInMessageLoop();
1327   EXPECT_TRUE(deleted);
1328 }
1329
1330 // Test navigating to a page that shows an interstitial without creating a new
1331 // navigation entry (this happens when the interstitial is triggered by a
1332 // sub-resource in the page), then hiding it without proceeding.
1333 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) {
1334   // Navigate to a page.
1335   GURL url1("http://www.google.com");
1336   test_rvh()->SendNavigate(1, url1);
1337   EXPECT_EQ(1, controller().GetEntryCount());
1338
1339   // Show an interstitial.
1340   TestInterstitialPage::InterstitialState state =
1341       TestInterstitialPage::INVALID;
1342   bool deleted = false;
1343   GURL url2("http://interstitial");
1344   TestInterstitialPage* interstitial =
1345       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1346   TestInterstitialPageStateGuard state_guard(interstitial);
1347   interstitial->Show();
1348   // The interstitial should not show until its navigation has committed.
1349   EXPECT_FALSE(interstitial->is_showing());
1350   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1351   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1352   // Let's commit the interstitial navigation.
1353   interstitial->TestDidNavigate(1, url2);
1354   EXPECT_TRUE(interstitial->is_showing());
1355   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1356   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1357   NavigationEntry* entry = controller().GetVisibleEntry();
1358   ASSERT_TRUE(entry != NULL);
1359   // The URL specified to the interstitial should have been ignored.
1360   EXPECT_TRUE(entry->GetURL() == url1);
1361
1362   // Now don't proceed.
1363   interstitial->DontProceed();
1364   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1365   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1366   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1367   entry = controller().GetVisibleEntry();
1368   ASSERT_TRUE(entry != NULL);
1369   EXPECT_TRUE(entry->GetURL() == url1);
1370   EXPECT_EQ(1, controller().GetEntryCount());
1371
1372   RunAllPendingInMessageLoop();
1373   EXPECT_TRUE(deleted);
1374 }
1375
1376 // Test navigating to a page (with the navigation initiated from the browser,
1377 // as when a URL is typed in the location bar) that shows an interstitial and
1378 // creates a new navigation entry, then proceeding.
1379 TEST_F(WebContentsImplTest,
1380        ShowInterstitialFromBrowserNewNavigationProceed) {
1381   // Navigate to a page.
1382   GURL url1("http://www.google.com");
1383   test_rvh()->SendNavigate(1, url1);
1384   EXPECT_EQ(1, controller().GetEntryCount());
1385
1386   // Initiate a browser navigation that will trigger the interstitial
1387   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1388                         PAGE_TRANSITION_TYPED, std::string());
1389
1390   // Show an interstitial.
1391   TestInterstitialPage::InterstitialState state =
1392       TestInterstitialPage::INVALID;
1393   bool deleted = false;
1394   GURL url2("http://interstitial");
1395   TestInterstitialPage* interstitial =
1396       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1397   TestInterstitialPageStateGuard state_guard(interstitial);
1398   interstitial->Show();
1399   // The interstitial should not show until its navigation has committed.
1400   EXPECT_FALSE(interstitial->is_showing());
1401   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1402   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1403   // Let's commit the interstitial navigation.
1404   interstitial->TestDidNavigate(1, url2);
1405   EXPECT_TRUE(interstitial->is_showing());
1406   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1407   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1408   NavigationEntry* entry = controller().GetVisibleEntry();
1409   ASSERT_TRUE(entry != NULL);
1410   EXPECT_TRUE(entry->GetURL() == url2);
1411
1412   // Then proceed.
1413   interstitial->Proceed();
1414   // The interstitial should show until the new navigation commits.
1415   RunAllPendingInMessageLoop();
1416   ASSERT_FALSE(deleted);
1417   EXPECT_EQ(TestInterstitialPage::OKED, state);
1418   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1419   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1420
1421   // Simulate the navigation to the page, that's when the interstitial gets
1422   // hidden.
1423   GURL url3("http://www.thepage.com");
1424   test_rvh()->SendNavigate(2, url3);
1425
1426   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1427   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1428   entry = controller().GetVisibleEntry();
1429   ASSERT_TRUE(entry != NULL);
1430   EXPECT_TRUE(entry->GetURL() == url3);
1431
1432   EXPECT_EQ(2, controller().GetEntryCount());
1433
1434   RunAllPendingInMessageLoop();
1435   EXPECT_TRUE(deleted);
1436 }
1437
1438 // Test navigating to a page (with the navigation initiated from the renderer,
1439 // as when clicking on a link in the page) that shows an interstitial and
1440 // creates a new navigation entry, then proceeding.
1441 TEST_F(WebContentsImplTest,
1442        ShowInterstitialFromRendererNewNavigationProceed) {
1443   // Navigate to a page.
1444   GURL url1("http://www.google.com");
1445   test_rvh()->SendNavigate(1, url1);
1446   EXPECT_EQ(1, controller().GetEntryCount());
1447
1448   // Show an interstitial.
1449   TestInterstitialPage::InterstitialState state =
1450       TestInterstitialPage::INVALID;
1451   bool deleted = false;
1452   GURL url2("http://interstitial");
1453   TestInterstitialPage* interstitial =
1454       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1455   TestInterstitialPageStateGuard state_guard(interstitial);
1456   interstitial->Show();
1457   // The interstitial should not show until its navigation has committed.
1458   EXPECT_FALSE(interstitial->is_showing());
1459   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1460   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1461   // Let's commit the interstitial navigation.
1462   interstitial->TestDidNavigate(1, url2);
1463   EXPECT_TRUE(interstitial->is_showing());
1464   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1465   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1466   NavigationEntry* entry = controller().GetVisibleEntry();
1467   ASSERT_TRUE(entry != NULL);
1468   EXPECT_TRUE(entry->GetURL() == url2);
1469
1470   // Then proceed.
1471   interstitial->Proceed();
1472   // The interstitial should show until the new navigation commits.
1473   RunAllPendingInMessageLoop();
1474   ASSERT_FALSE(deleted);
1475   EXPECT_EQ(TestInterstitialPage::OKED, state);
1476   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1477   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1478
1479   // Simulate the navigation to the page, that's when the interstitial gets
1480   // hidden.
1481   GURL url3("http://www.thepage.com");
1482   test_rvh()->SendNavigate(2, url3);
1483
1484   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1485   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1486   entry = controller().GetVisibleEntry();
1487   ASSERT_TRUE(entry != NULL);
1488   EXPECT_TRUE(entry->GetURL() == url3);
1489
1490   EXPECT_EQ(2, controller().GetEntryCount());
1491
1492   RunAllPendingInMessageLoop();
1493   EXPECT_TRUE(deleted);
1494 }
1495
1496 // Test navigating to a page that shows an interstitial without creating a new
1497 // navigation entry (this happens when the interstitial is triggered by a
1498 // sub-resource in the page), then proceeding.
1499 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) {
1500   // Navigate to a page so we have a navigation entry in the controller.
1501   GURL url1("http://www.google.com");
1502   test_rvh()->SendNavigate(1, url1);
1503   EXPECT_EQ(1, controller().GetEntryCount());
1504
1505   // Show an interstitial.
1506   TestInterstitialPage::InterstitialState state =
1507       TestInterstitialPage::INVALID;
1508   bool deleted = false;
1509   GURL url2("http://interstitial");
1510   TestInterstitialPage* interstitial =
1511       new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1512   TestInterstitialPageStateGuard state_guard(interstitial);
1513   interstitial->Show();
1514   // The interstitial should not show until its navigation has committed.
1515   EXPECT_FALSE(interstitial->is_showing());
1516   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1517   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1518   // Let's commit the interstitial navigation.
1519   interstitial->TestDidNavigate(1, url2);
1520   EXPECT_TRUE(interstitial->is_showing());
1521   EXPECT_TRUE(contents()->ShowingInterstitialPage());
1522   EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1523   NavigationEntry* entry = controller().GetVisibleEntry();
1524   ASSERT_TRUE(entry != NULL);
1525   // The URL specified to the interstitial should have been ignored.
1526   EXPECT_TRUE(entry->GetURL() == url1);
1527
1528   // Then proceed.
1529   interstitial->Proceed();
1530   // Since this is not a new navigation, the previous page is dismissed right
1531   // away and shows the original page.
1532   EXPECT_EQ(TestInterstitialPage::OKED, state);
1533   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1534   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1535   entry = controller().GetVisibleEntry();
1536   ASSERT_TRUE(entry != NULL);
1537   EXPECT_TRUE(entry->GetURL() == url1);
1538
1539   EXPECT_EQ(1, controller().GetEntryCount());
1540
1541   RunAllPendingInMessageLoop();
1542   EXPECT_TRUE(deleted);
1543 }
1544
1545 // Test navigating to a page that shows an interstitial, then navigating away.
1546 TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) {
1547   // Show interstitial.
1548   TestInterstitialPage::InterstitialState state =
1549       TestInterstitialPage::INVALID;
1550   bool deleted = false;
1551   GURL url("http://interstitial");
1552   TestInterstitialPage* interstitial =
1553       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1554   TestInterstitialPageStateGuard state_guard(interstitial);
1555   interstitial->Show();
1556   interstitial->TestDidNavigate(1, url);
1557
1558   // While interstitial showing, navigate to a new URL.
1559   const GURL url2("http://www.yahoo.com");
1560   test_rvh()->SendNavigate(1, url2);
1561
1562   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1563
1564   RunAllPendingInMessageLoop();
1565   EXPECT_TRUE(deleted);
1566 }
1567
1568 // Test navigating to a page that shows an interstitial, then going back.
1569 TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) {
1570   // Navigate to a page so we have a navigation entry in the controller.
1571   GURL url1("http://www.google.com");
1572   test_rvh()->SendNavigate(1, url1);
1573   EXPECT_EQ(1, controller().GetEntryCount());
1574
1575   // Show interstitial.
1576   TestInterstitialPage::InterstitialState state =
1577       TestInterstitialPage::INVALID;
1578   bool deleted = false;
1579   GURL interstitial_url("http://interstitial");
1580   TestInterstitialPage* interstitial =
1581       new TestInterstitialPage(contents(), true, interstitial_url,
1582                                &state, &deleted);
1583   TestInterstitialPageStateGuard state_guard(interstitial);
1584   interstitial->Show();
1585   interstitial->TestDidNavigate(2, interstitial_url);
1586
1587   // While the interstitial is showing, go back.
1588   controller().GoBack();
1589   test_rvh()->SendNavigate(1, url1);
1590
1591   // Make sure we are back to the original page and that the interstitial is
1592   // gone.
1593   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1594   NavigationEntry* entry = controller().GetVisibleEntry();
1595   ASSERT_TRUE(entry);
1596   EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1597
1598   RunAllPendingInMessageLoop();
1599   EXPECT_TRUE(deleted);
1600 }
1601
1602 // Test navigating to a page that shows an interstitial, has a renderer crash,
1603 // and then goes back.
1604 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) {
1605   // Navigate to a page so we have a navigation entry in the controller.
1606   GURL url1("http://www.google.com");
1607   test_rvh()->SendNavigate(1, url1);
1608   EXPECT_EQ(1, controller().GetEntryCount());
1609
1610   // Show interstitial.
1611   TestInterstitialPage::InterstitialState state =
1612       TestInterstitialPage::INVALID;
1613   bool deleted = false;
1614   GURL interstitial_url("http://interstitial");
1615   TestInterstitialPage* interstitial =
1616       new TestInterstitialPage(contents(), true, interstitial_url,
1617                                &state, &deleted);
1618   TestInterstitialPageStateGuard state_guard(interstitial);
1619   interstitial->Show();
1620   interstitial->TestDidNavigate(2, interstitial_url);
1621
1622   // Crash the renderer
1623   test_rvh()->OnMessageReceived(
1624       ViewHostMsg_RenderProcessGone(
1625           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1626
1627   // While the interstitial is showing, go back.
1628   controller().GoBack();
1629   test_rvh()->SendNavigate(1, url1);
1630
1631   // Make sure we are back to the original page and that the interstitial is
1632   // gone.
1633   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1634   NavigationEntry* entry = controller().GetVisibleEntry();
1635   ASSERT_TRUE(entry);
1636   EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1637
1638   RunAllPendingInMessageLoop();
1639   EXPECT_TRUE(deleted);
1640 }
1641
1642 // Test navigating to a page that shows an interstitial, has the renderer crash,
1643 // and then navigates to the interstitial.
1644 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) {
1645   // Navigate to a page so we have a navigation entry in the controller.
1646   GURL url1("http://www.google.com");
1647   test_rvh()->SendNavigate(1, url1);
1648   EXPECT_EQ(1, controller().GetEntryCount());
1649
1650   // Show interstitial.
1651   TestInterstitialPage::InterstitialState state =
1652       TestInterstitialPage::INVALID;
1653   bool deleted = false;
1654   GURL interstitial_url("http://interstitial");
1655   TestInterstitialPage* interstitial =
1656       new TestInterstitialPage(contents(), true, interstitial_url,
1657                                &state, &deleted);
1658   TestInterstitialPageStateGuard state_guard(interstitial);
1659   interstitial->Show();
1660
1661   // Crash the renderer
1662   test_rvh()->OnMessageReceived(
1663       ViewHostMsg_RenderProcessGone(
1664           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1665
1666   interstitial->TestDidNavigate(2, interstitial_url);
1667 }
1668
1669 // Test navigating to a page that shows an interstitial, then close the
1670 // contents.
1671 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) {
1672   // Show interstitial.
1673   TestInterstitialPage::InterstitialState state =
1674       TestInterstitialPage::INVALID;
1675   bool deleted = false;
1676   GURL url("http://interstitial");
1677   TestInterstitialPage* interstitial =
1678       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1679   TestInterstitialPageStateGuard state_guard(interstitial);
1680   interstitial->Show();
1681   interstitial->TestDidNavigate(1, url);
1682
1683   // Now close the contents.
1684   DeleteContents();
1685   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1686
1687   RunAllPendingInMessageLoop();
1688   EXPECT_TRUE(deleted);
1689 }
1690
1691 // Test navigating to a page that shows an interstitial, then close the
1692 // contents.
1693 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) {
1694   // Show interstitial.
1695   TestInterstitialPage::InterstitialState state =
1696       TestInterstitialPage::INVALID;
1697   bool deleted = false;
1698   GURL url("http://interstitial");
1699   TestInterstitialPage* interstitial =
1700       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1701   TestInterstitialPageStateGuard state_guard(interstitial);
1702   interstitial->Show();
1703   interstitial->TestDidNavigate(1, url);
1704   RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
1705       interstitial->GetRenderViewHostForTesting());
1706
1707   // Now close the contents.
1708   DeleteContents();
1709   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1710
1711   // Before the interstitial has a chance to process its shutdown task,
1712   // simulate quitting the browser.  This goes through all processes and
1713   // tells them to destruct.
1714   rvh->OnMessageReceived(
1715         ViewHostMsg_RenderProcessGone(0, 0, 0));
1716
1717   RunAllPendingInMessageLoop();
1718   EXPECT_TRUE(deleted);
1719 }
1720
1721 // Test that after Proceed is called and an interstitial is still shown, no more
1722 // commands get executed.
1723 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
1724   // Navigate to a page so we have a navigation entry in the controller.
1725   GURL url1("http://www.google.com");
1726   test_rvh()->SendNavigate(1, url1);
1727   EXPECT_EQ(1, controller().GetEntryCount());
1728
1729   // Show an interstitial.
1730   TestInterstitialPage::InterstitialState state =
1731       TestInterstitialPage::INVALID;
1732   bool deleted = false;
1733   GURL url2("http://interstitial");
1734   TestInterstitialPage* interstitial =
1735       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1736   TestInterstitialPageStateGuard state_guard(interstitial);
1737   interstitial->Show();
1738   interstitial->TestDidNavigate(1, url2);
1739
1740   // Run a command.
1741   EXPECT_EQ(0, interstitial->command_received_count());
1742   interstitial->TestDomOperationResponse("toto");
1743   EXPECT_EQ(1, interstitial->command_received_count());
1744
1745   // Then proceed.
1746   interstitial->Proceed();
1747   RunAllPendingInMessageLoop();
1748   ASSERT_FALSE(deleted);
1749
1750   // While the navigation to the new page is pending, send other commands, they
1751   // should be ignored.
1752   interstitial->TestDomOperationResponse("hello");
1753   interstitial->TestDomOperationResponse("hi");
1754   EXPECT_EQ(1, interstitial->command_received_count());
1755 }
1756
1757 // Test showing an interstitial while another interstitial is already showing.
1758 TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) {
1759   // Navigate to a page so we have a navigation entry in the controller.
1760   GURL start_url("http://www.google.com");
1761   test_rvh()->SendNavigate(1, start_url);
1762   EXPECT_EQ(1, controller().GetEntryCount());
1763
1764   // Show an interstitial.
1765   TestInterstitialPage::InterstitialState state1 =
1766       TestInterstitialPage::INVALID;
1767   bool deleted1 = false;
1768   GURL url1("http://interstitial1");
1769   TestInterstitialPage* interstitial1 =
1770       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1771   TestInterstitialPageStateGuard state_guard1(interstitial1);
1772   interstitial1->Show();
1773   interstitial1->TestDidNavigate(1, url1);
1774
1775   // Now show another interstitial.
1776   TestInterstitialPage::InterstitialState state2 =
1777       TestInterstitialPage::INVALID;
1778   bool deleted2 = false;
1779   GURL url2("http://interstitial2");
1780   TestInterstitialPage* interstitial2 =
1781       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1782   TestInterstitialPageStateGuard state_guard2(interstitial2);
1783   interstitial2->Show();
1784   interstitial2->TestDidNavigate(1, url2);
1785
1786   // Showing interstitial2 should have caused interstitial1 to go away.
1787   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1788   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1789
1790   RunAllPendingInMessageLoop();
1791   EXPECT_TRUE(deleted1);
1792   ASSERT_FALSE(deleted2);
1793
1794   // Let's make sure interstitial2 is working as intended.
1795   interstitial2->Proceed();
1796   GURL landing_url("http://www.thepage.com");
1797   test_rvh()->SendNavigate(2, landing_url);
1798
1799   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1800   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1801   NavigationEntry* entry = controller().GetVisibleEntry();
1802   ASSERT_TRUE(entry != NULL);
1803   EXPECT_TRUE(entry->GetURL() == landing_url);
1804   EXPECT_EQ(2, controller().GetEntryCount());
1805   RunAllPendingInMessageLoop();
1806   EXPECT_TRUE(deleted2);
1807 }
1808
1809 // Test showing an interstitial, proceeding and then navigating to another
1810 // interstitial.
1811 TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) {
1812   // Navigate to a page so we have a navigation entry in the controller.
1813   GURL start_url("http://www.google.com");
1814   test_rvh()->SendNavigate(1, start_url);
1815   EXPECT_EQ(1, controller().GetEntryCount());
1816
1817   // Show an interstitial.
1818   TestInterstitialPage::InterstitialState state1 =
1819       TestInterstitialPage::INVALID;
1820   bool deleted1 = false;
1821   GURL url1("http://interstitial1");
1822   TestInterstitialPage* interstitial1 =
1823       new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1824   TestInterstitialPageStateGuard state_guard1(interstitial1);
1825   interstitial1->Show();
1826   interstitial1->TestDidNavigate(1, url1);
1827
1828   // Take action.  The interstitial won't be hidden until the navigation is
1829   // committed.
1830   interstitial1->Proceed();
1831   EXPECT_EQ(TestInterstitialPage::OKED, state1);
1832
1833   // Now show another interstitial (simulating the navigation causing another
1834   // interstitial).
1835   TestInterstitialPage::InterstitialState state2 =
1836       TestInterstitialPage::INVALID;
1837   bool deleted2 = false;
1838   GURL url2("http://interstitial2");
1839   TestInterstitialPage* interstitial2 =
1840       new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1841   TestInterstitialPageStateGuard state_guard2(interstitial2);
1842   interstitial2->Show();
1843   interstitial2->TestDidNavigate(1, url2);
1844
1845   // Showing interstitial2 should have caused interstitial1 to go away.
1846   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1847   RunAllPendingInMessageLoop();
1848   EXPECT_TRUE(deleted1);
1849   ASSERT_FALSE(deleted2);
1850
1851   // Let's make sure interstitial2 is working as intended.
1852   interstitial2->Proceed();
1853   GURL landing_url("http://www.thepage.com");
1854   test_rvh()->SendNavigate(2, landing_url);
1855
1856   RunAllPendingInMessageLoop();
1857   EXPECT_TRUE(deleted2);
1858   EXPECT_FALSE(contents()->ShowingInterstitialPage());
1859   EXPECT_TRUE(contents()->GetInterstitialPage() == NULL);
1860   NavigationEntry* entry = controller().GetVisibleEntry();
1861   ASSERT_TRUE(entry != NULL);
1862   EXPECT_TRUE(entry->GetURL() == landing_url);
1863   EXPECT_EQ(2, controller().GetEntryCount());
1864 }
1865
1866 // Test that navigating away from an interstitial while it's loading cause it
1867 // not to show.
1868 TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) {
1869   // Show an interstitial.
1870   TestInterstitialPage::InterstitialState state =
1871       TestInterstitialPage::INVALID;
1872   bool deleted = false;
1873   GURL interstitial_url("http://interstitial");
1874   TestInterstitialPage* interstitial =
1875       new TestInterstitialPage(contents(), true, interstitial_url,
1876                                &state, &deleted);
1877   TestInterstitialPageStateGuard state_guard(interstitial);
1878   interstitial->Show();
1879
1880   // Let's simulate a navigation initiated from the browser before the
1881   // interstitial finishes loading.
1882   const GURL url("http://www.google.com");
1883   controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1884   EXPECT_FALSE(interstitial->is_showing());
1885   RunAllPendingInMessageLoop();
1886   ASSERT_FALSE(deleted);
1887
1888   // Now let's make the interstitial navigation commit.
1889   interstitial->TestDidNavigate(1, interstitial_url);
1890
1891   // After it loaded the interstitial should be gone.
1892   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1893
1894   RunAllPendingInMessageLoop();
1895   EXPECT_TRUE(deleted);
1896 }
1897
1898 // Test that a new request to show an interstitial while an interstitial is
1899 // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442.
1900 TEST_F(WebContentsImplTest, TwoQuickInterstitials) {
1901   GURL interstitial_url("http://interstitial");
1902
1903   // Show a first interstitial.
1904   TestInterstitialPage::InterstitialState state1 =
1905       TestInterstitialPage::INVALID;
1906   bool deleted1 = false;
1907   TestInterstitialPage* interstitial1 =
1908       new TestInterstitialPage(contents(), true, interstitial_url,
1909                                &state1, &deleted1);
1910   TestInterstitialPageStateGuard state_guard1(interstitial1);
1911   interstitial1->Show();
1912
1913   // Show another interstitial on that same contents before the first one had
1914   // time to load.
1915   TestInterstitialPage::InterstitialState state2 =
1916       TestInterstitialPage::INVALID;
1917   bool deleted2 = false;
1918   TestInterstitialPage* interstitial2 =
1919       new TestInterstitialPage(contents(), true, interstitial_url,
1920                                &state2, &deleted2);
1921   TestInterstitialPageStateGuard state_guard2(interstitial2);
1922   interstitial2->Show();
1923
1924   // The first interstitial should have been closed and deleted.
1925   EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1926   // The 2nd one should still be OK.
1927   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1928
1929   RunAllPendingInMessageLoop();
1930   EXPECT_TRUE(deleted1);
1931   ASSERT_FALSE(deleted2);
1932
1933   // Make the interstitial navigation commit it should be showing.
1934   interstitial2->TestDidNavigate(1, interstitial_url);
1935   EXPECT_EQ(interstitial2, contents()->GetInterstitialPage());
1936 }
1937
1938 // Test showing an interstitial and have its renderer crash.
1939 TEST_F(WebContentsImplTest, InterstitialCrasher) {
1940   // Show an interstitial.
1941   TestInterstitialPage::InterstitialState state =
1942       TestInterstitialPage::INVALID;
1943   bool deleted = false;
1944   GURL url("http://interstitial");
1945   TestInterstitialPage* interstitial =
1946       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1947   TestInterstitialPageStateGuard state_guard(interstitial);
1948   interstitial->Show();
1949   // Simulate a renderer crash before the interstitial is shown.
1950   interstitial->TestRenderViewTerminated(
1951       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
1952   // The interstitial should have been dismissed.
1953   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1954   RunAllPendingInMessageLoop();
1955   EXPECT_TRUE(deleted);
1956
1957   // Now try again but this time crash the intersitial after it was shown.
1958   interstitial =
1959       new TestInterstitialPage(contents(), true, url, &state, &deleted);
1960   interstitial->Show();
1961   interstitial->TestDidNavigate(1, url);
1962   // Simulate a renderer crash.
1963   interstitial->TestRenderViewTerminated(
1964       base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
1965   // The interstitial should have been dismissed.
1966   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1967   RunAllPendingInMessageLoop();
1968   EXPECT_TRUE(deleted);
1969 }
1970
1971 // Tests that showing an interstitial as a result of a browser initiated
1972 // navigation while an interstitial is showing does not remove the pending
1973 // entry (see http://crbug.com/9791).
1974 TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) {
1975   const char kUrl[] = "http://www.badguys.com/";
1976   const GURL kGURL(kUrl);
1977
1978   // Start a navigation to a page
1979   contents()->GetController().LoadURL(
1980       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1981
1982   // Simulate that navigation triggering an interstitial.
1983   TestInterstitialPage::InterstitialState state =
1984       TestInterstitialPage::INVALID;
1985   bool deleted = false;
1986   TestInterstitialPage* interstitial =
1987       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
1988   TestInterstitialPageStateGuard state_guard(interstitial);
1989   interstitial->Show();
1990   interstitial->TestDidNavigate(1, kGURL);
1991
1992   // Initiate a new navigation from the browser that also triggers an
1993   // interstitial.
1994   contents()->GetController().LoadURL(
1995       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1996   TestInterstitialPage::InterstitialState state2 =
1997       TestInterstitialPage::INVALID;
1998   bool deleted2 = false;
1999   TestInterstitialPage* interstitial2 =
2000       new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2);
2001   TestInterstitialPageStateGuard state_guard2(interstitial2);
2002   interstitial2->Show();
2003   interstitial2->TestDidNavigate(1, kGURL);
2004
2005   // Make sure we still have an entry.
2006   NavigationEntry* entry = contents()->GetController().GetPendingEntry();
2007   ASSERT_TRUE(entry);
2008   EXPECT_EQ(kUrl, entry->GetURL().spec());
2009
2010   // And that the first interstitial is gone, but not the second.
2011   EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2012   EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2013   RunAllPendingInMessageLoop();
2014   EXPECT_TRUE(deleted);
2015   EXPECT_FALSE(deleted2);
2016 }
2017
2018 // Tests that Javascript messages are not shown while an interstitial is
2019 // showing.
2020 TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) {
2021   const char kUrl[] = "http://www.badguys.com/";
2022   const GURL kGURL(kUrl);
2023
2024   // Start a navigation to a page
2025   contents()->GetController().LoadURL(
2026       kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2027   // DidNavigate from the page
2028   contents()->TestDidNavigate(rvh(), 1, kGURL, PAGE_TRANSITION_TYPED);
2029
2030   // Simulate showing an interstitial while the page is showing.
2031   TestInterstitialPage::InterstitialState state =
2032       TestInterstitialPage::INVALID;
2033   bool deleted = false;
2034   TestInterstitialPage* interstitial =
2035       new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2036   TestInterstitialPageStateGuard state_guard(interstitial);
2037   interstitial->Show();
2038   interstitial->TestDidNavigate(1, kGURL);
2039
2040   // While the interstitial is showing, let's simulate the hidden page
2041   // attempting to show a JS message.
2042   IPC::Message* dummy_message = new IPC::Message;
2043   bool did_suppress_message = false;
2044   contents()->RunJavaScriptMessage(contents()->GetRenderViewHost(),
2045       base::ASCIIToUTF16("This is an informative message"),
2046       base::ASCIIToUTF16("OK"),
2047       kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message,
2048       &did_suppress_message);
2049   EXPECT_TRUE(did_suppress_message);
2050 }
2051
2052 // Makes sure that if the source passed to CopyStateFromAndPrune has an
2053 // interstitial it isn't copied over to the destination.
2054 TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) {
2055   // Navigate to a page.
2056   GURL url1("http://www.google.com");
2057   test_rvh()->SendNavigate(1, url1);
2058   EXPECT_EQ(1, controller().GetEntryCount());
2059
2060   // Initiate a browser navigation that will trigger the interstitial
2061   controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
2062                         PAGE_TRANSITION_TYPED, std::string());
2063
2064   // Show an interstitial.
2065   TestInterstitialPage::InterstitialState state =
2066       TestInterstitialPage::INVALID;
2067   bool deleted = false;
2068   GURL url2("http://interstitial");
2069   TestInterstitialPage* interstitial =
2070       new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2071   TestInterstitialPageStateGuard state_guard(interstitial);
2072   interstitial->Show();
2073   interstitial->TestDidNavigate(1, url2);
2074   EXPECT_TRUE(interstitial->is_showing());
2075   EXPECT_EQ(2, controller().GetEntryCount());
2076
2077   // Create another NavigationController.
2078   GURL url3("http://foo2");
2079   scoped_ptr<TestWebContents> other_contents(
2080       static_cast<TestWebContents*>(CreateTestWebContents()));
2081   NavigationControllerImpl& other_controller = other_contents->GetController();
2082   other_contents->NavigateAndCommit(url3);
2083   other_contents->ExpectSetHistoryLengthAndPrune(
2084       NavigationEntryImpl::FromNavigationEntry(
2085           other_controller.GetEntryAtIndex(0))->site_instance(), 1,
2086       other_controller.GetEntryAtIndex(0)->GetPageID());
2087   other_controller.CopyStateFromAndPrune(&controller(), false);
2088
2089   // The merged controller should only have two entries: url1 and url2.
2090   ASSERT_EQ(2, other_controller.GetEntryCount());
2091   EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
2092   EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
2093   EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
2094
2095   // And the merged controller shouldn't be showing an interstitial.
2096   EXPECT_FALSE(other_contents->ShowingInterstitialPage());
2097 }
2098
2099 // Makes sure that CopyStateFromAndPrune cannot be called if the target is
2100 // showing an interstitial.
2101 TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) {
2102   // Navigate to a page.
2103   GURL url1("http://www.google.com");
2104   contents()->NavigateAndCommit(url1);
2105
2106   // Create another NavigationController.
2107   scoped_ptr<TestWebContents> other_contents(
2108       static_cast<TestWebContents*>(CreateTestWebContents()));
2109   NavigationControllerImpl& other_controller = other_contents->GetController();
2110
2111   // Navigate it to url2.
2112   GURL url2("http://foo2");
2113   other_contents->NavigateAndCommit(url2);
2114
2115   // Show an interstitial.
2116   TestInterstitialPage::InterstitialState state =
2117       TestInterstitialPage::INVALID;
2118   bool deleted = false;
2119   GURL url3("http://interstitial");
2120   TestInterstitialPage* interstitial =
2121       new TestInterstitialPage(other_contents.get(), true, url3, &state,
2122                                &deleted);
2123   TestInterstitialPageStateGuard state_guard(interstitial);
2124   interstitial->Show();
2125   interstitial->TestDidNavigate(1, url3);
2126   EXPECT_TRUE(interstitial->is_showing());
2127   EXPECT_EQ(2, other_controller.GetEntryCount());
2128
2129   // Ensure that we do not allow calling CopyStateFromAndPrune when an
2130   // interstitial is showing in the target.
2131   EXPECT_FALSE(other_controller.CanPruneAllButLastCommitted());
2132 }
2133
2134 // Regression test for http://crbug.com/168611 - the URLs passed by the
2135 // DidFinishLoad and DidFailLoadWithError IPCs should get filtered.
2136 TEST_F(WebContentsImplTest, FilterURLs) {
2137   TestWebContentsObserver observer(contents());
2138
2139   // A navigation to about:whatever should always look like a navigation to
2140   // about:blank
2141   GURL url_normalized(kAboutBlankURL);
2142   GURL url_from_ipc("about:whatever");
2143
2144   // We navigate the test WebContents to about:blank, since NavigateAndCommit
2145   // will use the given URL to create the NavigationEntry as well, and that
2146   // entry should contain the filtered URL.
2147   contents()->NavigateAndCommit(url_normalized);
2148
2149   // Check that an IPC with about:whatever is correctly normalized.
2150   contents()->TestDidFinishLoad(1, url_from_ipc, true);
2151
2152   EXPECT_EQ(url_normalized, observer.last_url());
2153
2154   // Create and navigate another WebContents.
2155   scoped_ptr<TestWebContents> other_contents(
2156       static_cast<TestWebContents*>(CreateTestWebContents()));
2157   TestWebContentsObserver other_observer(other_contents.get());
2158   other_contents->NavigateAndCommit(url_normalized);
2159
2160   // Check that an IPC with about:whatever is correctly normalized.
2161   other_contents->TestDidFailLoadWithError(
2162       1, url_from_ipc, true, 1, base::string16());
2163   EXPECT_EQ(url_normalized, other_observer.last_url());
2164 }
2165
2166 // Test that if a pending contents is deleted before it is shown, we don't
2167 // crash.
2168 TEST_F(WebContentsImplTest, PendingContents) {
2169   scoped_ptr<TestWebContents> other_contents(
2170       static_cast<TestWebContents*>(CreateTestWebContents()));
2171   contents()->AddPendingContents(other_contents.get());
2172   int route_id = other_contents->GetRenderViewHost()->GetRoutingID();
2173   other_contents.reset();
2174   EXPECT_EQ(NULL, contents()->GetCreatedWindow(route_id));
2175 }
2176
2177 TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) {
2178   const gfx::Size original_preferred_size(1024, 768);
2179   contents()->UpdatePreferredSize(original_preferred_size);
2180
2181   // With no capturers, expect the preferred size to be the one propagated into
2182   // WebContentsImpl via the RenderViewHostDelegate interface.
2183   EXPECT_EQ(contents()->GetCapturerCount(), 0);
2184   EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2185
2186   // Increment capturer count, but without specifying a capture size.  Expect
2187   // a "not set" preferred size.
2188   contents()->IncrementCapturerCount(gfx::Size());
2189   EXPECT_EQ(1, contents()->GetCapturerCount());
2190   EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize());
2191
2192   // Increment capturer count again, but with an overriding capture size.
2193   // Expect preferred size to now be overridden to the capture size.
2194   const gfx::Size capture_size(1280, 720);
2195   contents()->IncrementCapturerCount(capture_size);
2196   EXPECT_EQ(2, contents()->GetCapturerCount());
2197   EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2198
2199   // Increment capturer count a third time, but the expect that the preferred
2200   // size is still the first capture size.
2201   const gfx::Size another_capture_size(720, 480);
2202   contents()->IncrementCapturerCount(another_capture_size);
2203   EXPECT_EQ(3, contents()->GetCapturerCount());
2204   EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2205
2206   // Decrement capturer count twice, but expect the preferred size to still be
2207   // overridden.
2208   contents()->DecrementCapturerCount();
2209   contents()->DecrementCapturerCount();
2210   EXPECT_EQ(1, contents()->GetCapturerCount());
2211   EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2212
2213   // Decrement capturer count, and since the count has dropped to zero, the
2214   // original preferred size should be restored.
2215   contents()->DecrementCapturerCount();
2216   EXPECT_EQ(0, contents()->GetCapturerCount());
2217   EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2218 }
2219
2220 // Tests that GetLastActiveTime starts with a real, non-zero time and updates
2221 // on activity.
2222 TEST_F(WebContentsImplTest, GetLastActiveTime) {
2223   // The WebContents starts with a valid creation time.
2224   EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2225
2226   // Reset the last active time to a known-bad value.
2227   contents()->last_active_time_ = base::TimeTicks();
2228   ASSERT_TRUE(contents()->GetLastActiveTime().is_null());
2229
2230   // Simulate activating the WebContents. The active time should update.
2231   contents()->WasShown();
2232   EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2233 }
2234
2235 }  // namespace content