blob: 56b1dacfc74d92fa90ff26ac54d6dcff1cf43724 [file] [log] [blame]
[email protected]871035f2012-02-01 02:09:561// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]86c008e82009-08-28 20:26:052// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
avia2f4804a2015-12-24 23:11:135#include <stddef.h>
6
Jinho Bangb5216cec2018-01-17 19:43:117#include <memory>
vabr9984ea62017-04-10 11:33:498#include <utility>
9
Devlin Cronin313da2812017-11-10 17:40:4010#include "base/threading/thread_restrictions.h"
vabr9984ea62017-04-10 11:33:4911#include "base/values.h"
avia2f4804a2015-12-24 23:11:1312#include "build/build_config.h"
[email protected]86c008e82009-08-28 20:26:0513#include "chrome/browser/extensions/extension_apitest.h"
[email protected]eaa7dd182010-12-14 11:09:0014#include "chrome/browser/extensions/extension_service.h"
[email protected]8f9d4eb2011-02-05 01:39:1015#include "chrome/browser/extensions/extension_web_ui.h"
[email protected]8ecad5e2010-12-02 21:18:3316#include "chrome/browser/profiles/profile.h"
[email protected]7b5dc002010-11-16 23:08:1017#include "chrome/browser/ui/browser.h"
[email protected]47ae23372013-01-29 01:50:4818#include "chrome/browser/ui/tabs/tab_strip_model.h"
rockot3b99b8e2014-10-14 21:54:3519#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
[email protected]f845e142010-05-04 20:49:1920#include "chrome/common/url_constants.h"
[email protected]af44e7fb2011-07-29 18:32:3221#include "chrome/test/base/ui_test_utils.h"
brettwb1fc1b82016-02-02 00:19:0822#include "components/prefs/pref_service.h"
23#include "components/prefs/scoped_user_pref_update.h"
[email protected]ad23a092011-12-28 07:02:0424#include "content/public/browser/navigation_entry.h"
alexmos5e8b5e6a2017-03-15 21:52:4725#include "content/public/browser/render_widget_host_view.h"
[email protected]4ca15302012-01-03 05:53:2026#include "content/public/browser/web_contents.h"
alexmos5e8b5e6a2017-03-15 21:52:4727#include "content/public/test/browser_test_utils.h"
Devlin Cronin420995d2018-01-22 20:54:0628#include "extensions/browser/extension_creator.h"
[email protected]885c0e92012-11-13 20:27:4229#include "extensions/common/constants.h"
rdevlin.cronin5fafa5a2015-12-30 03:42:0230#include "extensions/test/extension_test_message_listener.h"
yoze8dc2f12014-09-09 23:16:3231#include "extensions/test/result_catcher.h"
alexmos5e8b5e6a2017-03-15 21:52:4732#include "net/dns/mock_host_resolver.h"
33#include "net/test/embedded_test_server/embedded_test_server.h"
[email protected]4ca15302012-01-03 05:53:2034
35using content::WebContents;
[email protected]86c008e82009-08-28 20:26:0536
rdevlin.cronin5fafa5a2015-12-30 03:42:0237namespace extensions {
38
[email protected]c5b8ab42010-04-14 22:06:5039class ExtensionOverrideTest : public ExtensionApiTest {
40 protected:
jambb11ed742017-05-01 17:27:5941 void SetUpOnMainThread() override {
42 ExtensionApiTest::SetUpOnMainThread();
43 host_resolver()->AddRule("*", "127.0.0.1");
44 ASSERT_TRUE(embedded_test_server()->Start());
45 }
46
[email protected]c5b8ab42010-04-14 22:06:5047 bool CheckHistoryOverridesContainsNoDupes() {
48 // There should be no duplicate entries in the preferences.
[email protected]023b3d12013-12-23 18:46:4949 const base::DictionaryValue* overrides =
[email protected]c5b8ab42010-04-14 22:06:5050 browser()->profile()->GetPrefs()->GetDictionary(
[email protected]8f9d4eb2011-02-05 01:39:1051 ExtensionWebUI::kExtensionURLOverrides);
[email protected]c5b8ab42010-04-14 22:06:5052
rdevlin.cronin5fafa5a2015-12-30 03:42:0253 const base::ListValue* values = nullptr;
[email protected]e2194742010-08-12 05:54:3454 if (!overrides->GetList("history", &values))
[email protected]c5b8ab42010-04-14 22:06:5055 return false;
56
57 std::set<std::string> seen_overrides;
dchengcb60e702016-05-25 18:30:4758 for (const auto& val : *values) {
rdevlin.cronin5fafa5a2015-12-30 03:42:0259 const base::DictionaryValue* dict = nullptr;
60 std::string entry;
jdoerriea5676c62017-04-11 18:09:1461 if (!val.GetAsDictionary(&dict) || !dict->GetString("entry", &entry) ||
rdevlin.cronin5fafa5a2015-12-30 03:42:0262 seen_overrides.count(entry) != 0)
[email protected]c5b8ab42010-04-14 22:06:5063 return false;
rdevlin.cronin5fafa5a2015-12-30 03:42:0264 seen_overrides.insert(entry);
[email protected]c5b8ab42010-04-14 22:06:5065 }
66
67 return true;
68 }
[email protected]c5b8ab42010-04-14 22:06:5069
rdevlin.cronin5fafa5a2015-12-30 03:42:0270 // Returns AssertionSuccess() if the given |web_contents| is being actively
71 // controlled by the extension with |extension_id|.
72 testing::AssertionResult ExtensionControlsPage(
73 content::WebContents* web_contents,
74 const std::string& extension_id) {
75 if (!web_contents->GetController().GetLastCommittedEntry())
76 return testing::AssertionFailure() << "No last committed entry.";
77 // We can't just use WebContents::GetLastCommittedURL() here because
78 // trickiness makes it think that it committed chrome://newtab.
79 GURL gurl = web_contents->GetController().GetLastCommittedEntry()->GetURL();
80 if (!gurl.SchemeIs(kExtensionScheme))
81 return testing::AssertionFailure() << gurl;
brettwb65cd5c2016-01-23 00:46:3882 if (gurl.host_piece() != extension_id)
rdevlin.cronin5fafa5a2015-12-30 03:42:0283 return testing::AssertionFailure() << gurl;
84 return testing::AssertionSuccess();
[email protected]bb0a6a02010-04-01 21:17:0085 }
[email protected]c670c242009-08-29 16:45:2786
rdevlin.cronin5fafa5a2015-12-30 03:42:0287 base::FilePath data_dir() {
88 return test_data_dir_.AppendASCII("override");
89 }
90};
91
92// Basic test for overriding the NTP.
93IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest, OverrideNewTab) {
94 const Extension* extension = LoadExtension(data_dir().AppendASCII("newtab"));
95 {
96 // Navigate to the new tab page. The overridden new tab page
97 // will call chrome.test.sendMessage('controlled by first').
98 ExtensionTestMessageListener listener(false);
99 ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab/"));
100 EXPECT_TRUE(ExtensionControlsPage(
101 browser()->tab_strip_model()->GetActiveWebContents(),
102 extension->id()));
103 EXPECT_TRUE(listener.WaitUntilSatisfied());
104 EXPECT_EQ("controlled by first", listener.message());
105 }
106}
107
108// Check having multiple extensions with the same override.
109IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest, OverrideNewTabMultiple) {
Devlin Cronin313da2812017-11-10 17:40:40110 base::ScopedAllowBlockingForTesting allow_blocking;
111
112 base::ScopedTempDir temp_dir;
113 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
114 base::FilePath temp_path = temp_dir.GetPath();
115 base::FilePath extension1_path = temp_path.AppendASCII("extension1.crx");
116 base::FilePath extension1_pem_path = temp_path.AppendASCII("extension1.pem");
117 base::FilePath extension1_unpacked_path = data_dir().AppendASCII("newtab");
118 ASSERT_TRUE(ExtensionCreator().Run(extension1_unpacked_path, extension1_path,
119 base::FilePath(), extension1_pem_path, 0));
120
121 base::FilePath extension2_path = temp_path.AppendASCII("extension2.crx");
122 base::FilePath extension2_pem_path = temp_path.AppendASCII("extension2.pem");
123 base::FilePath extension2_unpacked_path = data_dir().AppendASCII("newtab2");
124 ASSERT_TRUE(ExtensionCreator().Run(extension2_unpacked_path, extension2_path,
125 base::FilePath(), extension2_pem_path, 0));
126
rdevlin.cronin5fafa5a2015-12-30 03:42:02127 // Prefer IDs because loading/unloading invalidates the extension ptrs.
128 const std::string extension1_id =
Devlin Cronin313da2812017-11-10 17:40:40129 InstallExtensionWithPermissionsGranted(extension1_path, 1)->id();
rdevlin.cronin5fafa5a2015-12-30 03:42:02130 const std::string extension2_id =
Devlin Cronin313da2812017-11-10 17:40:40131 InstallExtensionWithPermissionsGranted(extension2_path, 1)->id();
132
rdevlin.cronin5fafa5a2015-12-30 03:42:02133 {
134 // Navigate to the new tab page. Last extension installed wins, so
135 // the new tab page should be controlled by the second extension.
136 ExtensionTestMessageListener listener(false);
137 ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab/"));
138 EXPECT_TRUE(ExtensionControlsPage(
139 browser()->tab_strip_model()->GetActiveWebContents(),
140 extension2_id));
141 EXPECT_TRUE(listener.WaitUntilSatisfied());
142 EXPECT_EQ("controlled by second", listener.message());
143 }
144
145 // Unload and reload the first extension. This should *not* result in the
146 // first extension moving to the front of the line.
147 ReloadExtension(extension1_id);
148
149 {
150 // The page should still be controlled by the second extension.
151 ExtensionTestMessageListener listener(false);
152 ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab/"));
153 EXPECT_TRUE(ExtensionControlsPage(
154 browser()->tab_strip_model()->GetActiveWebContents(),
155 extension2_id));
156 EXPECT_TRUE(listener.WaitUntilSatisfied());
157 EXPECT_EQ("controlled by second", listener.message());
158 }
159
Devlin Cronin313da2812017-11-10 17:40:40160 {
161 // Upgrade the first extension to a version that uses a different NTP url.
162 // This should *not* result in the first extension moving to the front of
163 // the line.
164 base::FilePath update_path = temp_path.AppendASCII("extension1_update.crx");
165 base::FilePath update_pem_path = extension1_pem_path;
166 base::FilePath update_unpacked_path =
167 data_dir().AppendASCII("newtab_upgrade");
168 ASSERT_TRUE(ExtensionCreator().Run(update_unpacked_path, update_path,
169 update_pem_path, base::FilePath(), 0));
170
171 const Extension* updated_extension =
172 UpdateExtension(extension1_id, update_path, 0);
173 ASSERT_TRUE(updated_extension);
174 EXPECT_EQ(extension1_id, updated_extension->id());
175 }
176
177 {
178 // The page should still be controlled by the second extension.
179 ExtensionTestMessageListener listener(false);
180 ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab/"));
181 EXPECT_TRUE(ExtensionControlsPage(
182 browser()->tab_strip_model()->GetActiveWebContents(), extension2_id));
183 EXPECT_TRUE(listener.WaitUntilSatisfied());
184 EXPECT_EQ("controlled by second", listener.message());
185 }
186
rdevlin.cronin5fafa5a2015-12-30 03:42:02187 // Unload the (controlling) second extension. Now, and only now, should
188 // extension1 take over.
189 UnloadExtension(extension2_id);
190
191 {
192 ExtensionTestMessageListener listener(false);
193 ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab/"));
194 EXPECT_TRUE(ExtensionControlsPage(
195 browser()->tab_strip_model()->GetActiveWebContents(),
196 extension1_id));
197 EXPECT_TRUE(listener.WaitUntilSatisfied());
Devlin Cronin313da2812017-11-10 17:40:40198 EXPECT_EQ("controlled by first upgrade", listener.message());
rdevlin.cronin5fafa5a2015-12-30 03:42:02199 }
200}
201
202// Test that unloading an extension overriding the page reloads the page with
203// the proper url.
204IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest,
205 OverridingExtensionUnloadedWithPageOpen) {
206 // Prefer IDs because loading/unloading invalidates the extension ptrs.
207 const std::string extension1_id =
208 LoadExtension(data_dir().AppendASCII("newtab"))->id();
209 const std::string extension2_id =
210 LoadExtension(data_dir().AppendASCII("newtab2"))->id();
211 {
212 // Navigate to the new tab page. Last extension installed wins, so
213 // the new tab page should be controlled by the second extension.
214 ExtensionTestMessageListener listener(false);
215 ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab/"));
216 EXPECT_TRUE(ExtensionControlsPage(
217 browser()->tab_strip_model()->GetActiveWebContents(),
218 extension2_id));
219 EXPECT_TRUE(listener.WaitUntilSatisfied());
220 EXPECT_EQ("controlled by second", listener.message());
221 }
222
223 {
224 // Unload the controlling extension. The page should be automatically
225 // reloaded with the new controlling extension.
226 ExtensionTestMessageListener listener(false);
227 UnloadExtension(extension2_id);
228 EXPECT_TRUE(listener.WaitUntilSatisfied());
229 EXPECT_EQ("controlled by first", listener.message());
230 EXPECT_TRUE(ExtensionControlsPage(
231 browser()->tab_strip_model()->GetActiveWebContents(),
232 extension1_id));
233 }
[email protected]bb0a6a02010-04-01 21:17:00234}
235
[email protected]e2d74792011-01-23 04:00:32236#if defined(OS_MACOSX)
237// Hangy: http://crbug.com/70511
tfarina38a7cd9b2014-12-13 17:22:42238#define MAYBE_OverrideNewTabIncognito DISABLED_OverrideNewTabIncognito
[email protected]e2d74792011-01-23 04:00:32239#else
tfarina38a7cd9b2014-12-13 17:22:42240#define MAYBE_OverrideNewTabIncognito OverrideNewTabIncognito
[email protected]e2d74792011-01-23 04:00:32241#endif
tfarina38a7cd9b2014-12-13 17:22:42242IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest, MAYBE_OverrideNewTabIncognito) {
rdevlin.cronin5fafa5a2015-12-30 03:42:02243 LoadExtension(data_dir().AppendASCII("newtab"));
[email protected]f845e142010-05-04 20:49:19244
245 // Navigate an incognito tab to the new tab page. We should get the actual
246 // new tab page because we can't load chrome-extension URLs in incognito.
erikchenff8b5c7a2015-07-13 23:41:20247 Browser* otr_browser =
248 OpenURLOffTheRecord(browser()->profile(), GURL("chrome://newtab/"));
[email protected]47ae23372013-01-29 01:50:48249 WebContents* tab = otr_browser->tab_strip_model()->GetActiveWebContents();
[email protected]afe9aba2013-08-16 20:31:34250 ASSERT_TRUE(tab->GetController().GetVisibleEntry());
251 EXPECT_FALSE(tab->GetController().GetVisibleEntry()->GetURL().
rdevlin.cronin5fafa5a2015-12-30 03:42:02252 SchemeIs(kExtensionScheme));
[email protected]f845e142010-05-04 20:49:19253}
254
alexmos5e8b5e6a2017-03-15 21:52:47255// Check that when an overridden new tab page has focus, a subframe navigation
256// on that page does not steal the focus away by focusing the omnibox.
257// See https://crbug.com/700124.
258IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest,
259 SubframeNavigationInOverridenNTPDoesNotAffectFocus) {
alexmos5e8b5e6a2017-03-15 21:52:47260 // Load an extension that overrides the new tab page.
261 const Extension* extension = LoadExtension(data_dir().AppendASCII("newtab"));
262
263 // Navigate to the new tab page. The overridden new tab page
264 // will call chrome.test.sendMessage('controlled by first').
265 ExtensionTestMessageListener listener("controlled by first", false);
266 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
267 WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
268 EXPECT_TRUE(ExtensionControlsPage(contents, extension->id()));
269 EXPECT_TRUE(listener.WaitUntilSatisfied());
270
271 // Start off with the main page focused.
272 contents->Focus();
273 EXPECT_TRUE(contents->GetRenderWidgetHostView()->HasFocus());
274
275 // Inject an iframe and navigate it to a cross-site URL. With
276 // --site-per-process, this will go into a separate process.
277 GURL cross_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
278 std::string script = "var f = document.createElement('iframe');\n"
279 "f.src = '" + cross_site_url.spec() + "';\n"
280 "document.body.appendChild(f);\n";
281 EXPECT_TRUE(ExecuteScript(contents, script));
282 WaitForLoadStop(contents);
283
284 // The page should still have focus. The cross-process subframe navigation
285 // above should not try to focus the omnibox, which would make this false.
286 EXPECT_TRUE(contents->GetRenderWidgetHostView()->HasFocus());
287}
288
[email protected]4f32c152010-05-27 01:09:30289// Times out consistently on Win, http://crbug.com/45173.
290#if defined(OS_WIN)
291#define MAYBE_OverrideHistory DISABLED_OverrideHistory
292#else
293#define MAYBE_OverrideHistory OverrideHistory
294#endif // defined(OS_WIN)
295
296IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest, MAYBE_OverrideHistory) {
[email protected]bb0a6a02010-04-01 21:17:00297 ASSERT_TRUE(RunExtensionTest("override/history")) << message_;
298 {
rdevlin.cronin5fafa5a2015-12-30 03:42:02299 ResultCatcher catcher;
[email protected]bb0a6a02010-04-01 21:17:00300 // Navigate to the history page. The overridden history page
301 // will call chrome.test.notifyPass() .
302 ui_test_utils::NavigateToURL(browser(), GURL("chrome://history/"));
303 ASSERT_TRUE(catcher.GetNextResult());
304 }
[email protected]86c008e82009-08-28 20:26:05305}
[email protected]c5b8ab42010-04-14 22:06:50306
307// Regression test for http://crbug.com/41442.
308IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest, ShouldNotCreateDuplicateEntries) {
rdevlin.cronin5fafa5a2015-12-30 03:42:02309 const Extension* extension =
[email protected]84df8332011-12-06 18:22:46310 LoadExtension(test_data_dir_.AppendASCII("override/history"));
311 ASSERT_TRUE(extension);
[email protected]c5b8ab42010-04-14 22:06:50312
313 // Simulate several LoadExtension() calls happening over the lifetime of
314 // a preferences file without corresponding UnloadExtension() calls.
315 for (size_t i = 0; i < 3; ++i) {
rdevlin.cronin5fafa5a2015-12-30 03:42:02316 ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
[email protected]c5b8ab42010-04-14 22:06:50317 browser()->profile(),
rdevlin.cronin5fafa5a2015-12-30 03:42:02318 URLOverrides::GetChromeURLOverrides(extension));
[email protected]c5b8ab42010-04-14 22:06:50319 }
320
321 ASSERT_TRUE(CheckHistoryOverridesContainsNoDupes());
322}
323
rdevlin.cronin5fafa5a2015-12-30 03:42:02324// TODO(devlin): This test seems a bit contrived. How would we end up with
325// duplicate entries created?
[email protected]c5b8ab42010-04-14 22:06:50326IN_PROC_BROWSER_TEST_F(ExtensionOverrideTest, ShouldCleanUpDuplicateEntries) {
327 // Simulate several LoadExtension() calls happening over the lifetime of
328 // a preferences file without corresponding UnloadExtension() calls. This is
329 // the same as the above test, except for that it is testing the case where
330 // the file already contains dupes when an extension is loaded.
Jinho Bangb5216cec2018-01-17 19:43:11331 auto list = std::make_unique<base::ListValue>();
rdevlin.cronin5fafa5a2015-12-30 03:42:02332 for (size_t i = 0; i < 3; ++i) {
dchengc963c7142016-04-08 03:55:22333 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
rdevlin.cronin5fafa5a2015-12-30 03:42:02334 dict->SetString("entry", "http://www.google.com/");
335 dict->SetBoolean("active", true);
336 list->Append(std::move(dict));
337 }
[email protected]c5b8ab42010-04-14 22:06:50338
[email protected]43d3bf82011-04-11 07:46:58339 {
340 DictionaryPrefUpdate update(browser()->profile()->GetPrefs(),
341 ExtensionWebUI::kExtensionURLOverrides);
vabr9984ea62017-04-10 11:33:49342 update.Get()->Set("history", std::move(list));
[email protected]43d3bf82011-04-11 07:46:58343 }
[email protected]c5b8ab42010-04-14 22:06:50344
345 ASSERT_FALSE(CheckHistoryOverridesContainsNoDupes());
346
rdevlin.cronin5fafa5a2015-12-30 03:42:02347 ExtensionWebUI::InitializeChromeURLOverrides(profile());
[email protected]c5b8ab42010-04-14 22:06:50348
349 ASSERT_TRUE(CheckHistoryOverridesContainsNoDupes());
350}
rdevlin.cronin5fafa5a2015-12-30 03:42:02351
352} // namespace extensions