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/base64.h"
6 #include "base/files/file_path.h"
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/path_service.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/api/messaging/incognito_connectability.h"
17 #include "chrome/browser/extensions/extension_apitest.h"
18 #include "chrome/browser/extensions/test_extension_dir.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/test/base/ui_test_utils.h"
25 #include "content/public/browser/notification_registrar.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/test/browser_test_utils.h"
28 #include "extensions/browser/event_router.h"
29 #include "extensions/browser/extension_prefs.h"
30 #include "extensions/browser/extension_system.h"
31 #include "extensions/common/api/runtime.h"
32 #include "extensions/common/extension_builder.h"
33 #include "extensions/common/value_builder.h"
34 #include "net/cert/asn1_util.h"
35 #include "net/cert/jwk_serializer.h"
36 #include "net/dns/mock_host_resolver.h"
37 #include "net/ssl/channel_id_service.h"
38 #include "net/test/embedded_test_server/embedded_test_server.h"
39 #include "net/url_request/url_request_context.h"
40 #include "net/url_request/url_request_context_getter.h"
43 namespace extensions
{
46 class MessageSender
: public content::NotificationObserver
{
50 extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING
,
51 content::NotificationService::AllSources());
55 static scoped_ptr
<base::ListValue
> BuildEventArguments(
56 const bool last_message
,
57 const std::string
& data
) {
58 base::DictionaryValue
* event
= new base::DictionaryValue();
59 event
->SetBoolean("lastMessage", last_message
);
60 event
->SetString("data", data
);
61 scoped_ptr
<base::ListValue
> arguments(new base::ListValue());
62 arguments
->Append(event
);
63 return arguments
.Pass();
66 static scoped_ptr
<Event
> BuildEvent(scoped_ptr
<base::ListValue
> event_args
,
69 scoped_ptr
<Event
> event(new Event("test.onMessage", event_args
.Pass()));
70 event
->restrict_to_browser_context
= profile
;
71 event
->event_url
= event_url
;
75 void Observe(int type
,
76 const content::NotificationSource
& source
,
77 const content::NotificationDetails
& details
) override
{
78 EventRouter
* event_router
=
79 EventRouter::Get(content::Source
<Profile
>(source
).ptr());
81 // Sends four messages to the extension. All but the third message sent
82 // from the origin http://b.com/ are supposed to arrive.
83 event_router
->BroadcastEvent(BuildEvent(
84 BuildEventArguments(false, "no restriction"),
85 content::Source
<Profile
>(source
).ptr(),
87 event_router
->BroadcastEvent(BuildEvent(
88 BuildEventArguments(false, "http://a.com/"),
89 content::Source
<Profile
>(source
).ptr(),
90 GURL("http://a.com/")));
91 event_router
->BroadcastEvent(BuildEvent(
92 BuildEventArguments(false, "http://b.com/"),
93 content::Source
<Profile
>(source
).ptr(),
94 GURL("http://b.com/")));
95 event_router
->BroadcastEvent(BuildEvent(
96 BuildEventArguments(true, "last message"),
97 content::Source
<Profile
>(source
).ptr(),
101 content::NotificationRegistrar registrar_
;
104 // Tests that message passing between extensions and content scripts works.
105 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, Messaging
) {
106 ASSERT_TRUE(StartEmbeddedTestServer());
107 ASSERT_TRUE(RunExtensionTest("messaging/connect")) << message_
;
110 // Tests that message passing from one extension to another works.
111 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, MessagingExternal
) {
112 ASSERT_TRUE(LoadExtension(
113 test_data_dir_
.AppendASCII("..").AppendASCII("good")
114 .AppendASCII("Extensions")
115 .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa")
116 .AppendASCII("1.0")));
118 ASSERT_TRUE(RunExtensionTest("messaging/connect_external")) << message_
;
121 // Tests that messages with event_urls are only passed to extensions with
122 // appropriate permissions.
123 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, MessagingEventURL
) {
124 MessageSender sender
;
125 ASSERT_TRUE(RunExtensionTest("messaging/event_url")) << message_
;
128 // Tests connecting from a panel to its extension.
129 class PanelMessagingTest
: public ExtensionApiTest
{
130 void SetUpCommandLine(CommandLine
* command_line
) override
{
131 ExtensionApiTest::SetUpCommandLine(command_line
);
132 command_line
->AppendSwitch(switches::kEnablePanels
);
136 IN_PROC_BROWSER_TEST_F(PanelMessagingTest
, MessagingPanel
) {
137 ASSERT_TRUE(RunExtensionTest("messaging/connect_panel")) << message_
;
140 // XXX(kalman): All web messaging tests disabled on windows due to extreme
141 // flakiness. See http://crbug.com/350517.
144 // Tests externally_connectable between a web page and an extension.
146 // TODO(kalman): Test between extensions. This is already tested in this file,
147 // but not with externally_connectable set in the manifest.
149 // TODO(kalman): Test with host permissions.
150 class ExternallyConnectableMessagingTest
: public ExtensionApiTest
{
152 // Result codes from the test. These must match up with |results| in
153 // c/t/d/extensions/api_test/externally_connectable/assertions.json.
156 NAMESPACE_NOT_DEFINED
= 1,
157 FUNCTION_NOT_DEFINED
= 2,
158 COULD_NOT_ESTABLISH_CONNECTION_ERROR
= 3,
160 INCORRECT_RESPONSE_SENDER
= 5,
161 INCORRECT_RESPONSE_MESSAGE
= 6,
164 bool AppendIframe(const GURL
& src
) {
166 CHECK(content::ExecuteScriptAndExtractBool(
167 browser()->tab_strip_model()->GetActiveWebContents(),
168 "actions.appendIframe('" + src
.spec() + "');", &result
));
172 Result
CanConnectAndSendMessagesToMainFrame(const Extension
* extension
,
173 const char* message
= NULL
) {
174 return CanConnectAndSendMessagesToFrame(
175 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
180 Result
CanConnectAndSendMessagesToIFrame(const Extension
* extension
,
181 const char* message
= NULL
) {
182 content::RenderFrameHost
* frame
= content::FrameMatchingPredicate(
183 browser()->tab_strip_model()->GetActiveWebContents(),
184 base::Bind(&content::FrameIsChildOfMainFrame
));
185 return CanConnectAndSendMessagesToFrame(frame
, extension
, message
);
188 Result
CanConnectAndSendMessagesToFrame(content::RenderFrameHost
* frame
,
189 const Extension
* extension
,
190 const char* message
) {
192 std::string command
= base::StringPrintf(
193 "assertions.canConnectAndSendMessages('%s', %s, %s)",
194 extension
->id().c_str(),
195 extension
->is_platform_app() ? "true" : "false",
196 message
? base::StringPrintf("'%s'", message
).c_str() : "undefined");
197 CHECK(content::ExecuteScriptAndExtractInt(frame
, command
, &result
));
198 return static_cast<Result
>(result
);
201 testing::AssertionResult
AreAnyNonWebApisDefinedForMainFrame() {
202 return AreAnyNonWebApisDefinedForFrame(
203 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
206 testing::AssertionResult
AreAnyNonWebApisDefinedForIFrame() {
207 content::RenderFrameHost
* frame
= content::FrameMatchingPredicate(
208 browser()->tab_strip_model()->GetActiveWebContents(),
209 base::Bind(&content::FrameIsChildOfMainFrame
));
210 return AreAnyNonWebApisDefinedForFrame(frame
);
213 testing::AssertionResult
AreAnyNonWebApisDefinedForFrame(
214 content::RenderFrameHost
* frame
) {
215 // All runtime API methods are non-web except for sendRequest and connect.
216 const char* non_messaging_apis
[] = {
221 "requestUpdateCheck",
230 "onBrowserUpdateAvailable",
236 // Note: no "id" here because this test method is used for hosted apps,
237 // which do have access to runtime.id.
240 // Turn the array into a JS array, which effectively gets eval()ed.
241 std::string as_js_array
;
242 for (size_t i
= 0; i
< arraysize(non_messaging_apis
); ++i
) {
243 as_js_array
+= as_js_array
.empty() ? "[" : ",";
244 as_js_array
+= base::StringPrintf("'%s'", non_messaging_apis
[i
]);
249 CHECK(content::ExecuteScriptAndExtractBool(
251 "assertions.areAnyRuntimePropertiesDefined(" + as_js_array
+ ")",
254 testing::AssertionSuccess() : testing::AssertionFailure();
257 std::string
GetTlsChannelIdFromPortConnect(const Extension
* extension
,
258 bool include_tls_channel_id
,
259 const char* message
= NULL
) {
260 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromPortConnect",
262 include_tls_channel_id
,
266 std::string
GetTlsChannelIdFromSendMessage(const Extension
* extension
,
267 bool include_tls_channel_id
,
268 const char* message
= NULL
) {
269 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromSendMessage",
271 include_tls_channel_id
,
275 GURL
GetURLForPath(const std::string
& host
, const std::string
& path
) {
276 std::string port
= base::IntToString(embedded_test_server()->port());
277 GURL::Replacements replacements
;
278 replacements
.SetHostStr(host
);
279 replacements
.SetPortStr(port
);
280 return embedded_test_server()->GetURL(path
).ReplaceComponents(replacements
);
283 GURL
chromium_org_url() {
284 return GetURLForPath("www.chromium.org", "/chromium.org.html");
287 GURL
google_com_url() {
288 return GetURLForPath("www.google.com", "/google.com.html");
291 scoped_refptr
<const Extension
> LoadChromiumConnectableExtension() {
292 scoped_refptr
<const Extension
> extension
=
293 LoadExtensionIntoDir(&web_connectable_dir_
,
296 " \"name\": \"chromium_connectable\","
298 " \"externally_connectable\": {"
299 " \"matches\": [\"*://*.chromium.org:*/*\"]"
303 CHECK(extension
.get());
307 scoped_refptr
<const Extension
> LoadChromiumConnectableApp() {
308 scoped_refptr
<const Extension
> extension
=
309 LoadExtensionIntoDir(&web_connectable_dir_
,
313 " \"scripts\": [\"background.js\"]"
316 " \"externally_connectable\": {"
317 " \"matches\": [\"*://*.chromium.org:*/*\"]"
319 " \"manifest_version\": 2,"
320 " \"name\": \"app_connectable\","
321 " \"version\": \"1.0\""
323 CHECK(extension
.get());
327 scoped_refptr
<const Extension
> LoadNotConnectableExtension() {
328 scoped_refptr
<const Extension
> extension
=
329 LoadExtensionIntoDir(¬_connectable_dir_
,
332 " \"name\": \"not_connectable\","
336 CHECK(extension
.get());
340 scoped_refptr
<const Extension
>
341 LoadChromiumConnectableExtensionWithTlsChannelId() {
342 return LoadExtensionIntoDir(&tls_channel_id_connectable_dir_
,
343 connectable_with_tls_channel_id_manifest());
346 scoped_refptr
<const Extension
> LoadChromiumHostedApp() {
347 scoped_refptr
<const Extension
> hosted_app
=
348 LoadExtensionIntoDir(&hosted_app_dir_
,
351 " \"name\": \"chromium_hosted_app\","
352 " \"version\": \"1.0\","
353 " \"manifest_version\": 2,"
355 " \"urls\": [\"%s\"],"
357 " \"web_url\": \"%s\""
361 chromium_org_url().spec().c_str(),
362 chromium_org_url().spec().c_str()));
363 CHECK(hosted_app
.get());
367 void InitializeTestServer() {
368 base::FilePath test_data
;
369 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &test_data
));
370 embedded_test_server()->ServeFilesFromDirectory(test_data
.AppendASCII(
371 "extensions/api_test/messaging/externally_connectable/sites"));
372 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
373 host_resolver()->AddRule("*", embedded_test_server()->base_url().host());
376 const char* close_background_message() {
377 return "closeBackgroundPage";
381 scoped_refptr
<const Extension
> LoadExtensionIntoDir(
382 TestExtensionDir
* dir
,
383 const std::string
& manifest
) {
384 dir
->WriteManifest(manifest
);
385 dir
->WriteFile(FILE_PATH_LITERAL("background.js"),
387 "function maybeClose(message) {\n"
388 " if (message.indexOf('%s') >= 0)\n"
389 " window.setTimeout(function() { window.close() }, 0);\n"
391 "chrome.runtime.onMessageExternal.addListener(\n"
392 " function(message, sender, reply) {\n"
393 " reply({ message: message, sender: sender });\n"
394 " maybeClose(message);\n"
396 "chrome.runtime.onConnectExternal.addListener(function(port) {\n"
397 " port.onMessage.addListener(function(message) {\n"
398 " port.postMessage({ message: message, sender: port.sender });\n"
399 " maybeClose(message);\n"
402 close_background_message()));
403 return LoadExtension(dir
->unpacked_path());
406 const char* common_manifest() {
407 return "\"version\": \"1.0\","
409 " \"scripts\": [\"background.js\"],"
410 " \"persistent\": false"
412 "\"manifest_version\": 2";
415 std::string
connectable_with_tls_channel_id_manifest() {
416 return base::StringPrintf(
418 " \"name\": \"chromium_connectable_with_tls_channel_id\","
420 " \"externally_connectable\": {"
421 " \"matches\": [\"*://*.chromium.org:*/*\"],"
422 " \"accepts_tls_channel_id\": true"
428 std::string
GetTlsChannelIdFromAssertion(const char* method
,
429 const Extension
* extension
,
430 bool include_tls_channel_id
,
431 const char* message
) {
433 std::string args
= "'" + extension
->id() + "', ";
434 args
+= include_tls_channel_id
? "true" : "false";
436 args
+= std::string(", '") + message
+ "'";
437 CHECK(content::ExecuteScriptAndExtractString(
438 browser()->tab_strip_model()->GetActiveWebContents(),
439 base::StringPrintf("assertions.%s(%s)", method
, args
.c_str()),
444 TestExtensionDir web_connectable_dir_
;
445 TestExtensionDir not_connectable_dir_
;
446 TestExtensionDir tls_channel_id_connectable_dir_
;
447 TestExtensionDir hosted_app_dir_
;
450 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
, NotInstalled
) {
451 InitializeTestServer();
453 scoped_refptr
<const Extension
> extension
=
455 .SetID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
456 .SetManifest(DictionaryBuilder()
457 .Set("name", "Fake extension")
459 .Set("manifest_version", 2))
462 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
463 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
464 CanConnectAndSendMessagesToMainFrame(extension
.get()));
465 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
467 ui_test_utils::NavigateToURL(browser(), google_com_url());
468 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
469 CanConnectAndSendMessagesToMainFrame(extension
.get()));
470 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
473 // Tests two extensions on the same sites: one web connectable, one not.
474 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
475 WebConnectableAndNotConnectable
) {
476 InitializeTestServer();
478 // Install the web connectable extension. chromium.org can connect to it,
480 scoped_refptr
<const Extension
> chromium_connectable
=
481 LoadChromiumConnectableExtension();
483 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
485 CanConnectAndSendMessagesToMainFrame(chromium_connectable
.get()));
486 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
488 ui_test_utils::NavigateToURL(browser(), google_com_url());
489 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
490 CanConnectAndSendMessagesToMainFrame(chromium_connectable
.get()));
491 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
493 // Install the non-connectable extension. Nothing can connect to it.
494 scoped_refptr
<const Extension
> not_connectable
=
495 LoadNotConnectableExtension();
497 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
498 // Namespace will be defined here because |chromium_connectable| can connect
499 // to it - so this will be the "cannot establish connection" error.
500 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
501 CanConnectAndSendMessagesToMainFrame(not_connectable
.get()));
502 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
504 ui_test_utils::NavigateToURL(browser(), google_com_url());
505 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
506 CanConnectAndSendMessagesToMainFrame(not_connectable
.get()));
507 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
510 // See http://crbug.com/297866
511 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
512 DISABLED_BackgroundPageClosesOnMessageReceipt
) {
513 InitializeTestServer();
515 // Install the web connectable extension.
516 scoped_refptr
<const Extension
> chromium_connectable
=
517 LoadChromiumConnectableExtension();
519 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
520 // If the background page closes after receipt of the message, it will still
521 // reply to this message...
523 CanConnectAndSendMessagesToMainFrame(chromium_connectable
.get(),
524 close_background_message()));
525 // and be re-opened by receipt of a subsequent message.
527 CanConnectAndSendMessagesToMainFrame(chromium_connectable
.get()));
530 // Tests a web connectable extension that doesn't receive TLS channel id.
531 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
532 WebConnectableWithoutTlsChannelId
) {
533 InitializeTestServer();
535 // Install the web connectable extension. chromium.org can connect to it,
537 scoped_refptr
<const Extension
> chromium_connectable
=
538 LoadChromiumConnectableExtension();
539 ASSERT_TRUE(chromium_connectable
.get());
541 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
542 // The web connectable extension doesn't request the TLS channel ID, so it
543 // doesn't get it, whether or not the page asks for it.
544 EXPECT_EQ(std::string(),
545 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), false));
546 EXPECT_EQ(std::string(),
547 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), true));
548 EXPECT_EQ(std::string(),
549 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), false));
550 EXPECT_EQ(std::string(),
551 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), true));
554 // Tests a web connectable extension that receives TLS channel id with a site
555 // that can't connect to it.
556 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
557 WebConnectableWithTlsChannelIdWithNonMatchingSite
) {
558 InitializeTestServer();
560 scoped_refptr
<const Extension
> chromium_connectable
=
561 LoadChromiumConnectableExtensionWithTlsChannelId();
562 ASSERT_TRUE(chromium_connectable
.get());
564 ui_test_utils::NavigateToURL(browser(), google_com_url());
565 // The extension requests the TLS channel ID, but it doesn't get it for a
566 // site that can't connect to it, regardless of whether the page asks for it.
567 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED
),
568 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), false));
569 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED
),
570 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), true));
571 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED
),
572 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), false));
573 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED
),
574 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), true));
577 // Tests a web connectable extension that receives TLS channel id on a site
578 // that can connect to it, but with no TLS channel ID having been generated.
579 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
580 WebConnectableWithTlsChannelIdWithEmptyTlsChannelId
) {
581 InitializeTestServer();
583 scoped_refptr
<const Extension
> chromium_connectable
=
584 LoadChromiumConnectableExtensionWithTlsChannelId();
585 ASSERT_TRUE(chromium_connectable
.get());
587 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
589 // Since the extension requests the TLS channel ID, it gets it for a site that
590 // can connect to it, but only if the page also asks to include it.
591 EXPECT_EQ(std::string(),
592 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), false));
593 EXPECT_EQ(std::string(),
594 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), false));
595 // If the page does ask for it, it isn't empty.
596 std::string tls_channel_id
=
597 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), true);
598 // Because the TLS channel ID has never been generated for this domain,
599 // no TLS channel ID is reported.
600 EXPECT_EQ(std::string(), tls_channel_id
);
603 // Flaky on Linux and Windows. http://crbug.com/315264
604 // Tests a web connectable extension that receives TLS channel id, but
605 // immediately closes its background page upon receipt of a message.
606 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
607 DISABLED_WebConnectableWithEmptyTlsChannelIdAndClosedBackgroundPage
) {
608 InitializeTestServer();
610 scoped_refptr
<const Extension
> chromium_connectable
=
611 LoadChromiumConnectableExtensionWithTlsChannelId();
613 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
614 // If the page does ask for it, it isn't empty, even if the background page
615 // closes upon receipt of the connect.
616 std::string tls_channel_id
= GetTlsChannelIdFromPortConnect(
617 chromium_connectable
.get(), true, close_background_message());
618 // Because the TLS channel ID has never been generated for this domain,
619 // no TLS channel ID is reported.
620 EXPECT_EQ(std::string(), tls_channel_id
);
621 // A subsequent connect will still succeed, even if the background page was
622 // previously closed.
624 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), true);
625 // And the empty value is still retrieved.
626 EXPECT_EQ(std::string(), tls_channel_id
);
629 // Tests that enabling and disabling an extension makes the runtime bindings
630 // appear and disappear.
632 // TODO(kalman): Test with multiple extensions that can be accessed by the same
634 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
635 EnablingAndDisabling
) {
636 InitializeTestServer();
638 scoped_refptr
<const Extension
> chromium_connectable
=
639 LoadChromiumConnectableExtension();
640 scoped_refptr
<const Extension
> not_connectable
=
641 LoadNotConnectableExtension();
643 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
645 CanConnectAndSendMessagesToMainFrame(chromium_connectable
.get()));
646 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
647 CanConnectAndSendMessagesToMainFrame(not_connectable
.get()));
649 DisableExtension(chromium_connectable
->id());
650 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
651 CanConnectAndSendMessagesToMainFrame(chromium_connectable
.get()));
653 EnableExtension(chromium_connectable
->id());
655 CanConnectAndSendMessagesToMainFrame(chromium_connectable
.get()));
656 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
657 CanConnectAndSendMessagesToMainFrame(not_connectable
.get()));
660 // Tests connection from incognito tabs when the user denies the connection
661 // request. Spanning mode only. A separate test for apps and extensions.
663 // TODO(kalman): ensure that we exercise split vs spanning incognito logic
664 // somewhere. This is a test that should be shared with the content script logic
665 // so it's not really our specific concern for web connectable.
667 // TODO(kalman): test messages from incognito extensions too.
668 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
669 FromIncognitoDenyApp
) {
670 InitializeTestServer();
672 scoped_refptr
<const Extension
> app
= LoadChromiumConnectableApp();
673 ASSERT_TRUE(app
->is_platform_app());
675 Browser
* incognito_browser
= ui_test_utils::OpenURLOffTheRecord(
676 profile()->GetOffTheRecordProfile(),
678 content::RenderFrameHost
* incognito_frame
= incognito_browser
->
679 tab_strip_model()->GetActiveWebContents()->GetMainFrame();
682 IncognitoConnectability::ScopedAlertTracker
alert_tracker(
683 IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY
);
685 // No connection because incognito-enabled hasn't been set for the app, and
686 // the user denied our interactive request.
688 COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
689 CanConnectAndSendMessagesToFrame(incognito_frame
, app
.get(), NULL
));
690 EXPECT_EQ(1, alert_tracker
.GetAndResetAlertCount());
692 // Try again. User has already denied so alert not shown.
694 COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
695 CanConnectAndSendMessagesToFrame(incognito_frame
, app
.get(), NULL
));
696 EXPECT_EQ(0, alert_tracker
.GetAndResetAlertCount());
699 // It's not possible to allow an app in incognito.
700 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(app
->id(), true);
701 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
702 CanConnectAndSendMessagesToFrame(incognito_frame
, app
.get(), NULL
));
705 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
706 FromIncognitoDenyExtension
) {
707 InitializeTestServer();
709 scoped_refptr
<const Extension
> extension
= LoadChromiumConnectableExtension();
711 Browser
* incognito_browser
= ui_test_utils::OpenURLOffTheRecord(
712 profile()->GetOffTheRecordProfile(), chromium_org_url());
713 content::RenderFrameHost
* incognito_frame
=
714 incognito_browser
->tab_strip_model()
715 ->GetActiveWebContents()
719 IncognitoConnectability::ScopedAlertTracker
alert_tracker(
720 IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY
);
722 // The alert doesn't show for extensions.
723 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
724 CanConnectAndSendMessagesToFrame(
725 incognito_frame
, extension
.get(), NULL
));
726 EXPECT_EQ(0, alert_tracker
.GetAndResetAlertCount());
729 // Allowing the extension in incognito mode will bypass the deny.
730 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(extension
->id(), true);
733 CanConnectAndSendMessagesToFrame(incognito_frame
, extension
.get(), NULL
));
736 // Tests connection from incognito tabs when the user accepts the connection
737 // request. Spanning mode only. Separate tests for apps and extensions.
739 // TODO(kalman): see comment above about split mode.
740 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
741 FromIncognitoAllowApp
) {
742 InitializeTestServer();
744 scoped_refptr
<const Extension
> app
= LoadChromiumConnectableApp();
745 ASSERT_TRUE(app
->is_platform_app());
747 Browser
* incognito_browser
= ui_test_utils::OpenURLOffTheRecord(
748 profile()->GetOffTheRecordProfile(),
750 content::RenderFrameHost
* incognito_frame
= incognito_browser
->
751 tab_strip_model()->GetActiveWebContents()->GetMainFrame();
754 IncognitoConnectability::ScopedAlertTracker
alert_tracker(
755 IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW
);
757 // Connection allowed even with incognito disabled, because the user
758 // accepted the interactive request.
760 OK
, CanConnectAndSendMessagesToFrame(incognito_frame
, app
.get(), NULL
));
761 EXPECT_EQ(1, alert_tracker
.GetAndResetAlertCount());
763 // Try again. User has already allowed.
765 OK
, CanConnectAndSendMessagesToFrame(incognito_frame
, app
.get(), NULL
));
766 EXPECT_EQ(0, alert_tracker
.GetAndResetAlertCount());
769 // Apps can't be allowed in incognito mode, but it's moot because it's
771 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(app
->id(), true);
773 CanConnectAndSendMessagesToFrame(incognito_frame
, app
.get(), NULL
));
776 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
777 FromIncognitoAllowExtension
) {
778 InitializeTestServer();
780 scoped_refptr
<const Extension
> extension
= LoadChromiumConnectableExtension();
782 Browser
* incognito_browser
= ui_test_utils::OpenURLOffTheRecord(
783 profile()->GetOffTheRecordProfile(), chromium_org_url());
784 content::RenderFrameHost
* incognito_frame
=
785 incognito_browser
->tab_strip_model()
786 ->GetActiveWebContents()
790 IncognitoConnectability::ScopedAlertTracker
alert_tracker(
791 IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW
);
793 // No alert is shown.
794 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
795 CanConnectAndSendMessagesToFrame(
796 incognito_frame
, extension
.get(), NULL
));
797 EXPECT_EQ(0, alert_tracker
.GetAndResetAlertCount());
800 // Allowing the extension in incognito mode is what allows connections.
801 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(extension
->id(), true);
804 CanConnectAndSendMessagesToFrame(incognito_frame
, extension
.get(), NULL
));
807 // Tests a connection from an iframe within a tab which doesn't have
808 // permission. Iframe should work.
809 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
810 FromIframeWithPermission
) {
811 InitializeTestServer();
813 scoped_refptr
<const Extension
> extension
= LoadChromiumConnectableExtension();
815 ui_test_utils::NavigateToURL(browser(), google_com_url());
816 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
817 CanConnectAndSendMessagesToMainFrame(extension
.get()));
818 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
820 ASSERT_TRUE(AppendIframe(chromium_org_url()));
822 EXPECT_EQ(OK
, CanConnectAndSendMessagesToIFrame(extension
.get()));
823 EXPECT_FALSE(AreAnyNonWebApisDefinedForIFrame());
826 // Tests connection from an iframe without permission within a tab that does.
827 // Iframe shouldn't work.
828 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
829 FromIframeWithoutPermission
) {
830 InitializeTestServer();
832 scoped_refptr
<const Extension
> extension
= LoadChromiumConnectableExtension();
834 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
835 EXPECT_EQ(OK
, CanConnectAndSendMessagesToMainFrame(extension
.get()));
836 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
838 ASSERT_TRUE(AppendIframe(google_com_url()));
840 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
841 CanConnectAndSendMessagesToIFrame(extension
.get()));
842 EXPECT_FALSE(AreAnyNonWebApisDefinedForIFrame());
845 // Tests externally_connectable between a web page and an extension with a
846 // TLS channel ID created for the origin.
847 class ExternallyConnectableMessagingWithTlsChannelIdTest
:
848 public ExternallyConnectableMessagingTest
{
850 ExternallyConnectableMessagingWithTlsChannelIdTest()
851 : tls_channel_id_created_(false, false) {
854 std::string
CreateTlsChannelId() {
855 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter(
856 profile()->GetRequestContext());
857 std::string channel_id_private_key
;
858 std::string channel_id_cert
;
859 net::ChannelIDService::RequestHandle request_handle
;
860 content::BrowserThread::PostTask(
861 content::BrowserThread::IO
,
864 &ExternallyConnectableMessagingWithTlsChannelIdTest::
865 CreateDomainBoundCertOnIOThread
,
866 base::Unretained(this),
867 base::Unretained(&channel_id_private_key
),
868 base::Unretained(&channel_id_cert
),
869 base::Unretained(&request_handle
),
870 request_context_getter
));
871 tls_channel_id_created_
.Wait();
872 // Create the expected value.
873 base::StringPiece spki
;
874 net::asn1::ExtractSPKIFromDERCert(channel_id_cert
, &spki
);
875 base::DictionaryValue jwk_value
;
876 net::JwkSerializer::ConvertSpkiFromDerToJwk(spki
, &jwk_value
);
877 std::string tls_channel_id_value
;
878 base::JSONWriter::Write(&jwk_value
, &tls_channel_id_value
);
879 return tls_channel_id_value
;
883 void CreateDomainBoundCertOnIOThread(
884 std::string
* channel_id_private_key
,
885 std::string
* channel_id_cert
,
886 net::ChannelIDService::RequestHandle
* request_handle
,
887 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter
) {
888 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
889 net::ChannelIDService
* channel_id_service
=
890 request_context_getter
->GetURLRequestContext()->
891 channel_id_service();
892 int status
= channel_id_service
->GetOrCreateChannelID(
893 chromium_org_url().host(),
894 channel_id_private_key
,
896 base::Bind(&ExternallyConnectableMessagingWithTlsChannelIdTest::
898 base::Unretained(this)),
900 if (status
== net::ERR_IO_PENDING
)
902 GotDomainBoundCert(status
);
905 void GotDomainBoundCert(int status
) {
906 ASSERT_TRUE(status
== net::OK
);
907 tls_channel_id_created_
.Signal();
910 base::WaitableEvent tls_channel_id_created_
;
913 // Tests a web connectable extension that receives TLS channel id on a site
914 // that can connect to it, with a TLS channel ID having been generated.
915 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingWithTlsChannelIdTest
,
916 WebConnectableWithNonEmptyTlsChannelId
) {
917 InitializeTestServer();
918 std::string expected_tls_channel_id_value
= CreateTlsChannelId();
920 scoped_refptr
<const Extension
> chromium_connectable
=
921 LoadChromiumConnectableExtensionWithTlsChannelId();
922 ASSERT_TRUE(chromium_connectable
.get());
924 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
926 // Since the extension requests the TLS channel ID, it gets it for a site that
927 // can connect to it, but only if the page also asks to send it.
928 EXPECT_EQ(std::string(),
929 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), false));
930 EXPECT_EQ(std::string(),
931 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), false));
933 // If the page does ask to send the TLS channel ID, it's sent and non-empty.
934 std::string tls_channel_id_from_port_connect
=
935 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), true);
936 EXPECT_NE(0u, tls_channel_id_from_port_connect
.size());
938 // The same value is received by both connect and sendMessage.
939 std::string tls_channel_id_from_send_message
=
940 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), true);
941 EXPECT_EQ(tls_channel_id_from_port_connect
, tls_channel_id_from_send_message
);
943 // And since a TLS channel ID exists for the domain, the value received is
944 // parseable as a JWK. (In particular, it has the same value we created by
945 // converting the public key to JWK with net::ConvertSpkiFromDerToJwk.)
946 std::string
tls_channel_id(tls_channel_id_from_port_connect
);
947 EXPECT_EQ(expected_tls_channel_id_value
, tls_channel_id
);
949 // The TLS channel ID shouldn't change from one connection to the next...
950 std::string tls_channel_id2
=
951 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), true);
952 EXPECT_EQ(tls_channel_id
, tls_channel_id2
);
954 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), true);
955 EXPECT_EQ(tls_channel_id
, tls_channel_id2
);
957 // nor should it change when navigating away, revisiting the page and
958 // requesting it again.
959 ui_test_utils::NavigateToURL(browser(), google_com_url());
960 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
962 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), true);
963 EXPECT_EQ(tls_channel_id
, tls_channel_id2
);
965 GetTlsChannelIdFromSendMessage(chromium_connectable
.get(), true);
966 EXPECT_EQ(tls_channel_id
, tls_channel_id2
);
969 // Tests a web connectable extension that receives TLS channel id, but
970 // immediately closes its background page upon receipt of a message.
971 // Same flakiness seen in http://crbug.com/297866
972 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingWithTlsChannelIdTest
,
973 DISABLED_WebConnectableWithNonEmptyTlsChannelIdAndClosedBackgroundPage
) {
974 InitializeTestServer();
975 std::string expected_tls_channel_id_value
= CreateTlsChannelId();
977 scoped_refptr
<const Extension
> chromium_connectable
=
978 LoadChromiumConnectableExtensionWithTlsChannelId();
980 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
981 // If the page does ask for it, it isn't empty, even if the background page
982 // closes upon receipt of the connect.
983 std::string tls_channel_id
= GetTlsChannelIdFromPortConnect(
984 chromium_connectable
.get(), true, close_background_message());
985 EXPECT_EQ(expected_tls_channel_id_value
, tls_channel_id
);
986 // A subsequent connect will still succeed, even if the background page was
987 // previously closed.
989 GetTlsChannelIdFromPortConnect(chromium_connectable
.get(), true);
990 // And the expected value is still retrieved.
991 EXPECT_EQ(expected_tls_channel_id_value
, tls_channel_id
);
994 IN_PROC_BROWSER_TEST_F(ExtensionApiTest
, MessagingUserGesture
) {
995 const char kManifest
[] = "{"
996 " \"name\": \"user_gesture\","
997 " \"version\": \"1.0\","
999 " \"scripts\": [\"background.js\"]"
1001 " \"manifest_version\": 2"
1004 TestExtensionDir receiver_dir
;
1005 receiver_dir
.WriteManifest(kManifest
);
1006 receiver_dir
.WriteFile(FILE_PATH_LITERAL("background.js"),
1007 "chrome.runtime.onMessageExternal.addListener(\n"
1008 " function(msg, sender, reply) {\n"
1009 " reply({result:chrome.test.isProcessingUserGesture()});\n"
1011 const Extension
* receiver
= LoadExtension(receiver_dir
.unpacked_path());
1012 ASSERT_TRUE(receiver
);
1014 TestExtensionDir sender_dir
;
1015 sender_dir
.WriteManifest(kManifest
);
1016 sender_dir
.WriteFile(FILE_PATH_LITERAL("background.js"), "");
1017 const Extension
* sender
= LoadExtension(sender_dir
.unpacked_path());
1018 ASSERT_TRUE(sender
);
1021 ExecuteScriptInBackgroundPage(sender
->id(),
1023 "chrome.test.runWithoutUserGesture(function() {\n"
1024 " chrome.runtime.sendMessage('%s', {}, function(response) {\n"
1025 " window.domAutomationController.send('' + response.result);\n"
1027 "});", receiver
->id().c_str())));
1030 ExecuteScriptInBackgroundPage(sender
->id(),
1032 "chrome.test.runWithUserGesture(function() {\n"
1033 " chrome.runtime.sendMessage('%s', {}, function(response) {\n"
1034 " window.domAutomationController.send('' + response.result);\n"
1036 "});", receiver
->id().c_str())));
1039 // Tests that a hosted app on a connectable site doesn't interfere with the
1040 // connectability of that site.
1041 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
, HostedAppOnWebsite
) {
1042 InitializeTestServer();
1044 scoped_refptr
<const Extension
> app
= LoadChromiumHostedApp();
1046 // The presence of the hosted app shouldn't give the ability to send messages.
1047 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
1048 EXPECT_EQ(NAMESPACE_NOT_DEFINED
,
1049 CanConnectAndSendMessagesToMainFrame(app
.get()));
1050 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
1052 // Once a connectable extension is installed, it should.
1053 scoped_refptr
<const Extension
> extension
= LoadChromiumConnectableExtension();
1054 EXPECT_EQ(OK
, CanConnectAndSendMessagesToMainFrame(extension
.get()));
1055 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
1058 // Tests that an invalid extension ID specified in a hosted app does not crash
1059 // the hosted app's renderer.
1061 // This is a regression test for http://crbug.com/326250#c12.
1062 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest
,
1063 InvalidExtensionIDFromHostedApp
) {
1064 InitializeTestServer();
1066 // The presence of the chromium hosted app triggers this bug. The chromium
1067 // connectable extension needs to be installed to set up the runtime bindings.
1068 LoadChromiumHostedApp();
1069 LoadChromiumConnectableExtension();
1071 scoped_refptr
<const Extension
> invalid
=
1073 // A bit scary that this works...
1075 .SetManifest(DictionaryBuilder()
1076 .Set("name", "Fake extension")
1077 .Set("version", "1")
1078 .Set("manifest_version", 2))
1081 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
1082 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR
,
1083 CanConnectAndSendMessagesToMainFrame(invalid
.get()));
1086 #endif // !defined(OS_WIN) - http://crbug.com/350517.
1090 }; // namespace extensions