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