web_contents_impl_browsertest.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2013 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/values.h"
6#include "content/browser/frame_host/navigation_entry_impl.h"
7#include "content/browser/web_contents/web_contents_impl.h"
8#include "content/public/browser/load_notification_details.h"
9#include "content/public/browser/navigation_controller.h"
10#include "content/public/browser/notification_details.h"
11#include "content/public/browser/notification_observer.h"
12#include "content/public/browser/notification_types.h"
13#include "content/public/browser/render_view_host.h"
14#include "content/public/browser/render_widget_host_view.h"
15#include "content/public/browser/web_contents_observer.h"
16#include "content/public/browser/web_contents_view.h"
17#include "content/public/common/content_paths.h"
18#include "content/public/test/browser_test_utils.h"
19#include "content/public/test/test_utils.h"
20#include "content/shell/browser/shell.h"
21#include "content/test/content_browser_test.h"
22#include "content/test/content_browser_test_utils.h"
23#include "net/test/embedded_test_server/embedded_test_server.h"
24
25namespace content {
26
27void ResizeWebContentsView(Shell* shell, const gfx::Size& size,
28                           bool set_start_page) {
29  // Shell::SizeTo is not implemented on Aura; WebContentsView::SizeContents
30  // works on Win and ChromeOS but not Linux - we need to resize the shell
31  // window on Linux because if we don't, the next layout of the unchanged shell
32  // window will resize WebContentsView back to the previous size.
33  // The cleaner and shorter SizeContents is preferred as more platforms convert
34  // to Aura.
35#if defined(TOOLKIT_GTK) || defined(OS_MACOSX)
36  shell->SizeTo(size);
37  // If |set_start_page| is true, start with blank page to make sure resize
38  // takes effect.
39  if (set_start_page)
40    NavigateToURL(shell, GURL("about://blank"));
41#else
42  shell->web_contents()->GetView()->SizeContents(size);
43#endif  // defined(TOOLKIT_GTK) || defined(OS_MACOSX)
44}
45
46class WebContentsImplBrowserTest : public ContentBrowserTest {
47 public:
48  WebContentsImplBrowserTest() {}
49
50 private:
51  DISALLOW_COPY_AND_ASSIGN(WebContentsImplBrowserTest);
52};
53
54// Keeps track of data from LoadNotificationDetails so we can later verify that
55// they are correct, after the LoadNotificationDetails object is deleted.
56class LoadStopNotificationObserver : public WindowedNotificationObserver {
57 public:
58  LoadStopNotificationObserver(NavigationController* controller)
59      : WindowedNotificationObserver(NOTIFICATION_LOAD_STOP,
60                                     Source<NavigationController>(controller)),
61        session_index_(-1),
62        controller_(NULL) {
63  }
64  virtual void Observe(int type,
65                       const NotificationSource& source,
66                       const NotificationDetails& details) OVERRIDE {
67    if (type == NOTIFICATION_LOAD_STOP) {
68      const Details<LoadNotificationDetails> load_details(details);
69      url_ = load_details->url;
70      session_index_ = load_details->session_index;
71      controller_ = load_details->controller;
72    }
73    WindowedNotificationObserver::Observe(type, source, details);
74  }
75
76  GURL url_;
77  int session_index_;
78  NavigationController* controller_;
79};
80
81// Starts a new navigation as soon as the current one commits, but does not
82// wait for it to complete.  This allows us to observe DidStopLoading while
83// a pending entry is present.
84class NavigateOnCommitObserver : public WebContentsObserver {
85 public:
86  NavigateOnCommitObserver(Shell* shell, GURL url)
87      : WebContentsObserver(shell->web_contents()),
88        shell_(shell),
89        url_(url),
90        done_(false) {
91  }
92
93  // WebContentsObserver:
94  virtual void NavigationEntryCommitted(
95      const LoadCommittedDetails& load_details) OVERRIDE {
96    if (!done_) {
97      done_ = true;
98      shell_->LoadURL(url_);
99    }
100  }
101
102  Shell* shell_;
103  GURL url_;
104  bool done_;
105};
106
107class RenderViewSizeDelegate : public WebContentsDelegate {
108 public:
109  void set_size_insets(const gfx::Size& size_insets) {
110    size_insets_ = size_insets;
111  }
112
113  // WebContentsDelegate:
114  virtual gfx::Size GetSizeForNewRenderView(
115      const WebContents* web_contents) const OVERRIDE {
116    gfx::Size size(web_contents->GetView()->GetContainerSize());
117    size.Enlarge(size_insets_.width(), size_insets_.height());
118    return size;
119  }
120
121 private:
122  gfx::Size size_insets_;
123};
124
125class RenderViewSizeObserver : public WebContentsObserver {
126 public:
127  RenderViewSizeObserver(Shell* shell, const gfx::Size& wcv_new_size)
128      : WebContentsObserver(shell->web_contents()),
129        shell_(shell),
130        wcv_new_size_(wcv_new_size) {
131  }
132
133  // WebContentsObserver:
134  virtual void RenderViewCreated(RenderViewHost* rvh) OVERRIDE {
135    rwhv_create_size_ = rvh->GetView()->GetViewBounds().size();
136  }
137
138  virtual void DidStartNavigationToPendingEntry(
139      const GURL& url,
140      NavigationController::ReloadType reload_type) OVERRIDE {
141    ResizeWebContentsView(shell_, wcv_new_size_, false);
142  }
143
144  gfx::Size rwhv_create_size() const { return rwhv_create_size_; }
145
146 private:
147  Shell* shell_;  // Weak ptr.
148  gfx::Size wcv_new_size_;
149  gfx::Size rwhv_create_size_;
150};
151
152// See: http://crbug.com/298193
153#if defined(OS_WIN)
154#define MAYBE_DidStopLoadingDetails DISABLED_DidStopLoadingDetails
155#else
156#define MAYBE_DidStopLoadingDetails DidStopLoadingDetails
157#endif
158
159// Test that DidStopLoading includes the correct URL in the details.
160IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
161                       MAYBE_DidStopLoadingDetails) {
162  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
163
164  LoadStopNotificationObserver load_observer(
165      &shell()->web_contents()->GetController());
166  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
167  load_observer.Wait();
168
169  EXPECT_EQ("/title1.html", load_observer.url_.path());
170  EXPECT_EQ(0, load_observer.session_index_);
171  EXPECT_EQ(&shell()->web_contents()->GetController(),
172            load_observer.controller_);
173}
174
175// See: http://crbug.com/298193
176#if defined(OS_WIN)
177#define MAYBE_DidStopLoadingDetailsWithPending \
178  DISABLED_DidStopLoadingDetailsWithPending
179#else
180#define MAYBE_DidStopLoadingDetailsWithPending DidStopLoadingDetailsWithPending
181#endif
182
183// Test that DidStopLoading includes the correct URL in the details when a
184// pending entry is present.
185IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
186                       MAYBE_DidStopLoadingDetailsWithPending) {
187  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
188
189  // Listen for the first load to stop.
190  LoadStopNotificationObserver load_observer(
191      &shell()->web_contents()->GetController());
192  // Start a new pending navigation as soon as the first load commits.
193  // We will hear a DidStopLoading from the first load as the new load
194  // is started.
195  NavigateOnCommitObserver commit_observer(
196      shell(), embedded_test_server()->GetURL("/title2.html"));
197  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
198  load_observer.Wait();
199
200  EXPECT_EQ("/title1.html", load_observer.url_.path());
201  EXPECT_EQ(0, load_observer.session_index_);
202  EXPECT_EQ(&shell()->web_contents()->GetController(),
203            load_observer.controller_);
204}
205// Test that a renderer-initiated navigation to an invalid URL does not leave
206// around a pending entry that could be used in a URL spoof.  We test this in
207// a browser test because our unit test framework incorrectly calls
208// DidStartProvisionalLoadForFrame for in-page navigations.
209// See http://crbug.com/280512.
210IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
211                       ClearNonVisiblePendingOnFail) {
212  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
213
214  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
215
216  // Navigate to an invalid URL and make sure it doesn't leave a pending entry.
217  LoadStopNotificationObserver load_observer1(
218      &shell()->web_contents()->GetController());
219  ASSERT_TRUE(ExecuteScript(shell()->web_contents(),
220                            "window.location.href=\"nonexistent:12121\";"));
221  load_observer1.Wait();
222  EXPECT_FALSE(shell()->web_contents()->GetController().GetPendingEntry());
223
224  LoadStopNotificationObserver load_observer2(
225      &shell()->web_contents()->GetController());
226  ASSERT_TRUE(ExecuteScript(shell()->web_contents(),
227                            "window.location.href=\"#foo\";"));
228  load_observer2.Wait();
229  EXPECT_EQ(embedded_test_server()->GetURL("/title1.html#foo"),
230            shell()->web_contents()->GetVisibleURL());
231}
232
233// TODO(sail): enable this for MAC when auto resizing of WebContentsViewCocoa is
234// fixed.
235// TODO(shrikant): enable this for Windows when issue with
236// force-compositing-mode is resolved (http://crbug.com/281726).
237#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID)
238#define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView
239#else
240#define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView
241#endif
242// Test that RenderViewHost is created and updated at the size specified by
243// WebContentsDelegate::GetSizeForNewRenderView().
244IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
245                       MAYBE_GetSizeForNewRenderView) {
246  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
247  // Create a new server with a different site.
248  net::SpawnedTestServer https_server(
249      net::SpawnedTestServer::TYPE_HTTPS,
250      net::SpawnedTestServer::kLocalhost,
251      base::FilePath(FILE_PATH_LITERAL("content/test/data")));
252  ASSERT_TRUE(https_server.Start());
253
254  scoped_ptr<RenderViewSizeDelegate> delegate(new RenderViewSizeDelegate());
255  shell()->web_contents()->SetDelegate(delegate.get());
256  ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get());
257
258  // When no size is set, RenderWidgetHostView adopts the size of
259  // WebContenntsView.
260  NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html"));
261  EXPECT_EQ(shell()->web_contents()->GetView()->GetContainerSize(),
262            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
263                size());
264
265  // When a size is set, RenderWidgetHostView and WebContentsView honor this
266  // size.
267  gfx::Size size(300, 300);
268  gfx::Size size_insets(-10, -15);
269  ResizeWebContentsView(shell(), size, true);
270  delegate->set_size_insets(size_insets);
271  NavigateToURL(shell(), https_server.GetURL("/"));
272  size.Enlarge(size_insets.width(), size_insets.height());
273  EXPECT_EQ(size,
274            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
275                size());
276  EXPECT_EQ(size, shell()->web_contents()->GetView()->GetContainerSize());
277
278  // If WebContentsView is resized after RenderWidgetHostView is created but
279  // before pending navigation entry is committed, both RenderWidgetHostView and
280  // WebContentsView use the new size of WebContentsView.
281  gfx::Size init_size(200, 200);
282  gfx::Size new_size(100, 100);
283  size_insets = gfx::Size(-20, -30);
284  ResizeWebContentsView(shell(), init_size, true);
285  delegate->set_size_insets(size_insets);
286  RenderViewSizeObserver observer(shell(), new_size);
287  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
288  // RenderWidgetHostView is created at specified size.
289  init_size.Enlarge(size_insets.width(), size_insets.height());
290  EXPECT_EQ(init_size, observer.rwhv_create_size());
291  // RenderViewSizeObserver resizes WebContentsView in
292  // DidStartNavigationToPendingEntry, so both WebContentsView and
293  // RenderWidgetHostView adopt this new size.
294  new_size.Enlarge(size_insets.width(), size_insets.height());
295  EXPECT_EQ(new_size,
296            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
297                size());
298  EXPECT_EQ(new_size, shell()->web_contents()->GetView()->GetContainerSize());
299}
300
301IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, OpenURLSubframe) {
302
303  // Navigate with source_frame_id 3, FrameTreeNode ID 4.
304  const GURL url("http://foo");
305  OpenURLParams params(url, Referrer(), 3, 4, CURRENT_TAB, PAGE_TRANSITION_LINK,
306                       true);
307  shell()->web_contents()->OpenURL(params);
308
309  // Make sure the NavigationEntry ends up with the FrameTreeNode ID.
310  NavigationController* controller = &shell()->web_contents()->GetController();
311  EXPECT_TRUE(controller->GetPendingEntry());
312  EXPECT_EQ(4, NavigationEntryImpl::FromNavigationEntry(
313                controller->GetPendingEntry())->frame_tree_node_id());
314}
315
316
317}  // namespace content
318