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/strings/stringprintf.h" 6 #include "base/strings/utf_string_conversions.h" 7 #include "chrome/browser/background/background_contents_service.h" 8 #include "chrome/browser/background/background_contents_service_factory.h" 9 #include "chrome/browser/background/background_mode_manager.h" 10 #include "chrome/browser/browser_process.h" 11 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/extensions/extension_apitest.h" 13 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/ui/browser.h" 16 #include "chrome/browser/ui/browser_dialogs.h" 17 #include "chrome/browser/ui/browser_window.h" 18 #include "chrome/common/chrome_switches.h" 19 #include "content/public/browser/notification_service.h" 20 #include "content/public/test/test_notification_tracker.h" 21 #include "content/public/test/test_utils.h" 22 #include "extensions/common/extension.h" 23 #include "extensions/common/switches.h" 24 #include "net/dns/mock_host_resolver.h" 25 #include "net/test/embedded_test_server/embedded_test_server.h" 26 27 #if defined(OS_MACOSX) 28 #include "base/mac/scoped_nsautorelease_pool.h" 29 #endif 30 31 using extensions::Extension; 32 33 class AppBackgroundPageApiTest : public ExtensionApiTest { 34 public: 35 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 36 ExtensionApiTest::SetUpCommandLine(command_line); 37 command_line->AppendSwitch(switches::kDisablePopupBlocking); 38 command_line->AppendSwitch(extensions::switches::kAllowHTTPBackgroundPage); 39 } 40 41 bool CreateApp(const std::string& app_manifest, 42 base::FilePath* app_dir) { 43 if (!app_dir_.CreateUniqueTempDir()) { 44 LOG(ERROR) << "Unable to create a temporary directory."; 45 return false; 46 } 47 base::FilePath manifest_path = app_dir_.path().AppendASCII("manifest.json"); 48 int bytes_written = file_util::WriteFile(manifest_path, 49 app_manifest.data(), 50 app_manifest.size()); 51 if (bytes_written != static_cast<int>(app_manifest.size())) { 52 LOG(ERROR) << "Unable to write complete manifest to file. Return code=" 53 << bytes_written; 54 return false; 55 } 56 *app_dir = app_dir_.path(); 57 return true; 58 } 59 60 bool WaitForBackgroundMode(bool expected_background_mode) { 61 #if defined(OS_CHROMEOS) 62 // BackgroundMode is not supported on chromeos, so we should test the 63 // behavior of BackgroundContents, but not the background mode state itself. 64 return true; 65 #else 66 BackgroundModeManager* manager = 67 g_browser_process->background_mode_manager(); 68 // If background mode is disabled on this platform (e.g. cros), then skip 69 // this check. 70 if (!manager || !manager->IsBackgroundModePrefEnabled()) { 71 DLOG(WARNING) << "Skipping check - background mode disabled"; 72 return true; 73 } 74 if (manager->IsBackgroundModeActive() == expected_background_mode) 75 return true; 76 77 // We are not currently in the expected state - wait for the state to 78 // change. 79 content::WindowedNotificationObserver watcher( 80 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED, 81 content::NotificationService::AllSources()); 82 watcher.Wait(); 83 return manager->IsBackgroundModeActive() == expected_background_mode; 84 #endif 85 } 86 87 void CloseBrowser(Browser* browser) { 88 content::WindowedNotificationObserver observer( 89 chrome::NOTIFICATION_BROWSER_CLOSED, 90 content::NotificationService::AllSources()); 91 browser->window()->Close(); 92 #if defined(OS_MACOSX) 93 // BrowserWindowController depends on the auto release pool being recycled 94 // in the message loop to delete itself, which frees the Browser object 95 // which fires this event. 96 AutoreleasePool()->Recycle(); 97 #endif 98 observer.Wait(); 99 } 100 101 void UnloadExtensionViaTask(const std::string& id) { 102 base::MessageLoop::current()->PostTask( 103 FROM_HERE, 104 base::Bind(&AppBackgroundPageApiTest::UnloadExtension, this, id)); 105 } 106 107 private: 108 base::ScopedTempDir app_dir_; 109 }; 110 111 // Disable on Mac only. http://crbug.com/95139 112 #if defined(OS_MACOSX) 113 #define MAYBE_Basic DISABLED_Basic 114 #else 115 #define MAYBE_Basic Basic 116 #endif 117 118 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, MAYBE_Basic) { 119 host_resolver()->AddRule("a.com", "127.0.0.1"); 120 ASSERT_TRUE(StartEmbeddedTestServer()); 121 122 std::string app_manifest = base::StringPrintf( 123 "{" 124 " \"name\": \"App\"," 125 " \"version\": \"0.1\"," 126 " \"manifest_version\": 2," 127 " \"app\": {" 128 " \"urls\": [" 129 " \"http://a.com/\"" 130 " ]," 131 " \"launch\": {" 132 " \"web_url\": \"http://a.com:%d/\"" 133 " }" 134 " }," 135 " \"permissions\": [\"background\"]" 136 "}", 137 embedded_test_server()->port()); 138 139 base::FilePath app_dir; 140 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 141 ASSERT_TRUE(LoadExtension(app_dir)); 142 // Background mode should not be active until a background page is created. 143 ASSERT_TRUE(WaitForBackgroundMode(false)); 144 ASSERT_TRUE(RunExtensionTest("app_background_page/basic")) << message_; 145 // The test closes the background contents, so we should fall back to no 146 // background mode at the end. 147 ASSERT_TRUE(WaitForBackgroundMode(false)); 148 } 149 150 // Crashy, http://crbug.com/69215. 151 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, DISABLED_LacksPermission) { 152 host_resolver()->AddRule("a.com", "127.0.0.1"); 153 ASSERT_TRUE(StartEmbeddedTestServer()); 154 155 std::string app_manifest = base::StringPrintf( 156 "{" 157 " \"name\": \"App\"," 158 " \"version\": \"0.1\"," 159 " \"manifest_version\": 2," 160 " \"app\": {" 161 " \"urls\": [" 162 " \"http://a.com/\"" 163 " ]," 164 " \"launch\": {" 165 " \"web_url\": \"http://a.com:%d/\"" 166 " }" 167 " }" 168 "}", 169 embedded_test_server()->port()); 170 171 base::FilePath app_dir; 172 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 173 ASSERT_TRUE(LoadExtension(app_dir)); 174 ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission")) 175 << message_; 176 ASSERT_TRUE(WaitForBackgroundMode(false)); 177 } 178 179 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, ManifestBackgroundPage) { 180 host_resolver()->AddRule("a.com", "127.0.0.1"); 181 ASSERT_TRUE(StartEmbeddedTestServer()); 182 183 std::string app_manifest = base::StringPrintf( 184 "{" 185 " \"name\": \"App\"," 186 " \"version\": \"0.1\"," 187 " \"manifest_version\": 2," 188 " \"app\": {" 189 " \"urls\": [" 190 " \"http://a.com/\"" 191 " ]," 192 " \"launch\": {" 193 " \"web_url\": \"http://a.com:%d/\"" 194 " }" 195 " }," 196 " \"permissions\": [\"background\"]," 197 " \"background\": {" 198 " \"page\": \"http://a.com:%d/test.html\"" 199 " }" 200 "}", 201 embedded_test_server()->port(), 202 embedded_test_server()->port()); 203 204 base::FilePath app_dir; 205 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 206 // Background mode should not be active now because no background app was 207 // loaded. 208 ASSERT_TRUE(LoadExtension(app_dir)); 209 // Background mode be active now because a background page was created when 210 // the app was loaded. 211 ASSERT_TRUE(WaitForBackgroundMode(true)); 212 213 const Extension* extension = GetSingleLoadedExtension(); 214 ASSERT_TRUE( 215 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 216 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 217 UnloadExtension(extension->id()); 218 } 219 220 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, NoJsBackgroundPage) { 221 // Keep the task manager up through this test to verify that a crash doesn't 222 // happen when window.open creates a background page that switches 223 // RenderViewHosts. See http://crbug.com/165138. 224 chrome::ShowTaskManager(browser()); 225 226 // Make sure that no BackgroundContentses get deleted (a signal that repeated 227 // window.open calls recreate instances, instead of being no-ops). 228 content::TestNotificationTracker background_deleted_tracker; 229 background_deleted_tracker.ListenFor( 230 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, 231 content::Source<Profile>(browser()->profile())); 232 233 host_resolver()->AddRule("a.com", "127.0.0.1"); 234 ASSERT_TRUE(StartEmbeddedTestServer()); 235 236 std::string app_manifest = base::StringPrintf( 237 "{" 238 " \"name\": \"App\"," 239 " \"version\": \"0.1\"," 240 " \"manifest_version\": 2," 241 " \"app\": {" 242 " \"urls\": [" 243 " \"http://a.com/\"" 244 " ]," 245 " \"launch\": {" 246 " \"web_url\": \"http://a.com:%d/test.html\"" 247 " }" 248 " }," 249 " \"permissions\": [\"background\"]," 250 " \"background\": {" 251 " \"allow_js_access\": false" 252 " }" 253 "}", 254 embedded_test_server()->port()); 255 256 base::FilePath app_dir; 257 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 258 ASSERT_TRUE(LoadExtension(app_dir)); 259 260 // There isn't a background page loaded initially. 261 const Extension* extension = GetSingleLoadedExtension(); 262 ASSERT_FALSE( 263 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 264 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 265 // The test makes sure that window.open returns null. 266 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js")) << message_; 267 // And after it runs there should be a background page. 268 ASSERT_TRUE( 269 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 270 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 271 272 EXPECT_EQ(0u, background_deleted_tracker.size()); 273 UnloadExtension(extension->id()); 274 } 275 276 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, NoJsManifestBackgroundPage) { 277 host_resolver()->AddRule("a.com", "127.0.0.1"); 278 ASSERT_TRUE(StartEmbeddedTestServer()); 279 280 std::string app_manifest = base::StringPrintf( 281 "{" 282 " \"name\": \"App\"," 283 " \"version\": \"0.1\"," 284 " \"manifest_version\": 2," 285 " \"app\": {" 286 " \"urls\": [" 287 " \"http://a.com/\"" 288 " ]," 289 " \"launch\": {" 290 " \"web_url\": \"http://a.com:%d/\"" 291 " }" 292 " }," 293 " \"permissions\": [\"background\"]," 294 " \"background\": {" 295 " \"page\": \"http://a.com:%d/bg.html\"," 296 " \"allow_js_access\": false" 297 " }" 298 "}", 299 embedded_test_server()->port(), 300 embedded_test_server()->port()); 301 302 base::FilePath app_dir; 303 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 304 ASSERT_TRUE(LoadExtension(app_dir)); 305 306 // The background page should load, but window.open should return null. 307 const Extension* extension = GetSingleLoadedExtension(); 308 ASSERT_TRUE( 309 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 310 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 311 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js_manifest")) << 312 message_; 313 UnloadExtension(extension->id()); 314 } 315 316 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, OpenTwoBackgroundPages) { 317 host_resolver()->AddRule("a.com", "127.0.0.1"); 318 ASSERT_TRUE(StartEmbeddedTestServer()); 319 320 std::string app_manifest = base::StringPrintf( 321 "{" 322 " \"name\": \"App\"," 323 " \"version\": \"0.1\"," 324 " \"manifest_version\": 2," 325 " \"app\": {" 326 " \"urls\": [" 327 " \"http://a.com/\"" 328 " ]," 329 " \"launch\": {" 330 " \"web_url\": \"http://a.com:%d/\"" 331 " }" 332 " }," 333 " \"permissions\": [\"background\"]" 334 "}", 335 embedded_test_server()->port()); 336 337 base::FilePath app_dir; 338 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 339 ASSERT_TRUE(LoadExtension(app_dir)); 340 const Extension* extension = GetSingleLoadedExtension(); 341 ASSERT_TRUE(RunExtensionTest("app_background_page/two_pages")) << message_; 342 UnloadExtension(extension->id()); 343 } 344 345 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, OpenTwoPagesWithManifest) { 346 host_resolver()->AddRule("a.com", "127.0.0.1"); 347 ASSERT_TRUE(StartEmbeddedTestServer()); 348 349 std::string app_manifest = base::StringPrintf( 350 "{" 351 " \"name\": \"App\"," 352 " \"version\": \"0.1\"," 353 " \"manifest_version\": 2," 354 " \"app\": {" 355 " \"urls\": [" 356 " \"http://a.com/\"" 357 " ]," 358 " \"launch\": {" 359 " \"web_url\": \"http://a.com:%d/\"" 360 " }" 361 " }," 362 " \"background\": {" 363 " \"page\": \"http://a.com:%d/bg.html\"" 364 " }," 365 " \"permissions\": [\"background\"]" 366 "}", 367 embedded_test_server()->port(), 368 embedded_test_server()->port()); 369 370 base::FilePath app_dir; 371 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 372 ASSERT_TRUE(LoadExtension(app_dir)); 373 const Extension* extension = GetSingleLoadedExtension(); 374 ASSERT_TRUE(RunExtensionTest("app_background_page/two_with_manifest")) << 375 message_; 376 UnloadExtension(extension->id()); 377 } 378 379 // Times out occasionally -- see crbug.com/108493 380 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, DISABLED_OpenPopupFromBGPage) { 381 host_resolver()->AddRule("a.com", "127.0.0.1"); 382 ASSERT_TRUE(StartEmbeddedTestServer()); 383 384 std::string app_manifest = base::StringPrintf( 385 "{" 386 " \"name\": \"App\"," 387 " \"version\": \"0.1\"," 388 " \"manifest_version\": 2," 389 " \"app\": {" 390 " \"urls\": [" 391 " \"http://a.com/\"" 392 " ]," 393 " \"launch\": {" 394 " \"web_url\": \"http://a.com:%d/\"" 395 " }" 396 " }," 397 " \"background\": { \"page\": \"http://a.com:%d/extensions/api_test/" 398 "app_background_page/bg_open/bg_open_bg.html\" }," 399 " \"permissions\": [\"background\"]" 400 "}", 401 embedded_test_server()->port(), 402 embedded_test_server()->port()); 403 404 base::FilePath app_dir; 405 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 406 ASSERT_TRUE(LoadExtension(app_dir)); 407 ASSERT_TRUE(RunExtensionTest("app_background_page/bg_open")) << message_; 408 } 409 410 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, DISABLED_OpenThenClose) { 411 host_resolver()->AddRule("a.com", "127.0.0.1"); 412 ASSERT_TRUE(StartEmbeddedTestServer()); 413 414 std::string app_manifest = base::StringPrintf( 415 "{" 416 " \"name\": \"App\"," 417 " \"version\": \"0.1\"," 418 " \"manifest_version\": 2," 419 " \"app\": {" 420 " \"urls\": [" 421 " \"http://a.com/\"" 422 " ]," 423 " \"launch\": {" 424 " \"web_url\": \"http://a.com:%d/\"" 425 " }" 426 " }," 427 " \"permissions\": [\"background\"]" 428 "}", 429 embedded_test_server()->port()); 430 431 base::FilePath app_dir; 432 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 433 ASSERT_TRUE(LoadExtension(app_dir)); 434 // There isn't a background page loaded initially. 435 const Extension* extension = GetSingleLoadedExtension(); 436 ASSERT_FALSE( 437 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 438 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 439 // Background mode should not be active until a background page is created. 440 ASSERT_TRUE(WaitForBackgroundMode(false)); 441 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_open")) << message_; 442 // Background mode should be active now because a background page was created. 443 ASSERT_TRUE(WaitForBackgroundMode(true)); 444 ASSERT_TRUE( 445 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 446 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 447 // Now close the BackgroundContents. 448 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_close")) << message_; 449 // Background mode should no longer be active. 450 ASSERT_TRUE(WaitForBackgroundMode(false)); 451 ASSERT_FALSE( 452 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 453 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 454 } 455 456 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, UnloadExtensionWhileHidden) { 457 host_resolver()->AddRule("a.com", "127.0.0.1"); 458 ASSERT_TRUE(StartEmbeddedTestServer()); 459 460 std::string app_manifest = base::StringPrintf( 461 "{" 462 " \"name\": \"App\"," 463 " \"version\": \"0.1\"," 464 " \"manifest_version\": 2," 465 " \"app\": {" 466 " \"urls\": [" 467 " \"http://a.com/\"" 468 " ]," 469 " \"launch\": {" 470 " \"web_url\": \"http://a.com:%d/\"" 471 " }" 472 " }," 473 " \"permissions\": [\"background\"]," 474 " \"background\": {" 475 " \"page\": \"http://a.com:%d/test.html\"" 476 " }" 477 "}", 478 embedded_test_server()->port(), 479 embedded_test_server()->port()); 480 481 base::FilePath app_dir; 482 ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); 483 // Background mode should not be active now because no background app was 484 // loaded. 485 ASSERT_TRUE(LoadExtension(app_dir)); 486 // Background mode be active now because a background page was created when 487 // the app was loaded. 488 ASSERT_TRUE(WaitForBackgroundMode(true)); 489 490 const Extension* extension = GetSingleLoadedExtension(); 491 ASSERT_TRUE( 492 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())-> 493 GetAppBackgroundContents(ASCIIToUTF16(extension->id()))); 494 495 // Close all browsers - app should continue running. 496 set_exit_when_last_browser_closes(false); 497 CloseBrowser(browser()); 498 499 // Post a task to unload the extension - this should cause Chrome to exit 500 // cleanly (not crash). 501 UnloadExtensionViaTask(extension->id()); 502 content::RunAllPendingInMessageLoop(); 503 ASSERT_TRUE(WaitForBackgroundMode(false)); 504 } 505