blob: 26e77d42f07019dd73950f5ca4641e22eb9317fe [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"
lfg910f2f92014-09-19 05:31:0927#include "extensions/test/extension_test_message_listener.h"
yoze8dc2f12014-09-09 23:16:3228#include "extensions/test/result_catcher.h"
Devlin Cronind2389082018-08-14 01:53:2629#include "extensions/test/test_extension_dir.h"
rdevlin.cronine6e20022017-06-13 18:23:4030#include "net/dns/mock_host_resolver.h"
rdevlin.cronin83a4b3a2015-10-28 21:43:5831#include "net/test/embedded_test_server/embedded_test_server.h"
Devlin Cronine80e6fc12019-02-05 00:44:2332#include "third_party/blink/public/platform/web_mouse_event.h"
[email protected]fad73672012-06-15 23:26:0633
[email protected]adafe5b2013-08-09 10:35:0434namespace extensions {
35namespace {
36
Devlin Cronine80e6fc12019-02-05 00:44:2337void MouseDownInWebContents(content::WebContents* web_contents) {
38 blink::WebMouseEvent mouse_event(
39 blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
40 blink::WebInputEvent::GetStaticTimeStampForTests());
41 mouse_event.button = blink::WebMouseEvent::Button::kLeft;
42 mouse_event.SetPositionInWidget(10, 10);
43 mouse_event.click_count = 1;
44 web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent(
45 mouse_event);
46}
47
48void MouseUpInWebContents(content::WebContents* web_contents) {
49 blink::WebMouseEvent mouse_event(
50 blink::WebInputEvent::kMouseUp, blink::WebInputEvent::kNoModifiers,
51 blink::WebInputEvent::GetStaticTimeStampForTests());
52 mouse_event.button = blink::WebMouseEvent::Button::kLeft;
53 mouse_event.SetPositionInWidget(10, 10);
54 mouse_event.click_count = 1;
55 web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent(
56 mouse_event);
57}
58
Devlin Cronin242d19d22019-03-12 18:08:4859class ExtensionBindingsApiTest : public ExtensionApiTest {
jochen7923c2a2015-07-14 10:04:4560 public:
Devlin Cronina3fe3d602017-11-22 04:47:4361 ExtensionBindingsApiTest() {}
62 ~ExtensionBindingsApiTest() override {}
63
jochen7923c2a2015-07-14 10:04:4564 void SetUpOnMainThread() override {
rdevlin.cronin17160cc62016-11-23 05:33:0865 ExtensionApiTest::SetUpOnMainThread();
rdevlin.cronine6e20022017-06-13 18:23:4066 host_resolver()->AddRule("*", "127.0.0.1");
67 ASSERT_TRUE(StartEmbeddedTestServer());
jochen7923c2a2015-07-14 10:04:4568 }
Devlin Cronina3fe3d602017-11-22 04:47:4369
70 private:
Devlin Cronina3fe3d602017-11-22 04:47:4371 DISALLOW_COPY_AND_ASSIGN(ExtensionBindingsApiTest);
jochen7923c2a2015-07-14 10:04:4572};
[email protected]adafe5b2013-08-09 10:35:0473
Devlin Cronin242d19d22019-03-12 18:08:4874IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
[email protected]7eef3942013-08-14 02:53:4975 UnavailableBindingsNeverRegistered) {
76 // Test will request the 'storage' permission.
77 PermissionsRequestFunction::SetIgnoreUserGestureForTests(true);
78 ASSERT_TRUE(RunExtensionTest(
79 "bindings/unavailable_bindings_never_registered")) << message_;
80}
81
Devlin Cronin242d19d22019-03-12 18:08:4882IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
[email protected]adafe5b2013-08-09 10:35:0483 ExceptionInHandlerShouldNotCrash) {
[email protected]c80b8ee2011-12-03 04:26:5284 ASSERT_TRUE(RunExtensionSubtest(
85 "bindings/exception_in_handler_should_not_crash",
86 "page.html")) << message_;
87}
[email protected]fad73672012-06-15 23:26:0688
89// Tests that an error raised during an async function still fires
[email protected]754ea8b72013-01-08 15:10:3190// the callback, but sets chrome.runtime.lastError.
Devlin Cronin242d19d22019-03-12 18:08:4891IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, LastError) {
[email protected]fad73672012-06-15 23:26:0692 ASSERT_TRUE(LoadExtension(
Devlin Croninc3a1e5072017-08-17 17:02:4993 test_data_dir_.AppendASCII("bindings").AppendASCII("last_error")));
[email protected]fad73672012-06-15 23:26:0694
95 // Get the ExtensionHost that is hosting our background page.
[email protected]98b6d942013-11-10 00:34:0796 extensions::ProcessManager* manager =
reillyg0ea3fa902014-10-28 15:30:2397 extensions::ProcessManager::Get(browser()->profile());
[email protected]3a1dc572012-07-31 22:25:1398 extensions::ExtensionHost* host = FindHostWithPath(manager, "/bg.html", 1);
[email protected]fad73672012-06-15 23:26:0699
100 bool result = false;
David Benjamin5a792652018-06-08 02:15:42101 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(host->host_contents(),
102 "testLastError()", &result));
[email protected]fad73672012-06-15 23:26:06103 EXPECT_TRUE(result);
104}
[email protected]52eafbd2013-04-03 04:43:19105
[email protected]adafe5b2013-08-09 10:35:04106// Regression test that we don't delete our own bindings with about:blank
107// iframes.
Devlin Cronin242d19d22019-03-12 18:08:48108IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, AboutBlankIframe) {
[email protected]adafe5b2013-08-09 10:35:04109 ResultCatcher catcher;
110 ExtensionTestMessageListener listener("load", true);
111
112 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("bindings")
113 .AppendASCII("about_blank_iframe")));
114
115 ASSERT_TRUE(listener.WaitUntilSatisfied());
116
117 const Extension* extension = LoadExtension(
118 test_data_dir_.AppendASCII("bindings")
119 .AppendASCII("internal_apis_not_on_chrome_object"));
120 ASSERT_TRUE(extension);
121 listener.Reply(extension->id());
122
123 ASSERT_TRUE(catcher.GetNextResult()) << message_;
124}
125
Devlin Cronin242d19d22019-03-12 18:08:48126IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
[email protected]adafe5b2013-08-09 10:35:04127 InternalAPIsNotOnChromeObject) {
[email protected]52eafbd2013-04-03 04:43:19128 ASSERT_TRUE(RunExtensionSubtest(
129 "bindings/internal_apis_not_on_chrome_object",
130 "page.html")) << message_;
131}
[email protected]adafe5b2013-08-09 10:35:04132
[email protected]fc034482013-08-09 20:25:14133// Tests that we don't override events when bindings are re-injected.
134// Regression test for http://crbug.com/269149.
rpaquay96bf3b7d2014-11-26 00:19:08135// Regression test for http://crbug.com/436593.
Henrik Grunell6b7d9db2017-06-14 10:27:20136// Flaky on Mac. http://crbug.com/733064.
Giovanni Ortuño Urquidi7ce215452017-06-14 03:34:08137#if defined(OS_MACOSX)
138#define MAYBE_EventOverriding DISABLED_EventOverriding
139#else
140#define MAYBE_EventOverriding EventOverriding
141#endif
Devlin Cronin242d19d22019-03-12 18:08:48142IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, MAYBE_EventOverriding) {
[email protected]fc034482013-08-09 20:25:14143 ASSERT_TRUE(RunExtensionTest("bindings/event_overriding")) << message_;
rdevlin.cronind734f682017-06-13 21:23:11144 // The extension test removes a window and, during window removal, sends the
145 // success message. Make sure we flush all pending tasks.
146 base::RunLoop().RunUntilIdle();
[email protected]fc034482013-08-09 20:25:14147}
148
kalman1bd5b182015-01-13 19:01:18149// Tests the effectiveness of the 'nocompile' feature file property.
150// Regression test for http://crbug.com/356133.
Devlin Cronin242d19d22019-03-12 18:08:48151IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, Nocompile) {
kalman1bd5b182015-01-13 19:01:18152 ASSERT_TRUE(RunExtensionSubtest("bindings/nocompile", "page.html"))
153 << message_;
154}
155
Devlin Cronin242d19d22019-03-12 18:08:48156IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, ApiEnums) {
rdevlin.cronin2ba3c88d2015-03-03 01:18:22157 ASSERT_TRUE(RunExtensionTest("bindings/api_enums")) << message_;
Nico Weber42c88d62019-02-11 03:09:25158}
rdevlin.cronin2ba3c88d2015-03-03 01:18:22159
jochen7923c2a2015-07-14 10:04:45160// Regression test for http://crbug.com/504011 - proper access checks on
161// getModuleSystem().
Devlin Cronin242d19d22019-03-12 18:08:48162IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, ModuleSystem) {
jochen7923c2a2015-07-14 10:04:45163 ASSERT_TRUE(RunExtensionTest("bindings/module_system")) << message_;
164}
165
Devlin Cronin242d19d22019-03-12 18:08:48166IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, NoExportOverriding) {
rdevlin.cronin83a4b3a2015-10-28 21:43:58167 // We need to create runtime bindings in the web page. An extension that's
168 // externally connectable will do that for us.
169 ASSERT_TRUE(LoadExtension(
170 test_data_dir_.AppendASCII("bindings")
171 .AppendASCII("externally_connectable_everywhere")));
172
173 ui_test_utils::NavigateToURL(
174 browser(),
175 embedded_test_server()->GetURL(
176 "/extensions/api_test/bindings/override_exports.html"));
177
178 // See chrome/test/data/extensions/api_test/bindings/override_exports.html.
179 std::string result;
180 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
181 browser()->tab_strip_model()->GetActiveWebContents(),
182 "window.domAutomationController.send("
183 "document.getElementById('status').textContent.trim());",
184 &result));
185 EXPECT_EQ("success", result);
186}
187
Devlin Cronin242d19d22019-03-12 18:08:48188IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, NoGinDefineOverriding) {
rdevlin.cronin415b73b2015-11-13 01:14:47189 // We need to create runtime bindings in the web page. An extension that's
190 // externally connectable will do that for us.
191 ASSERT_TRUE(LoadExtension(
192 test_data_dir_.AppendASCII("bindings")
193 .AppendASCII("externally_connectable_everywhere")));
194
195 ui_test_utils::NavigateToURL(
196 browser(),
197 embedded_test_server()->GetURL(
198 "/extensions/api_test/bindings/override_gin_define.html"));
199 ASSERT_FALSE(
200 browser()->tab_strip_model()->GetActiveWebContents()->IsCrashed());
201
202 // See chrome/test/data/extensions/api_test/bindings/override_gin_define.html.
203 std::string result;
204 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
205 browser()->tab_strip_model()->GetActiveWebContents(),
206 "window.domAutomationController.send("
207 "document.getElementById('status').textContent.trim());",
208 &result));
209 EXPECT_EQ("success", result);
210}
211
Devlin Cronin242d19d22019-03-12 18:08:48212IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, HandlerFunctionTypeChecking) {
rdevlin.cronina5ecbc82015-10-29 23:41:29213 ui_test_utils::NavigateToURL(
214 browser(),
215 embedded_test_server()->GetURL(
216 "/extensions/api_test/bindings/handler_function_type_checking.html"));
217 content::WebContents* web_contents =
218 browser()->tab_strip_model()->GetActiveWebContents();
219 EXPECT_FALSE(web_contents->IsCrashed());
220 // See handler_function_type_checking.html.
221 std::string result;
222 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
223 web_contents,
224 "window.domAutomationController.send("
225 "document.getElementById('status').textContent.trim());",
226 &result));
227 EXPECT_EQ("success", result);
228}
229
Devlin Cronin242d19d22019-03-12 18:08:48230IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
rdevlin.cronin75b803b2016-03-02 00:13:47231 MoreNativeFunctionInterceptionTests) {
rdevlin.cronin75b803b2016-03-02 00:13:47232 // We need to create runtime bindings in the web page. An extension that's
233 // externally connectable will do that for us.
234 ASSERT_TRUE(
235 LoadExtension(test_data_dir_.AppendASCII("bindings")
236 .AppendASCII("externally_connectable_everywhere")));
237
238 ui_test_utils::NavigateToURL(
239 browser(),
240 embedded_test_server()->GetURL(
241 "/extensions/api_test/bindings/function_interceptions.html"));
242 content::WebContents* web_contents =
243 browser()->tab_strip_model()->GetActiveWebContents();
244 EXPECT_FALSE(web_contents->IsCrashed());
245 // See function_interceptions.html.
246 std::string result;
247 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
248 web_contents, "window.domAutomationController.send(window.testStatus);",
249 &result));
250 EXPECT_EQ("success", result);
251}
252
asargent79b64c32016-08-04 17:17:14253class FramesExtensionBindingsApiTest : public ExtensionBindingsApiTest {
254 public:
255 void SetUpCommandLine(base::CommandLine* command_line) override {
256 ExtensionBindingsApiTest::SetUpCommandLine(command_line);
Devlin Croninc3a1e5072017-08-17 17:02:49257 command_line->AppendSwitch(::switches::kDisablePopupBlocking);
asargent79b64c32016-08-04 17:17:14258 }
259};
260
261// This tests that web pages with iframes or child windows pointing at
262// chrome-extenison:// urls, both web_accessible and nonexistent pages, don't
263// get improper extensions bindings injected while they briefly still point at
264// about:blank and are still scriptable by their parent.
265//
266// The general idea is to load up 2 extensions, one which listens for external
267// messages ("receiver") and one which we'll try first faking messages from in
268// the web page's iframe, as well as actually send a message from later
269// ("sender").
Devlin Cronin242d19d22019-03-12 18:08:48270IN_PROC_BROWSER_TEST_F(FramesExtensionBindingsApiTest, FramesBeforeNavigation) {
asargent79b64c32016-08-04 17:17:14271 // Load the sender and receiver extensions, and make sure they are ready.
272 ExtensionTestMessageListener sender_ready("sender_ready", true);
273 const Extension* sender = LoadExtension(
274 test_data_dir_.AppendASCII("bindings").AppendASCII("message_sender"));
275 ASSERT_NE(nullptr, sender);
276 ASSERT_TRUE(sender_ready.WaitUntilSatisfied());
277
278 ExtensionTestMessageListener receiver_ready("receiver_ready", false);
279 const Extension* receiver =
280 LoadExtension(test_data_dir_.AppendASCII("bindings")
281 .AppendASCII("external_message_listener"));
282 ASSERT_NE(nullptr, receiver);
283 ASSERT_TRUE(receiver_ready.WaitUntilSatisfied());
284
285 // Load the web page which tries to impersonate the sender extension via
286 // scripting iframes/child windows before they finish navigating to pages
287 // within the sender extension.
asargent79b64c32016-08-04 17:17:14288 ui_test_utils::NavigateToURL(
289 browser(),
290 embedded_test_server()->GetURL(
291 "/extensions/api_test/bindings/frames_before_navigation.html"));
292
293 bool page_success = false;
294 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
295 browser()->tab_strip_model()->GetWebContentsAt(0), "getResult()",
296 &page_success));
297 EXPECT_TRUE(page_success);
298
299 // Reply to |sender|, causing it to send a message over to |receiver|, and
300 // then ask |receiver| for the total message count. It should be 1 since
301 // |receiver| should not have received any impersonated messages.
302 sender_ready.Reply(receiver->id());
303 int message_count = 0;
304 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
305 ProcessManager::Get(profile())
306 ->GetBackgroundHostForExtension(receiver->id())
307 ->host_contents(),
308 "getMessageCountAfterReceivingRealSenderMessage()", &message_count));
309 EXPECT_EQ(1, message_count);
310}
311
Devlin Cronin242d19d22019-03-12 18:08:48312IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, TestFreezingChrome) {
rdevlin.cronin741da002017-04-24 20:27:41313 ui_test_utils::NavigateToURL(
314 browser(), embedded_test_server()->GetURL(
315 "/extensions/api_test/bindings/freeze.html"));
316 content::WebContents* web_contents =
317 browser()->tab_strip_model()->GetActiveWebContents();
318 ASSERT_FALSE(web_contents->IsCrashed());
319}
320
rdevlin.cronine6e20022017-06-13 18:23:40321// Tests interaction with event filter parsing.
Devlin Cronin242d19d22019-03-12 18:08:48322IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, TestEventFilterParsing) {
rdevlin.cronine6e20022017-06-13 18:23:40323 ExtensionTestMessageListener listener("ready", false);
324 ASSERT_TRUE(
325 LoadExtension(test_data_dir_.AppendASCII("bindings/event_filter")));
326 ASSERT_TRUE(listener.WaitUntilSatisfied());
327
328 ResultCatcher catcher;
329 ui_test_utils::NavigateToURL(
330 browser(), embedded_test_server()->GetURL("example.com", "/title1.html"));
331 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
332}
333
rdevlin.cronin350824d42017-06-16 14:47:35334// crbug.com/733337
Devlin Cronin242d19d22019-03-12 18:08:48335IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, ValidationInterception) {
rdevlin.cronin350824d42017-06-16 14:47:35336 // We need to create runtime bindings in the web page. An extension that's
337 // externally connectable will do that for us.
338 ASSERT_TRUE(
339 LoadExtension(test_data_dir_.AppendASCII("bindings")
340 .AppendASCII("externally_connectable_everywhere")));
341
342 content::WebContents* web_contents =
343 browser()->tab_strip_model()->GetActiveWebContents();
344 ui_test_utils::NavigateToURL(
345 browser(),
346 embedded_test_server()->GetURL(
347 "/extensions/api_test/bindings/validation_interception.html"));
348 content::WaitForLoadStop(web_contents);
349 ASSERT_FALSE(web_contents->IsCrashed());
350 bool caught = false;
351 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
352 web_contents, "domAutomationController.send(caught)", &caught));
353 EXPECT_TRUE(caught);
354}
355
Devlin Cronin242d19d22019-03-12 18:08:48356IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, UncaughtExceptionLogging) {
Devlin Cronin5cf20f02017-10-10 14:25:04357 ASSERT_TRUE(RunExtensionTest("bindings/uncaught_exception_logging"))
358 << message_;
359}
360
Alex Moshchuke63b9e92017-10-14 00:27:22361// Verify that when a web frame embeds an extension subframe, and that subframe
362// is the only active portion of the extension, the subframe gets proper JS
363// bindings. See https://crbug.com/760341.
Devlin Cronin242d19d22019-03-12 18:08:48364IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
Alex Moshchuke63b9e92017-10-14 00:27:22365 ExtensionSubframeGetsBindings) {
366 // Load an extension that does not have a background page or popup, so it
367 // won't be activated just yet.
368 const extensions::Extension* extension =
369 LoadExtension(test_data_dir_.AppendASCII("bindings")
370 .AppendASCII("extension_subframe_gets_bindings"));
371 ASSERT_TRUE(extension);
372
373 // Navigate current tab to a web URL with a subframe.
374 content::WebContents* web_contents =
375 browser()->tab_strip_model()->GetActiveWebContents();
376 ui_test_utils::NavigateToURL(browser(),
377 embedded_test_server()->GetURL("/iframe.html"));
378
379 // Navigate the subframe to the extension URL, which should activate the
380 // extension.
381 GURL extension_url(extension->GetResourceURL("page.html"));
382 ResultCatcher catcher;
383 content::NavigateIframeToURL(web_contents, "test", extension_url);
384 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
385}
386
Devlin Cronin242d19d22019-03-12 18:08:48387IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
Devlin Croninb15c4f4e2017-12-15 21:20:11388 ExtensionListenersRemoveContext) {
389 const Extension* extension = LoadExtension(
390 test_data_dir_.AppendASCII("bindings/listeners_destroy_context"));
391 ASSERT_TRUE(extension);
392
393 ExtensionTestMessageListener listener("ready", true);
394
395 // Navigate to a web page with an iframe (the iframe is title1.html).
396 GURL main_frame_url = embedded_test_server()->GetURL("a.com", "/iframe.html");
397 ui_test_utils::NavigateToURL(browser(), main_frame_url);
398
399 content::WebContents* tab =
400 browser()->tab_strip_model()->GetActiveWebContents();
401
402 content::RenderFrameHost* main_frame = tab->GetMainFrame();
403 content::RenderFrameHost* subframe = ChildFrameAt(main_frame, 0);
404 content::RenderFrameDeletedObserver subframe_deleted(subframe);
405
406 // Wait for the extension's content script to be ready.
407 ASSERT_TRUE(listener.WaitUntilSatisfied());
408
409 // It's actually critical to the test that these frames are in the same
410 // process, because otherwise a crash in the iframe wouldn't be detectable
411 // (since we rely on JS execution in the main frame to tell if the renderer
412 // crashed - see comment below).
413 content::RenderProcessHost* main_frame_process = main_frame->GetProcess();
414 EXPECT_EQ(main_frame_process, subframe->GetProcess());
415
416 ExtensionTestMessageListener failure_listener("failed", false);
417
418 // Tell the extension to register listeners that will remove the iframe, and
419 // trigger them.
420 listener.Reply("go!");
421
422 // The frame will be deleted.
423 subframe_deleted.WaitUntilDeleted();
424
425 // Unfortunately, we don't have a good way of checking if something crashed
426 // after the frame was removed. WebContents::IsCrashed() seems like it should
427 // work, but is insufficient. Instead, use JS execution as the source of
428 // true.
429 EXPECT_FALSE(tab->IsCrashed());
430 EXPECT_EQ(main_frame_url, main_frame->GetLastCommittedURL());
431 EXPECT_EQ(main_frame_process, main_frame->GetProcess());
432 bool renderer_valid = false;
433 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
434 main_frame, "domAutomationController.send(true);", &renderer_valid));
435 EXPECT_TRUE(renderer_valid);
436 EXPECT_FALSE(failure_listener.was_satisfied());
437}
438
Devlin Cronin242d19d22019-03-12 18:08:48439IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, UseAPIsAfterContextRemoval) {
Devlin Croninb48b6f92018-01-12 23:17:59440 EXPECT_TRUE(RunExtensionTest("bindings/invalidate_context")) << message_;
441}
442
Devlin Cronin6553cc382019-03-28 22:46:46443// Tests that we don't crash if the extension invalidates the context in a
444// callback with a runtime.lastError present. Regression test for
445// https://crbug.com/944014.
446IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
447 InvalidateContextInCallbackWithLastError) {
448 TestExtensionDir dir;
449 dir.WriteManifest(
450 R"({
451 "name": "Invalidate Context in onDisconnect",
452 "version": "0.1",
453 "manifest_version": 2,
454 "background": {"scripts": ["background.js"]}
455 })");
456
457 constexpr char kFrameHtml[] =
458 R"(<html>
459 <body></body>
460 <script src="frame.js"></script>
461 </html>)";
462 constexpr char kFrameJs[] =
463 R"(chrome.tabs.executeScript({code: ''}, () => {
464 // We expect a last error to be present, since we don't have access
465 // to the tab.
466 chrome.test.assertTrue(!!chrome.runtime.lastError);
467 // Remove the frame from the DOM. This causes blink to remove the
468 // associated script contexts.
469 parent.document.body.removeChild(
470 parent.document.body.querySelector('iframe'));
471 });)";
472 constexpr char kBackgroundJs[] =
473 R"(let frame = document.createElement('iframe');
474 frame.src = 'frame.html';
475 let observer = new MutationObserver((mutationList) => {
476 for (let mutation of mutationList) {
477 if (mutation.removedNodes.length == 0)
478 continue;
479 chrome.test.assertEq(1, mutation.removedNodes.length);
480 chrome.test.assertEq('IFRAME', mutation.removedNodes[0].tagName);
481 chrome.test.notifyPass();
482 break;
483 }
484 });
485 observer.observe(document.body, {childList: true});
486 document.body.appendChild(frame);)";
487 dir.WriteFile(FILE_PATH_LITERAL("frame.html"), kFrameHtml);
488 dir.WriteFile(FILE_PATH_LITERAL("frame.js"), kFrameJs);
489 dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs);
490
491 ResultCatcher catcher;
492 const Extension* extension = LoadExtension(dir.UnpackedPath());
493 ASSERT_TRUE(extension);
494 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
495}
496
Devlin Cronin47e79042018-02-02 18:32:26497// TODO(devlin): Can this be combined with
498// ExtensionBindingsApiTest.UseAPIsAfterContextRemoval?
Devlin Cronin242d19d22019-03-12 18:08:48499IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, UseAppAPIAfterFrameRemoval) {
Devlin Cronin47e79042018-02-02 18:32:26500 ASSERT_TRUE(RunExtensionTest("crazy_extension"));
501}
502
Devlin Cronind2389082018-08-14 01:53:26503// Tests attaching two listeners from the same extension but different pages,
504// then removing one, and ensuring the second is still notified.
505// Regression test for https://crbug.com/868763.
Devlin Cronin242d19d22019-03-12 18:08:48506IN_PROC_BROWSER_TEST_F(
Devlin Cronind2389082018-08-14 01:53:26507 ExtensionBindingsApiTest,
508 MultipleEventListenersFromDifferentContextsAndTheSameExtension) {
509 // A script that listens for tab creation and populates the result in a
510 // global variable.
511 constexpr char kTestPageScript[] = R"(
512 window.tabEventId = -1;
513 function registerListener() {
514 chrome.tabs.onCreated.addListener((tab) => {
515 window.tabEventId = tab.id;
516 });
517 }
518 )";
519 TestExtensionDir test_dir;
520 test_dir.WriteManifest(R"(
521 {
522 "name": "Duplicate event listeners",
523 "manifest_version": 2,
524 "version": "0.1"
525 })");
526 test_dir.WriteFile(FILE_PATH_LITERAL("page.html"),
527 R"(<html><script src="page.js"></script></html>)");
528 test_dir.WriteFile(FILE_PATH_LITERAL("page.js"), kTestPageScript);
529
530 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
531 ASSERT_TRUE(extension);
532
533 // Set up: open two tabs to the same extension page, and wait for each to
534 // load.
535 const GURL page_url = extension->GetResourceURL("page.html");
536 ui_test_utils::NavigateToURLWithDisposition(
537 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
538 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
539 content::WebContents* first_tab =
540 browser()->tab_strip_model()->GetActiveWebContents();
541 ui_test_utils::NavigateToURLWithDisposition(
542 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
543 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
544 content::WebContents* second_tab =
545 browser()->tab_strip_model()->GetActiveWebContents();
546
547 // Initially, there are no listeners registered.
548 EventRouter* event_router = EventRouter::Get(profile());
549 EXPECT_FALSE(event_router->ExtensionHasEventListener(extension->id(),
550 "tabs.onCreated"));
551
552 // Register both lsiteners, and verify they were added.
553 ASSERT_TRUE(content::ExecuteScript(first_tab, "registerListener()"));
554 ASSERT_TRUE(content::ExecuteScript(second_tab, "registerListener()"));
555 EXPECT_TRUE(event_router->ExtensionHasEventListener(extension->id(),
556 "tabs.onCreated"));
557
558 // Close one of the extension pages.
559 constexpr bool add_to_history = false;
560 content::WebContentsDestroyedWatcher watcher(second_tab);
561 chrome::CloseWebContents(browser(), second_tab, add_to_history);
562 watcher.Wait();
563 // Hacky round trip to the renderer to flush IPCs.
564 ASSERT_TRUE(content::ExecuteScript(first_tab, ""));
565
566 // Since the second page is still open, the extension should still be
567 // registered as a listener.
568 EXPECT_TRUE(event_router->ExtensionHasEventListener(extension->id(),
569 "tabs.onCreated"));
570
571 // Open a new tab.
572 ui_test_utils::NavigateToURLWithDisposition(
573 browser(), GURL("chrome://newtab"),
574 WindowOpenDisposition::NEW_FOREGROUND_TAB,
575 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
576 content::WebContents* new_tab =
577 browser()->tab_strip_model()->GetActiveWebContents();
578
579 // The extension should have been notified about the new tab, and have
580 // recorded the result.
581 int result_tab_id = -1;
582 EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
583 first_tab, "domAutomationController.send(window.tabEventId)",
584 &result_tab_id));
585 EXPECT_EQ(SessionTabHelper::IdForTab(new_tab).id(), result_tab_id);
586}
587
Devlin Cronin242d19d22019-03-12 18:08:48588enum UserActivationType {
589 kUserActivationV1,
590 kUserActivationV2,
591};
592
593class ExtensionBindingsUserGestureTest
594 : public ExtensionBindingsApiTest,
595 public ::testing::WithParamInterface<UserActivationType> {
596 public:
597 ExtensionBindingsUserGestureTest() {}
598 ~ExtensionBindingsUserGestureTest() override {}
599
600 void SetUp() override {
601 UserActivationType user_activation_type = GetParam();
602 if (user_activation_type == kUserActivationV2) {
603 scoped_feature_list_.InitAndEnableFeature(features::kUserActivationV2);
604 } else {
605 DCHECK_EQ(kUserActivationV1, user_activation_type);
606 scoped_feature_list_.InitAndDisableFeature(features::kUserActivationV2);
607 }
608
609 ExtensionBindingsApiTest::SetUp();
610 }
611
612 private:
613 base::test::ScopedFeatureList scoped_feature_list_;
614
615 DISALLOW_COPY_AND_ASSIGN(ExtensionBindingsUserGestureTest);
616};
Devlin Cronine80e6fc12019-02-05 00:44:23617
Devlin Croninc3d017992018-09-11 01:21:20618// Verifies that user gestures are carried through extension messages.
Devlin Cronine80e6fc12019-02-05 00:44:23619IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
Devlin Croninc3d017992018-09-11 01:21:20620 UserGestureFromExtensionMessageTest) {
621 TestExtensionDir test_dir;
622 test_dir.WriteManifest(
623 R"({
624 "name": "User Gesture Content Script",
625 "manifest_version": 2,
626 "version": "0.1",
627 "background": { "scripts": ["background.js"] },
628 "content_scripts": [{
629 "matches": ["*://*.example.com:*/*"],
630 "js": ["content_script.js"],
631 "run_at": "document_end"
632 }]
633 })");
634 test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
635 R"(const button = document.getElementById('go-button');
636 button.addEventListener('click', () => {
637 chrome.runtime.sendMessage('clicked');
638 });)");
639 test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
640 R"(chrome.runtime.onMessage.addListener((message) => {
641 chrome.test.sendMessage(
642 'Clicked: ' +
643 chrome.test.isProcessingUserGesture());
644 });)");
645
646 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
647 ASSERT_TRUE(extension);
648
649 const GURL url = embedded_test_server()->GetURL(
650 "example.com", "/extensions/page_with_button.html");
651 ui_test_utils::NavigateToURL(browser(), url);
652
653 content::WebContents* tab =
654 browser()->tab_strip_model()->GetActiveWebContents();
655
656 {
657 // Passing a message without an active user gesture shouldn't result in a
658 // gesture being active on the receiving end.
659 ExtensionTestMessageListener listener(false);
660 content::EvalJsResult result =
661 content::EvalJs(tab, "document.getElementById('go-button').click()",
662 content::EXECUTE_SCRIPT_NO_USER_GESTURE);
663 EXPECT_TRUE(result.value.is_none());
664
665 EXPECT_TRUE(listener.WaitUntilSatisfied());
666 EXPECT_EQ("Clicked: false", listener.message());
667 }
668
669 {
670 // If there is an active user gesture when the message is sent, we should
671 // synthesize a user gesture on the receiving end.
672 ExtensionTestMessageListener listener(false);
673 content::EvalJsResult result =
674 content::EvalJs(tab, "document.getElementById('go-button').click()");
675 EXPECT_TRUE(result.value.is_none());
676
677 EXPECT_TRUE(listener.WaitUntilSatisfied());
678 EXPECT_EQ("Clicked: true", listener.message());
679 }
680}
681
682// Verifies that user gestures from API calls are active when the callback is
683// triggered.
Devlin Cronine80e6fc12019-02-05 00:44:23684IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
Devlin Croninc3d017992018-09-11 01:21:20685 UserGestureInExtensionAPICallback) {
686 TestExtensionDir test_dir;
687 test_dir.WriteManifest(
688 R"({
689 "name": "User Gesture Extension API Callback",
690 "manifest_version": 2,
691 "version": "0.1"
692 })");
693 test_dir.WriteFile(FILE_PATH_LITERAL("page.html"), "<html></html>");
694
695 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
696 ASSERT_TRUE(extension);
697
698 const GURL extension_page = extension->GetResourceURL("page.html");
699 ui_test_utils::NavigateToURL(browser(), extension_page);
700
701 content::WebContents* tab =
702 browser()->tab_strip_model()->GetActiveWebContents();
703
704 constexpr char kScript[] =
705 R"(chrome.tabs.query({}, (tabs) => {
706 let message;
707 if (chrome.runtime.lastError)
708 message = 'Unexpected error: ' + chrome.runtime.lastError;
709 else
710 message = 'Has gesture: ' + chrome.test.isProcessingUserGesture();
711 domAutomationController.send(message);
712 });)";
713
714 {
715 // Triggering an API without an active gesture shouldn't result in a
716 // gesture in the callback.
717 std::string message;
718 EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
719 tab, kScript, &message));
720 EXPECT_EQ("Has gesture: false", message);
721 }
722 {
723 // If there was an active gesture at the time of the API call, there should
724 // be an active gesture in the callback.
725 std::string message;
726 EXPECT_TRUE(content::ExecuteScriptAndExtractString(tab, kScript, &message));
727 EXPECT_EQ("Has gesture: true", message);
728 }
729}
730
Devlin Cronine80e6fc12019-02-05 00:44:23731// Tests that a web page can consume a user gesture after an extension sends and
732// receives a reply during the same user gesture.
733// Regression test for https://crbug.com/921141.
734IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
735 WebUserGestureAfterMessagingCallback) {
736 TestExtensionDir test_dir;
737 test_dir.WriteManifest(
738 R"({
739 "name": "User Gesture Messaging Test",
740 "version": "0.1",
741 "manifest_version": 2,
742 "content_scripts": [{
743 "matches": ["*://*/*"],
744 "js": ["content_script.js"],
745 "run_at": "document_start"
746 }],
747 "background": {
748 "scripts": ["background.js"]
749 }
750 })");
751 test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
752 R"(window.addEventListener('mousedown', () => {
753 chrome.runtime.sendMessage('hello', () => {
754 let message = chrome.test.isProcessingUserGesture() ?
755 'got reply' : 'no user gesture';
756 chrome.test.sendMessage(message);
757 });
758 });)");
759 test_dir.WriteFile(
760 FILE_PATH_LITERAL("background.js"),
761 R"(chrome.runtime.onMessage.addListener((message, sender, respond) => {
762 respond('reply');
763 });
764 chrome.test.sendMessage('ready');)");
765
766 const Extension* extension = nullptr;
767 {
768 ExtensionTestMessageListener listener("ready", false);
769 extension = LoadExtension(test_dir.UnpackedPath());
770 ASSERT_TRUE(extension);
771 EXPECT_TRUE(listener.WaitUntilSatisfied());
772 }
773
774 ui_test_utils::NavigateToURL(
775 browser(), embedded_test_server()->GetURL(
776 "/extensions/api_test/bindings/user_gesture_test.html"));
777 content::WebContents* web_contents =
778 browser()->tab_strip_model()->GetActiveWebContents();
779 ASSERT_TRUE(web_contents);
780
781 {
782 ExtensionTestMessageListener listener("got reply", false);
783 listener.set_failure_message("no user gesture");
784 MouseDownInWebContents(web_contents);
785 EXPECT_TRUE(listener.WaitUntilSatisfied());
786 }
787
788 MouseUpInWebContents(web_contents);
789
790 EXPECT_EQ("success",
791 content::EvalJs(web_contents, "window.getEnteredFullscreen",
792 content::EXECUTE_SCRIPT_NO_USER_GESTURE));
793}
794
795// Tests that a web page can consume a user gesture after an extension calls a
796// method and receives the response in the callback.
797// Regression test for https://crbug.com/921141.
798IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
799 WebUserGestureAfterApiCallback) {
800 TestExtensionDir test_dir;
801 test_dir.WriteManifest(
802 R"({
803 "name": "User Gesture Messaging Test",
804 "version": "0.1",
805 "manifest_version": 2,
806 "content_scripts": [{
807 "matches": ["*://*/*"],
808 "js": ["content_script.js"],
809 "run_at": "document_start"
810 }],
811 "permissions": ["storage"]
812 })");
813 test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
814 R"(window.addEventListener('mousedown', () => {
815 chrome.storage.local.get('foo', () => {
816 let message = chrome.test.isProcessingUserGesture() ?
817 'got reply' : 'no user gesture';
818 chrome.test.sendMessage(message);
819 });
820 });)");
821
822 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
823 ASSERT_TRUE(extension);
824
825 ui_test_utils::NavigateToURL(
826 browser(), embedded_test_server()->GetURL(
827 "/extensions/api_test/bindings/user_gesture_test.html"));
828 content::WebContents* web_contents =
829 browser()->tab_strip_model()->GetActiveWebContents();
830 ASSERT_TRUE(web_contents);
831
832 {
833 ExtensionTestMessageListener listener("got reply", false);
834 listener.set_failure_message("no user gesture");
835 MouseDownInWebContents(web_contents);
836 EXPECT_TRUE(listener.WaitUntilSatisfied());
837 }
838
839 MouseUpInWebContents(web_contents);
840
841 EXPECT_EQ("success",
842 content::EvalJs(web_contents, "window.getEnteredFullscreen",
843 content::EXECUTE_SCRIPT_NO_USER_GESTURE));
844}
845
Devlin Cronind900688a2019-02-01 01:36:11846// Tests that bindings are properly instantiated for a window navigated to an
847// extension URL after being opened with an undefined URL.
848// Regression test for https://crbug.com/925118.
Devlin Cronin242d19d22019-03-12 18:08:48849IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest,
Devlin Cronind900688a2019-02-01 01:36:11850 TestBindingsAvailableWithNavigatedBlankWindow) {
851 constexpr char kManifest[] =
852 R"({
853 "name": "chrome.runtime bug checker",
854 "description": "test case for crbug.com/925118",
855 "version": "0",
856 "manifest_version": 2
857 })";
858 constexpr char kOpenerHTML[] =
859 R"(<!DOCTYPE html>
860 <html>
861 <head>
862 <script src='opener.js'></script>
863 </head>
864 <body>
865 </body>
866 </html>)";
867 // opener.js opens a blank window and then navigates it to an extension URL
868 // (where extension APIs should be available).
869 constexpr char kOpenerJS[] =
870 R"(const url = chrome.runtime.getURL('/page.html');
871 const win = window.open(undefined, '');
872 win.location = url;
873 chrome.test.notifyPass())";
874 constexpr char kPageHTML[] =
875 R"(<!DOCTYPE html>
876 <html>
877 This space intentionally left blank.
878 </html>)";
879 TestExtensionDir extension_dir;
880 extension_dir.WriteManifest(kManifest);
881 extension_dir.WriteFile(FILE_PATH_LITERAL("opener.html"), kOpenerHTML);
882 extension_dir.WriteFile(FILE_PATH_LITERAL("opener.js"), kOpenerJS);
883 extension_dir.WriteFile(FILE_PATH_LITERAL("page.html"), kPageHTML);
884
885 const Extension* extension = LoadExtension(extension_dir.UnpackedPath());
886 const GURL target_url = extension->GetResourceURL("page.html");
887
888 ResultCatcher catcher;
889 content::TestNavigationObserver observer(target_url);
890 observer.StartWatchingNewWebContents();
891 ui_test_utils::NavigateToURL(browser(),
892 extension->GetResourceURL("opener.html"));
893 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
894 observer.Wait();
895 EXPECT_TRUE(observer.last_navigation_succeeded());
896
897 content::WebContents* web_contents =
898 browser()->tab_strip_model()->GetActiveWebContents();
899 EXPECT_EQ(target_url, web_contents->GetLastCommittedURL());
900
901 // Check whether bindings are available. They should be.
902 constexpr char kScript[] =
903 R"(let message;
904 if (!chrome.runtime)
905 message = 'Runtime not defined';
906 else if (!chrome.tabs)
907 message = 'Tabs not defined';
908 else
909 message = 'success';
910 domAutomationController.send(message);)";
911 std::string result;
912 // Note: Can't use EvalJs() because of CSP in extension pages.
913 EXPECT_TRUE(
914 content::ExecuteScriptAndExtractString(web_contents, kScript, &result));
915 EXPECT_EQ("success", result);
916}
917
Devlin Cronine80e6fc12019-02-05 00:44:23918INSTANTIATE_TEST_SUITE_P(,
919 ExtensionBindingsUserGestureTest,
Devlin Cronin242d19d22019-03-12 18:08:48920 ::testing::Values(kUserActivationV1,
921 kUserActivationV2));
Devlin Croninc3a1e5072017-08-17 17:02:49922
[email protected]adafe5b2013-08-09 10:35:04923} // namespace
924} // namespace extensions