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