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 virtual void SetUpCommandLine(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 virtual ~AppBackgroundPageNaClTest() {
132 virtual void SetUpOnMainThread() override
{
133 AppBackgroundPageApiTest::SetUpOnMainThread();
134 #if !defined(DISABLE_NACL)
135 nacl::NaClProcessHost::SetPpapiKeepAliveThrottleForTesting(50);
137 extensions::ProcessManager::SetEventPageIdleTimeForTesting(1000);
138 extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1000);
141 const Extension
* extension() { return extension_
; }
144 void LaunchTestingApp() {
145 base::FilePath app_dir
;
146 PathService::Get(chrome::DIR_GEN_TEST_DATA
, &app_dir
);
147 app_dir
= app_dir
.AppendASCII(
148 "ppapi/tests/extensions/background_keepalive/newlib");
149 extension_
= LoadExtension(app_dir
);
150 ASSERT_TRUE(extension_
);
154 const Extension
* extension_
;
157 // Produces an extensions::ProcessManager::ImpulseCallbackForTesting callback
158 // that will match a specified goal and can be waited on.
159 class ImpulseCallbackCounter
{
161 explicit ImpulseCallbackCounter(extensions::ProcessManager
* manager
,
162 const std::string
& extension_id
)
166 extension_id_(extension_id
) {
169 extensions::ProcessManager::ImpulseCallbackForTesting
170 SetGoalAndGetCallback(int goal
) {
173 message_loop_runner_
= new content::MessageLoopRunner();
174 return base::Bind(&ImpulseCallbackCounter::ImpulseCallback
,
175 base::Unretained(this),
176 message_loop_runner_
->QuitClosure(),
181 message_loop_runner_
->Run();
184 void ImpulseCallback(
185 const base::Closure
& quit_callback
,
186 const std::string
& extension_id_from_test
,
187 const std::string
& extension_id_from_manager
) {
188 if (extension_id_from_test
== extension_id_from_manager
) {
189 if (++observed_
>= goal_
) {
190 // Clear callback to free reference to message loop.
191 manager_
->SetKeepaliveImpulseCallbackForTesting(
192 extensions::ProcessManager::ImpulseCallbackForTesting());
193 manager_
->SetKeepaliveImpulseDecrementCallbackForTesting(
194 extensions::ProcessManager::ImpulseCallbackForTesting());
202 extensions::ProcessManager
* manager_
;
203 const std::string extension_id_
;
204 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
209 // Disable on Mac only. http://crbug.com/95139
210 #if defined(OS_MACOSX)
211 #define MAYBE_Basic DISABLED_Basic
213 #define MAYBE_Basic Basic
216 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, MAYBE_Basic
) {
217 host_resolver()->AddRule("a.com", "127.0.0.1");
218 ASSERT_TRUE(StartEmbeddedTestServer());
220 std::string app_manifest
= base::StringPrintf(
222 " \"name\": \"App\","
223 " \"version\": \"0.1\","
224 " \"manifest_version\": 2,"
230 " \"web_url\": \"http://a.com:%d/\""
233 " \"permissions\": [\"background\"]"
235 embedded_test_server()->port());
237 base::FilePath app_dir
;
238 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
239 ASSERT_TRUE(LoadExtension(app_dir
));
240 // Background mode should not be active until a background page is created.
241 ASSERT_TRUE(WaitForBackgroundMode(false));
242 ASSERT_TRUE(RunExtensionTest("app_background_page/basic")) << message_
;
243 // The test closes the background contents, so we should fall back to no
244 // background mode at the end.
245 ASSERT_TRUE(WaitForBackgroundMode(false));
248 // Crashy, http://crbug.com/69215.
249 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_LacksPermission
) {
250 host_resolver()->AddRule("a.com", "127.0.0.1");
251 ASSERT_TRUE(StartEmbeddedTestServer());
253 std::string app_manifest
= base::StringPrintf(
255 " \"name\": \"App\","
256 " \"version\": \"0.1\","
257 " \"manifest_version\": 2,"
263 " \"web_url\": \"http://a.com:%d/\""
267 embedded_test_server()->port());
269 base::FilePath app_dir
;
270 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
271 ASSERT_TRUE(LoadExtension(app_dir
));
272 ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission"))
274 ASSERT_TRUE(WaitForBackgroundMode(false));
277 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, ManifestBackgroundPage
) {
278 host_resolver()->AddRule("a.com", "127.0.0.1");
279 ASSERT_TRUE(StartEmbeddedTestServer());
281 std::string app_manifest
= base::StringPrintf(
283 " \"name\": \"App\","
284 " \"version\": \"0.1\","
285 " \"manifest_version\": 2,"
291 " \"web_url\": \"http://a.com:%d/\""
294 " \"permissions\": [\"background\"],"
296 " \"page\": \"http://a.com:%d/test.html\""
299 embedded_test_server()->port(),
300 embedded_test_server()->port());
302 base::FilePath app_dir
;
303 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
304 // Background mode should not be active now because no background app was
306 ASSERT_TRUE(LoadExtension(app_dir
));
307 // Background mode be active now because a background page was created when
308 // the app was loaded.
309 ASSERT_TRUE(WaitForBackgroundMode(true));
311 const Extension
* extension
= GetSingleLoadedExtension();
313 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
314 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
315 UnloadExtension(extension
->id());
318 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, NoJsBackgroundPage
) {
319 // Keep the task manager up through this test to verify that a crash doesn't
320 // happen when window.open creates a background page that switches
321 // RenderViewHosts. See http://crbug.com/165138.
322 chrome::ShowTaskManager(browser());
324 // Make sure that no BackgroundContentses get deleted (a signal that repeated
325 // window.open calls recreate instances, instead of being no-ops).
326 content::TestNotificationTracker background_deleted_tracker
;
327 background_deleted_tracker
.ListenFor(
328 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED
,
329 content::Source
<Profile
>(browser()->profile()));
331 host_resolver()->AddRule("a.com", "127.0.0.1");
332 ASSERT_TRUE(StartEmbeddedTestServer());
334 std::string app_manifest
= base::StringPrintf(
336 " \"name\": \"App\","
337 " \"version\": \"0.1\","
338 " \"manifest_version\": 2,"
344 " \"web_url\": \"http://a.com:%d/test.html\""
347 " \"permissions\": [\"background\"],"
349 " \"allow_js_access\": false"
352 embedded_test_server()->port());
354 base::FilePath app_dir
;
355 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
356 ASSERT_TRUE(LoadExtension(app_dir
));
358 // There isn't a background page loaded initially.
359 const Extension
* extension
= GetSingleLoadedExtension();
361 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
362 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
363 // The test makes sure that window.open returns null.
364 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js")) << message_
;
365 // And after it runs there should be a background page.
367 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
368 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
370 EXPECT_EQ(0u, background_deleted_tracker
.size());
371 UnloadExtension(extension
->id());
374 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, NoJsManifestBackgroundPage
) {
375 host_resolver()->AddRule("a.com", "127.0.0.1");
376 ASSERT_TRUE(StartEmbeddedTestServer());
378 std::string app_manifest
= base::StringPrintf(
380 " \"name\": \"App\","
381 " \"version\": \"0.1\","
382 " \"manifest_version\": 2,"
388 " \"web_url\": \"http://a.com:%d/\""
391 " \"permissions\": [\"background\"],"
393 " \"page\": \"http://a.com:%d/bg.html\","
394 " \"allow_js_access\": false"
397 embedded_test_server()->port(),
398 embedded_test_server()->port());
400 base::FilePath app_dir
;
401 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
402 ASSERT_TRUE(LoadExtension(app_dir
));
404 // The background page should load, but window.open should return null.
405 const Extension
* extension
= GetSingleLoadedExtension();
407 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
408 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
409 ASSERT_TRUE(RunExtensionTest("app_background_page/no_js_manifest")) <<
411 UnloadExtension(extension
->id());
414 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, OpenTwoBackgroundPages
) {
415 host_resolver()->AddRule("a.com", "127.0.0.1");
416 ASSERT_TRUE(StartEmbeddedTestServer());
418 std::string app_manifest
= base::StringPrintf(
420 " \"name\": \"App\","
421 " \"version\": \"0.1\","
422 " \"manifest_version\": 2,"
428 " \"web_url\": \"http://a.com:%d/\""
431 " \"permissions\": [\"background\"]"
433 embedded_test_server()->port());
435 base::FilePath app_dir
;
436 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
437 ASSERT_TRUE(LoadExtension(app_dir
));
438 const Extension
* extension
= GetSingleLoadedExtension();
439 ASSERT_TRUE(RunExtensionTest("app_background_page/two_pages")) << message_
;
440 UnloadExtension(extension
->id());
443 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, OpenTwoPagesWithManifest
) {
444 host_resolver()->AddRule("a.com", "127.0.0.1");
445 ASSERT_TRUE(StartEmbeddedTestServer());
447 std::string app_manifest
= base::StringPrintf(
449 " \"name\": \"App\","
450 " \"version\": \"0.1\","
451 " \"manifest_version\": 2,"
457 " \"web_url\": \"http://a.com:%d/\""
461 " \"page\": \"http://a.com:%d/bg.html\""
463 " \"permissions\": [\"background\"]"
465 embedded_test_server()->port(),
466 embedded_test_server()->port());
468 base::FilePath app_dir
;
469 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
470 ASSERT_TRUE(LoadExtension(app_dir
));
471 const Extension
* extension
= GetSingleLoadedExtension();
472 ASSERT_TRUE(RunExtensionTest("app_background_page/two_with_manifest")) <<
474 UnloadExtension(extension
->id());
477 // Times out occasionally -- see crbug.com/108493
478 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_OpenPopupFromBGPage
) {
479 host_resolver()->AddRule("a.com", "127.0.0.1");
480 ASSERT_TRUE(StartEmbeddedTestServer());
482 std::string app_manifest
= base::StringPrintf(
484 " \"name\": \"App\","
485 " \"version\": \"0.1\","
486 " \"manifest_version\": 2,"
492 " \"web_url\": \"http://a.com:%d/\""
495 " \"background\": { \"page\": \"http://a.com:%d/extensions/api_test/"
496 "app_background_page/bg_open/bg_open_bg.html\" },"
497 " \"permissions\": [\"background\"]"
499 embedded_test_server()->port(),
500 embedded_test_server()->port());
502 base::FilePath app_dir
;
503 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
504 ASSERT_TRUE(LoadExtension(app_dir
));
505 ASSERT_TRUE(RunExtensionTest("app_background_page/bg_open")) << message_
;
508 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, DISABLED_OpenThenClose
) {
509 host_resolver()->AddRule("a.com", "127.0.0.1");
510 ASSERT_TRUE(StartEmbeddedTestServer());
512 std::string app_manifest
= base::StringPrintf(
514 " \"name\": \"App\","
515 " \"version\": \"0.1\","
516 " \"manifest_version\": 2,"
522 " \"web_url\": \"http://a.com:%d/\""
525 " \"permissions\": [\"background\"]"
527 embedded_test_server()->port());
529 base::FilePath app_dir
;
530 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
531 ASSERT_TRUE(LoadExtension(app_dir
));
532 // There isn't a background page loaded initially.
533 const Extension
* extension
= GetSingleLoadedExtension();
535 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
536 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
537 // Background mode should not be active until a background page is created.
538 ASSERT_TRUE(WaitForBackgroundMode(false));
539 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_open")) << message_
;
540 // Background mode should be active now because a background page was created.
541 ASSERT_TRUE(WaitForBackgroundMode(true));
543 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
544 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
545 // Now close the BackgroundContents.
546 ASSERT_TRUE(RunExtensionTest("app_background_page/basic_close")) << message_
;
547 // Background mode should no longer be active.
548 ASSERT_TRUE(WaitForBackgroundMode(false));
550 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
551 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
554 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest
, UnloadExtensionWhileHidden
) {
555 host_resolver()->AddRule("a.com", "127.0.0.1");
556 ASSERT_TRUE(StartEmbeddedTestServer());
558 std::string app_manifest
= base::StringPrintf(
560 " \"name\": \"App\","
561 " \"version\": \"0.1\","
562 " \"manifest_version\": 2,"
568 " \"web_url\": \"http://a.com:%d/\""
571 " \"permissions\": [\"background\"],"
573 " \"page\": \"http://a.com:%d/test.html\""
576 embedded_test_server()->port(),
577 embedded_test_server()->port());
579 base::FilePath app_dir
;
580 ASSERT_TRUE(CreateApp(app_manifest
, &app_dir
));
581 // Background mode should not be active now because no background app was
583 ASSERT_TRUE(LoadExtension(app_dir
));
584 // Background mode be active now because a background page was created when
585 // the app was loaded.
586 ASSERT_TRUE(WaitForBackgroundMode(true));
588 const Extension
* extension
= GetSingleLoadedExtension();
590 BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
591 GetAppBackgroundContents(ASCIIToUTF16(extension
->id())));
593 // Close all browsers - app should continue running.
594 set_exit_when_last_browser_closes(false);
595 CloseBrowser(browser());
597 // Post a task to unload the extension - this should cause Chrome to exit
598 // cleanly (not crash).
599 UnloadExtensionViaTask(extension
->id());
600 content::RunAllPendingInMessageLoop();
601 ASSERT_TRUE(WaitForBackgroundMode(false));
604 // Verify active NaCl embeds cause many keepalive impulses to be sent.
605 // Disabled on Windows due to flakiness: http://crbug.com/346278
607 #define MAYBE_BackgroundKeepaliveActive DISABLED_BackgroundKeepaliveActive
609 #define MAYBE_BackgroundKeepaliveActive BackgroundKeepaliveActive
611 IN_PROC_BROWSER_TEST_F(AppBackgroundPageNaClTest
,
612 MAYBE_BackgroundKeepaliveActive
) {
613 #if !defined(DISABLE_NACL)
614 ExtensionTestMessageListener
nacl_modules_loaded("nacl_modules_loaded", true);
616 extensions::ProcessManager
* manager
=
617 extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
618 ImpulseCallbackCounter
active_impulse_counter(manager
, extension()->id());
619 EXPECT_TRUE(nacl_modules_loaded
.WaitUntilSatisfied());
621 // Target .5 seconds: .5 seconds / 50ms throttle * 2 embeds == 20 impulses.
622 manager
->SetKeepaliveImpulseCallbackForTesting(
623 active_impulse_counter
.SetGoalAndGetCallback(20));
624 active_impulse_counter
.Wait();
628 // Verify that nacl modules that go idle will not send keepalive impulses.
629 // Disabled on windows due to Win XP failures:
630 // DesktopWindowTreeHostWin::HandleCreate not implemented. crbug.com/331954
632 #define MAYBE_BackgroundKeepaliveIdle DISABLED_BackgroundKeepaliveIdle
634 // ASAN errors appearing: https://crbug.com/332440
635 #define MAYBE_BackgroundKeepaliveIdle DISABLED_BackgroundKeepaliveIdle
637 IN_PROC_BROWSER_TEST_F(AppBackgroundPageNaClTest
,
638 MAYBE_BackgroundKeepaliveIdle
) {
639 #if !defined(DISABLE_NACL)
640 ExtensionTestMessageListener
nacl_modules_loaded("nacl_modules_loaded", true);
642 extensions::ProcessManager
* manager
=
643 extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
644 ImpulseCallbackCounter
idle_impulse_counter(manager
, extension()->id());
645 EXPECT_TRUE(nacl_modules_loaded
.WaitUntilSatisfied());
647 manager
->SetKeepaliveImpulseDecrementCallbackForTesting(
648 idle_impulse_counter
.SetGoalAndGetCallback(1));
649 nacl_modules_loaded
.Reply("be idle");
650 idle_impulse_counter
.Wait();