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.
4
5 #include <string>
6
7 #include "base/auto_reset.h"
8 #include "base/macros.h"
9 #include "base/run_loop.h"
10 #include "base/scoped_observer.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/extensions/extension_browsertest.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/test/browser_test.h"
18 #include "content/public/test/browser_test_utils.h"
19 #include "content/public/test/test_utils.h"
20 #include "extensions/browser/notification_types.h"
21 #include "extensions/browser/process_manager.h"
22 #include "extensions/browser/process_manager_observer.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/test/background_page_watcher.h"
25 #include "extensions/test/extension_test_message_listener.h"
26 #include "extensions/test/test_extension_dir.h"
27 #include "net/dns/mock_host_resolver.h"
28 #include "net/test/embedded_test_server/embedded_test_server.h"
29
30 namespace extensions {
31 namespace {
32
33 // manifest.json:
34 //
35 // This uses single quotes for brevity, which will be replaced by double quotes
36 // when installing the extension.
37 //
38 // Expects a single string replacement of the "background" property, including
39 // trailing comma, or nothing if there is no background page.
40 const char* kManifestJson =
41 "{\n"
42 " %s\n"
43 " 'content_scripts': [{\n"
44 " 'js': ['content_script.js'],\n"
45 " 'matches': ['<all_urls>'],\n"
46 " 'run_at': 'document_start'\n"
47 " }],\n"
48 " 'manifest_version': 2,\n"
49 " 'name': 'wake_event_page_apitest',\n"
50 " 'version': '1'\n"
51 "}\n";
52
53 // content_script.js:
54 //
55 // This content script just wakes the event page whenever it runs, then sends a
56 // chrome.test message with the result.
57 //
58 // Note: The wake-event-page function is exposed to content scripts via the
59 // chrome.test API for testing purposes only. In production its intended use
60 // case is from workers.
61 const char* kContentScriptJs =
62 "chrome.test.getWakeEventPage()(function(success) {\n"
63 " chrome.test.sendMessage(success ? 'success' : 'failure');\n"
64 "});\n";
65
66 class WakeEventPageTest : public ExtensionBrowserTest {
67 public:
WakeEventPageTest()68 WakeEventPageTest() {}
69
SetUpOnMainThread()70 void SetUpOnMainThread() override {
71 ExtensionBrowserTest::SetUpOnMainThread();
72 host_resolver()->AddRule("*", "127.0.0.1");
73 }
74
75 protected:
76 enum BackgroundPageConfiguration { EVENT, PERSISTENT, NONE };
77
RunTest(bool expect_success,BackgroundPageConfiguration bg_config,bool should_close,bool will_be_open)78 void RunTest(bool expect_success,
79 BackgroundPageConfiguration bg_config,
80 bool should_close,
81 bool will_be_open) {
82 ASSERT_TRUE(embedded_test_server()->Start());
83
84 GURL web_url = embedded_test_server()->GetURL("example.com", "/empty.html");
85
86 TestExtensionDir extension_dir;
87 {
88 std::string manifest_json;
89 switch (bg_config) {
90 case EVENT:
91 manifest_json =
92 base::StringPrintf(kManifestJson,
93 " 'background': {\n"
94 " 'persistent': false,\n"
95 " 'scripts': ['background.js']\n"
96 " },");
97 break;
98 case PERSISTENT:
99 manifest_json =
100 base::StringPrintf(kManifestJson,
101 " 'background': {\n"
102 " 'persistent': true,\n"
103 " 'scripts': ['background.js']\n"
104 " },");
105 break;
106 case NONE:
107 manifest_json = base::StringPrintf(kManifestJson, "");
108 break;
109 }
110 base::ReplaceChars(manifest_json, "'", "\"", &manifest_json);
111 extension_dir.WriteManifest(manifest_json);
112 // Empty background page. Closing/opening it is driven by this test.
113 extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "");
114 extension_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
115 kContentScriptJs);
116 }
117
118 // Install the extension, then close its background page if desired.
119 // TODO(https://crbug.com/898682): Waiting for content scripts to load
120 // should be done as part of the extension loading process.
121 content::WindowedNotificationObserver scripts_updated_observer(
122 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
123 content::NotificationService::AllSources());
124 const Extension* extension = LoadExtension(extension_dir.UnpackedPath());
125 scripts_updated_observer.Wait();
126 CHECK(extension);
127
128 // Regardless of |will_be_open|, we haven't closed the background page yet,
129 // so it should always open if it exists.
130 if (bg_config != NONE)
131 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
132
133 if (should_close) {
134 GetBackgroundPage(extension->id())->Close();
135 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
136 EXPECT_FALSE(GetBackgroundPage(extension->id()));
137 }
138
139 // Start a content script to wake up the background page, if it's closed.
140 {
141 ExtensionTestMessageListener listener(false /* will_reply */);
142 ui_test_utils::NavigateToURL(browser(), web_url);
143 ASSERT_TRUE(listener.WaitUntilSatisfied());
144 EXPECT_EQ(expect_success ? "success" : "failure", listener.message());
145 }
146
147 EXPECT_EQ(will_be_open, GetBackgroundPage(extension->id()) != nullptr);
148
149 // Run the content script again. The background page will be awaken iff
150 // |will_be_open| is true, but if not, this is a harmless no-op.
151 {
152 ExtensionTestMessageListener listener(false /* will_reply */);
153 ui_test_utils::NavigateToURL(browser(), web_url);
154 ASSERT_TRUE(listener.WaitUntilSatisfied());
155 EXPECT_EQ(expect_success ? "success" : "failure", listener.message());
156 }
157
158 EXPECT_EQ(will_be_open, GetBackgroundPage(extension->id()) != nullptr);
159 }
160
161 private:
GetBackgroundPage(const std::string & extension_id)162 ExtensionHost* GetBackgroundPage(const std::string& extension_id) {
163 return process_manager()->GetBackgroundHostForExtension(extension_id);
164 }
165
process_manager()166 ProcessManager* process_manager() { return ProcessManager::Get(profile()); }
167
168 DISALLOW_COPY_AND_ASSIGN(WakeEventPageTest);
169 };
170
IN_PROC_BROWSER_TEST_F(WakeEventPageTest,ClosedEventPage)171 IN_PROC_BROWSER_TEST_F(WakeEventPageTest, ClosedEventPage) {
172 RunTest(true /* expect_success */, EVENT, true /* should_close */,
173 true /* will_be_open */);
174 }
175
IN_PROC_BROWSER_TEST_F(WakeEventPageTest,OpenEventPage)176 IN_PROC_BROWSER_TEST_F(WakeEventPageTest, OpenEventPage) {
177 RunTest(true /* expect_success */, EVENT, false /* should_close */,
178 true /* will_be_open */);
179 }
180
IN_PROC_BROWSER_TEST_F(WakeEventPageTest,ClosedPersistentBackgroundPage)181 IN_PROC_BROWSER_TEST_F(WakeEventPageTest, ClosedPersistentBackgroundPage) {
182 // Note: this is an odd test, because persistent background pages aren't
183 // supposed to close. Extensions can close them with window.close() but why
184 // would they do that? Test it anyway.
185 RunTest(false /* expect_success */, PERSISTENT, true /* should_close */,
186 false /* will_be_open */);
187 }
188
IN_PROC_BROWSER_TEST_F(WakeEventPageTest,OpenPersistentBackgroundPage)189 IN_PROC_BROWSER_TEST_F(WakeEventPageTest, OpenPersistentBackgroundPage) {
190 RunTest(true /* expect_success */, PERSISTENT, false /* should_close */,
191 true /* will_be_open */);
192 }
193
IN_PROC_BROWSER_TEST_F(WakeEventPageTest,NoBackgroundPage)194 IN_PROC_BROWSER_TEST_F(WakeEventPageTest, NoBackgroundPage) {
195 RunTest(false /* expect_success */, NONE, false /* should_close */,
196 false /* will_be_open */);
197 }
198
199 } // namespace
200 } // namespace extensions
201