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