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.
5 #include "base/path_service.h"
6 #include "base/strings/stringprintf.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/background/background_contents_service.h"
9 #include "chrome/browser/background/background_contents_service_factory.h"
10 #include "chrome/browser/background/background_mode_manager.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/extensions/extension_apitest.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_dialogs.h"
18 #include "chrome/browser/ui/browser_window.h"
19 #include "chrome/browser/ui/extensions/application_launch.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/test/test_notification_tracker.h"
24 #include "content/public/test/test_utils.h"
25 #include "extensions/browser/process_manager.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/switches.h"
28 #include "extensions/test/extension_test_message_listener.h"
29 #include "net/dns/mock_host_resolver.h"
30 #include "net/test/embedded_test_server/embedded_test_server.h"
32 #if !defined(DISABLE_NACL)
33 #include "components/nacl/browser/nacl_process_host.h"
36 #if defined(OS_MACOSX)
37 #include "base/mac/scoped_nsautorelease_pool.h"
40 using base::ASCIIToUTF16
;
41 using extensions::Extension
;
43 class AppBackgroundPageApiTest
: public ExtensionApiTest
{
45 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
46 ExtensionApiTest::SetUpCommandLine(command_line
);
47 command_line
->AppendSwitch(switches::kDisablePopupBlocking
);
48 command_line
->AppendSwitch(extensions::switches::kAllowHTTPBackgroundPage
);
51 bool CreateApp(const std::string
& app_manifest
,
52 base::FilePath
* app_dir
) {
53 if (!app_dir_
.CreateUniqueTempDir()) {
54 LOG(ERROR
) << "Unable to create a temporary directory.";
57 base::FilePath manifest_path
= app_dir_
.path().AppendASCII("manifest.json");
58 int bytes_written
= base::WriteFile(manifest_path
,
61 if (bytes_written
!= static_cast<int>(app_manifest
.size())) {
62 LOG(ERROR
) << "Unable to write complete manifest to file. Return code="
66 *app_dir
= app_dir_
.path();
70 bool WaitForBackgroundMode(bool expected_background_mode
) {
71 #if defined(OS_CHROMEOS)
72 // BackgroundMode is not supported on chromeos, so we should test the
73 // behavior of BackgroundContents, but not the background mode state itself.
76 BackgroundModeManager
* manager
=
77 g_browser_process
->background_mode_manager();
78 // If background mode is disabled on this platform (e.g. cros), then skip
80 if (!manager
|| !manager
->IsBackgroundModePrefEnabled()) {
81 DLOG(WARNING
) << "Skipping check - background mode disabled";
84 if (manager
->IsBackgroundModeActive() == expected_background_mode
)
87 // We are not currently in the expected state - wait for the state to
89 content::WindowedNotificationObserver
watcher(
90 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED
,
91 content::NotificationService::AllSources());
93 return manager
->IsBackgroundModeActive() == expected_background_mode
;
97 void CloseBrowser(Browser
* browser
) {
98 content::WindowedNotificationObserver
observer(
99 chrome::NOTIFICATION_BROWSER_CLOSED
,
100 content::NotificationService::AllSources());
101 browser
->window()->Close();
102 #if defined(OS_MACOSX)
103 // BrowserWindowController depends on the auto release pool being recycled
104 // in the message loop to delete itself, which frees the Browser object
105 // which fires this event.
106 AutoreleasePool()->Recycle();
111 void UnloadExtensionViaTask(const std::string
& id
) {
112 base::MessageLoop::current()->PostTask(
114 base::Bind(&AppBackgroundPageApiTest::UnloadExtension
, this, id
));
118 base::ScopedTempDir app_dir_
;
123 // Fixture to assist in testing v2 app background pages containing
124 // Native Client embeds.
125 class AppBackgroundPageNaClTest
: public AppBackgroundPageApiTest
{
127 AppBackgroundPageNaClTest()
128 : extension_(NULL
) {}
129 ~AppBackgroundPageNaClTest() override
{}
131 void SetUpOnMainThread() override
{
132 AppBackgroundPageApiTest::SetUpOnMainThread();
133 #if !defined(DISABLE_NACL)
134 nacl::NaClProcessHost::SetPpapiKeepAliveThrottleForTesting(50);
136 extensions::ProcessManager::SetEventPageIdleTimeForTesting(1000);
137 extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1000);
140 const Extension
* extension() { return extension_
; }
143 void LaunchTestingApp() {
144 base::FilePath app_dir
;
145 PathService::Get(chrome::DIR_GEN_TEST_DATA
, &app_dir
);
146 app_dir
= app_dir
.AppendASCII(
147 "ppapi/tests/extensions/background_keepalive/newlib");
148 extension_
= LoadExtension(app_dir
);
149 ASSERT_TRUE(extension_
);
153 const Extension
* extension_
;
156 // Produces an extensions::ProcessManager::ImpulseCallbackForTesting callback
157 // that will match a specified goal and can be waited on.
158 class ImpulseCallbackCounter
{
160 explicit ImpulseCallbackCounter(extensions::ProcessManager
* manager
,
161 const std::string
& extension_id
)
165 extension_id_(extension_id
) {
168 extensions::ProcessManager::ImpulseCallbackForTesting
169 SetGoalAndGetCallback(int goal
) {
172 message_loop_runner_
= new content::MessageLoopRunner();
173 return base::Bind(&ImpulseCallbackCounter::ImpulseCallback
,
174 base::Unretained(this),
175 message_loop_runner_
->QuitClosure(),
180 message_loop_runner_
->Run();
183 void ImpulseCallback(
184 const base::Closure
& quit_callback
,
185 const std::string
& extension_id_from_test
,
186 const std::string
& extension_id_from_manager
) {
187 if (extension_id_from_test
== extension_id_from_manager
) {
188 if (++observed_
>= goal_
) {
189 // Clear callback to free reference to message loop.
190 manager_
->SetKeepaliveImpulseCallbackForTesting(
191 extensions::ProcessManager::ImpulseCallbackForTesting());
192 manager_
->SetKeepaliveImpulseDecrementCallbackForTesting(
193 extensions::ProcessManager::ImpulseCallbackForTesting());
201 extensions::ProcessManager
* manager_
;
202 const std::string extension_id_
;
203 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
208 // Disable on Mac only. http://crbug.com/95139
209 #if defined(OS_MACOSX)
210 #define MAYBE_Basic DISABLED_Basic
212 #define MAYBE_Basic Basic
215 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, MAYBE_Basic
) {
216 host_resolver()->AddRule("a.com", "127.0.0.1");
217 ASSERT_TRUE(StartEmbeddedTestServer());
219 std::string app_manifest
= base::StringPrintf(
221 " \"name\": \"App\","
222 " \"version\": \"0.1\","
223 " \"manifest_version\": 2,"
229 " \"web_url\": \"http://a.com:%u/\""
232 " \"permissions\": [\"background\"]"
234 embedded_test_server()->port());
236 base::FilePath app_dir
;
237 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
238 ASSERT_TRUE(LoadExtension(app_dir
));
239 // Background mode should not be active until a background page is created.
240 ASSERT_TRUE(WaitForBackgroundMode(false));
241 ASSERT_TRUE(RunExtensionTest("app_background_page/basic")) << message_
;
242 // The test closes the background contents, so we should fall back to no
243 // background mode at the end.
244 ASSERT_TRUE(WaitForBackgroundMode(false));
247 // Crashy, http://crbug.com/69215.
248 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_LacksPermission
) {
249 host_resolver()->AddRule("a.com", "127.0.0.1");
250 ASSERT_TRUE(StartEmbeddedTestServer());
252 std::string app_manifest
= base::StringPrintf(
254 " \"name\": \"App\","
255 " \"version\": \"0.1\","
256 " \"manifest_version\": 2,"
262 " \"web_url\": \"http://a.com:%u/\""
266 embedded_test_server()->port());
268 base::FilePath app_dir
;
269 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
270 ASSERT_TRUE(LoadExtension(app_dir
));
271 ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission"))
273 ASSERT_TRUE(WaitForBackgroundMode(false));
276 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, ManifestBackgroundPage
) {
277 host_resolver()->AddRule("a.com", "127.0.0.1");
278 ASSERT_TRUE(StartEmbeddedTestServer());
280 std::string app_manifest
= base::StringPrintf(
282 " \"name\": \"App\","
283 " \"version\": \"0.1\","
284 " \"manifest_version\": 2,"
290 " \"web_url\": \"http://a.com:%u/\""
293 " \"permissions\": [\"background\"],"
295 " \"page\": \"http://a.com:%u/test.html\""
298 embedded_test_server()->port(),
299 embedded_test_server()->port());
301 base::FilePath app_dir
;
302 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
303 // Background mode should not be active now because no background app was
305 ASSERT_TRUE(LoadExtension(app_dir
));
306 // Background mode be active now because a background page was created when
307 // the app was loaded.
308 ASSERT_TRUE(WaitForBackgroundMode(true));
310 const Extension
* extension
= GetSingleLoadedExtension();
312 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
313 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
314 UnloadExtension(extension
->id());
317 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, NoJsBackgroundPage
) {
318 // Keep the task manager up through this test to verify that a crash doesn't
319 // happen when window.open creates a background page that switches
320 // RenderViewHosts. See http://crbug.com/165138.
321 chrome::ShowTaskManager(browser());
323 // Make sure that no BackgroundContentses get deleted (a signal that repeated
324 // window.open calls recreate instances, instead of being no-ops).
325 content::TestNotificationTracker background_deleted_tracker
;
326 background_deleted_tracker
.ListenFor(
327 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED
,
328 content::Source
<Profile
>(browser()->profile()));
330 host_resolver()->AddRule("a.com", "127.0.0.1");
331 ASSERT_TRUE(StartEmbeddedTestServer());
333 std::string app_manifest
= base::StringPrintf(
335 " \"name\": \"App\","
336 " \"version\": \"0.1\","
337 " \"manifest_version\": 2,"
343 " \"web_url\": \"http://a.com:%u/test.html\""
346 " \"permissions\": [\"background\"],"
348 " \"allow_js_access\": false"
351 embedded_test_server()->port());
353 base::FilePath app_dir
;
354 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
355 ASSERT_TRUE(LoadExtension(app_dir
));
357 // There isn't a background page loaded initially.
358 const Extension
* extension
= GetSingleLoadedExtension();
360 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
361 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
362 // The test makes sure that window.open returns null.
363 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js")) << message_
;
364 // And after it runs there should be a background page.
366 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
367 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
369 EXPECT_EQ(0u, background_deleted_tracker
.size());
370 UnloadExtension(extension
->id());
373 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, NoJsManifestBackgroundPage
) {
374 host_resolver()->AddRule("a.com", "127.0.0.1");
375 ASSERT_TRUE(StartEmbeddedTestServer());
377 std::string app_manifest
= base::StringPrintf(
379 " \"name\": \"App\","
380 " \"version\": \"0.1\","
381 " \"manifest_version\": 2,"
387 " \"web_url\": \"http://a.com:%u/\""
390 " \"permissions\": [\"background\"],"
392 " \"page\": \"http://a.com:%u/bg.html\","
393 " \"allow_js_access\": false"
396 embedded_test_server()->port(),
397 embedded_test_server()->port());
399 base::FilePath app_dir
;
400 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
401 ASSERT_TRUE(LoadExtension(app_dir
));
403 // The background page should load, but window.open should return null.
404 const Extension
* extension
= GetSingleLoadedExtension();
406 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
407 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
408 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js_manifest")) <<
410 UnloadExtension(extension
->id());
413 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, OpenTwoBackgroundPages
) {
414 host_resolver()->AddRule("a.com", "127.0.0.1");
415 ASSERT_TRUE(StartEmbeddedTestServer());
417 std::string app_manifest
= base::StringPrintf(
419 " \"name\": \"App\","
420 " \"version\": \"0.1\","
421 " \"manifest_version\": 2,"
427 " \"web_url\": \"http://a.com:%u/\""
430 " \"permissions\": [\"background\"]"
432 embedded_test_server()->port());
434 base::FilePath app_dir
;
435 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
436 ASSERT_TRUE(LoadExtension(app_dir
));
437 const Extension
* extension
= GetSingleLoadedExtension();
438 ASSERT_TRUE(RunExtensionTest("app_background_page/two_pages")) << message_
;
439 UnloadExtension(extension
->id());
442 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, OpenTwoPagesWithManifest
) {
443 host_resolver()->AddRule("a.com", "127.0.0.1");
444 ASSERT_TRUE(StartEmbeddedTestServer());
446 std::string app_manifest
= base::StringPrintf(
448 " \"name\": \"App\","
449 " \"version\": \"0.1\","
450 " \"manifest_version\": 2,"
456 " \"web_url\": \"http://a.com:%u/\""
460 " \"page\": \"http://a.com:%u/bg.html\""
462 " \"permissions\": [\"background\"]"
464 embedded_test_server()->port(),
465 embedded_test_server()->port());
467 base::FilePath app_dir
;
468 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
469 ASSERT_TRUE(LoadExtension(app_dir
));
470 const Extension
* extension
= GetSingleLoadedExtension();
471 ASSERT_TRUE(RunExtensionTest("app_background_page/two_with_manifest")) <<
473 UnloadExtension(extension
->id());
476 // Times out occasionally -- see crbug.com/108493
477 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_OpenPopupFromBGPage
) {
478 host_resolver()->AddRule("a.com", "127.0.0.1");
479 ASSERT_TRUE(StartEmbeddedTestServer());
481 std::string app_manifest
= base::StringPrintf(
483 " \"name\": \"App\","
484 " \"version\": \"0.1\","
485 " \"manifest_version\": 2,"
491 " \"web_url\": \"http://a.com:%u/\""
494 " \"background\": { \"page\": \"http://a.com:%u/extensions/api_test/"
495 "app_background_page/bg_open/bg_open_bg.html\" },"
496 " \"permissions\": [\"background\"]"
498 embedded_test_server()->port(),
499 embedded_test_server()->port());
501 base::FilePath app_dir
;
502 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
503 ASSERT_TRUE(LoadExtension(app_dir
));
504 ASSERT_TRUE(RunExtensionTest("app_background_page/bg_open")) << message_
;
507 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_OpenThenClose
) {
508 host_resolver()->AddRule("a.com", "127.0.0.1");
509 ASSERT_TRUE(StartEmbeddedTestServer());
511 std::string app_manifest
= base::StringPrintf(
513 " \"name\": \"App\","
514 " \"version\": \"0.1\","
515 " \"manifest_version\": 2,"
521 " \"web_url\": \"http://a.com:%u/\""
524 " \"permissions\": [\"background\"]"
526 embedded_test_server()->port());
528 base::FilePath app_dir
;
529 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
530 ASSERT_TRUE(LoadExtension(app_dir
));
531 // There isn't a background page loaded initially.
532 const Extension
* extension
= GetSingleLoadedExtension();
534 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
535 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
536 // Background mode should not be active until a background page is created.
537 ASSERT_TRUE(WaitForBackgroundMode(false));
538 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_open")) << message_
;
539 // Background mode should be active now because a background page was created.
540 ASSERT_TRUE(WaitForBackgroundMode(true));
542 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
543 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
544 // Now close the BackgroundContents.
545 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_close")) << message_
;
546 // Background mode should no longer be active.
547 ASSERT_TRUE(WaitForBackgroundMode(false));
549 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
550 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
553 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, UnloadExtensionWhileHidden
) {
554 host_resolver()->AddRule("a.com", "127.0.0.1");
555 ASSERT_TRUE(StartEmbeddedTestServer());
557 std::string app_manifest
= base::StringPrintf(
559 " \"name\": \"App\","
560 " \"version\": \"0.1\","
561 " \"manifest_version\": 2,"
567 " \"web_url\": \"http://a.com:%u/\""
570 " \"permissions\": [\"background\"],"
572 " \"page\": \"http://a.com:%u/test.html\""
575 embedded_test_server()->port(),
576 embedded_test_server()->port());
578 base::FilePath app_dir
;
579 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
580 // Background mode should not be active now because no background app was
582 ASSERT_TRUE(LoadExtension(app_dir
));
583 // Background mode be active now because a background page was created when
584 // the app was loaded.
585 ASSERT_TRUE(WaitForBackgroundMode(true));
587 const Extension
* extension
= GetSingleLoadedExtension();
589 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
590 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
592 // Close all browsers - app should continue running.
593 set_exit_when_last_browser_closes(false);
594 CloseBrowser(browser());
596 // Post a task to unload the extension - this should cause Chrome to exit
597 // cleanly (not crash).
598 UnloadExtensionViaTask(extension
->id());
599 content::RunAllPendingInMessageLoop();
600 ASSERT_TRUE(WaitForBackgroundMode(false));
603 // Verify active NaCl embeds cause many keepalive impulses to be sent.
604 // Disabled on Windows due to flakiness: http://crbug.com/346278
606 #define MAYBE_BackgroundKeepaliveActive DISABLED_BackgroundKeepaliveActive
608 #define MAYBE_BackgroundKeepaliveActive BackgroundKeepaliveActive
610 IN_PROC_BROWSER_TEST_F(AppBackgroundPageNaClTest
,
611 MAYBE_BackgroundKeepaliveActive
) {
612 #if !defined(DISABLE_NACL)
613 ExtensionTestMessageListener
nacl_modules_loaded("nacl_modules_loaded", true);
615 extensions::ProcessManager
* manager
=
616 extensions::ProcessManager::Get(browser()->profile());
617 ImpulseCallbackCounter
active_impulse_counter(manager
, extension()->id());
618 EXPECT_TRUE(nacl_modules_loaded
.WaitUntilSatisfied());
620 // Target .5 seconds: .5 seconds / 50ms throttle * 2 embeds == 20 impulses.
621 manager
->SetKeepaliveImpulseCallbackForTesting(
622 active_impulse_counter
.SetGoalAndGetCallback(20));
623 active_impulse_counter
.Wait();
627 // Verify that nacl modules that go idle will not send keepalive impulses.
628 // Disabled on windows due to Win XP failures:
629 // DesktopWindowTreeHostWin::HandleCreate not implemented. crbug.com/331954
631 #define MAYBE_BackgroundKeepaliveIdle DISABLED_BackgroundKeepaliveIdle
633 // ASAN errors appearing: https://crbug.com/332440
634 #define MAYBE_BackgroundKeepaliveIdle DISABLED_BackgroundKeepaliveIdle
636 IN_PROC_BROWSER_TEST_F(AppBackgroundPageNaClTest
,
637 MAYBE_BackgroundKeepaliveIdle
) {
638 #if !defined(DISABLE_NACL)
639 ExtensionTestMessageListener
nacl_modules_loaded("nacl_modules_loaded", true);
641 extensions::ProcessManager
* manager
=
642 extensions::ProcessManager::Get(browser()->profile());
643 ImpulseCallbackCounter
idle_impulse_counter(manager
, extension()->id());
644 EXPECT_TRUE(nacl_modules_loaded
.WaitUntilSatisfied());
646 manager
->SetKeepaliveImpulseDecrementCallbackForTesting(
647 idle_impulse_counter
.SetGoalAndGetCallback(1));
648 nacl_modules_loaded
.Reply("be idle");
649 idle_impulse_counter
.Wait();