1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/files/file_path.h"
6 #include "base/path_service.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/values.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/event_router.h"
12 #include "chrome/browser/extensions/extension_apitest.h"
13 #include "chrome/browser/extensions/extension_prefs.h"
14 #include "chrome/browser/extensions/extension_system.h"
15 #include "chrome/browser/extensions/test_extension_dir.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "content/public/browser/notification_registrar.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/test/browser_test_utils.h"
25 #include "net/dns/mock_host_resolver.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h"
29 namespace extensions
{
32 class MessageSender
: public content::NotificationObserver
{
35 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING
,
36 content::NotificationService::AllSources());
40 static scoped_ptr
<base::ListValue
> BuildEventArguments(
41 const bool last_message
,
42 const std::string
& data
) {
43 DictionaryValue
* event
= new DictionaryValue();
44 event
->SetBoolean("lastMessage", last_message
);
45 event
->SetString("data", data
);
46 scoped_ptr
<base::ListValue
> arguments(new base::ListValue());
47 arguments
->Append(event
);
48 return arguments
.Pass();
51 static scoped_ptr
<Event
> BuildEvent(scoped_ptr
<base::ListValue
> event_args
,
54 scoped_ptr
<Event
> event(new Event("test.onMessage", event_args
.Pass()));
55 event
->restrict_to_profile
= profile
;
56 event
->event_url
= event_url
;
60 virtual void Observe(int type
,
61 const content::NotificationSource
& source
,
62 const content::NotificationDetails
& details
) OVERRIDE
{
63 EventRouter
* event_router
= ExtensionSystem::Get(
64 content::Source
<Profile
>(source
).ptr())->event_router();
66 // Sends four messages to the extension. All but the third message sent
67 // from the origin http://b.com/ are supposed to arrive.
68 event_router
->BroadcastEvent(BuildEvent(
69 BuildEventArguments(false, "no restriction"),
70 content::Source
<Profile
>(source
).ptr(),
72 event_router
->BroadcastEvent(BuildEvent(
73 BuildEventArguments(false, "http://a.com/"),
74 content::Source
<Profile
>(source
).ptr(),
75 GURL("http://a.com/")));
76 event_router
->BroadcastEvent(BuildEvent(
77 BuildEventArguments(false, "http://b.com/"),
78 content::Source
<Profile
>(source
).ptr(),
79 GURL("http://b.com/")));
80 event_router
->BroadcastEvent(BuildEvent(
81 BuildEventArguments(true, "last message"),
82 content::Source
<Profile
>(source
).ptr(),
86 content::NotificationRegistrar registrar_
;
89 // Tests that message passing between extensions and content scripts works.
90 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, Messaging
) {
91 ASSERT_TRUE(StartEmbeddedTestServer());
92 ASSERT_TRUE(RunExtensionTest("messaging/connect")) << message_
;
95 // Tests that message passing from one extension to another works.
96 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, MessagingExternal
) {
97 ASSERT_TRUE(LoadExtension(
98 test_data_dir_
.AppendASCII("..").AppendASCII("good")
99 .AppendASCII("Extensions")
100 .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa")
101 .AppendASCII("1.0")));
103 ASSERT_TRUE(RunExtensionTest("messaging/connect_external")) << message_
;
106 // Tests that messages with event_urls are only passed to extensions with
107 // appropriate permissions.
108 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, MessagingEventURL
) {
109 MessageSender sender
;
110 ASSERT_TRUE(RunExtensionTest("messaging/event_url")) << message_
;
113 // Tests connecting from a panel to its extension.
114 class PanelMessagingTest
: public ExtensionApiTest
{
115 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
116 ExtensionApiTest::SetUpCommandLine(command_line
);
117 command_line
->AppendSwitch(switches::kEnablePanels
);
121 IN_PROC_BROWSER_TEST_F(PanelMessagingTest
, MessagingPanel
) {
122 ASSERT_TRUE(RunExtensionTest("messaging/connect_panel")) << message_
;
125 // Tests externally_connectable between a web page and an extension.
127 // TODO(kalman): Test between extensions. This is already tested in this file,
128 // but not with externally_connectable set in the manifest.
130 // TODO(kalman): Test with host permissions.
131 class ExternallyConnectableMessagingTest
: public ExtensionApiTest
{
133 // Result codes from the test. These must match up with |results| in
134 // c/t/d/extensions/api_test/externally_connectable/assertions.json.
137 NAMESPACE_NOT_DEFINED
= 1,
138 FUNCTION_NOT_DEFINED
= 2,
139 COULD_NOT_ESTABLISH_CONNECTION_ERROR
= 3,
141 INCORRECT_RESPONSE_SENDER
= 5,
142 INCORRECT_RESPONSE_MESSAGE
= 6,
145 bool AppendIframe(const GURL
& src
) {
147 CHECK(content::ExecuteScriptAndExtractBool(
148 browser()->tab_strip_model()->GetActiveWebContents(),
149 "actions.appendIframe('" + src
.spec() + "');", &result
));
153 Result
CanConnectAndSendMessages(const std::string
& extension_id
) {
154 return CanConnectAndSendMessages(browser(), extension_id
, "");
157 Result
CanConnectAndSendMessages(const std::string
& extension_id
,
158 const char* frame_xpath
,
159 const char* message
) {
160 return CanConnectAndSendMessages(browser(), extension_id
, frame_xpath
,
164 Result
CanConnectAndSendMessages(Browser
* browser
,
165 const std::string
& extension_id
) {
166 return CanConnectAndSendMessages(browser
, extension_id
, "");
169 Result
CanConnectAndSendMessages(const std::string
& extension_id
,
170 const char* frame_xpath
) {
171 return CanConnectAndSendMessages(browser(), extension_id
, frame_xpath
);
174 Result
CanConnectAndSendMessages(Browser
* browser
,
175 const std::string
& extension_id
,
176 const char* frame_xpath
,
177 const char* message
= NULL
) {
179 std::string args
= "'" + extension_id
+ "'";
181 args
+= std::string(", '") + message
+ "'";
182 CHECK(content::ExecuteScriptInFrameAndExtractInt(
183 browser
->tab_strip_model()->GetActiveWebContents(),
185 base::StringPrintf("assertions.canConnectAndSendMessages(%s)",
186 args
.c_str()).c_str(),
188 return static_cast<Result
>(result
);
191 testing::AssertionResult
AreAnyNonWebApisDefined() {
192 return AreAnyNonWebApisDefined("");
195 testing::AssertionResult
AreAnyNonWebApisDefined(const char* frame_xpath
) {
196 // All runtime API methods are non-web except for sendRequest and connect.
197 const char* non_messaging_apis
[] = {
202 "requestUpdateCheck",
211 "onBrowserUpdateAvailable",
220 // Turn the array into a JS array, which effectively gets eval()ed.
221 std::string as_js_array
;
222 for (size_t i
= 0; i
< arraysize(non_messaging_apis
); ++i
) {
223 as_js_array
+= as_js_array
.empty() ? "[" : ",";
224 as_js_array
+= base::StringPrintf("'%s'", non_messaging_apis
[i
]);
229 CHECK(content::ExecuteScriptInFrameAndExtractBool(
230 browser()->tab_strip_model()->GetActiveWebContents(),
232 "assertions.areAnyRuntimePropertiesDefined(" + as_js_array
+ ")",
235 testing::AssertionSuccess() : testing::AssertionFailure();
238 GURL
GetURLForPath(const std::string
& host
, const std::string
& path
) {
239 std::string port
= base::IntToString(embedded_test_server()->port());
240 GURL::Replacements replacements
;
241 replacements
.SetHostStr(host
);
242 replacements
.SetPortStr(port
);
243 return embedded_test_server()->GetURL(path
).ReplaceComponents(replacements
);
246 GURL
chromium_org_url() {
247 return GetURLForPath("www.chromium.org", "/chromium.org.html");
250 GURL
google_com_url() {
251 return GetURLForPath("www.google.com", "/google.com.html");
254 const Extension
* LoadChromiumConnectableExtension() {
255 const Extension
* extension
=
256 LoadExtensionIntoDir(&web_connectable_dir_
, base::StringPrintf(
258 " \"name\": \"chromium_connectable\","
260 " \"externally_connectable\": {"
261 " \"matches\": [\"*://*.chromium.org:*/*\"]"
269 const Extension
* LoadNotConnectableExtension() {
270 const Extension
* extension
=
271 LoadExtensionIntoDir(¬_connectable_dir_
, base::StringPrintf(
273 " \"name\": \"not_connectable\","
281 void InitializeTestServer() {
282 base::FilePath test_data
;
283 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &test_data
));
284 embedded_test_server()->ServeFilesFromDirectory(test_data
.AppendASCII(
285 "extensions/api_test/messaging/externally_connectable/sites"));
286 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
287 host_resolver()->AddRule("*", embedded_test_server()->base_url().host());
290 const char* close_background_message() {
291 return "closeBackgroundPage";
295 const Extension
* LoadExtensionIntoDir(TestExtensionDir
* dir
,
296 const std::string
& manifest
) {
297 dir
->WriteManifest(manifest
);
298 dir
->WriteFile(FILE_PATH_LITERAL("background.js"),
300 "function maybeClose(message) {\n"
301 " if (message.indexOf('%s') >= 0)\n"
302 " window.setTimeout(function() { window.close() }, 0);\n"
304 "chrome.runtime.onMessageExternal.addListener(\n"
305 " function(message, sender, reply) {\n"
306 " reply({ message: message, sender: sender });\n"
307 " maybeClose(message);\n"
309 "chrome.runtime.onConnectExternal.addListener(function(port) {\n"
310 " port.onMessage.addListener(function(message) {\n"
311 " port.postMessage({ message: message, sender: port.sender });\n"
312 " maybeClose(message);\n"
315 close_background_message()));
316 return LoadExtension(dir
->unpacked_path());
319 const char* common_manifest() {
320 return "\"version\": \"1.0\","
322 " \"scripts\": [\"background.js\"],"
323 " \"persistent\": false"
325 "\"manifest_version\": 2";
328 TestExtensionDir web_connectable_dir_
;
329 TestExtensionDir not_connectable_dir_
;
332 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
, NotInstalled
) {
333 InitializeTestServer();
335 const char kFakeId
[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
337 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
338 EXPECT_EQ(NAMESPACE_NOT_DEFINED
, CanConnectAndSendMessages(kFakeId
));
339 EXPECT_FALSE(AreAnyNonWebApisDefined());
341 ui_test_utils::NavigateToURL(browser(), google_com_url());
342 EXPECT_EQ(NAMESPACE_NOT_DEFINED
, CanConnectAndSendMessages(kFakeId
));
343 EXPECT_FALSE(AreAnyNonWebApisDefined());
346 // Tests two extensions on the same sites: one web connectable, one not.
347 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
348 WebConnectableAndNotConnectable
) {
349 InitializeTestServer();
351 // Install the web connectable extension. chromium.org can connect to it,
353 const Extension
* chromium_connectable
= LoadChromiumConnectableExtension();
355 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
356 EXPECT_EQ(OK
, CanConnectAndSendMessages(chromium_connectable
->id()));
357 EXPECT_FALSE(AreAnyNonWebApisDefined());
359 ui_test_utils::NavigateToURL(browser(), google_com_url());
360 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
361 CanConnectAndSendMessages(chromium_connectable
->id()));
362 EXPECT_FALSE(AreAnyNonWebApisDefined());
364 // Install the non-connectable extension. Nothing can connect to it.
365 const Extension
* not_connectable
= LoadNotConnectableExtension();
367 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
368 // Namespace will be defined here because |chromium_connectable| can connect
369 // to it - so this will be the "cannot establish connection" error.
370 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
371 CanConnectAndSendMessages(not_connectable
->id()));
372 EXPECT_FALSE(AreAnyNonWebApisDefined());
374 ui_test_utils::NavigateToURL(browser(), google_com_url());
375 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
376 CanConnectAndSendMessages(not_connectable
->id()));
377 EXPECT_FALSE(AreAnyNonWebApisDefined());
380 // See http://crbug.com/297866
381 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
382 DISABLED_BackgroundPageClosesOnMessageReceipt
) {
383 InitializeTestServer();
385 // Install the web connectable extension.
386 const Extension
* chromium_connectable
= LoadChromiumConnectableExtension();
388 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
389 // If the background page closes after receipt of the message, it will still
390 // reply to this message...
391 EXPECT_EQ(OK
, CanConnectAndSendMessages(chromium_connectable
->id(),
393 close_background_message()));
394 // and be re-opened by receipt of a subsequent message.
395 EXPECT_EQ(OK
, CanConnectAndSendMessages(chromium_connectable
->id()));
398 // Tests that enabling and disabling an extension makes the runtime bindings
399 // appear and disappear.
401 // TODO(kalman): Test with multiple extensions that can be accessed by the same
403 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
404 EnablingAndDisabling
) {
405 InitializeTestServer();
407 const Extension
* chromium_connectable
= LoadChromiumConnectableExtension();
408 const Extension
* not_connectable
= LoadNotConnectableExtension();
410 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
411 EXPECT_EQ(OK
, CanConnectAndSendMessages(chromium_connectable
->id()));
412 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
413 CanConnectAndSendMessages(not_connectable
->id()));
415 DisableExtension(chromium_connectable
->id());
416 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
417 CanConnectAndSendMessages(chromium_connectable
->id()));
419 EnableExtension(chromium_connectable
->id());
420 EXPECT_EQ(OK
, CanConnectAndSendMessages(chromium_connectable
->id()));
421 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
422 CanConnectAndSendMessages(not_connectable
->id()));
425 // Tests connection from incognito tabs. Spanning mode only.
427 // TODO(kalman): ensure that we exercise split vs spanning incognito logic
428 // somewhere. This is a test that should be shared with the content script logic
429 // so it's not really our specific concern for web connectable.
430 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
, FromIncognito
) {
431 InitializeTestServer();
433 const Extension
* chromium_connectable
= LoadChromiumConnectableExtension();
435 Browser
* incognito_browser
= ui_test_utils::OpenURLOffTheRecord(
436 profile()->GetOffTheRecordProfile(),
439 // No connection because incognito enabled hasn't been set.
440 const std::string
& id
= chromium_connectable
->id();
441 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
442 CanConnectAndSendMessages(incognito_browser
, id
));
445 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(id
, true);
446 EXPECT_EQ(OK
, CanConnectAndSendMessages(incognito_browser
, id
));
449 // Tests a connection from an iframe within a tab which doesn't have
450 // permission. Iframe should work.
451 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
452 FromIframeWithPermission
) {
453 InitializeTestServer();
455 const Extension
* extension
= LoadChromiumConnectableExtension();
457 ui_test_utils::NavigateToURL(browser(), google_com_url());
458 EXPECT_EQ(NAMESPACE_NOT_DEFINED
, CanConnectAndSendMessages(extension
->id()));
459 EXPECT_FALSE(AreAnyNonWebApisDefined());
461 ASSERT_TRUE(AppendIframe(chromium_org_url()));
463 const char* frame_xpath
= "//iframe[1]";
464 EXPECT_EQ(OK
, CanConnectAndSendMessages(extension
->id(), frame_xpath
));
465 EXPECT_FALSE(AreAnyNonWebApisDefined(frame_xpath
));
468 // Tests connection from an iframe without permission within a tab that does.
469 // Iframe shouldn't work.
470 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
471 FromIframeWithoutPermission
) {
472 InitializeTestServer();
474 const Extension
* extension
= LoadChromiumConnectableExtension();
476 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
477 EXPECT_EQ(OK
, CanConnectAndSendMessages(extension
->id()));
478 EXPECT_FALSE(AreAnyNonWebApisDefined());
480 ASSERT_TRUE(AppendIframe(google_com_url()));
482 const char* frame_xpath
= "//iframe[1]";
483 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
484 CanConnectAndSendMessages(extension
->id(), frame_xpath
));
485 EXPECT_FALSE(AreAnyNonWebApisDefined(frame_xpath
));
489 }; // namespace extensions