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/command_line.h" 6 #include "base/strings/stringprintf.h" 7 #include "base/strings/utf_string_conversions.h" 8 #include "content/browser/web_contents/web_contents_impl.h" 9 #include "content/public/browser/notification_observer.h" 10 #include "content/public/browser/notification_service.h" 11 #include "content/public/browser/notification_types.h" 12 #include "content/public/browser/web_contents_observer.h" 13 #include "content/public/common/content_switches.h" 14 #include "content/public/test/browser_test_utils.h" 15 #include "content/public/test/test_utils.h" 16 #include "content/shell/shell.h" 17 #include "content/test/content_browser_test.h" 18 #include "content/test/content_browser_test_utils.h" 19 20 namespace content { 21 22 class SitePerProcessWebContentsObserver: public WebContentsObserver { 23 public: 24 explicit SitePerProcessWebContentsObserver(WebContents* web_contents) 25 : WebContentsObserver(web_contents), 26 navigation_succeeded_(true) {} 27 virtual ~SitePerProcessWebContentsObserver() {} 28 29 virtual void DidFailProvisionalLoad( 30 int64 frame_id, 31 bool is_main_frame, 32 const GURL& validated_url, 33 int error_code, 34 const string16& error_description, 35 RenderViewHost* render_view_host) OVERRIDE { 36 navigation_url_ = validated_url; 37 navigation_succeeded_ = false; 38 } 39 40 virtual void DidCommitProvisionalLoadForFrame( 41 int64 frame_id, 42 bool is_main_frame, 43 const GURL& url, 44 PageTransition transition_type, 45 RenderViewHost* render_view_host) OVERRIDE{ 46 navigation_url_ = url; 47 navigation_succeeded_ = true; 48 } 49 50 const GURL& navigation_url() const { 51 return navigation_url_; 52 } 53 54 int navigation_succeeded() const { return navigation_succeeded_; } 55 56 private: 57 GURL navigation_url_; 58 bool navigation_succeeded_; 59 60 DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver); 61 }; 62 63 class RedirectNotificationObserver : public NotificationObserver { 64 public: 65 // Register to listen for notifications of the given type from either a 66 // specific source, or from all sources if |source| is 67 // NotificationService::AllSources(). 68 RedirectNotificationObserver(int notification_type, 69 const NotificationSource& source); 70 virtual ~RedirectNotificationObserver(); 71 72 // Wait until the specified notification occurs. If the notification was 73 // emitted between the construction of this object and this call then it 74 // returns immediately. 75 void Wait(); 76 77 // Returns NotificationService::AllSources() if we haven't observed a 78 // notification yet. 79 const NotificationSource& source() const { 80 return source_; 81 } 82 83 const NotificationDetails& details() const { 84 return details_; 85 } 86 87 // NotificationObserver: 88 virtual void Observe(int type, 89 const NotificationSource& source, 90 const NotificationDetails& details) OVERRIDE; 91 92 private: 93 bool seen_; 94 bool seen_twice_; 95 bool running_; 96 NotificationRegistrar registrar_; 97 98 NotificationSource source_; 99 NotificationDetails details_; 100 scoped_refptr<MessageLoopRunner> message_loop_runner_; 101 102 DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver); 103 }; 104 105 RedirectNotificationObserver::RedirectNotificationObserver( 106 int notification_type, 107 const NotificationSource& source) 108 : seen_(false), 109 running_(false), 110 source_(NotificationService::AllSources()) { 111 registrar_.Add(this, notification_type, source); 112 } 113 114 RedirectNotificationObserver::~RedirectNotificationObserver() {} 115 116 void RedirectNotificationObserver::Wait() { 117 if (seen_ && seen_twice_) 118 return; 119 120 running_ = true; 121 message_loop_runner_ = new MessageLoopRunner; 122 message_loop_runner_->Run(); 123 EXPECT_TRUE(seen_); 124 } 125 126 void RedirectNotificationObserver::Observe( 127 int type, 128 const NotificationSource& source, 129 const NotificationDetails& details) { 130 source_ = source; 131 details_ = details; 132 seen_twice_ = seen_; 133 seen_ = true; 134 if (!running_) 135 return; 136 137 message_loop_runner_->Quit(); 138 running_ = false; 139 } 140 141 class SitePerProcessBrowserTest : public ContentBrowserTest { 142 public: 143 SitePerProcessBrowserTest() {} 144 145 bool NavigateIframeToURL(Shell* window, 146 const GURL& url, 147 std::string iframe_id) { 148 std::string script = base::StringPrintf( 149 "var iframes = document.getElementById('%s');iframes.src='%s';", 150 iframe_id.c_str(), url.spec().c_str()); 151 WindowedNotificationObserver load_observer( 152 NOTIFICATION_LOAD_STOP, 153 Source<NavigationController>( 154 &shell()->web_contents()->GetController())); 155 bool result = ExecuteScript(window->web_contents(), script); 156 load_observer.Wait(); 157 return result; 158 } 159 160 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 161 command_line->AppendSwitch(switches::kSitePerProcess); 162 } 163 }; 164 165 // TODO(nasko): Disable this test until out-of-process iframes is ready and the 166 // security checks are back in place. 167 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrossSiteIframe) { 168 ASSERT_TRUE(test_server()->Start()); 169 net::SpawnedTestServer https_server( 170 net::SpawnedTestServer::TYPE_HTTPS, 171 net::SpawnedTestServer::kLocalhost, 172 base::FilePath(FILE_PATH_LITERAL("content/test/data"))); 173 ASSERT_TRUE(https_server.Start()); 174 GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); 175 176 NavigateToURL(shell(), main_url); 177 178 SitePerProcessWebContentsObserver observer(shell()->web_contents()); 179 { 180 // Load same-site page into Iframe. 181 GURL http_url(test_server()->GetURL("files/title1.html")); 182 EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test")); 183 EXPECT_EQ(observer.navigation_url(), http_url); 184 EXPECT_TRUE(observer.navigation_succeeded()); 185 } 186 187 { 188 // Load cross-site page into Iframe. 189 GURL https_url(https_server.GetURL("files/title1.html")); 190 EXPECT_TRUE(NavigateIframeToURL(shell(), https_url, "test")); 191 EXPECT_EQ(observer.navigation_url(), https_url); 192 EXPECT_FALSE(observer.navigation_succeeded()); 193 } 194 } 195 196 // TODO(nasko): Disable this test until out-of-process iframes is ready and the 197 // security checks are back in place. 198 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 199 DISABLED_CrossSiteIframeRedirectOnce) { 200 ASSERT_TRUE(test_server()->Start()); 201 net::SpawnedTestServer https_server( 202 net::SpawnedTestServer::TYPE_HTTPS, 203 net::SpawnedTestServer::kLocalhost, 204 base::FilePath(FILE_PATH_LITERAL("content/test/data"))); 205 ASSERT_TRUE(https_server.Start()); 206 207 GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); 208 GURL http_url(test_server()->GetURL("files/title1.html")); 209 GURL https_url(https_server.GetURL("files/title1.html")); 210 211 NavigateToURL(shell(), main_url); 212 213 SitePerProcessWebContentsObserver observer(shell()->web_contents()); 214 { 215 // Load cross-site client-redirect page into Iframe. 216 // Should be blocked. 217 GURL client_redirect_https_url(https_server.GetURL( 218 "client-redirect?files/title1.html")); 219 EXPECT_TRUE(NavigateIframeToURL(shell(), 220 client_redirect_https_url, "test")); 221 // DidFailProvisionalLoad when navigating to client_redirect_https_url. 222 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url); 223 EXPECT_FALSE(observer.navigation_succeeded()); 224 } 225 226 { 227 // Load cross-site server-redirect page into Iframe, 228 // which redirects to same-site page. 229 GURL server_redirect_http_url(https_server.GetURL( 230 "server-redirect?" + http_url.spec())); 231 EXPECT_TRUE(NavigateIframeToURL(shell(), 232 server_redirect_http_url, "test")); 233 EXPECT_EQ(observer.navigation_url(), http_url); 234 EXPECT_TRUE(observer.navigation_succeeded()); 235 } 236 237 { 238 // Load cross-site server-redirect page into Iframe, 239 // which redirects to cross-site page. 240 GURL server_redirect_http_url(https_server.GetURL( 241 "server-redirect?files/title1.html")); 242 EXPECT_TRUE(NavigateIframeToURL(shell(), 243 server_redirect_http_url, "test")); 244 // DidFailProvisionalLoad when navigating to https_url. 245 EXPECT_EQ(observer.navigation_url(), https_url); 246 EXPECT_FALSE(observer.navigation_succeeded()); 247 } 248 249 { 250 // Load same-site server-redirect page into Iframe, 251 // which redirects to cross-site page. 252 GURL server_redirect_http_url(test_server()->GetURL( 253 "server-redirect?" + https_url.spec())); 254 EXPECT_TRUE(NavigateIframeToURL(shell(), 255 server_redirect_http_url, "test")); 256 257 EXPECT_EQ(observer.navigation_url(), https_url); 258 EXPECT_FALSE(observer.navigation_succeeded()); 259 } 260 261 { 262 // Load same-site client-redirect page into Iframe, 263 // which redirects to cross-site page. 264 GURL client_redirect_http_url(test_server()->GetURL( 265 "client-redirect?" + https_url.spec())); 266 267 RedirectNotificationObserver load_observer2( 268 NOTIFICATION_LOAD_STOP, 269 Source<NavigationController>( 270 &shell()->web_contents()->GetController())); 271 272 EXPECT_TRUE(NavigateIframeToURL(shell(), 273 client_redirect_http_url, "test")); 274 275 // Same-site Client-Redirect Page should be loaded successfully. 276 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); 277 EXPECT_TRUE(observer.navigation_succeeded()); 278 279 // Redirecting to Cross-site Page should be blocked. 280 load_observer2.Wait(); 281 EXPECT_EQ(observer.navigation_url(), https_url); 282 EXPECT_FALSE(observer.navigation_succeeded()); 283 } 284 285 { 286 // Load same-site server-redirect page into Iframe, 287 // which redirects to same-site page. 288 GURL server_redirect_http_url(test_server()->GetURL( 289 "server-redirect?files/title1.html")); 290 EXPECT_TRUE(NavigateIframeToURL(shell(), 291 server_redirect_http_url, "test")); 292 EXPECT_EQ(observer.navigation_url(), http_url); 293 EXPECT_TRUE(observer.navigation_succeeded()); 294 } 295 296 { 297 // Load same-site client-redirect page into Iframe, 298 // which redirects to same-site page. 299 GURL client_redirect_http_url(test_server()->GetURL( 300 "client-redirect?" + http_url.spec())); 301 RedirectNotificationObserver load_observer2( 302 NOTIFICATION_LOAD_STOP, 303 Source<NavigationController>( 304 &shell()->web_contents()->GetController())); 305 306 EXPECT_TRUE(NavigateIframeToURL(shell(), 307 client_redirect_http_url, "test")); 308 309 // Same-site Client-Redirect Page should be loaded successfully. 310 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); 311 EXPECT_TRUE(observer.navigation_succeeded()); 312 313 // Redirecting to Same-site Page should be loaded successfully. 314 load_observer2.Wait(); 315 EXPECT_EQ(observer.navigation_url(), http_url); 316 EXPECT_TRUE(observer.navigation_succeeded()); 317 } 318 } 319 320 // TODO(nasko): Disable this test until out-of-process iframes is ready and the 321 // security checks are back in place. 322 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 323 DISABLED_CrossSiteIframeRedirectTwice) { 324 ASSERT_TRUE(test_server()->Start()); 325 net::SpawnedTestServer https_server( 326 net::SpawnedTestServer::TYPE_HTTPS, 327 net::SpawnedTestServer::kLocalhost, 328 base::FilePath(FILE_PATH_LITERAL("content/test/data"))); 329 ASSERT_TRUE(https_server.Start()); 330 331 GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); 332 GURL http_url(test_server()->GetURL("files/title1.html")); 333 GURL https_url(https_server.GetURL("files/title1.html")); 334 335 NavigateToURL(shell(), main_url); 336 337 SitePerProcessWebContentsObserver observer(shell()->web_contents()); 338 { 339 // Load client-redirect page pointing to a cross-site client-redirect page, 340 // which eventually redirects back to same-site page. 341 GURL client_redirect_https_url(https_server.GetURL( 342 "client-redirect?" + http_url.spec())); 343 GURL client_redirect_http_url(test_server()->GetURL( 344 "client-redirect?" + client_redirect_https_url.spec())); 345 346 // We should wait until second client redirect get cancelled. 347 RedirectNotificationObserver load_observer2( 348 NOTIFICATION_LOAD_STOP, 349 Source<NavigationController>( 350 &shell()->web_contents()->GetController())); 351 352 EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test")); 353 354 // DidFailProvisionalLoad when navigating to client_redirect_https_url. 355 load_observer2.Wait(); 356 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url); 357 EXPECT_FALSE(observer.navigation_succeeded()); 358 } 359 360 { 361 // Load server-redirect page pointing to a cross-site server-redirect page, 362 // which eventually redirect back to same-site page. 363 GURL server_redirect_https_url(https_server.GetURL( 364 "server-redirect?" + http_url.spec())); 365 GURL server_redirect_http_url(test_server()->GetURL( 366 "server-redirect?" + server_redirect_https_url.spec())); 367 EXPECT_TRUE(NavigateIframeToURL(shell(), 368 server_redirect_http_url, "test")); 369 EXPECT_EQ(observer.navigation_url(), http_url); 370 EXPECT_TRUE(observer.navigation_succeeded()); 371 } 372 373 { 374 // Load server-redirect page pointing to a cross-site server-redirect page, 375 // which eventually redirects back to cross-site page. 376 GURL server_redirect_https_url(https_server.GetURL( 377 "server-redirect?" + https_url.spec())); 378 GURL server_redirect_http_url(test_server()->GetURL( 379 "server-redirect?" + server_redirect_https_url.spec())); 380 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test")); 381 382 // DidFailProvisionalLoad when navigating to https_url. 383 EXPECT_EQ(observer.navigation_url(), https_url); 384 EXPECT_FALSE(observer.navigation_succeeded()); 385 } 386 387 { 388 // Load server-redirect page pointing to a cross-site client-redirect page, 389 // which eventually redirects back to same-site page. 390 GURL client_redirect_http_url(https_server.GetURL( 391 "client-redirect?" + http_url.spec())); 392 GURL server_redirect_http_url(test_server()->GetURL( 393 "server-redirect?" + client_redirect_http_url.spec())); 394 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test")); 395 396 // DidFailProvisionalLoad when navigating to client_redirect_http_url. 397 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); 398 EXPECT_FALSE(observer.navigation_succeeded()); 399 } 400 } 401 402 } 403