web_contents_impl_unittest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/interstitial_page_delegate.h"
16#include "content/public/browser/navigation_details.h"
17#include "content/public/browser/notification_details.h"
18#include "content/public/browser/notification_source.h"
19#include "content/public/browser/notification_source.h"
20#include "content/public/browser/render_widget_host_view.h"
21#include "content/public/browser/web_contents_observer.h"
22#include "content/public/browser/web_ui_controller.h"
23#include "content/public/common/bindings_policy.h"
24#include "content/public/common/content_constants.h"
25#include "content/public/common/url_constants.h"
26#include "content/public/test/mock_render_process_host.h"
27#include "content/public/test/test_browser_thread.h"
28#include "content/public/test/test_utils.h"
29#include "content/test/test_content_browser_client.h"
30#include "content/test/test_content_client.h"
31#include "content/test/test_web_contents.h"
32#include "testing/gtest/include/gtest/gtest.h"
33
34namespace content {
35namespace {
36
37const char kTestWebUIUrl[] = "chrome://blah";
38
39class WebContentsImplTestWebUIControllerFactory
40    : public WebUIControllerFactory {
41 public:
42  virtual WebUIController* CreateWebUIControllerForURL(
43      WebUI* web_ui, const GURL& url) const OVERRIDE {
44   if (!UseWebUI(url))
45     return NULL;
46
47   return new WebUIController(web_ui);
48  }
49
50  virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
51      const GURL& url) const OVERRIDE {
52    return WebUI::kNoWebUI;
53  }
54
55  virtual bool UseWebUIForURL(BrowserContext* browser_context,
56                              const GURL& url) const OVERRIDE {
57    return UseWebUI(url);
58  }
59
60  virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
61                                      const GURL& url) const OVERRIDE {
62    return UseWebUI(url);
63  }
64
65 private:
66  bool UseWebUI(const GURL& url) const {
67    return url == GURL(kTestWebUIUrl);
68  }
69};
70
71class TestInterstitialPage;
72
73class TestInterstitialPageDelegate : public InterstitialPageDelegate {
74 public:
75  TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page)
76      : interstitial_page_(interstitial_page) {}
77  virtual void CommandReceived(const std::string& command) OVERRIDE;
78  virtual std::string GetHTMLContents() OVERRIDE { return std::string(); }
79  virtual void OnDontProceed() OVERRIDE;
80  virtual void OnProceed() OVERRIDE;
81 private:
82  TestInterstitialPage* interstitial_page_;
83};
84
85class TestInterstitialPage : public InterstitialPageImpl {
86 public:
87  enum InterstitialState {
88    INVALID = 0,    // Hasn't yet been initialized.
89    UNDECIDED,      // Initialized, but no decision taken yet.
90    OKED,           // Proceed was called.
91    CANCELED        // DontProceed was called.
92  };
93
94  class Delegate {
95   public:
96    virtual void TestInterstitialPageDeleted(
97        TestInterstitialPage* interstitial) = 0;
98
99   protected:
100    virtual ~Delegate() {}
101  };
102
103  // IMPORTANT NOTE: if you pass stack allocated values for |state| and
104  // |deleted| (like all interstitial related tests do at this point), make sure
105  // to create an instance of the TestInterstitialPageStateGuard class on the
106  // stack in your test.  This will ensure that the TestInterstitialPage states
107  // are cleared when the test finishes.
108  // Not doing so will cause stack trashing if your test does not hide the
109  // interstitial, as in such a case it will be destroyed in the test TearDown
110  // method and will dereference the |deleted| local variable which by then is
111  // out of scope.
112  TestInterstitialPage(WebContentsImpl* contents,
113                       bool new_navigation,
114                       const GURL& url,
115                       InterstitialState* state,
116                       bool* deleted)
117      : InterstitialPageImpl(
118            contents, new_navigation, url,
119            new TestInterstitialPageDelegate(this)),
120        state_(state),
121        deleted_(deleted),
122        command_received_count_(0),
123        delegate_(NULL) {
124    *state_ = UNDECIDED;
125    *deleted_ = false;
126  }
127
128  virtual ~TestInterstitialPage() {
129    if (deleted_)
130      *deleted_ = true;
131    if (delegate_)
132      delegate_->TestInterstitialPageDeleted(this);
133  }
134
135  void OnDontProceed() {
136    if (state_)
137      *state_ = CANCELED;
138  }
139  void OnProceed() {
140    if (state_)
141      *state_ = OKED;
142  }
143
144  int command_received_count() const {
145    return command_received_count_;
146  }
147
148  void TestDomOperationResponse(const std::string& json_string) {
149    if (enabled())
150      CommandReceived();
151  }
152
153  void TestDidNavigate(int page_id, const GURL& url) {
154    ViewHostMsg_FrameNavigate_Params params;
155    InitNavigateParams(&params, page_id, url, PAGE_TRANSITION_TYPED);
156    DidNavigate(GetRenderViewHostForTesting(), params);
157  }
158
159  void TestRenderViewTerminated(base::TerminationStatus status,
160                                int error_code) {
161    RenderViewTerminated(GetRenderViewHostForTesting(), status, error_code);
162  }
163
164  bool is_showing() const {
165    return static_cast<TestRenderWidgetHostView*>(
166        GetRenderViewHostForTesting()->GetView())->is_showing();
167  }
168
169  void ClearStates() {
170    state_ = NULL;
171    deleted_ = NULL;
172    delegate_ = NULL;
173  }
174
175  void CommandReceived() {
176    command_received_count_++;
177  }
178
179  void set_delegate(Delegate* delegate) {
180    delegate_ = delegate;
181  }
182
183 protected:
184  virtual RenderViewHost* CreateRenderViewHost() OVERRIDE {
185    return new TestRenderViewHost(
186        SiteInstance::Create(web_contents()->GetBrowserContext()),
187        this, this, MSG_ROUTING_NONE, MSG_ROUTING_NONE, false);
188  }
189
190  virtual WebContentsView* CreateWebContentsView() OVERRIDE {
191    return NULL;
192  }
193
194 private:
195  InterstitialState* state_;
196  bool* deleted_;
197  int command_received_count_;
198  Delegate* delegate_;
199};
200
201void TestInterstitialPageDelegate::CommandReceived(const std::string& command) {
202  interstitial_page_->CommandReceived();
203}
204
205void TestInterstitialPageDelegate::OnDontProceed() {
206  interstitial_page_->OnDontProceed();
207}
208
209void TestInterstitialPageDelegate::OnProceed() {
210  interstitial_page_->OnProceed();
211}
212
213class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
214 public:
215  explicit TestInterstitialPageStateGuard(
216      TestInterstitialPage* interstitial_page)
217      : interstitial_page_(interstitial_page) {
218    DCHECK(interstitial_page_);
219    interstitial_page_->set_delegate(this);
220  }
221  virtual ~TestInterstitialPageStateGuard() {
222    if (interstitial_page_)
223      interstitial_page_->ClearStates();
224  }
225
226  virtual void TestInterstitialPageDeleted(
227      TestInterstitialPage* interstitial) OVERRIDE {
228    DCHECK(interstitial_page_ == interstitial);
229    interstitial_page_ = NULL;
230  }
231
232 private:
233  TestInterstitialPage* interstitial_page_;
234};
235
236class WebContentsImplTest : public RenderViewHostImplTestHarness {
237 public:
238  virtual void SetUp() {
239    RenderViewHostImplTestHarness::SetUp();
240    WebUIControllerFactory::RegisterFactory(&factory_);
241  }
242
243  virtual void TearDown() {
244    WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
245    RenderViewHostImplTestHarness::TearDown();
246  }
247
248 private:
249  WebContentsImplTestWebUIControllerFactory factory_;
250};
251
252class TestWebContentsObserver : public WebContentsObserver {
253 public:
254  TestWebContentsObserver(WebContents* contents)
255      : WebContentsObserver(contents) {
256  }
257  virtual ~TestWebContentsObserver() {}
258
259  virtual void DidFinishLoad(int64 frame_id,
260                             const GURL& validated_url,
261                             bool is_main_frame,
262                             RenderViewHost* render_view_host) OVERRIDE {
263    last_url_ = validated_url;
264  }
265  virtual void DidFailLoad(int64 frame_id,
266                           const GURL& validated_url,
267                           bool is_main_frame,
268                           int error_code,
269                           const string16& error_description,
270                           RenderViewHost* render_view_host) OVERRIDE {
271    last_url_ = validated_url;
272  }
273
274  const GURL& last_url() const { return last_url_; }
275
276 private:
277  GURL last_url_;
278
279  DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
280};
281
282}  // namespace
283
284// Test to make sure that title updates get stripped of whitespace.
285TEST_F(WebContentsImplTest, UpdateTitle) {
286  NavigationControllerImpl& cont =
287      static_cast<NavigationControllerImpl&>(controller());
288  ViewHostMsg_FrameNavigate_Params params;
289  InitNavigateParams(&params, 0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED);
290
291  LoadCommittedDetails details;
292  cont.RendererDidNavigate(params, &details);
293
294  contents()->UpdateTitle(rvh(), 0, ASCIIToUTF16("    Lots O' Whitespace\n"),
295                          base::i18n::LEFT_TO_RIGHT);
296  EXPECT_EQ(ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
297}
298
299// Test view source mode for a webui page.
300TEST_F(WebContentsImplTest, NTPViewSource) {
301  NavigationControllerImpl& cont =
302      static_cast<NavigationControllerImpl&>(controller());
303  const char kUrl[] = "view-source:chrome://blah";
304  const GURL kGURL(kUrl);
305
306  process()->sink().ClearMessages();
307
308  cont.LoadURL(
309      kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
310  rvh()->GetDelegate()->RenderViewCreated(rvh());
311  // Did we get the expected message?
312  EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
313      ViewMsg_EnableViewSourceMode::ID));
314
315  ViewHostMsg_FrameNavigate_Params params;
316  InitNavigateParams(&params, 0, kGURL, PAGE_TRANSITION_TYPED);
317  LoadCommittedDetails details;
318  cont.RendererDidNavigate(params, &details);
319  // Also check title and url.
320  EXPECT_EQ(ASCIIToUTF16(kUrl), contents()->GetTitle());
321}
322
323// Test to ensure UpdateMaxPageID is working properly.
324TEST_F(WebContentsImplTest, UpdateMaxPageID) {
325  SiteInstance* instance1 = contents()->GetSiteInstance();
326  scoped_refptr<SiteInstance> instance2(SiteInstance::Create(NULL));
327
328  // Starts at -1.
329  EXPECT_EQ(-1, contents()->GetMaxPageID());
330  EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1));
331  EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
332
333  // Make sure max_page_id_ is monotonically increasing per SiteInstance.
334  contents()->UpdateMaxPageID(3);
335  contents()->UpdateMaxPageID(1);
336  EXPECT_EQ(3, contents()->GetMaxPageID());
337  EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
338  EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
339
340  contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7);
341  EXPECT_EQ(3, contents()->GetMaxPageID());
342  EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
343  EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
344}
345
346// Test simple same-SiteInstance navigation.
347TEST_F(WebContentsImplTest, SimpleNavigation) {
348  TestRenderViewHost* orig_rvh = test_rvh();
349  SiteInstance* instance1 = contents()->GetSiteInstance();
350  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
351
352  // Navigate to URL
353  const GURL url("http://www.google.com");
354  controller().LoadURL(
355      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
356  EXPECT_FALSE(contents()->cross_navigation_pending());
357  EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
358  // Controller's pending entry will have a NULL site instance until we assign
359  // it in DidNavigate.
360  EXPECT_TRUE(
361      NavigationEntryImpl::FromNavigationEntry(controller().GetActiveEntry())->
362          site_instance() == NULL);
363
364  // DidNavigate from the page
365  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
366  EXPECT_FALSE(contents()->cross_navigation_pending());
367  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
368  EXPECT_EQ(instance1, orig_rvh->GetSiteInstance());
369  // Controller's entry should now have the SiteInstance, or else we won't be
370  // able to find it later.
371  EXPECT_EQ(
372      instance1,
373      NavigationEntryImpl::FromNavigationEntry(controller().GetActiveEntry())->
374          site_instance());
375}
376
377// Test that we reject NavigateToEntry if the url is over kMaxURLChars.
378TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
379  // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
380  const GURL url(std::string("http://example.org/").append(
381      kMaxURLChars + 1, 'a'));
382
383  controller().LoadURL(
384      url, Referrer(), PAGE_TRANSITION_GENERATED, std::string());
385  EXPECT_TRUE(controller().GetActiveEntry() == NULL);
386}
387
388// Test that navigating across a site boundary creates a new RenderViewHost
389// with a new SiteInstance.  Going back should do the same.
390TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
391  contents()->transition_cross_site = true;
392  TestRenderViewHost* orig_rvh = test_rvh();
393  int orig_rvh_delete_count = 0;
394  orig_rvh->set_delete_counter(&orig_rvh_delete_count);
395  SiteInstance* instance1 = contents()->GetSiteInstance();
396
397  // Navigate to URL.  First URL should use first RenderViewHost.
398  const GURL url("http://www.google.com");
399  controller().LoadURL(
400      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
401  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
402
403  EXPECT_FALSE(contents()->cross_navigation_pending());
404  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
405  EXPECT_EQ(url, contents()->GetLastCommittedURL());
406  EXPECT_EQ(url, contents()->GetActiveURL());
407
408  // Navigate to new site
409  const GURL url2("http://www.yahoo.com");
410  controller().LoadURL(
411      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
412  EXPECT_TRUE(contents()->cross_navigation_pending());
413  EXPECT_EQ(url, contents()->GetLastCommittedURL());
414  EXPECT_EQ(url2, contents()->GetActiveURL());
415  TestRenderViewHost* pending_rvh =
416      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
417  int pending_rvh_delete_count = 0;
418  pending_rvh->set_delete_counter(&pending_rvh_delete_count);
419
420  // Navigations should be suspended in pending_rvh until ShouldCloseACK.
421  EXPECT_TRUE(pending_rvh->are_navigations_suspended());
422  orig_rvh->SendShouldCloseACK(true);
423  EXPECT_FALSE(pending_rvh->are_navigations_suspended());
424
425  // DidNavigate from the pending page
426  contents()->TestDidNavigate(
427      pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
428  SiteInstance* instance2 = contents()->GetSiteInstance();
429
430  EXPECT_FALSE(contents()->cross_navigation_pending());
431  EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
432  EXPECT_EQ(url2, contents()->GetLastCommittedURL());
433  EXPECT_EQ(url2, contents()->GetActiveURL());
434  EXPECT_NE(instance1, instance2);
435  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
436  // We keep the original RVH around, swapped out.
437  EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
438      orig_rvh));
439  EXPECT_EQ(orig_rvh_delete_count, 0);
440
441  // Going back should switch SiteInstances again.  The first SiteInstance is
442  // stored in the NavigationEntry, so it should be the same as at the start.
443  // We should use the same RVH as before, swapping it back in.
444  controller().GoBack();
445  TestRenderViewHost* goback_rvh =
446      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
447  EXPECT_EQ(orig_rvh, goback_rvh);
448  EXPECT_TRUE(contents()->cross_navigation_pending());
449
450  // Navigations should be suspended in goback_rvh until ShouldCloseACK.
451  EXPECT_TRUE(goback_rvh->are_navigations_suspended());
452  pending_rvh->SendShouldCloseACK(true);
453  EXPECT_FALSE(goback_rvh->are_navigations_suspended());
454
455  // DidNavigate from the back action
456  contents()->TestDidNavigate(
457      goback_rvh, 1, url2, PAGE_TRANSITION_TYPED);
458  EXPECT_FALSE(contents()->cross_navigation_pending());
459  EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost());
460  EXPECT_EQ(instance1, contents()->GetSiteInstance());
461  // The pending RVH should now be swapped out, not deleted.
462  EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
463      IsOnSwappedOutList(pending_rvh));
464  EXPECT_EQ(pending_rvh_delete_count, 0);
465
466  // Close contents and ensure RVHs are deleted.
467  DeleteContents();
468  EXPECT_EQ(orig_rvh_delete_count, 1);
469  EXPECT_EQ(pending_rvh_delete_count, 1);
470}
471
472// Test that navigating across a site boundary after a crash creates a new
473// RVH without requiring a cross-site transition (i.e., PENDING state).
474TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
475  contents()->transition_cross_site = true;
476  TestRenderViewHost* orig_rvh = test_rvh();
477  int orig_rvh_delete_count = 0;
478  orig_rvh->set_delete_counter(&orig_rvh_delete_count);
479  SiteInstance* instance1 = contents()->GetSiteInstance();
480
481  // Navigate to URL.  First URL should use first RenderViewHost.
482  const GURL url("http://www.google.com");
483  controller().LoadURL(
484      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
485  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
486
487  EXPECT_FALSE(contents()->cross_navigation_pending());
488  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
489
490  // Crash the renderer.
491  orig_rvh->set_render_view_created(false);
492
493  // Navigate to new site.  We should not go into PENDING.
494  const GURL url2("http://www.yahoo.com");
495  controller().LoadURL(
496      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
497  RenderViewHost* new_rvh = rvh();
498  EXPECT_FALSE(contents()->cross_navigation_pending());
499  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
500  EXPECT_NE(orig_rvh, new_rvh);
501  EXPECT_EQ(orig_rvh_delete_count, 1);
502
503  // DidNavigate from the new page
504  contents()->TestDidNavigate(new_rvh, 1, url2, PAGE_TRANSITION_TYPED);
505  SiteInstance* instance2 = contents()->GetSiteInstance();
506
507  EXPECT_FALSE(contents()->cross_navigation_pending());
508  EXPECT_EQ(new_rvh, rvh());
509  EXPECT_NE(instance1, instance2);
510  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
511
512  // Close contents and ensure RVHs are deleted.
513  DeleteContents();
514  EXPECT_EQ(orig_rvh_delete_count, 1);
515}
516
517// Test that opening a new contents in the same SiteInstance and then navigating
518// both contentses to a new site will place both contentses in a single
519// SiteInstance.
520TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
521  contents()->transition_cross_site = true;
522  TestRenderViewHost* orig_rvh = test_rvh();
523  SiteInstance* instance1 = contents()->GetSiteInstance();
524
525  // Navigate to URL.  First URL should use first RenderViewHost.
526  const GURL url("http://www.google.com");
527  controller().LoadURL(
528      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
529  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
530
531  // Open a new contents with the same SiteInstance, navigated to the same site.
532  scoped_ptr<TestWebContents> contents2(
533      TestWebContents::Create(browser_context_.get(), instance1));
534  contents2->transition_cross_site = true;
535  contents2->GetController().LoadURL(url, Referrer(),
536                                    PAGE_TRANSITION_TYPED,
537                                    std::string());
538  // Need this page id to be 2 since the site instance is the same (which is the
539  // scope of page IDs) and we want to consider this a new page.
540  contents2->TestDidNavigate(
541      contents2->GetRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED);
542
543  // Navigate first contents to a new site.
544  const GURL url2a("http://www.yahoo.com");
545  controller().LoadURL(
546      url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string());
547  orig_rvh->SendShouldCloseACK(true);
548  TestRenderViewHost* pending_rvh_a =
549      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
550  contents()->TestDidNavigate(
551      pending_rvh_a, 1, url2a, PAGE_TRANSITION_TYPED);
552  SiteInstance* instance2a = contents()->GetSiteInstance();
553  EXPECT_NE(instance1, instance2a);
554
555  // Navigate second contents to the same site as the first tab.
556  const GURL url2b("http://mail.yahoo.com");
557  contents2->GetController().LoadURL(url2b, Referrer(),
558                                    PAGE_TRANSITION_TYPED,
559                                    std::string());
560  TestRenderViewHost* rvh2 =
561      static_cast<TestRenderViewHost*>(contents2->GetRenderViewHost());
562  rvh2->SendShouldCloseACK(true);
563  TestRenderViewHost* pending_rvh_b =
564      static_cast<TestRenderViewHost*>(contents2->GetPendingRenderViewHost());
565  EXPECT_TRUE(pending_rvh_b != NULL);
566  EXPECT_TRUE(contents2->cross_navigation_pending());
567
568  // NOTE(creis): We used to be in danger of showing a crash page here if the
569  // second contents hadn't navigated somewhere first (bug 1145430).  That case
570  // is now covered by the CrossSiteBoundariesAfterCrash test.
571  contents2->TestDidNavigate(
572      pending_rvh_b, 2, url2b, PAGE_TRANSITION_TYPED);
573  SiteInstance* instance2b = contents2->GetSiteInstance();
574  EXPECT_NE(instance1, instance2b);
575
576  // Both contentses should now be in the same SiteInstance.
577  EXPECT_EQ(instance2a, instance2b);
578}
579
580// Test that we can find an opener RVH even if it's pending.
581// http://crbug.com/176252.
582TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
583  contents()->transition_cross_site = true;
584  TestRenderViewHost* orig_rvh = test_rvh();
585
586  // Navigate to a URL.
587  const GURL url("http://www.google.com");
588  controller().LoadURL(
589      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
590  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
591
592  // Start to navigate first tab to a new site, so that it has a pending RVH.
593  const GURL url2("http://www.yahoo.com");
594  controller().LoadURL(
595      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
596  orig_rvh->SendShouldCloseACK(true);
597  TestRenderViewHost* pending_rvh =
598      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
599
600  // While it is still pending, simulate opening a new tab with the first tab
601  // as its opener.  This will call WebContentsImpl::CreateOpenerRenderViews
602  // on the opener to ensure that an RVH exists.
603  int opener_routing_id = contents()->CreateOpenerRenderViews(
604      pending_rvh->GetSiteInstance());
605
606  // We should find the pending RVH and not create a new one.
607  EXPECT_EQ(pending_rvh->GetRoutingID(), opener_routing_id);
608}
609
610// Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
611// to determine whether a navigation is cross-site.
612TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
613  contents()->transition_cross_site = true;
614  RenderViewHost* orig_rvh = rvh();
615  SiteInstance* instance1 = contents()->GetSiteInstance();
616
617  // Navigate to URL.
618  const GURL url("http://www.google.com");
619  controller().LoadURL(
620      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
621  contents()->TestDidNavigate(
622      orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
623
624  // Open a related contents to a second site.
625  scoped_ptr<TestWebContents> contents2(
626      TestWebContents::Create(browser_context_.get(), instance1));
627  contents2->transition_cross_site = true;
628  const GURL url2("http://www.yahoo.com");
629  contents2->GetController().LoadURL(url2, Referrer(),
630                                    PAGE_TRANSITION_TYPED,
631                                    std::string());
632  // The first RVH in contents2 isn't live yet, so we shortcut the cross site
633  // pending.
634  TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>(
635      contents2->GetRenderViewHost());
636  EXPECT_FALSE(contents2->cross_navigation_pending());
637  contents2->TestDidNavigate(rvh2, 2, url2, PAGE_TRANSITION_TYPED);
638  SiteInstance* instance2 = contents2->GetSiteInstance();
639  EXPECT_NE(instance1, instance2);
640  EXPECT_FALSE(contents2->cross_navigation_pending());
641
642  // Simulate a link click in first contents to second site.  Doesn't switch
643  // SiteInstances, because we don't intercept WebKit navigations.
644  contents()->TestDidNavigate(
645      orig_rvh, 2, url2, PAGE_TRANSITION_TYPED);
646  SiteInstance* instance3 = contents()->GetSiteInstance();
647  EXPECT_EQ(instance1, instance3);
648  EXPECT_FALSE(contents()->cross_navigation_pending());
649
650  // Navigate to the new site.  Doesn't switch SiteInstancees, because we
651  // compare against the current URL, not the SiteInstance's site.
652  const GURL url3("http://mail.yahoo.com");
653  controller().LoadURL(
654      url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
655  EXPECT_FALSE(contents()->cross_navigation_pending());
656  contents()->TestDidNavigate(
657      orig_rvh, 3, url3, PAGE_TRANSITION_TYPED);
658  SiteInstance* instance4 = contents()->GetSiteInstance();
659  EXPECT_EQ(instance1, instance4);
660}
661
662// Test that the onbeforeunload and onunload handlers run when navigating
663// across site boundaries.
664TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
665  contents()->transition_cross_site = true;
666  TestRenderViewHost* orig_rvh = test_rvh();
667  SiteInstance* instance1 = contents()->GetSiteInstance();
668
669  // Navigate to URL.  First URL should use first RenderViewHost.
670  const GURL url("http://www.google.com");
671  controller().LoadURL(
672      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
673  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
674  EXPECT_FALSE(contents()->cross_navigation_pending());
675  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
676
677  // Navigate to new site, but simulate an onbeforeunload denial.
678  const GURL url2("http://www.yahoo.com");
679  controller().LoadURL(
680      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
681  EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
682  base::TimeTicks now = base::TimeTicks::Now();
683  orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false, now, now));
684  EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
685  EXPECT_FALSE(contents()->cross_navigation_pending());
686  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
687
688  // Navigate again, but simulate an onbeforeunload approval.
689  controller().LoadURL(
690      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
691  EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
692  now = base::TimeTicks::Now();
693  orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
694  EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
695  EXPECT_TRUE(contents()->cross_navigation_pending());
696  TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
697      contents()->GetPendingRenderViewHost());
698
699  // We won't hear DidNavigate until the onunload handler has finished running.
700  // (No way to simulate that here, but it involves a call from RDH to
701  // WebContentsImpl::OnCrossSiteResponse.)
702
703  // DidNavigate from the pending page
704  contents()->TestDidNavigate(
705      pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
706  SiteInstance* instance2 = contents()->GetSiteInstance();
707  EXPECT_FALSE(contents()->cross_navigation_pending());
708  EXPECT_EQ(pending_rvh, rvh());
709  EXPECT_NE(instance1, instance2);
710  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
711}
712
713// Test that during a slow cross-site navigation, the original renderer can
714// navigate to a different URL and have it displayed, canceling the slow
715// navigation.
716TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
717  contents()->transition_cross_site = true;
718  TestRenderViewHost* orig_rvh = test_rvh();
719  SiteInstance* instance1 = contents()->GetSiteInstance();
720
721  // Navigate to URL.  First URL should use first RenderViewHost.
722  const GURL url("http://www.google.com");
723  controller().LoadURL(
724      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
725  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
726  EXPECT_FALSE(contents()->cross_navigation_pending());
727  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
728
729  // Navigate to new site, simulating an onbeforeunload approval.
730  const GURL url2("http://www.yahoo.com");
731  controller().LoadURL(
732      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
733  EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
734  base::TimeTicks now = base::TimeTicks::Now();
735  orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
736  EXPECT_TRUE(contents()->cross_navigation_pending());
737
738  // Suppose the original renderer navigates before the new one is ready.
739  orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
740
741  // Verify that the pending navigation is cancelled.
742  EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
743  SiteInstance* instance2 = contents()->GetSiteInstance();
744  EXPECT_FALSE(contents()->cross_navigation_pending());
745  EXPECT_EQ(orig_rvh, rvh());
746  EXPECT_EQ(instance1, instance2);
747  EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL);
748}
749
750TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
751  contents()->transition_cross_site = true;
752
753  // Start with a web ui page, which gets a new RVH with WebUI bindings.
754  const GURL url1("chrome://blah");
755  controller().LoadURL(
756      url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
757  TestRenderViewHost* ntp_rvh = test_rvh();
758  contents()->TestDidNavigate(ntp_rvh, 1, url1, PAGE_TRANSITION_TYPED);
759  NavigationEntry* entry1 = controller().GetLastCommittedEntry();
760  SiteInstance* instance1 = contents()->GetSiteInstance();
761
762  EXPECT_FALSE(contents()->cross_navigation_pending());
763  EXPECT_EQ(ntp_rvh, contents()->GetRenderViewHost());
764  EXPECT_EQ(url1, entry1->GetURL());
765  EXPECT_EQ(instance1,
766            NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
767  EXPECT_TRUE(ntp_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
768
769  // Navigate to new site.
770  const GURL url2("http://www.google.com");
771  controller().LoadURL(
772      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
773  EXPECT_TRUE(contents()->cross_navigation_pending());
774  TestRenderViewHost* google_rvh =
775      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
776
777  // Simulate beforeunload approval.
778  EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack());
779  base::TimeTicks now = base::TimeTicks::Now();
780  ntp_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
781
782  // DidNavigate from the pending page.
783  contents()->TestDidNavigate(
784      google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
785  NavigationEntry* entry2 = controller().GetLastCommittedEntry();
786  SiteInstance* instance2 = contents()->GetSiteInstance();
787
788  EXPECT_FALSE(contents()->cross_navigation_pending());
789  EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
790  EXPECT_NE(instance1, instance2);
791  EXPECT_FALSE(contents()->GetPendingRenderViewHost());
792  EXPECT_EQ(url2, entry2->GetURL());
793  EXPECT_EQ(instance2,
794            NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
795  EXPECT_FALSE(google_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
796
797  // Navigate to third page on same site.
798  const GURL url3("http://news.google.com");
799  controller().LoadURL(
800      url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
801  EXPECT_FALSE(contents()->cross_navigation_pending());
802  contents()->TestDidNavigate(
803      google_rvh, 2, url3, PAGE_TRANSITION_TYPED);
804  NavigationEntry* entry3 = controller().GetLastCommittedEntry();
805  SiteInstance* instance3 = contents()->GetSiteInstance();
806
807  EXPECT_FALSE(contents()->cross_navigation_pending());
808  EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
809  EXPECT_EQ(instance2, instance3);
810  EXPECT_FALSE(contents()->GetPendingRenderViewHost());
811  EXPECT_EQ(url3, entry3->GetURL());
812  EXPECT_EQ(instance3,
813            NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
814
815  // Go back within the site.
816  controller().GoBack();
817  EXPECT_FALSE(contents()->cross_navigation_pending());
818  EXPECT_EQ(entry2, controller().GetPendingEntry());
819
820  // Before that commits, go back again.
821  controller().GoBack();
822  EXPECT_TRUE(contents()->cross_navigation_pending());
823  EXPECT_TRUE(contents()->GetPendingRenderViewHost());
824  EXPECT_EQ(entry1, controller().GetPendingEntry());
825
826  // Simulate beforeunload approval.
827  EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack());
828  now = base::TimeTicks::Now();
829  google_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
830
831  // DidNavigate from the first back. This aborts the second back's pending RVH.
832  contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED);
833
834  // We should commit this page and forget about the second back.
835  EXPECT_FALSE(contents()->cross_navigation_pending());
836  EXPECT_FALSE(controller().GetPendingEntry());
837  EXPECT_EQ(google_rvh, contents()->GetRenderViewHost());
838  EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
839
840  // We should not have corrupted the NTP entry.
841  EXPECT_EQ(instance3,
842            NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
843  EXPECT_EQ(instance2,
844            NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
845  EXPECT_EQ(instance1,
846            NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
847  EXPECT_EQ(url1, entry1->GetURL());
848}
849
850// Test that during a slow cross-site navigation, a sub-frame navigation in the
851// original renderer will not cancel the slow navigation (bug 42029).
852TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
853  contents()->transition_cross_site = true;
854  TestRenderViewHost* orig_rvh = test_rvh();
855
856  // Navigate to URL.  First URL should use first RenderViewHost.
857  const GURL url("http://www.google.com");
858  controller().LoadURL(
859      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
860  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
861  EXPECT_FALSE(contents()->cross_navigation_pending());
862  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
863
864  // Start navigating to new site.
865  const GURL url2("http://www.yahoo.com");
866  controller().LoadURL(
867      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
868
869  // Simulate a sub-frame navigation arriving and ensure the RVH is still
870  // waiting for a before unload response.
871  orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"),
872                                       PAGE_TRANSITION_AUTO_SUBFRAME);
873  EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
874
875  // Now simulate the onbeforeunload approval and verify the navigation is
876  // not canceled.
877  base::TimeTicks now = base::TimeTicks::Now();
878  orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
879  EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
880  EXPECT_TRUE(contents()->cross_navigation_pending());
881}
882
883// Test that a cross-site navigation is not preempted if the previous
884// renderer sends a FrameNavigate message just before being told to stop.
885// We should only preempt the cross-site navigation if the previous renderer
886// has started a new navigation.  See http://crbug.com/79176.
887TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
888  contents()->transition_cross_site = true;
889
890  // Navigate to NTP URL.
891  const GURL url("chrome://blah");
892  controller().LoadURL(
893      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
894  TestRenderViewHost* orig_rvh = test_rvh();
895  EXPECT_FALSE(contents()->cross_navigation_pending());
896
897  // Navigate to new site, with the beforeunload request in flight.
898  const GURL url2("http://www.yahoo.com");
899  controller().LoadURL(
900      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
901  TestRenderViewHost* pending_rvh =
902      static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost());
903  EXPECT_TRUE(contents()->cross_navigation_pending());
904  EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
905
906  // Suppose the first navigation tries to commit now, with a
907  // ViewMsg_Stop in flight.  This should not cancel the pending navigation,
908  // but it should act as if the beforeunload ack arrived.
909  orig_rvh->SendNavigate(1, GURL("chrome://blah"));
910  EXPECT_TRUE(contents()->cross_navigation_pending());
911  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
912  EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
913
914  // The pending navigation should be able to commit successfully.
915  contents()->TestDidNavigate(pending_rvh, 1, url2, PAGE_TRANSITION_TYPED);
916  EXPECT_FALSE(contents()->cross_navigation_pending());
917  EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost());
918}
919
920// Test that the original renderer cannot preempt a cross-site navigation once
921// the unload request has been made.  At this point, the cross-site navigation
922// is almost ready to be displayed, and the original renderer is only given a
923// short chance to run an unload handler.  Prevents regression of bug 23942.
924TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) {
925  contents()->transition_cross_site = true;
926  TestRenderViewHost* orig_rvh = test_rvh();
927  SiteInstance* instance1 = contents()->GetSiteInstance();
928
929  // Navigate to URL.  First URL should use first RenderViewHost.
930  const GURL url("http://www.google.com");
931  controller().LoadURL(
932      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
933  contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED);
934  EXPECT_FALSE(contents()->cross_navigation_pending());
935  EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost());
936
937  // Navigate to new site, simulating an onbeforeunload approval.
938  const GURL url2("http://www.yahoo.com");
939  controller().LoadURL(
940      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
941  base::TimeTicks now = base::TimeTicks::Now();
942  orig_rvh->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true, now, now));
943  EXPECT_TRUE(contents()->cross_navigation_pending());
944  TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
945      contents()->GetPendingRenderViewHost());
946
947  // Simulate the pending renderer's response, which leads to an unload request
948  // being sent to orig_rvh.
949  contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(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