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