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