blob: 58c7308489ac9f078af8c24f3a003b8a487c8a70 [file] [log] [blame]
[email protected]fad73672012-06-15 23:26:061// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]c80b8ee2011-12-03 04:26:522// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Contains holistic tests of the bindings infrastructure
6
Gabriel Charette078e3662017-08-28 22:59:047#include "base/run_loop.h"
Devlin Cronina3fe3d602017-11-22 04:47:438#include "base/test/scoped_feature_list.h"
[email protected]7eef3942013-08-14 02:53:499#include "chrome/browser/extensions/api/permissions/permissions_api.h"
[email protected]c80b8ee2011-12-03 04:26:5210#include "chrome/browser/extensions/extension_apitest.h"
jochen7923c2a2015-07-14 10:04:4511#include "chrome/browser/net/url_request_mock_util.h"
Devlin Cronind2389082018-08-14 01:53:2612#include "chrome/browser/sessions/session_tab_helper.h"
[email protected]fad73672012-06-15 23:26:0613#include "chrome/browser/ui/browser.h"
Devlin Cronind2389082018-08-14 01:53:2614#include "chrome/browser/ui/browser_tabstrip.h"
rdevlin.cronin83a4b3a2015-10-28 21:43:5815#include "chrome/browser/ui/tabs/tab_strip_model.h"
asargent79b64c32016-08-04 17:17:1416#include "chrome/common/chrome_switches.h"
rdevlin.cronin83a4b3a2015-10-28 21:43:5817#include "chrome/test/base/ui_test_utils.h"
jochen7923c2a2015-07-14 10:04:4518#include "content/public/browser/browser_thread.h"
Devlin Croninb15c4f4e2017-12-15 21:20:1119#include "content/public/browser/render_frame_host.h"
Devlin Cronine80e6fc12019-02-05 00:44:2320#include "content/public/browser/render_view_host.h"
21#include "content/public/common/content_features.h"
[email protected]7d478cb2012-07-24 17:19:4222#include "content/public/test/browser_test_utils.h"
Devlin Cronind900688a2019-02-01 01:36:1123#include "content/public/test/test_navigation_observer.h"
Devlin Cronind2389082018-08-14 01:53:2624#include "extensions/browser/event_router.h"
[email protected]22401dc2014-03-21 01:38:5725#include "extensions/browser/extension_host.h"
[email protected]98b6d942013-11-10 00:34:0726#include "extensions/browser/process_manager.h"
Devlin Cronina3fe3d602017-11-22 04:47:4327#include "extensions/common/extension_features.h"
lfg910f2f92014-09-19 05:31:0928#include "extensions/test/extension_test_message_listener.h"
yoze8dc2f12014-09-09 23:16:3229#include "extensions/test/result_catcher.h"
Devlin Cronind2389082018-08-14 01:53:2630#include "extensions/test/test_extension_dir.h"
rdevlin.cronine6e20022017-06-13 18:23:4031#include "net/dns/mock_host_resolver.h"
rdevlin.cronin83a4b3a2015-10-28 21:43:5832#include "net/test/embedded_test_server/embedded_test_server.h"
Devlin Cronine80e6fc12019-02-05 00:44:2333#include "third_party/blink/public/platform/web_mouse_event.h"
[email protected]fad73672012-06-15 23:26:0634
[email protected]adafe5b2013-08-09 10:35:0435namespace extensions {
36namespace {
37
Devlin Cronine80e6fc12019-02-05 00:44:2338void MouseDownInWebContents(content::WebContents* web_contents) {
39 blink::WebMouseEvent mouse_event(
40 blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
41 blink::WebInputEvent::GetStaticTimeStampForTests());
42 mouse_event.button = blink::WebMouseEvent::Button::kLeft;
43 mouse_event.SetPositionInWidget(10, 10);
44 mouse_event.click_count = 1;
45 web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent(
46 mouse_event);
47}
48
49void MouseUpInWebContents(content::WebContents* web_contents) {
50 blink::WebMouseEvent mouse_event(
51 blink::WebInputEvent::kMouseUp, blink::WebInputEvent::kNoModifiers,
52 blink::WebInputEvent::GetStaticTimeStampForTests());
53 mouse_event.button = blink::WebMouseEvent::Button::kLeft;
54 mouse_event.SetPositionInWidget(10, 10);
55 mouse_event.click_count = 1;
56 web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent(
57 mouse_event);
58}
59
60enum BindingsFeatureType {
61 kNativeBindings,
62 kJavaScriptBindings,
63};
64
65enum UserActivationType {
66 kUserActivationV1,
67 kUserActivationV2,
68};
69
70struct BindingsApiTestConfig {
71 BindingsFeatureType bindings_type;
72 base::Optional<UserActivationType> user_activation_type;
73};
Devlin Croninc3a1e5072017-08-17 17:02:4974
75class ExtensionBindingsApiTest
76 : public ExtensionApiTest,
Devlin Cronine80e6fc12019-02-05 00:44:2377 public ::testing::WithParamInterface<BindingsApiTestConfig> {
jochen7923c2a2015-07-14 10:04:4578 public:
Devlin Cronina3fe3d602017-11-22 04:47:4379 ExtensionBindingsApiTest() {}
80 ~ExtensionBindingsApiTest() override {}
81
82 void SetUp() override {
Devlin Cronine80e6fc12019-02-05 00:44:2383 BindingsApiTestConfig config = GetParam();
84 std::vector<base::Feature> enable_features;
85 std::vector<base::Feature> disable_features;
86
87 if (config.bindings_type == kNativeBindings) {
88 enable_features.push_back(extensions_features::kNativeCrxBindings);
Devlin Cronina3fe3d602017-11-22 04:47:4389 } else {
Devlin Cronine80e6fc12019-02-05 00:44:2390 DCHECK_EQ(kJavaScriptBindings, config.bindings_type);
91 disable_features.push_back(extensions_features::kNativeCrxBindings);
Devlin Cronina3fe3d602017-11-22 04:47:4392 }
Devlin Cronine80e6fc12019-02-05 00:44:2393
94 if (config.user_activation_type.has_value()) {
95 if (*config.user_activation_type == kUserActivationV2) {
96 enable_features.push_back(features::kUserActivationV2);
97 } else {
98 DCHECK_EQ(kUserActivationV1, *config.user_activation_type);
99 disable_features.push_back(features::kUserActivationV2);
100 }
101 }
102
103 scoped_feature_list_.InitWithFeatures(enable_features, disable_features);
104
Devlin Cronina3fe3d602017-11-22 04:47:43105 ExtensionApiTest::SetUp();
Devlin Croninc3a1e5072017-08-17 17:02:49106 }
107
jochen7923c2a2015-07-14 10:04:45108 void SetUpOnMainThread() override {
rdevlin.cronin17160cc62016-11-23 05:33:08109 ExtensionApiTest::SetUpOnMainThread();
rdevlin.cronine6e20022017-06-13 18:23:40110 host_resolver()->AddRule("*", "127.0.0.1");
111 ASSERT_TRUE(StartEmbeddedTestServer());
jochen7923c2a2015-07-14 10:04:45112 }
Devlin Cronina3fe3d602017-11-22 04:47:43113
114 private:
115 base::test::ScopedFeatureList scoped_feature_list_;
116
117 DISALLOW_COPY_AND_ASSIGN(ExtensionBindingsApiTest);
jochen7923c2a2015-07-14 10:04:45118};
[email protected]adafe5b2013-08-09 10:35:04119
Devlin Croninc3a1e5072017-08-17 17:02:49120IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
[email protected]7eef3942013-08-14 02:53:49121 UnavailableBindingsNeverRegistered) {
122 // Test will request the 'storage' permission.
123 PermissionsRequestFunction::SetIgnoreUserGestureForTests(true);
124 ASSERT_TRUE(RunExtensionTest(
125 "bindings/unavailable_bindings_never_registered")) << message_;
126}
127
Devlin Croninc3a1e5072017-08-17 17:02:49128IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
[email protected]adafe5b2013-08-09 10:35:04129 ExceptionInHandlerShouldNotCrash) {
[email protected]c80b8ee2011-12-03 04:26:52130 ASSERT_TRUE(RunExtensionSubtest(
131 "bindings/exception_in_handler_should_not_crash",
132 "page.html")) << message_;
133}
[email protected]fad73672012-06-15 23:26:06134
135// Tests that an error raised during an async function still fires
[email protected]754ea8b72013-01-08 15:10:31136// the callback, but sets chrome.runtime.lastError.
Devlin Croninc3a1e5072017-08-17 17:02:49137IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, LastError) {
[email protected]fad73672012-06-15 23:26:06138 ASSERT_TRUE(LoadExtension(
Devlin Croninc3a1e5072017-08-17 17:02:49139 test_data_dir_.AppendASCII("bindings").AppendASCII("last_error")));
[email protected]fad73672012-06-15 23:26:06140
141 // Get the ExtensionHost that is hosting our background page.
[email protected]98b6d942013-11-10 00:34:07142 extensions::ProcessManager* manager =
reillyg0ea3fa902014-10-28 15:30:23143 extensions::ProcessManager::Get(browser()->profile());
[email protected]3a1dc572012-07-31 22:25:13144 extensions::ExtensionHost* host = FindHostWithPath(manager, "/bg.html", 1);
[email protected]fad73672012-06-15 23:26:06145
146 bool result = false;
David Benjamin5a792652018-06-08 02:15:42147 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(host->host_contents(),
148 "testLastError()", &result));
[email protected]fad73672012-06-15 23:26:06149 EXPECT_TRUE(result);
150}
[email protected]52eafbd2013-04-03 04:43:19151
[email protected]adafe5b2013-08-09 10:35:04152// Regression test that we don't delete our own bindings with about:blank
153// iframes.
Devlin Croninc3a1e5072017-08-17 17:02:49154IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, AboutBlankIframe) {
[email protected]adafe5b2013-08-09 10:35:04155 ResultCatcher catcher;
156 ExtensionTestMessageListener listener("load", true);
157
158 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("bindings")
159 .AppendASCII("about_blank_iframe")));
160
161 ASSERT_TRUE(listener.WaitUntilSatisfied());
162
163 const Extension* extension = LoadExtension(
164 test_data_dir_.AppendASCII("bindings")
165 .AppendASCII("internal_apis_not_on_chrome_object"));
166 ASSERT_TRUE(extension);
167 listener.Reply(extension->id());
168
169 ASSERT_TRUE(catcher.GetNextResult()) << message_;
170}
171
Devlin Croninc3a1e5072017-08-17 17:02:49172IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
[email protected]adafe5b2013-08-09 10:35:04173 InternalAPIsNotOnChromeObject) {
[email protected]52eafbd2013-04-03 04:43:19174 ASSERT_TRUE(RunExtensionSubtest(
175 "bindings/internal_apis_not_on_chrome_object",
176 "page.html")) << message_;
177}
[email protected]adafe5b2013-08-09 10:35:04178
[email protected]fc034482013-08-09 20:25:14179// Tests that we don't override events when bindings are re-injected.
180// Regression test for http://crbug.com/269149.
rpaquay96bf3b7d2014-11-26 00:19:08181// Regression test for http://crbug.com/436593.
Henrik Grunell6b7d9db2017-06-14 10:27:20182// Flaky on Mac. http://crbug.com/733064.
Giovanni Ortuño Urquidi7ce215452017-06-14 03:34:08183#if defined(OS_MACOSX)
184#define MAYBE_EventOverriding DISABLED_EventOverriding
185#else
186#define MAYBE_EventOverriding EventOverriding
187#endif
Devlin Croninc3a1e5072017-08-17 17:02:49188IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, MAYBE_EventOverriding) {
[email protected]fc034482013-08-09 20:25:14189 ASSERT_TRUE(RunExtensionTest("bindings/event_overriding")) << message_;
rdevlin.cronind734f682017-06-13 21:23:11190 // The extension test removes a window and, during window removal, sends the
191 // success message. Make sure we flush all pending tasks.
192 base::RunLoop().RunUntilIdle();
[email protected]fc034482013-08-09 20:25:14193}
194
kalman1bd5b182015-01-13 19:01:18195// Tests the effectiveness of the 'nocompile' feature file property.
196// Regression test for http://crbug.com/356133.
Devlin Croninc3a1e5072017-08-17 17:02:49197IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, Nocompile) {
kalman1bd5b182015-01-13 19:01:18198 ASSERT_TRUE(RunExtensionSubtest("bindings/nocompile", "page.html"))
199 << message_;
200}
201
Devlin Croninc3a1e5072017-08-17 17:02:49202IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, ApiEnums) {
rdevlin.cronin2ba3c88d2015-03-03 01:18:22203 ASSERT_TRUE(RunExtensionTest("bindings/api_enums")) << message_;
Nico Weber42c88d62019-02-11 03:09:25204}
rdevlin.cronin2ba3c88d2015-03-03 01:18:22205
jochen7923c2a2015-07-14 10:04:45206// Regression test for http://crbug.com/504011 - proper access checks on
207// getModuleSystem().
Devlin Croninc3a1e5072017-08-17 17:02:49208IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, ModuleSystem) {
jochen7923c2a2015-07-14 10:04:45209 ASSERT_TRUE(RunExtensionTest("bindings/module_system")) << message_;
210}
211
Devlin Croninc3a1e5072017-08-17 17:02:49212IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, NoExportOverriding) {
rdevlin.cronin83a4b3a2015-10-28 21:43:58213 // We need to create runtime bindings in the web page. An extension that's
214 // externally connectable will do that for us.
215 ASSERT_TRUE(LoadExtension(
216 test_data_dir_.AppendASCII("bindings")
217 .AppendASCII("externally_connectable_everywhere")));
218
219 ui_test_utils::NavigateToURL(
220 browser(),
221 embedded_test_server()->GetURL(
222 "/extensions/api_test/bindings/override_exports.html"));
223
224 // See chrome/test/data/extensions/api_test/bindings/override_exports.html.
225 std::string result;
226 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
227 browser()->tab_strip_model()->GetActiveWebContents(),
228 "window.domAutomationController.send("
229 "document.getElementById('status').textContent.trim());",
230 &result));
231 EXPECT_EQ("success", result);
232}
233
Devlin Croninc3a1e5072017-08-17 17:02:49234IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, NoGinDefineOverriding) {
rdevlin.cronin415b73b2015-11-13 01:14:47235 // We need to create runtime bindings in the web page. An extension that's
236 // externally connectable will do that for us.
237 ASSERT_TRUE(LoadExtension(
238 test_data_dir_.AppendASCII("bindings")
239 .AppendASCII("externally_connectable_everywhere")));
240
241 ui_test_utils::NavigateToURL(
242 browser(),
243 embedded_test_server()->GetURL(
244 "/extensions/api_test/bindings/override_gin_define.html"));
245 ASSERT_FALSE(
246 browser()->tab_strip_model()->GetActiveWebContents()->IsCrashed());
247
248 // See chrome/test/data/extensions/api_test/bindings/override_gin_define.html.
249 std::string result;
250 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
251 browser()->tab_strip_model()->GetActiveWebContents(),
252 "window.domAutomationController.send("
253 "document.getElementById('status').textContent.trim());",
254 &result));
255 EXPECT_EQ("success", result);
256}
257
Devlin Croninc3a1e5072017-08-17 17:02:49258IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, HandlerFunctionTypeChecking) {
rdevlin.cronina5ecbc82015-10-29 23:41:29259 ui_test_utils::NavigateToURL(
260 browser(),
261 embedded_test_server()->GetURL(
262 "/extensions/api_test/bindings/handler_function_type_checking.html"));
263 content::WebContents* web_contents =
264 browser()->tab_strip_model()->GetActiveWebContents();
265 EXPECT_FALSE(web_contents->IsCrashed());
266 // See handler_function_type_checking.html.
267 std::string result;
268 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
269 web_contents,
270 "window.domAutomationController.send("
271 "document.getElementById('status').textContent.trim());",
272 &result));
273 EXPECT_EQ("success", result);
274}
275
Devlin Croninc3a1e5072017-08-17 17:02:49276IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
rdevlin.cronin75b803b2016-03-02 00:13:47277 MoreNativeFunctionInterceptionTests) {
rdevlin.cronin75b803b2016-03-02 00:13:47278 // We need to create runtime bindings in the web page. An extension that's
279 // externally connectable will do that for us.
280 ASSERT_TRUE(
281 LoadExtension(test_data_dir_.AppendASCII("bindings")
282 .AppendASCII("externally_connectable_everywhere")));
283
284 ui_test_utils::NavigateToURL(
285 browser(),
286 embedded_test_server()->GetURL(
287 "/extensions/api_test/bindings/function_interceptions.html"));
288 content::WebContents* web_contents =
289 browser()->tab_strip_model()->GetActiveWebContents();
290 EXPECT_FALSE(web_contents->IsCrashed());
291 // See function_interceptions.html.
292 std::string result;
293 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
294 web_contents, "window.domAutomationController.send(window.testStatus);",
295 &result));
296 EXPECT_EQ("success", result);
297}
298
asargent79b64c32016-08-04 17:17:14299class FramesExtensionBindingsApiTest : public ExtensionBindingsApiTest {
300 public:
301 void SetUpCommandLine(base::CommandLine* command_line) override {
302 ExtensionBindingsApiTest::SetUpCommandLine(command_line);
Devlin Croninc3a1e5072017-08-17 17:02:49303 command_line->AppendSwitch(::switches::kDisablePopupBlocking);
asargent79b64c32016-08-04 17:17:14304 }
305};
306
307// This tests that web pages with iframes or child windows pointing at
308// chrome-extenison:// urls, both web_accessible and nonexistent pages, don't
309// get improper extensions bindings injected while they briefly still point at
310// about:blank and are still scriptable by their parent.
311//
312// The general idea is to load up 2 extensions, one which listens for external
313// messages ("receiver") and one which we'll try first faking messages from in
314// the web page's iframe, as well as actually send a message from later
315// ("sender").
Dave Tapuska61ed7fbb2017-09-05 21:42:08316IN_PROC_BROWSER_TEST_P(FramesExtensionBindingsApiTest, FramesBeforeNavigation) {
asargent79b64c32016-08-04 17:17:14317 // Load the sender and receiver extensions, and make sure they are ready.
318 ExtensionTestMessageListener sender_ready("sender_ready", true);
319 const Extension* sender = LoadExtension(
320 test_data_dir_.AppendASCII("bindings").AppendASCII("message_sender"));
321 ASSERT_NE(nullptr, sender);
322 ASSERT_TRUE(sender_ready.WaitUntilSatisfied());
323
324 ExtensionTestMessageListener receiver_ready("receiver_ready", false);
325 const Extension* receiver =
326 LoadExtension(test_data_dir_.AppendASCII("bindings")
327 .AppendASCII("external_message_listener"));
328 ASSERT_NE(nullptr, receiver);
329 ASSERT_TRUE(receiver_ready.WaitUntilSatisfied());
330
331 // Load the web page which tries to impersonate the sender extension via
332 // scripting iframes/child windows before they finish navigating to pages
333 // within the sender extension.
asargent79b64c32016-08-04 17:17:14334 ui_test_utils::NavigateToURL(
335 browser(),
336 embedded_test_server()->GetURL(
337 "/extensions/api_test/bindings/frames_before_navigation.html"));
338
339 bool page_success = false;
340 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
341 browser()->tab_strip_model()->GetWebContentsAt(0), "getResult()",
342 &page_success));
343 EXPECT_TRUE(page_success);
344
345 // Reply to |sender|, causing it to send a message over to |receiver|, and
346 // then ask |receiver| for the total message count. It should be 1 since
347 // |receiver| should not have received any impersonated messages.
348 sender_ready.Reply(receiver->id());
349 int message_count = 0;
350 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
351 ProcessManager::Get(profile())
352 ->GetBackgroundHostForExtension(receiver->id())
353 ->host_contents(),
354 "getMessageCountAfterReceivingRealSenderMessage()", &message_count));
355 EXPECT_EQ(1, message_count);
356}
357
Devlin Croninc3a1e5072017-08-17 17:02:49358IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, TestFreezingChrome) {
rdevlin.cronin741da002017-04-24 20:27:41359 ui_test_utils::NavigateToURL(
360 browser(), embedded_test_server()->GetURL(
361 "/extensions/api_test/bindings/freeze.html"));
362 content::WebContents* web_contents =
363 browser()->tab_strip_model()->GetActiveWebContents();
364 ASSERT_FALSE(web_contents->IsCrashed());
365}
366
rdevlin.cronine6e20022017-06-13 18:23:40367// Tests interaction with event filter parsing.
Devlin Croninc3a1e5072017-08-17 17:02:49368IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, TestEventFilterParsing) {
rdevlin.cronine6e20022017-06-13 18:23:40369 ExtensionTestMessageListener listener("ready", false);
370 ASSERT_TRUE(
371 LoadExtension(test_data_dir_.AppendASCII("bindings/event_filter")));
372 ASSERT_TRUE(listener.WaitUntilSatisfied());
373
374 ResultCatcher catcher;
375 ui_test_utils::NavigateToURL(
376 browser(), embedded_test_server()->GetURL("example.com", "/title1.html"));
377 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
378}
379
rdevlin.cronin350824d42017-06-16 14:47:35380// crbug.com/733337
Devlin Croninc3a1e5072017-08-17 17:02:49381IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, ValidationInterception) {
rdevlin.cronin350824d42017-06-16 14:47:35382 // We need to create runtime bindings in the web page. An extension that's
383 // externally connectable will do that for us.
384 ASSERT_TRUE(
385 LoadExtension(test_data_dir_.AppendASCII("bindings")
386 .AppendASCII("externally_connectable_everywhere")));
387
388 content::WebContents* web_contents =
389 browser()->tab_strip_model()->GetActiveWebContents();
390 ui_test_utils::NavigateToURL(
391 browser(),
392 embedded_test_server()->GetURL(
393 "/extensions/api_test/bindings/validation_interception.html"));
394 content::WaitForLoadStop(web_contents);
395 ASSERT_FALSE(web_contents->IsCrashed());
396 bool caught = false;
397 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
398 web_contents, "domAutomationController.send(caught)", &caught));
399 EXPECT_TRUE(caught);
400}
401
Devlin Cronin5cf20f02017-10-10 14:25:04402IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, UncaughtExceptionLogging) {
403 ASSERT_TRUE(RunExtensionTest("bindings/uncaught_exception_logging"))
404 << message_;
405}
406
Alex Moshchuke63b9e92017-10-14 00:27:22407// Verify that when a web frame embeds an extension subframe, and that subframe
408// is the only active portion of the extension, the subframe gets proper JS
409// bindings. See https://crbug.com/760341.
410IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
411 ExtensionSubframeGetsBindings) {
412 // Load an extension that does not have a background page or popup, so it
413 // won't be activated just yet.
414 const extensions::Extension* extension =
415 LoadExtension(test_data_dir_.AppendASCII("bindings")
416 .AppendASCII("extension_subframe_gets_bindings"));
417 ASSERT_TRUE(extension);
418
419 // Navigate current tab to a web URL with a subframe.
420 content::WebContents* web_contents =
421 browser()->tab_strip_model()->GetActiveWebContents();
422 ui_test_utils::NavigateToURL(browser(),
423 embedded_test_server()->GetURL("/iframe.html"));
424
425 // Navigate the subframe to the extension URL, which should activate the
426 // extension.
427 GURL extension_url(extension->GetResourceURL("page.html"));
428 ResultCatcher catcher;
429 content::NavigateIframeToURL(web_contents, "test", extension_url);
430 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
431}
432
Devlin Croninb15c4f4e2017-12-15 21:20:11433IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
434 ExtensionListenersRemoveContext) {
435 const Extension* extension = LoadExtension(
436 test_data_dir_.AppendASCII("bindings/listeners_destroy_context"));
437 ASSERT_TRUE(extension);
438
439 ExtensionTestMessageListener listener("ready", true);
440
441 // Navigate to a web page with an iframe (the iframe is title1.html).
442 GURL main_frame_url = embedded_test_server()->GetURL("a.com", "/iframe.html");
443 ui_test_utils::NavigateToURL(browser(), main_frame_url);
444
445 content::WebContents* tab =
446 browser()->tab_strip_model()->GetActiveWebContents();
447
448 content::RenderFrameHost* main_frame = tab->GetMainFrame();
449 content::RenderFrameHost* subframe = ChildFrameAt(main_frame, 0);
450 content::RenderFrameDeletedObserver subframe_deleted(subframe);
451
452 // Wait for the extension's content script to be ready.
453 ASSERT_TRUE(listener.WaitUntilSatisfied());
454
455 // It's actually critical to the test that these frames are in the same
456 // process, because otherwise a crash in the iframe wouldn't be detectable
457 // (since we rely on JS execution in the main frame to tell if the renderer
458 // crashed - see comment below).
459 content::RenderProcessHost* main_frame_process = main_frame->GetProcess();
460 EXPECT_EQ(main_frame_process, subframe->GetProcess());
461
462 ExtensionTestMessageListener failure_listener("failed", false);
463
464 // Tell the extension to register listeners that will remove the iframe, and
465 // trigger them.
466 listener.Reply("go!");
467
468 // The frame will be deleted.
469 subframe_deleted.WaitUntilDeleted();
470
471 // Unfortunately, we don't have a good way of checking if something crashed
472 // after the frame was removed. WebContents::IsCrashed() seems like it should
473 // work, but is insufficient. Instead, use JS execution as the source of
474 // true.
475 EXPECT_FALSE(tab->IsCrashed());
476 EXPECT_EQ(main_frame_url, main_frame->GetLastCommittedURL());
477 EXPECT_EQ(main_frame_process, main_frame->GetProcess());
478 bool renderer_valid = false;
479 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
480 main_frame, "domAutomationController.send(true);", &renderer_valid));
481 EXPECT_TRUE(renderer_valid);
482 EXPECT_FALSE(failure_listener.was_satisfied());
483}
484
Devlin Croninb48b6f92018-01-12 23:17:59485IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, UseAPIsAfterContextRemoval) {
486 EXPECT_TRUE(RunExtensionTest("bindings/invalidate_context")) << message_;
487}
488
Devlin Cronin47e79042018-02-02 18:32:26489// TODO(devlin): Can this be combined with
490// ExtensionBindingsApiTest.UseAPIsAfterContextRemoval?
491IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest, UseAppAPIAfterFrameRemoval) {
492 ASSERT_TRUE(RunExtensionTest("crazy_extension"));
493}
494
Devlin Cronind2389082018-08-14 01:53:26495// Tests attaching two listeners from the same extension but different pages,
496// then removing one, and ensuring the second is still notified.
497// Regression test for https://crbug.com/868763.
498IN_PROC_BROWSER_TEST_P(
499 ExtensionBindingsApiTest,
500 MultipleEventListenersFromDifferentContextsAndTheSameExtension) {
501 // A script that listens for tab creation and populates the result in a
502 // global variable.
503 constexpr char kTestPageScript[] = R"(
504 window.tabEventId = -1;
505 function registerListener() {
506 chrome.tabs.onCreated.addListener((tab) => {
507 window.tabEventId = tab.id;
508 });
509 }
510 )";
511 TestExtensionDir test_dir;
512 test_dir.WriteManifest(R"(
513 {
514 "name": "Duplicate event listeners",
515 "manifest_version": 2,
516 "version": "0.1"
517 })");
518 test_dir.WriteFile(FILE_PATH_LITERAL("page.html"),
519 R"(<html><script src="page.js"></script></html>)");
520 test_dir.WriteFile(FILE_PATH_LITERAL("page.js"), kTestPageScript);
521
522 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
523 ASSERT_TRUE(extension);
524
525 // Set up: open two tabs to the same extension page, and wait for each to
526 // load.
527 const GURL page_url = extension->GetResourceURL("page.html");
528 ui_test_utils::NavigateToURLWithDisposition(
529 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
530 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
531 content::WebContents* first_tab =
532 browser()->tab_strip_model()->GetActiveWebContents();
533 ui_test_utils::NavigateToURLWithDisposition(
534 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
535 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
536 content::WebContents* second_tab =
537 browser()->tab_strip_model()->GetActiveWebContents();
538
539 // Initially, there are no listeners registered.
540 EventRouter* event_router = EventRouter::Get(profile());
541 EXPECT_FALSE(event_router->ExtensionHasEventListener(extension->id(),
542 "tabs.onCreated"));
543
544 // Register both lsiteners, and verify they were added.
545 ASSERT_TRUE(content::ExecuteScript(first_tab, "registerListener()"));
546 ASSERT_TRUE(content::ExecuteScript(second_tab, "registerListener()"));
547 EXPECT_TRUE(event_router->ExtensionHasEventListener(extension->id(),
548 "tabs.onCreated"));
549
550 // Close one of the extension pages.
551 constexpr bool add_to_history = false;
552 content::WebContentsDestroyedWatcher watcher(second_tab);
553 chrome::CloseWebContents(browser(), second_tab, add_to_history);
554 watcher.Wait();
555 // Hacky round trip to the renderer to flush IPCs.
556 ASSERT_TRUE(content::ExecuteScript(first_tab, ""));
557
558 // Since the second page is still open, the extension should still be
559 // registered as a listener.
560 EXPECT_TRUE(event_router->ExtensionHasEventListener(extension->id(),
561 "tabs.onCreated"));
562
563 // Open a new tab.
564 ui_test_utils::NavigateToURLWithDisposition(
565 browser(), GURL("chrome://newtab"),
566 WindowOpenDisposition::NEW_FOREGROUND_TAB,
567 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
568 content::WebContents* new_tab =
569 browser()->tab_strip_model()->GetActiveWebContents();
570
571 // The extension should have been notified about the new tab, and have
572 // recorded the result.
573 int result_tab_id = -1;
574 EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
575 first_tab, "domAutomationController.send(window.tabEventId)",
576 &result_tab_id));
577 EXPECT_EQ(SessionTabHelper::IdForTab(new_tab).id(), result_tab_id);
578}
579
Devlin Cronine80e6fc12019-02-05 00:44:23580// Alias purely for test instantiation purposes.
581using ExtensionBindingsUserGestureTest = ExtensionBindingsApiTest;
582
Devlin Croninc3d017992018-09-11 01:21:20583// Verifies that user gestures are carried through extension messages.
Devlin Cronine80e6fc12019-02-05 00:44:23584IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
Devlin Croninc3d017992018-09-11 01:21:20585 UserGestureFromExtensionMessageTest) {
586 TestExtensionDir test_dir;
587 test_dir.WriteManifest(
588 R"({
589 "name": "User Gesture Content Script",
590 "manifest_version": 2,
591 "version": "0.1",
592 "background": { "scripts": ["background.js"] },
593 "content_scripts": [{
594 "matches": ["*://*.example.com:*/*"],
595 "js": ["content_script.js"],
596 "run_at": "document_end"
597 }]
598 })");
599 test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
600 R"(const button = document.getElementById('go-button');
601 button.addEventListener('click', () => {
602 chrome.runtime.sendMessage('clicked');
603 });)");
604 test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
605 R"(chrome.runtime.onMessage.addListener((message) => {
606 chrome.test.sendMessage(
607 'Clicked: ' +
608 chrome.test.isProcessingUserGesture());
609 });)");
610
611 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
612 ASSERT_TRUE(extension);
613
614 const GURL url = embedded_test_server()->GetURL(
615 "example.com", "/extensions/page_with_button.html");
616 ui_test_utils::NavigateToURL(browser(), url);
617
618 content::WebContents* tab =
619 browser()->tab_strip_model()->GetActiveWebContents();
620
621 {
622 // Passing a message without an active user gesture shouldn't result in a
623 // gesture being active on the receiving end.
624 ExtensionTestMessageListener listener(false);
625 content::EvalJsResult result =
626 content::EvalJs(tab, "document.getElementById('go-button').click()",
627 content::EXECUTE_SCRIPT_NO_USER_GESTURE);
628 EXPECT_TRUE(result.value.is_none());
629
630 EXPECT_TRUE(listener.WaitUntilSatisfied());
631 EXPECT_EQ("Clicked: false", listener.message());
632 }
633
634 {
635 // If there is an active user gesture when the message is sent, we should
636 // synthesize a user gesture on the receiving end.
637 ExtensionTestMessageListener listener(false);
638 content::EvalJsResult result =
639 content::EvalJs(tab, "document.getElementById('go-button').click()");
640 EXPECT_TRUE(result.value.is_none());
641
642 EXPECT_TRUE(listener.WaitUntilSatisfied());
643 EXPECT_EQ("Clicked: true", listener.message());
644 }
645}
646
647// Verifies that user gestures from API calls are active when the callback is
648// triggered.
Devlin Cronine80e6fc12019-02-05 00:44:23649IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
Devlin Croninc3d017992018-09-11 01:21:20650 UserGestureInExtensionAPICallback) {
651 TestExtensionDir test_dir;
652 test_dir.WriteManifest(
653 R"({
654 "name": "User Gesture Extension API Callback",
655 "manifest_version": 2,
656 "version": "0.1"
657 })");
658 test_dir.WriteFile(FILE_PATH_LITERAL("page.html"), "<html></html>");
659
660 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
661 ASSERT_TRUE(extension);
662
663 const GURL extension_page = extension->GetResourceURL("page.html");
664 ui_test_utils::NavigateToURL(browser(), extension_page);
665
666 content::WebContents* tab =
667 browser()->tab_strip_model()->GetActiveWebContents();
668
669 constexpr char kScript[] =
670 R"(chrome.tabs.query({}, (tabs) => {
671 let message;
672 if (chrome.runtime.lastError)
673 message = 'Unexpected error: ' + chrome.runtime.lastError;
674 else
675 message = 'Has gesture: ' + chrome.test.isProcessingUserGesture();
676 domAutomationController.send(message);
677 });)";
678
679 {
680 // Triggering an API without an active gesture shouldn't result in a
681 // gesture in the callback.
682 std::string message;
683 EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
684 tab, kScript, &message));
685 EXPECT_EQ("Has gesture: false", message);
686 }
687 {
688 // If there was an active gesture at the time of the API call, there should
689 // be an active gesture in the callback.
690 std::string message;
691 EXPECT_TRUE(content::ExecuteScriptAndExtractString(tab, kScript, &message));
692 EXPECT_EQ("Has gesture: true", message);
693 }
694}
695
Devlin Cronine80e6fc12019-02-05 00:44:23696// Tests that a web page can consume a user gesture after an extension sends and
697// receives a reply during the same user gesture.
698// Regression test for https://crbug.com/921141.
699IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
700 WebUserGestureAfterMessagingCallback) {
701 TestExtensionDir test_dir;
702 test_dir.WriteManifest(
703 R"({
704 "name": "User Gesture Messaging Test",
705 "version": "0.1",
706 "manifest_version": 2,
707 "content_scripts": [{
708 "matches": ["*://*/*"],
709 "js": ["content_script.js"],
710 "run_at": "document_start"
711 }],
712 "background": {
713 "scripts": ["background.js"]
714 }
715 })");
716 test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
717 R"(window.addEventListener('mousedown', () => {
718 chrome.runtime.sendMessage('hello', () => {
719 let message = chrome.test.isProcessingUserGesture() ?
720 'got reply' : 'no user gesture';
721 chrome.test.sendMessage(message);
722 });
723 });)");
724 test_dir.WriteFile(
725 FILE_PATH_LITERAL("background.js"),
726 R"(chrome.runtime.onMessage.addListener((message, sender, respond) => {
727 respond('reply');
728 });
729 chrome.test.sendMessage('ready');)");
730
731 const Extension* extension = nullptr;
732 {
733 ExtensionTestMessageListener listener("ready", false);
734 extension = LoadExtension(test_dir.UnpackedPath());
735 ASSERT_TRUE(extension);
736 EXPECT_TRUE(listener.WaitUntilSatisfied());
737 }
738
739 ui_test_utils::NavigateToURL(
740 browser(), embedded_test_server()->GetURL(
741 "/extensions/api_test/bindings/user_gesture_test.html"));
742 content::WebContents* web_contents =
743 browser()->tab_strip_model()->GetActiveWebContents();
744 ASSERT_TRUE(web_contents);
745
746 {
747 ExtensionTestMessageListener listener("got reply", false);
748 listener.set_failure_message("no user gesture");
749 MouseDownInWebContents(web_contents);
750 EXPECT_TRUE(listener.WaitUntilSatisfied());
751 }
752
753 MouseUpInWebContents(web_contents);
754
755 EXPECT_EQ("success",
756 content::EvalJs(web_contents, "window.getEnteredFullscreen",
757 content::EXECUTE_SCRIPT_NO_USER_GESTURE));
758}
759
760// Tests that a web page can consume a user gesture after an extension calls a
761// method and receives the response in the callback.
762// Regression test for https://crbug.com/921141.
763IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
764 WebUserGestureAfterApiCallback) {
765 TestExtensionDir test_dir;
766 test_dir.WriteManifest(
767 R"({
768 "name": "User Gesture Messaging Test",
769 "version": "0.1",
770 "manifest_version": 2,
771 "content_scripts": [{
772 "matches": ["*://*/*"],
773 "js": ["content_script.js"],
774 "run_at": "document_start"
775 }],
776 "permissions": ["storage"]
777 })");
778 test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
779 R"(window.addEventListener('mousedown', () => {
780 chrome.storage.local.get('foo', () => {
781 let message = chrome.test.isProcessingUserGesture() ?
782 'got reply' : 'no user gesture';
783 chrome.test.sendMessage(message);
784 });
785 });)");
786
787 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
788 ASSERT_TRUE(extension);
789
790 ui_test_utils::NavigateToURL(
791 browser(), embedded_test_server()->GetURL(
792 "/extensions/api_test/bindings/user_gesture_test.html"));
793 content::WebContents* web_contents =
794 browser()->tab_strip_model()->GetActiveWebContents();
795 ASSERT_TRUE(web_contents);
796
797 {
798 ExtensionTestMessageListener listener("got reply", false);
799 listener.set_failure_message("no user gesture");
800 MouseDownInWebContents(web_contents);
801 EXPECT_TRUE(listener.WaitUntilSatisfied());
802 }
803
804 MouseUpInWebContents(web_contents);
805
806 EXPECT_EQ("success",
807 content::EvalJs(web_contents, "window.getEnteredFullscreen",
808 content::EXECUTE_SCRIPT_NO_USER_GESTURE));
809}
810
Devlin Cronind900688a2019-02-01 01:36:11811// Tests that bindings are properly instantiated for a window navigated to an
812// extension URL after being opened with an undefined URL.
813// Regression test for https://crbug.com/925118.
814IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
815 TestBindingsAvailableWithNavigatedBlankWindow) {
816 constexpr char kManifest[] =
817 R"({
818 "name": "chrome.runtime bug checker",
819 "description": "test case for crbug.com/925118",
820 "version": "0",
821 "manifest_version": 2
822 })";
823 constexpr char kOpenerHTML[] =
824 R"(<!DOCTYPE html>
825 <html>
826 <head>
827 <script src='opener.js'></script>
828 </head>
829 <body>
830 </body>
831 </html>)";
832 // opener.js opens a blank window and then navigates it to an extension URL
833 // (where extension APIs should be available).
834 constexpr char kOpenerJS[] =
835 R"(const url = chrome.runtime.getURL('/page.html');
836 const win = window.open(undefined, '');
837 win.location = url;
838 chrome.test.notifyPass())";
839 constexpr char kPageHTML[] =
840 R"(<!DOCTYPE html>
841 <html>
842 This space intentionally left blank.
843 </html>)";
844 TestExtensionDir extension_dir;
845 extension_dir.WriteManifest(kManifest);
846 extension_dir.WriteFile(FILE_PATH_LITERAL("opener.html"), kOpenerHTML);
847 extension_dir.WriteFile(FILE_PATH_LITERAL("opener.js"), kOpenerJS);
848 extension_dir.WriteFile(FILE_PATH_LITERAL("page.html"), kPageHTML);
849
850 const Extension* extension = LoadExtension(extension_dir.UnpackedPath());
851 const GURL target_url = extension->GetResourceURL("page.html");
852
853 ResultCatcher catcher;
854 content::TestNavigationObserver observer(target_url);
855 observer.StartWatchingNewWebContents();
856 ui_test_utils::NavigateToURL(browser(),
857 extension->GetResourceURL("opener.html"));
858 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
859 observer.Wait();
860 EXPECT_TRUE(observer.last_navigation_succeeded());
861
862 content::WebContents* web_contents =
863 browser()->tab_strip_model()->GetActiveWebContents();
864 EXPECT_EQ(target_url, web_contents->GetLastCommittedURL());
865
866 // Check whether bindings are available. They should be.
867 constexpr char kScript[] =
868 R"(let message;
869 if (!chrome.runtime)
870 message = 'Runtime not defined';
871 else if (!chrome.tabs)
872 message = 'Tabs not defined';
873 else
874 message = 'success';
875 domAutomationController.send(message);)";
876 std::string result;
877 // Note: Can't use EvalJs() because of CSP in extension pages.
878 EXPECT_TRUE(
879 content::ExecuteScriptAndExtractString(web_contents, kScript, &result));
880 EXPECT_EQ("success", result);
881}
882
Devlin Croninc3a1e5072017-08-17 17:02:49883// Run core bindings API tests with both native and JS-based bindings. This
884// ensures we have some minimum level of coverage while in the experimental
885// phase, when native bindings may be enabled on trunk but not at 100% stable.
Devlin Cronine80e6fc12019-02-05 00:44:23886constexpr BindingsApiTestConfig kBindingsConfigs[] = {
887 {kNativeBindings, base::nullopt},
888 {kJavaScriptBindings, base::nullopt},
889};
Devlin Croninc3a1e5072017-08-17 17:02:49890
Devlin Cronine80e6fc12019-02-05 00:44:23891// Run user-gesture related tests with combinations of native and JS-based
892// bindings as well as UAv1 and UAv2.
893constexpr BindingsApiTestConfig kBindingsAndUserGestureConfigs[] = {
894 {kNativeBindings, kUserActivationV1},
895 {kNativeBindings, kUserActivationV2},
896 {kJavaScriptBindings, kUserActivationV1},
897 {kJavaScriptBindings, kUserActivationV2},
898};
899
900INSTANTIATE_TEST_SUITE_P(,
901 ExtensionBindingsApiTest,
902 ::testing::ValuesIn(kBindingsConfigs));
903INSTANTIATE_TEST_SUITE_P(,
Victor Costan4a060e82019-01-28 18:25:34904 FramesExtensionBindingsApiTest,
Devlin Cronine80e6fc12019-02-05 00:44:23905 ::testing::ValuesIn(kBindingsConfigs));
906INSTANTIATE_TEST_SUITE_P(,
907 ExtensionBindingsUserGestureTest,
908 ::testing::ValuesIn(kBindingsAndUserGestureConfigs));
Devlin Croninc3a1e5072017-08-17 17:02:49909
[email protected]adafe5b2013-08-09 10:35:04910} // namespace
911} // namespace extensions