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