blob: f8b596790e225a0f9833e55c6c3e1a83501a9568 [file] [log] [blame]
[email protected]bcabac762013-05-29 23:33:241// Copyright 2013 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// This file contains tests for extension loading, reloading, and
6// unloading behavior.
7
[email protected]bcabac762013-05-29 23:33:248#include "base/run_loop.h"
[email protected]44580842013-07-09 21:36:539#include "base/strings/stringprintf.h"
[email protected]ca975942014-01-07 12:06:4710#include "base/version.h"
avia2f4804a2015-12-24 23:11:1311#include "build/build_config.h"
Alex Clarkede60d0c2018-09-25 16:53:4812#include "chrome/browser/devtools/devtools_window.h"
13#include "chrome/browser/devtools/devtools_window_testing.h"
rockota34fed222015-09-25 21:56:3114#include "chrome/browser/extensions/devtools_util.h"
[email protected]bcabac762013-05-29 23:33:2415#include "chrome/browser/extensions/extension_browsertest.h"
rockota34fed222015-09-25 21:56:3116#include "chrome/browser/extensions/extension_service.h"
[email protected]ca975942014-01-07 12:06:4717#include "chrome/browser/profiles/profile.h"
[email protected]bcabac762013-05-29 23:33:2418#include "chrome/browser/ui/tabs/tab_strip_model.h"
19#include "chrome/test/base/in_process_browser_test.h"
20#include "chrome/test/base/ui_test_utils.h"
rockota34fed222015-09-25 21:56:3121#include "content/public/browser/devtools_agent_host.h"
rob1166ab32016-02-29 20:26:0222#include "content/public/test/browser_test_utils.h"
[email protected]ca975942014-01-07 12:06:4723#include "extensions/browser/extension_registry.h"
rockota34fed222015-09-25 21:56:3124#include "extensions/browser/process_manager.h"
robertshield9cf42462017-07-19 23:44:0825#include "extensions/common/permissions/permissions_data.h"
rob1166ab32016-02-29 20:26:0226#include "extensions/test/extension_test_message_listener.h"
Devlin Cronin4f455a22018-01-25 01:36:4527#include "extensions/test/test_extension_dir.h"
[email protected]bcabac762013-05-29 23:33:2428#include "net/test/embedded_test_server/embedded_test_server.h"
29#include "testing/gmock/include/gmock/gmock.h"
30
wychen7b07e7b2017-01-10 17:48:2931#if defined(OS_WIN)
32#include "base/win/windows_version.h"
33#endif
34
[email protected]bcabac762013-05-29 23:33:2435namespace extensions {
36namespace {
37
[email protected]bcabac762013-05-29 23:33:2438class ExtensionLoadingTest : public ExtensionBrowserTest {
39};
40
41// Check the fix for http://crbug.com/178542.
42IN_PROC_BROWSER_TEST_F(ExtensionLoadingTest,
43 UpgradeAfterNavigatingFromOverriddenNewTabPage) {
svaldeza01f7d92015-11-18 17:47:5644 ASSERT_TRUE(embedded_test_server()->Start());
[email protected]bcabac762013-05-29 23:33:2445
46 TestExtensionDir extension_dir;
robertshield9cf42462017-07-19 23:44:0847 const char kManifestTemplate[] =
[email protected]44580842013-07-09 21:36:5348 "{"
rockota34fed222015-09-25 21:56:3149 " 'name': 'Overrides New Tab',"
50 " 'version': '%d',"
51 " 'description': 'Overrides New Tab',"
52 " 'manifest_version': 2,"
53 " 'background': {"
54 " 'persistent': false,"
55 " 'scripts': ['event.js']"
[email protected]44580842013-07-09 21:36:5356 " },"
rockota34fed222015-09-25 21:56:3157 " 'chrome_url_overrides': {"
58 " 'newtab': 'newtab.html'"
[email protected]44580842013-07-09 21:36:5359 " }"
60 "}";
rockota34fed222015-09-25 21:56:3161 extension_dir.WriteManifestWithSingleQuotes(
robertshield9cf42462017-07-19 23:44:0862 base::StringPrintf(kManifestTemplate, 1));
[email protected]bcabac762013-05-29 23:33:2463 extension_dir.WriteFile(FILE_PATH_LITERAL("event.js"), "");
64 extension_dir.WriteFile(FILE_PATH_LITERAL("newtab.html"),
65 "<h1>Overridden New Tab Page</h1>");
66
67 const Extension* new_tab_extension =
68 InstallExtension(extension_dir.Pack(), 1 /*new install*/);
69 ASSERT_TRUE(new_tab_extension);
70
71 // Visit the New Tab Page to get a renderer using the extension into history.
72 ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"));
73
74 // Navigate that tab to a non-extension URL to swap out the extension's
75 // renderer.
76 const GURL test_link_from_NTP =
77 embedded_test_server()->GetURL("/README.chromium");
78 EXPECT_THAT(test_link_from_NTP.spec(), testing::EndsWith("/README.chromium"))
79 << "Check that the test server started.";
80 NavigateInRenderer(browser()->tab_strip_model()->GetActiveWebContents(),
81 test_link_from_NTP);
82
83 // Increase the extension's version.
rockota34fed222015-09-25 21:56:3184 extension_dir.WriteManifestWithSingleQuotes(
robertshield9cf42462017-07-19 23:44:0885 base::StringPrintf(kManifestTemplate, 2));
[email protected]bcabac762013-05-29 23:33:2486
87 // Upgrade the extension.
88 new_tab_extension = UpdateExtension(
89 new_tab_extension->id(), extension_dir.Pack(), 0 /*expected upgrade*/);
Devlin Cronin03bf2d22017-12-20 08:21:0590 EXPECT_THAT(new_tab_extension->version().components(),
[email protected]bcabac762013-05-29 23:33:2491 testing::ElementsAre(2));
92
93 // The extension takes a couple round-trips to the renderer in order
94 // to crash, so open a new tab to wait long enough.
95 AddTabAtIndex(browser()->tab_strip_model()->count(),
96 GURL("http://www.google.com/"),
Sylvain Defresnec6ccc77d2014-09-19 10:19:3597 ui::PAGE_TRANSITION_TYPED);
[email protected]bcabac762013-05-29 23:33:2498
99 // Check that the extension hasn't crashed.
[email protected]ca975942014-01-07 12:06:47100 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
101 EXPECT_EQ(0U, registry->terminated_extensions().size());
102 EXPECT_TRUE(registry->enabled_extensions().Contains(new_tab_extension->id()));
[email protected]bcabac762013-05-29 23:33:24103}
104
robertshield9cf42462017-07-19 23:44:08105IN_PROC_BROWSER_TEST_F(ExtensionLoadingTest,
106 UpgradeAddingNewTabPagePermissionNoPrompt) {
robertshield9cf42462017-07-19 23:44:08107 ASSERT_TRUE(embedded_test_server()->Start());
108
109 TestExtensionDir extension_dir;
110 const char kManifestTemplate[] =
111 "{"
112 " 'name': 'Overrides New Tab',"
113 " 'version': '%d',"
114 " 'description': 'Will override New Tab soon',"
115 " %s" // Placeholder for future NTP url override block.
116 " 'manifest_version': 2"
117 "}";
118 extension_dir.WriteManifestWithSingleQuotes(
119 base::StringPrintf(kManifestTemplate, 1, ""));
120 extension_dir.WriteFile(FILE_PATH_LITERAL("event.js"), "");
121 extension_dir.WriteFile(FILE_PATH_LITERAL("newtab.html"),
122 "<h1>Overridden New Tab Page</h1>");
123
124 const Extension* new_tab_extension =
125 InstallExtension(extension_dir.Pack(), 1 /*new install*/);
126 ASSERT_TRUE(new_tab_extension);
127
128 EXPECT_FALSE(new_tab_extension->permissions_data()->HasAPIPermission(
129 APIPermission::kNewTabPageOverride));
130
131 // Navigate that tab to a non-extension URL to swap out the extension's
132 // renderer.
133 const GURL test_link_from_ntp =
134 embedded_test_server()->GetURL("/README.chromium");
135 EXPECT_THAT(test_link_from_ntp.spec(), testing::EndsWith("/README.chromium"))
136 << "Check that the test server started.";
137 NavigateInRenderer(browser()->tab_strip_model()->GetActiveWebContents(),
138 test_link_from_ntp);
139
140 // Increase the extension's version and add the NTP url override which will
141 // add the kNewTabPageOverride permission.
142 const char ntp_override_string[] =
143 " 'chrome_url_overrides': {"
144 " 'newtab': 'newtab.html'"
145 " },";
146 extension_dir.WriteManifestWithSingleQuotes(
147 base::StringPrintf(kManifestTemplate, 2, ntp_override_string));
148
149 // Upgrade the extension, ensure that the upgrade 'worked' in the sense that
150 // the extension is still present and not disabled and that it now has the
151 // new API permission.
152 // TODO(robertshield): Update this once most of the population is on M62+
153 // and adding NTP permissions implies a permission upgrade.
154 new_tab_extension = UpdateExtension(
155 new_tab_extension->id(), extension_dir.Pack(), 0 /*expected upgrade*/);
156 ASSERT_NE(nullptr, new_tab_extension);
157
158 EXPECT_TRUE(new_tab_extension->permissions_data()->HasAPIPermission(
159 APIPermission::kNewTabPageOverride));
Devlin Cronin03bf2d22017-12-20 08:21:05160 EXPECT_THAT(new_tab_extension->version().components(),
robertshield9cf42462017-07-19 23:44:08161 testing::ElementsAre(2));
162}
163
rockota34fed222015-09-25 21:56:31164// Tests the behavior described in http://crbug.com/532088.
165IN_PROC_BROWSER_TEST_F(ExtensionLoadingTest,
166 KeepAliveWithDevToolsOpenOnReload) {
svaldeza01f7d92015-11-18 17:47:56167 ASSERT_TRUE(embedded_test_server()->Start());
rockota34fed222015-09-25 21:56:31168
169 TestExtensionDir extension_dir;
170 const char manifest_contents[] =
171 "{"
172 " 'name': 'Test With Lazy Background Page',"
173 " 'version': '0',"
174 " 'manifest_version': 2,"
175 " 'app': {"
176 " 'background': {"
177 " 'scripts': ['event.js']"
178 " }"
179 " }"
180 "}";
181 extension_dir.WriteManifestWithSingleQuotes(manifest_contents);
182 extension_dir.WriteFile(FILE_PATH_LITERAL("event.js"), "");
183
184 const Extension* extension =
185 InstallExtension(extension_dir.Pack(), 1 /*new install*/);
186 ASSERT_TRUE(extension);
187 std::string extension_id = extension->id();
David Bertoni3e1e9fa2018-08-29 20:39:30188 const auto dev_tools_activity =
189 std::make_pair(Activity::DEV_TOOLS, std::string());
rockota34fed222015-09-25 21:56:31190
191 ProcessManager* process_manager = ProcessManager::Get(profile());
192 EXPECT_EQ(0, process_manager->GetLazyKeepaliveCount(extension));
David Bertoni3e1e9fa2018-08-29 20:39:30193 ProcessManager::ActivitiesMultiset activities =
194 process_manager->GetLazyKeepaliveActivities(extension);
195 EXPECT_TRUE(activities.empty());
rockota34fed222015-09-25 21:56:31196
197 devtools_util::InspectBackgroundPage(extension, profile());
198 EXPECT_EQ(1, process_manager->GetLazyKeepaliveCount(extension));
David Bertoni3e1e9fa2018-08-29 20:39:30199 activities = process_manager->GetLazyKeepaliveActivities(extension);
200 EXPECT_THAT(activities, testing::UnorderedElementsAre(dev_tools_activity));
rockota34fed222015-09-25 21:56:31201
202 // Opening DevTools will cause the background page to load. Wait for it.
203 WaitForExtensionViewsToLoad();
204
205 ReloadExtension(extension_id);
206
207 // Flush the MessageLoop to ensure that DevTools has a chance to be reattached
208 // and the background page has a chance to begin reloading.
209 base::RunLoop().RunUntilIdle();
210
211 // And wait for the background page to finish loading again.
212 WaitForExtensionViewsToLoad();
213
214 // Ensure that our DevtoolsAgentHost is actually connected to the new
215 // background WebContents.
216 content::WebContents* background_contents =
217 process_manager->GetBackgroundHostForExtension(extension_id)
218 ->host_contents();
219 EXPECT_TRUE(content::DevToolsAgentHost::HasFor(background_contents));
220
221 // The old Extension object is no longer valid.
222 extension = ExtensionRegistry::Get(profile())
223 ->enabled_extensions().GetByID(extension_id);
224
225 // Keepalive count should stabilize back to 1, because DevTools is still open.
226 EXPECT_EQ(1, process_manager->GetLazyKeepaliveCount(extension));
David Bertoni3e1e9fa2018-08-29 20:39:30227 activities = process_manager->GetLazyKeepaliveActivities(extension);
228 EXPECT_THAT(activities, testing::UnorderedElementsAre(dev_tools_activity));
rockota34fed222015-09-25 21:56:31229}
230
rob1166ab32016-02-29 20:26:02231// Tests whether the extension runtime stays valid when an extension reloads
232// while a devtools extension is hammering the frame with eval requests.
233// Regression test for https://crbug.com/544182
234IN_PROC_BROWSER_TEST_F(ExtensionLoadingTest, RuntimeValidWhileDevToolsOpen) {
235 TestExtensionDir devtools_dir;
236 TestExtensionDir inspect_dir;
237
238 const char kDevtoolsManifest[] =
239 "{"
240 " 'name': 'Devtools',"
241 " 'version': '1',"
242 " 'manifest_version': 2,"
243 " 'devtools_page': 'devtools.html'"
244 "}";
245
246 const char kDevtoolsJs[] =
247 "setInterval(function() {"
248 " chrome.devtools.inspectedWindow.eval('1', function() {"
249 " });"
250 "}, 4);"
251 "chrome.test.sendMessage('devtools_page_ready');";
252
253 const char kTargetManifest[] =
254 "{"
255 " 'name': 'Inspect target',"
256 " 'version': '1',"
257 " 'manifest_version': 2,"
258 " 'background': {"
259 " 'scripts': ['background.js']"
260 " }"
261 "}";
262
263 // A script to duck-type whether it runs in a background page.
264 const char kTargetJs[] =
265 "var is_valid = !!(chrome.tabs && chrome.tabs.create);";
266
267 devtools_dir.WriteManifestWithSingleQuotes(kDevtoolsManifest);
268 devtools_dir.WriteFile(FILE_PATH_LITERAL("devtools.js"), kDevtoolsJs);
269 devtools_dir.WriteFile(FILE_PATH_LITERAL("devtools.html"),
270 "<script src='devtools.js'></script>");
271
272 inspect_dir.WriteManifestWithSingleQuotes(kTargetManifest);
273 inspect_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kTargetJs);
vabr9142fe22016-09-08 13:19:22274 const Extension* devtools_ext = LoadExtension(devtools_dir.UnpackedPath());
rob1166ab32016-02-29 20:26:02275 ASSERT_TRUE(devtools_ext);
276
vabr9142fe22016-09-08 13:19:22277 const Extension* inspect_ext = LoadExtension(inspect_dir.UnpackedPath());
rob1166ab32016-02-29 20:26:02278 ASSERT_TRUE(inspect_ext);
279 const std::string inspect_ext_id = inspect_ext->id();
280
281 // Open the devtools and wait until the devtools_page is ready.
282 ExtensionTestMessageListener devtools_ready("devtools_page_ready", false);
283 devtools_util::InspectBackgroundPage(inspect_ext, profile());
284 ASSERT_TRUE(devtools_ready.WaitUntilSatisfied());
285
286 // Reload the extension. The devtools window will stay open, but temporarily
287 // be detached. As soon as the background is attached again, the devtools
288 // continues with spamming eval requests.
289 ReloadExtension(inspect_ext_id);
290 WaitForExtensionViewsToLoad();
291
292 content::WebContents* bg_contents =
293 ProcessManager::Get(profile())
294 ->GetBackgroundHostForExtension(inspect_ext_id)
295 ->host_contents();
296 ASSERT_TRUE(bg_contents);
297
298 // Now check whether the extension runtime is valid (see kTargetJs).
299 bool is_valid = false;
300 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
301 bg_contents, "domAutomationController.send(is_valid);", &is_valid));
302 EXPECT_TRUE(is_valid);
Alex Clarkede60d0c2018-09-25 16:53:48303
304 // Tidy up.
305 DevToolsWindowTesting::CloseDevToolsWindowSync(
306 DevToolsWindow::FindDevToolsWindow(
307 content::DevToolsAgentHost::GetOrCreateFor(bg_contents).get()));
rob1166ab32016-02-29 20:26:02308}
309
[email protected]bcabac762013-05-29 23:33:24310} // namespace
311} // namespace extensions