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/location.h"
6 #include "base/path_service.h"
7 #include "base/single_thread_task_runner.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "chrome/browser/background/background_contents_service.h"
12 #include "chrome/browser/background/background_contents_service_factory.h"
13 #include "chrome/browser/background/background_mode_manager.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/extensions/extension_apitest.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_dialogs.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/extensions/application_launch.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/test/test_notification_tracker.h"
26 #include "content/public/test/test_utils.h"
27 #include "extensions/browser/process_manager.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/switches.h"
30 #include "extensions/test/extension_test_message_listener.h"
31 #include "net/dns/mock_host_resolver.h"
32 #include "net/test/embedded_test_server/embedded_test_server.h"
34 #if !defined(DISABLE_NACL)
35 #include "components/nacl/browser/nacl_process_host.h"
38 #if defined(OS_MACOSX)
39 #include "base/mac/scoped_nsautorelease_pool.h"
42 using base::ASCIIToUTF16
;
43 using extensions::Extension
;
45 class AppBackgroundPageApiTest
: public ExtensionApiTest
{
47 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
48 ExtensionApiTest::SetUpCommandLine(command_line
);
49 command_line
->AppendSwitch(switches::kDisablePopupBlocking
);
50 command_line
->AppendSwitch(extensions::switches::kAllowHTTPBackgroundPage
);
53 bool CreateApp(const std::string
& app_manifest
,
54 base::FilePath
* app_dir
) {
55 if (!app_dir_
.CreateUniqueTempDir()) {
56 LOG(ERROR
) << "Unable to create a temporary directory.";
59 base::FilePath manifest_path
= app_dir_
.path().AppendASCII("manifest.json");
60 int bytes_written
= base::WriteFile(manifest_path
,
63 if (bytes_written
!= static_cast<int>(app_manifest
.size())) {
64 LOG(ERROR
) << "Unable to write complete manifest to file. Return code="
68 *app_dir
= app_dir_
.path();
72 bool WaitForBackgroundMode(bool expected_background_mode
) {
73 #if defined(OS_CHROMEOS)
74 // BackgroundMode is not supported on chromeos, so we should test the
75 // behavior of BackgroundContents, but not the background mode state itself.
78 BackgroundModeManager
* manager
=
79 g_browser_process
->background_mode_manager();
80 // If background mode is disabled on this platform (e.g. cros), then skip
82 if (!manager
|| !manager
->IsBackgroundModePrefEnabled()) {
83 DLOG(WARNING
) << "Skipping check - background mode disabled";
86 if (manager
->IsBackgroundModeActive() == expected_background_mode
)
89 // We are not currently in the expected state - wait for the state to
91 content::WindowedNotificationObserver
watcher(
92 chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED
,
93 content::NotificationService::AllSources());
95 return manager
->IsBackgroundModeActive() == expected_background_mode
;
99 void UnloadExtensionViaTask(const std::string
& id
) {
100 base::ThreadTaskRunnerHandle::Get()->PostTask(
102 base::Bind(&AppBackgroundPageApiTest::UnloadExtension
, this, id
));
106 base::ScopedTempDir app_dir_
;
111 // Fixture to assist in testing v2 app background pages containing
112 // Native Client embeds.
113 class AppBackgroundPageNaClTest
: public AppBackgroundPageApiTest
{
115 AppBackgroundPageNaClTest()
116 : extension_(NULL
) {}
117 ~AppBackgroundPageNaClTest() override
{}
119 void SetUpOnMainThread() override
{
120 AppBackgroundPageApiTest::SetUpOnMainThread();
121 #if !defined(DISABLE_NACL)
122 nacl::NaClProcessHost::SetPpapiKeepAliveThrottleForTesting(50);
124 extensions::ProcessManager::SetEventPageIdleTimeForTesting(1000);
125 extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1000);
128 const Extension
* extension() { return extension_
; }
131 void LaunchTestingApp() {
132 base::FilePath app_dir
;
133 PathService::Get(chrome::DIR_GEN_TEST_DATA
, &app_dir
);
134 app_dir
= app_dir
.AppendASCII(
135 "ppapi/tests/extensions/background_keepalive/newlib");
136 extension_
= LoadExtension(app_dir
);
137 ASSERT_TRUE(extension_
);
141 const Extension
* extension_
;
144 // Produces an extensions::ProcessManager::ImpulseCallbackForTesting callback
145 // that will match a specified goal and can be waited on.
146 class ImpulseCallbackCounter
{
148 explicit ImpulseCallbackCounter(extensions::ProcessManager
* manager
,
149 const std::string
& extension_id
)
153 extension_id_(extension_id
) {
156 extensions::ProcessManager::ImpulseCallbackForTesting
157 SetGoalAndGetCallback(int goal
) {
160 message_loop_runner_
= new content::MessageLoopRunner();
161 return base::Bind(&ImpulseCallbackCounter::ImpulseCallback
,
162 base::Unretained(this),
163 message_loop_runner_
->QuitClosure(),
168 message_loop_runner_
->Run();
171 void ImpulseCallback(
172 const base::Closure
& quit_callback
,
173 const std::string
& extension_id_from_test
,
174 const std::string
& extension_id_from_manager
) {
175 if (extension_id_from_test
== extension_id_from_manager
) {
176 if (++observed_
>= goal_
) {
177 // Clear callback to free reference to message loop.
178 manager_
->SetKeepaliveImpulseCallbackForTesting(
179 extensions::ProcessManager::ImpulseCallbackForTesting());
180 manager_
->SetKeepaliveImpulseDecrementCallbackForTesting(
181 extensions::ProcessManager::ImpulseCallbackForTesting());
189 extensions::ProcessManager
* manager_
;
190 const std::string extension_id_
;
191 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
196 // Disable on Mac only. http://crbug.com/95139
197 #if defined(OS_MACOSX)
198 #define MAYBE_Basic DISABLED_Basic
200 #define MAYBE_Basic Basic
203 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, MAYBE_Basic
) {
204 host_resolver()->AddRule("a.com", "127.0.0.1");
205 ASSERT_TRUE(StartEmbeddedTestServer());
207 std::string app_manifest
= base::StringPrintf(
209 " \"name\": \"App\","
210 " \"version\": \"0.1\","
211 " \"manifest_version\": 2,"
217 " \"web_url\": \"http://a.com:%u/\""
220 " \"permissions\": [\"background\"]"
222 embedded_test_server()->port());
224 base::FilePath app_dir
;
225 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
226 ASSERT_TRUE(LoadExtension(app_dir
));
227 // Background mode should not be active until a background page is created.
228 ASSERT_TRUE(WaitForBackgroundMode(false));
229 ASSERT_TRUE(RunExtensionTest("app_background_page/basic")) << message_
;
230 // The test closes the background contents, so we should fall back to no
231 // background mode at the end.
232 ASSERT_TRUE(WaitForBackgroundMode(false));
235 // Crashy, http://crbug.com/69215.
236 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_LacksPermission
) {
237 host_resolver()->AddRule("a.com", "127.0.0.1");
238 ASSERT_TRUE(StartEmbeddedTestServer());
240 std::string app_manifest
= base::StringPrintf(
242 " \"name\": \"App\","
243 " \"version\": \"0.1\","
244 " \"manifest_version\": 2,"
250 " \"web_url\": \"http://a.com:%u/\""
254 embedded_test_server()->port());
256 base::FilePath app_dir
;
257 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
258 ASSERT_TRUE(LoadExtension(app_dir
));
259 ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission"))
261 ASSERT_TRUE(WaitForBackgroundMode(false));
264 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, ManifestBackgroundPage
) {
265 host_resolver()->AddRule("a.com", "127.0.0.1");
266 ASSERT_TRUE(StartEmbeddedTestServer());
268 std::string app_manifest
= base::StringPrintf(
270 " \"name\": \"App\","
271 " \"version\": \"0.1\","
272 " \"manifest_version\": 2,"
278 " \"web_url\": \"http://a.com:%u/\""
281 " \"permissions\": [\"background\"],"
283 " \"page\": \"http://a.com:%u/test.html\""
286 embedded_test_server()->port(),
287 embedded_test_server()->port());
289 base::FilePath app_dir
;
290 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
291 // Background mode should not be active now because no background app was
293 ASSERT_TRUE(LoadExtension(app_dir
));
294 // Background mode be active now because a background page was created when
295 // the app was loaded.
296 ASSERT_TRUE(WaitForBackgroundMode(true));
298 const Extension
* extension
= GetSingleLoadedExtension();
300 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
301 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
302 UnloadExtension(extension
->id());
305 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, NoJsBackgroundPage
) {
306 // Keep the task manager up through this test to verify that a crash doesn't
307 // happen when window.open creates a background page that switches
308 // RenderViewHosts. See http://crbug.com/165138.
309 chrome::ShowTaskManager(browser());
311 // Make sure that no BackgroundContentses get deleted (a signal that repeated
312 // window.open calls recreate instances, instead of being no-ops).
313 content::TestNotificationTracker background_deleted_tracker
;
314 background_deleted_tracker
.ListenFor(
315 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED
,
316 content::Source
<Profile
>(browser()->profile()));
318 host_resolver()->AddRule("a.com", "127.0.0.1");
319 ASSERT_TRUE(StartEmbeddedTestServer());
321 std::string app_manifest
= base::StringPrintf(
323 " \"name\": \"App\","
324 " \"version\": \"0.1\","
325 " \"manifest_version\": 2,"
331 " \"web_url\": \"http://a.com:%u/test.html\""
334 " \"permissions\": [\"background\"],"
336 " \"allow_js_access\": false"
339 embedded_test_server()->port());
341 base::FilePath app_dir
;
342 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
343 ASSERT_TRUE(LoadExtension(app_dir
));
345 // There isn't a background page loaded initially.
346 const Extension
* extension
= GetSingleLoadedExtension();
348 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
349 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
350 // The test makes sure that window.open returns null.
351 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js")) << message_
;
352 // And after it runs there should be a background page.
354 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
355 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
357 EXPECT_EQ(0u, background_deleted_tracker
.size());
358 UnloadExtension(extension
->id());
361 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, NoJsManifestBackgroundPage
) {
362 host_resolver()->AddRule("a.com", "127.0.0.1");
363 ASSERT_TRUE(StartEmbeddedTestServer());
365 std::string app_manifest
= base::StringPrintf(
367 " \"name\": \"App\","
368 " \"version\": \"0.1\","
369 " \"manifest_version\": 2,"
375 " \"web_url\": \"http://a.com:%u/\""
378 " \"permissions\": [\"background\"],"
380 " \"page\": \"http://a.com:%u/bg.html\","
381 " \"allow_js_access\": false"
384 embedded_test_server()->port(),
385 embedded_test_server()->port());
387 base::FilePath app_dir
;
388 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
389 ASSERT_TRUE(LoadExtension(app_dir
));
391 // The background page should load, but window.open should return null.
392 const Extension
* extension
= GetSingleLoadedExtension();
394 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
395 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
396 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js_manifest")) <<
398 UnloadExtension(extension
->id());
401 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, OpenTwoBackgroundPages
) {
402 host_resolver()->AddRule("a.com", "127.0.0.1");
403 ASSERT_TRUE(StartEmbeddedTestServer());
405 std::string app_manifest
= base::StringPrintf(
407 " \"name\": \"App\","
408 " \"version\": \"0.1\","
409 " \"manifest_version\": 2,"
415 " \"web_url\": \"http://a.com:%u/\""
418 " \"permissions\": [\"background\"]"
420 embedded_test_server()->port());
422 base::FilePath app_dir
;
423 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
424 ASSERT_TRUE(LoadExtension(app_dir
));
425 const Extension
* extension
= GetSingleLoadedExtension();
426 ASSERT_TRUE(RunExtensionTest("app_background_page/two_pages")) << message_
;
427 UnloadExtension(extension
->id());
430 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, OpenTwoPagesWithManifest
) {
431 host_resolver()->AddRule("a.com", "127.0.0.1");
432 ASSERT_TRUE(StartEmbeddedTestServer());
434 std::string app_manifest
= base::StringPrintf(
436 " \"name\": \"App\","
437 " \"version\": \"0.1\","
438 " \"manifest_version\": 2,"
444 " \"web_url\": \"http://a.com:%u/\""
448 " \"page\": \"http://a.com:%u/bg.html\""
450 " \"permissions\": [\"background\"]"
452 embedded_test_server()->port(),
453 embedded_test_server()->port());
455 base::FilePath app_dir
;
456 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
457 ASSERT_TRUE(LoadExtension(app_dir
));
458 const Extension
* extension
= GetSingleLoadedExtension();
459 ASSERT_TRUE(RunExtensionTest("app_background_page/two_with_manifest")) <<
461 UnloadExtension(extension
->id());
464 // Times out occasionally -- see crbug.com/108493
465 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_OpenPopupFromBGPage
) {
466 host_resolver()->AddRule("a.com", "127.0.0.1");
467 ASSERT_TRUE(StartEmbeddedTestServer());
469 std::string app_manifest
= base::StringPrintf(
471 " \"name\": \"App\","
472 " \"version\": \"0.1\","
473 " \"manifest_version\": 2,"
479 " \"web_url\": \"http://a.com:%u/\""
482 " \"background\": { \"page\": \"http://a.com:%u/extensions/api_test/"
483 "app_background_page/bg_open/bg_open_bg.html\" },"
484 " \"permissions\": [\"background\"]"
486 embedded_test_server()->port(),
487 embedded_test_server()->port());
489 base::FilePath app_dir
;
490 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
491 ASSERT_TRUE(LoadExtension(app_dir
));
492 ASSERT_TRUE(RunExtensionTest("app_background_page/bg_open")) << message_
;
495 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_OpenThenClose
) {
496 host_resolver()->AddRule("a.com", "127.0.0.1");
497 ASSERT_TRUE(StartEmbeddedTestServer());
499 std::string app_manifest
= base::StringPrintf(
501 " \"name\": \"App\","
502 " \"version\": \"0.1\","
503 " \"manifest_version\": 2,"
509 " \"web_url\": \"http://a.com:%u/\""
512 " \"permissions\": [\"background\"]"
514 embedded_test_server()->port());
516 base::FilePath app_dir
;
517 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
518 ASSERT_TRUE(LoadExtension(app_dir
));
519 // There isn't a background page loaded initially.
520 const Extension
* extension
= GetSingleLoadedExtension();
522 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
523 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
524 // Background mode should not be active until a background page is created.
525 ASSERT_TRUE(WaitForBackgroundMode(false));
526 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_open")) << message_
;
527 // Background mode should be active now because a background page was created.
528 ASSERT_TRUE(WaitForBackgroundMode(true));
530 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
531 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
532 // Now close the BackgroundContents.
533 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_close")) << message_
;
534 // Background mode should no longer be active.
535 ASSERT_TRUE(WaitForBackgroundMode(false));
537 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
538 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
541 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, UnloadExtensionWhileHidden
) {
542 host_resolver()->AddRule("a.com", "127.0.0.1");
543 ASSERT_TRUE(StartEmbeddedTestServer());
545 std::string app_manifest
= base::StringPrintf(
547 " \"name\": \"App\","
548 " \"version\": \"0.1\","
549 " \"manifest_version\": 2,"
555 " \"web_url\": \"http://a.com:%u/\""
558 " \"permissions\": [\"background\"],"
560 " \"page\": \"http://a.com:%u/test.html\""
563 embedded_test_server()->port(),
564 embedded_test_server()->port());
566 base::FilePath app_dir
;
567 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
568 // Background mode should not be active now because no background app was
570 ASSERT_TRUE(LoadExtension(app_dir
));
571 // Background mode be active now because a background page was created when
572 // the app was loaded.
573 ASSERT_TRUE(WaitForBackgroundMode(true));
575 const Extension
* extension
= GetSingleLoadedExtension();
577 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
578 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
580 // Close all browsers - app should continue running.
581 set_exit_when_last_browser_closes(false);
582 CloseBrowserSynchronously(browser());
584 // Post a task to unload the extension - this should cause Chrome to exit
585 // cleanly (not crash).
586 UnloadExtensionViaTask(extension
->id());
587 content::RunAllPendingInMessageLoop();
588 ASSERT_TRUE(WaitForBackgroundMode(false));
591 // Verify active NaCl embeds cause many keepalive impulses to be sent.
592 // Disabled on Windows due to flakiness: http://crbug.com/346278
594 #define MAYBE_BackgroundKeepaliveActive DISABLED_BackgroundKeepaliveActive
596 // Disabling other platforms too since the test started failing
597 // consistently. http://crbug.com/490440
598 #define MAYBE_BackgroundKeepaliveActive DISABLED_BackgroundKeepaliveActive
600 IN_PROC_BROWSER_TEST_F(AppBackgroundPageNaClTest
,
601 MAYBE_BackgroundKeepaliveActive
) {
602 #if !defined(DISABLE_NACL)
603 ExtensionTestMessageListener
nacl_modules_loaded("nacl_modules_loaded", true);
605 extensions::ProcessManager
* manager
=
606 extensions::ProcessManager::Get(browser()->profile());
607 ImpulseCallbackCounter
active_impulse_counter(manager
, extension()->id());
608 EXPECT_TRUE(nacl_modules_loaded
.WaitUntilSatisfied());
610 // Target .5 seconds: .5 seconds / 50ms throttle * 2 embeds == 20 impulses.
611 manager
->SetKeepaliveImpulseCallbackForTesting(
612 active_impulse_counter
.SetGoalAndGetCallback(20));
613 active_impulse_counter
.Wait();
617 // Verify that nacl modules that go idle will not send keepalive impulses.
618 // Disabled on windows due to Win XP failures:
619 // DesktopWindowTreeHostWin::HandleCreate not implemented. crbug.com/331954
621 #define MAYBE_BackgroundKeepaliveIdle DISABLED_BackgroundKeepaliveIdle
623 // ASAN errors appearing: https://crbug.com/332440
624 #define MAYBE_BackgroundKeepaliveIdle DISABLED_BackgroundKeepaliveIdle
626 IN_PROC_BROWSER_TEST_F(AppBackgroundPageNaClTest
,
627 MAYBE_BackgroundKeepaliveIdle
) {
628 #if !defined(DISABLE_NACL)
629 ExtensionTestMessageListener
nacl_modules_loaded("nacl_modules_loaded", true);
631 extensions::ProcessManager
* manager
=
632 extensions::ProcessManager::Get(browser()->profile());
633 ImpulseCallbackCounter
idle_impulse_counter(manager
, extension()->id());
634 EXPECT_TRUE(nacl_modules_loaded
.WaitUntilSatisfied());
636 manager
->SetKeepaliveImpulseDecrementCallbackForTesting(
637 idle_impulse_counter
.SetGoalAndGetCallback(1));
638 nacl_modules_loaded
.Reply("be idle");
639 idle_impulse_counter
.Wait();