1 // Copyright 2015 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.
7 #include "base/auto_reset.h"
8 #include "base/run_loop.h"
9 #include "base/scoped_observer.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/browser/extensions/extension_browsertest.h"
13 #include "chrome/browser/extensions/test_extension_dir.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "content/public/test/browser_test_utils.h"
17 #include "extensions/browser/process_manager.h"
18 #include "extensions/browser/process_manager_observer.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/test/extension_test_message_listener.h"
21 #include "net/dns/mock_host_resolver.h"
22 #include "net/test/embedded_test_server/embedded_test_server.h"
24 namespace extensions
{
29 // This uses single quotes for brevity, which will be replaced by double quotes
30 // when installing the extension.
32 // Expects a single string replacement of the "background" property, including
33 // trailing comma, or nothing if there is no background page.
34 const char* kManifestJson
=
37 " 'content_scripts': [{\n"
38 " 'js': ['content_script.js'],\n"
39 " 'matches': ['<all_urls>'],\n"
40 " 'run_at': 'document_start'\n"
42 " 'manifest_version': 2,\n"
43 " 'name': 'wake_event_page_apitest',\n"
49 // This content script just wakes the event page whenever it runs, then sends a
50 // chrome.test message with the result.
52 // Note: The wake-event-page function is exposed to content scripts via the
53 // chrome.test API for testing purposes only. In production its intended use
54 // case is from workers.
55 const char* kContentScriptJs
=
56 "chrome.test.getWakeEventPage()(function(success) {\n"
57 " chrome.test.sendMessage(success ? 'success' : 'failure');\n"
60 class BackgroundPageWatcher
: public ProcessManagerObserver
{
62 BackgroundPageWatcher(ProcessManager
* process_manager
,
63 const Extension
* extension
)
64 : process_manager_(process_manager
),
65 extension_id_(extension
->id()),
66 is_waiting_for_open_(false),
67 is_waiting_for_close_(false) {}
69 // Returns when the background page is open. If the background page is
70 // already open, returns immediately.
71 void WaitForOpen() { WaitForOpenState(true); }
73 // Returns when the background page is closed. If the background page is
74 // already closed, returns immediately.
75 void WaitForClose() { WaitForOpenState(false); }
78 // Returns when the background page has open state of |wait_for_open|. If the
79 // background page is already in that state, returns immediately.
80 void WaitForOpenState(bool wait_for_open
) {
81 if (IsBackgroundPageOpen() == wait_for_open
)
83 ScopedObserver
<ProcessManager
, ProcessManagerObserver
> observer(this);
84 observer
.Add(process_manager_
);
85 bool* flag
= wait_for_open
? &is_waiting_for_open_
: &is_waiting_for_close_
;
86 base::AutoReset
<bool> set_flag(flag
, true);
87 base::RunLoop run_loop
;
88 base::AutoReset
<base::Closure
> set_quit_run_loop(&quit_run_loop_
,
89 run_loop
.QuitClosure());
90 CHECK_EQ(wait_for_open
, IsBackgroundPageOpen());
93 bool IsBackgroundPageOpen() {
94 return process_manager_
->GetBackgroundHostForExtension(extension_id_
) !=
98 void OnBackgroundHostCreated(ExtensionHost
* host
) override
{
99 if (is_waiting_for_open_
&& host
->extension()->id() == extension_id_
)
100 quit_run_loop_
.Run();
103 void OnBackgroundHostClose(const std::string
& extension_id
) override
{
104 if (is_waiting_for_close_
&& extension_id
== extension_id_
)
105 quit_run_loop_
.Run();
108 ProcessManager
* const process_manager_
;
109 const std::string extension_id_
;
111 base::Closure quit_run_loop_
;
112 bool is_waiting_for_open_
;
113 bool is_waiting_for_close_
;
115 DISALLOW_COPY_AND_ASSIGN(BackgroundPageWatcher
);
118 class WakeEventPageTest
: public ExtensionBrowserTest
{
120 WakeEventPageTest() {}
123 enum BackgroundPageConfiguration
{ EVENT
, PERSISTENT
, NONE
};
125 void RunTest(bool expect_success
,
126 BackgroundPageConfiguration bg_config
,
129 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
131 GURL web_url
= embedded_test_server()->GetURL("example.com", "/empty.html");
132 host_resolver()->AddRule(web_url
.host(), "127.0.0.1");
134 TestExtensionDir extension_dir
;
136 std::string manifest_json
;
140 base::StringPrintf(kManifestJson
,
142 " 'persistent': false,\n"
143 " 'scripts': ['background.js']\n"
148 base::StringPrintf(kManifestJson
,
150 " 'persistent': true,\n"
151 " 'scripts': ['background.js']\n"
155 manifest_json
= base::StringPrintf(kManifestJson
, "");
158 base::ReplaceChars(manifest_json
, "'", "\"", &manifest_json
);
159 extension_dir
.WriteManifest(manifest_json
);
160 // Empty background page. Closing/opening it is driven by this test.
161 extension_dir
.WriteFile(FILE_PATH_LITERAL("background.js"), "");
162 extension_dir
.WriteFile(FILE_PATH_LITERAL("content_script.js"),
166 // Install the extension, then close its background page if desired..
167 const Extension
* extension
= LoadExtension(extension_dir
.unpacked_path());
170 // Regardless of |will_be_open|, we haven't closed the background page yet,
171 // so it should always open if it exists.
172 if (bg_config
!= NONE
)
173 BackgroundPageWatcher(process_manager(), extension
).WaitForOpen();
176 GetBackgroundPage(extension
->id())->Close();
177 BackgroundPageWatcher(process_manager(), extension
).WaitForClose();
178 EXPECT_FALSE(GetBackgroundPage(extension
->id()));
181 // Start a content script to wake up the background page, if it's closed.
183 ExtensionTestMessageListener
listener(false /* will_reply */);
184 ui_test_utils::NavigateToURL(browser(), web_url
);
185 ASSERT_TRUE(listener
.WaitUntilSatisfied());
186 EXPECT_EQ(expect_success
? "success" : "failure", listener
.message());
189 EXPECT_EQ(will_be_open
, GetBackgroundPage(extension
->id()) != nullptr);
191 // Run the content script again. The background page will be awaken iff
192 // |will_be_open| is true, but if not, this is a harmless no-op.
194 ExtensionTestMessageListener
listener(false /* will_reply */);
195 ui_test_utils::NavigateToURL(browser(), web_url
);
196 ASSERT_TRUE(listener
.WaitUntilSatisfied());
197 EXPECT_EQ(expect_success
? "success" : "failure", listener
.message());
200 EXPECT_EQ(will_be_open
, GetBackgroundPage(extension
->id()) != nullptr);
204 ExtensionHost
* GetBackgroundPage(const std::string
& extension_id
) {
205 return process_manager()->GetBackgroundHostForExtension(extension_id
);
208 ProcessManager
* process_manager() { return ProcessManager::Get(profile()); }
210 DISALLOW_COPY_AND_ASSIGN(WakeEventPageTest
);
213 IN_PROC_BROWSER_TEST_F(WakeEventPageTest
, ClosedEventPage
) {
214 RunTest(true /* expect_success */, EVENT
, true /* should_close */,
215 true /* will_be_open */);
218 IN_PROC_BROWSER_TEST_F(WakeEventPageTest
, OpenEventPage
) {
219 RunTest(true /* expect_success */, EVENT
, false /* should_close */,
220 true /* will_be_open */);
223 IN_PROC_BROWSER_TEST_F(WakeEventPageTest
, ClosedPersistentBackgroundPage
) {
224 // Note: this is an odd test, because persistent background pages aren't
225 // supposed to close. Extensions can close them with window.close() but why
226 // would they do that? Test it anyway.
227 RunTest(false /* expect_success */, PERSISTENT
, true /* should_close */,
228 false /* will_be_open */);
231 IN_PROC_BROWSER_TEST_F(WakeEventPageTest
, OpenPersistentBackgroundPage
) {
232 RunTest(true /* expect_success */, PERSISTENT
, false /* should_close */,
233 true /* will_be_open */);
236 IN_PROC_BROWSER_TEST_F(WakeEventPageTest
, NoBackgroundPage
) {
237 RunTest(false /* expect_success */, NONE
, false /* should_close */,
238 false /* will_be_open */);
242 } // namespace extensions