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.
5 #include "base/values.h"
6 #include "content/browser/web_contents/web_contents_impl.h"
7 #include "content/public/browser/load_notification_details.h"
8 #include "content/public/browser/navigation_controller.h"
9 #include "content/public/browser/notification_details.h"
10 #include "content/public/browser/notification_observer.h"
11 #include "content/public/browser/notification_types.h"
12 #include "content/public/browser/render_view_host.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"
26 void 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
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
39 NavigateToURL(shell, GURL("about://blank"));
41 shell->web_contents()->GetView()->SizeContents(size);
42 #endif // defined(TOOLKIT_GTK) || defined(OS_MACOSX)
45 class WebContentsImplBrowserTest : public ContentBrowserTest {
47 WebContentsImplBrowserTest() {}
50 DISALLOW_COPY_AND_ASSIGN(WebContentsImplBrowserTest);
53 // Keeps track of data from LoadNotificationDetails so we can later verify that
54 // they are correct, after the LoadNotificationDetails object is deleted.
55 class LoadStopNotificationObserver : public WindowedNotificationObserver {
57 LoadStopNotificationObserver(NavigationController* controller)
58 : WindowedNotificationObserver(NOTIFICATION_LOAD_STOP,
59 Source<NavigationController>(controller)),
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;
72 WindowedNotificationObserver::Observe(type, source, details);
77 NavigationController* controller_;
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.
83 class NavigateOnCommitObserver : public WebContentsObserver {
85 NavigateOnCommitObserver(Shell* shell, GURL url)
86 : WebContentsObserver(shell->web_contents()),
92 // WebContentsObserver:
93 virtual void NavigationEntryCommitted(
94 const LoadCommittedDetails& load_details) OVERRIDE {
97 shell_->LoadURL(url_);
106 class RenderViewSizeDelegate : public WebContentsDelegate {
108 void set_size_insets(const gfx::Size& size_insets) {
109 size_insets_ = size_insets;
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());
121 gfx::Size size_insets_;
124 class RenderViewSizeObserver : public WebContentsObserver {
126 RenderViewSizeObserver(Shell* shell, const gfx::Size& wcv_new_size)
127 : WebContentsObserver(shell->web_contents()),
129 wcv_new_size_(wcv_new_size) {
132 // WebContentsObserver:
133 virtual void RenderViewCreated(RenderViewHost* rvh) OVERRIDE {
134 rwhv_create_size_ = rvh->GetView()->GetViewBounds().size();
137 virtual void NavigateToPendingEntry(
139 NavigationController::ReloadType reload_type) OVERRIDE {
140 ResizeWebContentsView(shell_, wcv_new_size_, false);
143 gfx::Size rwhv_create_size() const { return rwhv_create_size_; }
146 Shell* shell_; // Weak ptr.
147 gfx::Size wcv_new_size_;
148 gfx::Size rwhv_create_size_;
151 // See: http://crbug.com/298193
153 #define MAYBE_DidStopLoadingDetails DISABLED_DidStopLoadingDetails
155 #define MAYBE_DidStopLoadingDetails DidStopLoadingDetails
158 // Test that DidStopLoading includes the correct URL in the details.
159 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
160 MAYBE_DidStopLoadingDetails) {
161 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
163 LoadStopNotificationObserver load_observer(
164 &shell()->web_contents()->GetController());
165 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
166 load_observer.Wait();
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_);
174 // See: http://crbug.com/298193
176 #define MAYBE_DidStopLoadingDetailsWithPending \
177 DISABLED_DidStopLoadingDetailsWithPending
179 #define MAYBE_DidStopLoadingDetailsWithPending DidStopLoadingDetailsWithPending
182 // Test that DidStopLoading includes the correct URL in the details when a
183 // pending entry is present.
184 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
185 MAYBE_DidStopLoadingDetailsWithPending) {
186 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
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
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();
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_);
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.
209 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
210 ClearNonVisiblePendingOnFail) {
211 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
213 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
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());
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());
232 // TODO(sail): enable this for MAC when auto resizing of WebContentsViewCocoa is
234 // TODO(shrikant): enable this for Windows when issue with
235 // force-compositing-mode is resolved (http://crbug.com/281726).
236 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID)
237 #define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView
239 #define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView
241 // Test that RenderViewHost is created and updated at the size specified by
242 // WebContentsDelegate::GetSizeForNewRenderView().
243 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
244 MAYBE_GetSizeForNewRenderView) {
245 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
246 // Create a new server with a different site.
247 net::SpawnedTestServer https_server(
248 net::SpawnedTestServer::TYPE_HTTPS,
249 net::SpawnedTestServer::kLocalhost,
250 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
251 ASSERT_TRUE(https_server.Start());
253 scoped_ptr<RenderViewSizeDelegate> delegate(new RenderViewSizeDelegate());
254 shell()->web_contents()->SetDelegate(delegate.get());
255 ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get());
257 // When no size is set, RenderWidgetHostView adopts the size of
259 NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html"));
260 EXPECT_EQ(shell()->web_contents()->GetView()->GetContainerSize(),
261 shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
264 // When a size is set, RenderWidgetHostView and WebContentsView honor this
266 gfx::Size size(300, 300);
267 gfx::Size size_insets(-10, -15);
268 ResizeWebContentsView(shell(), size, true);
269 delegate->set_size_insets(size_insets);
270 NavigateToURL(shell(), https_server.GetURL("/"));
271 size.Enlarge(size_insets.width(), size_insets.height());
273 shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
275 EXPECT_EQ(size, shell()->web_contents()->GetView()->GetContainerSize());
277 // If WebContentsView is resized after RenderWidgetHostView is created but
278 // before pending navigation entry is committed, both RenderWidgetHostView and
279 // WebContentsView use the new size of WebContentsView.
280 gfx::Size init_size(200, 200);
281 gfx::Size new_size(100, 100);
282 size_insets = gfx::Size(-20, -30);
283 ResizeWebContentsView(shell(), init_size, true);
284 delegate->set_size_insets(size_insets);
285 RenderViewSizeObserver observer(shell(), new_size);
286 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
287 // RenderWidgetHostView is created at specified size.
288 init_size.Enlarge(size_insets.width(), size_insets.height());
289 EXPECT_EQ(init_size, observer.rwhv_create_size());
290 // RenderViewSizeObserver resizes WebContentsView in NavigateToPendingEntry,
291 // so both WebContentsView and RenderWidgetHostView adopt this new size.
292 new_size.Enlarge(size_insets.width(), size_insets.height());
294 shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
296 EXPECT_EQ(new_size, shell()->web_contents()->GetView()->GetContainerSize());
299 } // namespace content