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