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/common/extensions/api/runtime.h"
25 #include "chrome/test/base/ui_test_utils.h"
26 #include "content/public/browser/notification_registrar.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/test/browser_test_utils.h"
29 #include "extensions/browser/event_router.h"
30 #include "extensions/browser/extension_prefs.h"
31 #include "extensions/browser/extension_system.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/server_bound_cert_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 {
49 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
50 content::NotificationService::AllSources());
54 static scoped_ptr<base::ListValue> BuildEventArguments(
55 const bool last_message,
56 const std::string& data) {
57 base::DictionaryValue* event = new base::DictionaryValue();
58 event->SetBoolean("lastMessage", last_message);
59 event->SetString("data", data);
60 scoped_ptr<base::ListValue> arguments(new base::ListValue());
61 arguments->Append(event);
62 return arguments.Pass();
65 static scoped_ptr<Event> BuildEvent(scoped_ptr<base::ListValue> event_args,
68 scoped_ptr<Event> event(new Event("test.onMessage", event_args.Pass()));
69 event->restrict_to_browser_context = profile;
70 event->event_url = event_url;
74 virtual void Observe(int type,
75 const content::NotificationSource& source,
76 const content::NotificationDetails& details) OVERRIDE {
77 EventRouter* event_router = ExtensionSystem::Get(
78 content::Source<Profile>(source).ptr())->event_router();
80 // Sends four messages to the extension. All but the third message sent
81 // from the origin http://b.com/ are supposed to arrive.
82 event_router->BroadcastEvent(BuildEvent(
83 BuildEventArguments(false, "no restriction"),
84 content::Source<Profile>(source).ptr(),
86 event_router->BroadcastEvent(BuildEvent(
87 BuildEventArguments(false, "http://a.com/"),
88 content::Source<Profile>(source).ptr(),
89 GURL("http://a.com/")));
90 event_router->BroadcastEvent(BuildEvent(
91 BuildEventArguments(false, "http://b.com/"),
92 content::Source<Profile>(source).ptr(),
93 GURL("http://b.com/")));
94 event_router->BroadcastEvent(BuildEvent(
95 BuildEventArguments(true, "last message"),
96 content::Source<Profile>(source).ptr(),
100 content::NotificationRegistrar registrar_;
103 // Tests that message passing between extensions and content scripts works.
104 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Messaging) {
105 ASSERT_TRUE(StartEmbeddedTestServer());
106 ASSERT_TRUE(RunExtensionTest("messaging/connect")) << message_;
109 // Tests that message passing from one extension to another works.
110 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingExternal) {
111 ASSERT_TRUE(LoadExtension(
112 test_data_dir_.AppendASCII("..").AppendASCII("good")
113 .AppendASCII("Extensions")
114 .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa")
115 .AppendASCII("1.0")));
117 ASSERT_TRUE(RunExtensionTest("messaging/connect_external")) << message_;
120 // Tests that messages with event_urls are only passed to extensions with
121 // appropriate permissions.
122 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingEventURL) {
123 MessageSender sender;
124 ASSERT_TRUE(RunExtensionTest("messaging/event_url")) << message_;
127 // Tests connecting from a panel to its extension.
128 class PanelMessagingTest : public ExtensionApiTest {
129 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
130 ExtensionApiTest::SetUpCommandLine(command_line);
131 command_line->AppendSwitch(switches::kEnablePanels);
135 IN_PROC_BROWSER_TEST_F(PanelMessagingTest, MessagingPanel) {
136 ASSERT_TRUE(RunExtensionTest("messaging/connect_panel")) << message_;
139 // XXX(kalman): All web messaging tests disabled on windows due to extreme
140 // flakiness. See http://crbug.com/350517.
143 // Tests externally_connectable between a web page and an extension.
145 // TODO(kalman): Test between extensions. This is already tested in this file,
146 // but not with externally_connectable set in the manifest.
148 // TODO(kalman): Test with host permissions.
149 class ExternallyConnectableMessagingTest : public ExtensionApiTest {
151 // Result codes from the test. These must match up with |results| in
152 // c/t/d/extensions/api_test/externally_connectable/assertions.json.
155 NAMESPACE_NOT_DEFINED = 1,
156 FUNCTION_NOT_DEFINED = 2,
157 COULD_NOT_ESTABLISH_CONNECTION_ERROR = 3,
159 INCORRECT_RESPONSE_SENDER = 5,
160 INCORRECT_RESPONSE_MESSAGE = 6,
163 bool AppendIframe(const GURL& src) {
165 CHECK(content::ExecuteScriptAndExtractBool(
166 browser()->tab_strip_model()->GetActiveWebContents(),
167 "actions.appendIframe('" + src.spec() + "');", &result));
171 Result CanConnectAndSendMessagesToMainFrame(const Extension* extension,
172 const char* message = NULL) {
173 return CanConnectAndSendMessagesToFrame(
174 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
179 Result CanConnectAndSendMessagesToIFrame(const Extension* extension,
180 const char* message = NULL) {
181 content::RenderFrameHost* frame = content::FrameMatchingPredicate(
182 browser()->tab_strip_model()->GetActiveWebContents(),
183 base::Bind(&content::FrameIsChildOfMainFrame));
184 return CanConnectAndSendMessagesToFrame(frame, extension, message);
187 Result CanConnectAndSendMessagesToFrame(content::RenderFrameHost* frame,
188 const Extension* extension,
189 const char* message) {
191 std::string command = base::StringPrintf(
192 "assertions.canConnectAndSendMessages('%s', %s, %s)",
193 extension->id().c_str(),
194 extension->is_platform_app() ? "true" : "false",
195 message ? base::StringPrintf("'%s'", message).c_str() : "undefined");
196 CHECK(content::ExecuteScriptAndExtractInt(frame, command, &result));
197 return static_cast<Result>(result);
200 testing::AssertionResult AreAnyNonWebApisDefinedForMainFrame() {
201 return AreAnyNonWebApisDefinedForFrame(
202 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
205 testing::AssertionResult AreAnyNonWebApisDefinedForIFrame() {
206 content::RenderFrameHost* frame = content::FrameMatchingPredicate(
207 browser()->tab_strip_model()->GetActiveWebContents(),
208 base::Bind(&content::FrameIsChildOfMainFrame));
209 return AreAnyNonWebApisDefinedForFrame(frame);
212 testing::AssertionResult AreAnyNonWebApisDefinedForFrame(
213 content::RenderFrameHost* frame) {
214 // All runtime API methods are non-web except for sendRequest and connect.
215 const char* non_messaging_apis[] = {
220 "requestUpdateCheck",
229 "onBrowserUpdateAvailable",
235 // Note: no "id" here because this test method is used for hosted apps,
236 // which do have access to runtime.id.
239 // Turn the array into a JS array, which effectively gets eval()ed.
240 std::string as_js_array;
241 for (size_t i = 0; i < arraysize(non_messaging_apis); ++i) {
242 as_js_array += as_js_array.empty() ? "[" : ",";
243 as_js_array += base::StringPrintf("'%s'", non_messaging_apis[i]);
248 CHECK(content::ExecuteScriptAndExtractBool(
250 "assertions.areAnyRuntimePropertiesDefined(" + as_js_array + ")",
253 testing::AssertionSuccess() : testing::AssertionFailure();
256 std::string GetTlsChannelIdFromPortConnect(const Extension* extension,
257 bool include_tls_channel_id,
258 const char* message = NULL) {
259 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromPortConnect",
261 include_tls_channel_id,
265 std::string GetTlsChannelIdFromSendMessage(const Extension* extension,
266 bool include_tls_channel_id,
267 const char* message = NULL) {
268 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromSendMessage",
270 include_tls_channel_id,
274 GURL GetURLForPath(const std::string& host, const std::string& path) {
275 std::string port = base::IntToString(embedded_test_server()->port());
276 GURL::Replacements replacements;
277 replacements.SetHostStr(host);
278 replacements.SetPortStr(port);
279 return embedded_test_server()->GetURL(path).ReplaceComponents(replacements);
282 GURL chromium_org_url() {
283 return GetURLForPath("www.chromium.org", "/chromium.org.html");
286 GURL google_com_url() {
287 return GetURLForPath("www.google.com", "/google.com.html");
290 scoped_refptr<const Extension> LoadChromiumConnectableExtension() {
291 scoped_refptr<const Extension> extension =
292 LoadExtensionIntoDir(&web_connectable_dir_,
295 " \"name\": \"chromium_connectable\","
297 " \"externally_connectable\": {"
298 " \"matches\": [\"*://*.chromium.org:*/*\"]"
302 CHECK(extension.get());
306 scoped_refptr<const Extension> LoadChromiumConnectableApp() {
307 scoped_refptr<const Extension> extension =
308 LoadExtensionIntoDir(&web_connectable_dir_,
312 " \"scripts\": [\"background.js\"]"
315 " \"externally_connectable\": {"
316 " \"matches\": [\"*://*.chromium.org:*/*\"]"
318 " \"manifest_version\": 2,"
319 " \"name\": \"app_connectable\","
320 " \"version\": \"1.0\""
322 CHECK(extension.get());
326 scoped_refptr<const Extension> LoadNotConnectableExtension() {
327 scoped_refptr<const Extension> extension =
328 LoadExtensionIntoDir(¬_connectable_dir_,
331 " \"name\": \"not_connectable\","
335 CHECK(extension.get());
339 scoped_refptr<const Extension>
340 LoadChromiumConnectableExtensionWithTlsChannelId() {
341 return LoadExtensionIntoDir(&tls_channel_id_connectable_dir_,
342 connectable_with_tls_channel_id_manifest());
345 scoped_refptr<const Extension> LoadChromiumHostedApp() {
346 scoped_refptr<const Extension> hosted_app =
347 LoadExtensionIntoDir(&hosted_app_dir_,
350 " \"name\": \"chromium_hosted_app\","
351 " \"version\": \"1.0\","
352 " \"manifest_version\": 2,"
354 " \"urls\": [\"%s\"],"
356 " \"web_url\": \"%s\""
360 chromium_org_url().spec().c_str(),
361 chromium_org_url().spec().c_str()));
362 CHECK(hosted_app.get());
366 void InitializeTestServer() {
367 base::FilePath test_data;
368 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data));
369 embedded_test_server()->ServeFilesFromDirectory(test_data.AppendASCII(
370 "extensions/api_test/messaging/externally_connectable/sites"));
371 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
372 host_resolver()->AddRule("*", embedded_test_server()->base_url().host());
375 const char* close_background_message() {
376 return "closeBackgroundPage";
380 scoped_refptr<const Extension> LoadExtensionIntoDir(
381 TestExtensionDir* dir,
382 const std::string& manifest) {
383 dir->WriteManifest(manifest);
384 dir->WriteFile(FILE_PATH_LITERAL("background.js"),
386 "function maybeClose(message) {\n"
387 " if (message.indexOf('%s') >= 0)\n"
388 " window.setTimeout(function() { window.close() }, 0);\n"
390 "chrome.runtime.onMessageExternal.addListener(\n"
391 " function(message, sender, reply) {\n"
392 " reply({ message: message, sender: sender });\n"
393 " maybeClose(message);\n"
395 "chrome.runtime.onConnectExternal.addListener(function(port) {\n"
396 " port.onMessage.addListener(function(message) {\n"
397 " port.postMessage({ message: message, sender: port.sender });\n"
398 " maybeClose(message);\n"
401 close_background_message()));
402 return LoadExtension(dir->unpacked_path());
405 const char* common_manifest() {
406 return "\"version\": \"1.0\","
408 " \"scripts\": [\"background.js\"],"
409 " \"persistent\": false"
411 "\"manifest_version\": 2";
414 std::string connectable_with_tls_channel_id_manifest() {
415 return base::StringPrintf(
417 " \"name\": \"chromium_connectable_with_tls_channel_id\","
419 " \"externally_connectable\": {"
420 " \"matches\": [\"*://*.chromium.org:*/*\"],"
421 " \"accepts_tls_channel_id\": true"
427 std::string GetTlsChannelIdFromAssertion(const char* method,
428 const Extension* extension,
429 bool include_tls_channel_id,
430 const char* message) {
432 std::string args = "'" + extension->id() + "', ";
433 args += include_tls_channel_id ? "true" : "false";
435 args += std::string(", '") + message + "'";
436 CHECK(content::ExecuteScriptAndExtractString(
437 browser()->tab_strip_model()->GetActiveWebContents(),
438 base::StringPrintf("assertions.%s(%s)", method, args.c_str()),
443 TestExtensionDir web_connectable_dir_;
444 TestExtensionDir not_connectable_dir_;
445 TestExtensionDir tls_channel_id_connectable_dir_;
446 TestExtensionDir hosted_app_dir_;
449 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, NotInstalled) {
450 InitializeTestServer();
452 scoped_refptr<const Extension> extension =
454 .SetID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
455 .SetManifest(DictionaryBuilder()
456 .Set("name", "Fake extension")
458 .Set("manifest_version", 2))
461 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
462 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
463 CanConnectAndSendMessagesToMainFrame(extension));
464 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
466 ui_test_utils::NavigateToURL(browser(), google_com_url());
467 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
468 CanConnectAndSendMessagesToMainFrame(extension));
469 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
472 // Tests two extensions on the same sites: one web connectable, one not.
473 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
474 WebConnectableAndNotConnectable) {
475 InitializeTestServer();
477 // Install the web connectable extension. chromium.org can connect to it,
479 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
481 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
482 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(chromium_connectable));
483 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
485 ui_test_utils::NavigateToURL(browser(), google_com_url());
486 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
487 CanConnectAndSendMessagesToMainFrame(chromium_connectable));
488 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
490 // Install the non-connectable extension. Nothing can connect to it.
491 const Extension* not_connectable = LoadNotConnectableExtension();
493 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
494 // Namespace will be defined here because |chromium_connectable| can connect
495 // to it - so this will be the "cannot establish connection" error.
496 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
497 CanConnectAndSendMessagesToMainFrame(not_connectable));
498 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
500 ui_test_utils::NavigateToURL(browser(), google_com_url());
501 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
502 CanConnectAndSendMessagesToMainFrame(not_connectable));
503 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
506 // See http://crbug.com/297866
507 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
508 DISABLED_BackgroundPageClosesOnMessageReceipt) {
509 InitializeTestServer();
511 // Install the web connectable extension.
512 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
514 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
515 // If the background page closes after receipt of the message, it will still
516 // reply to this message...
518 CanConnectAndSendMessagesToMainFrame(chromium_connectable,
519 close_background_message()));
520 // and be re-opened by receipt of a subsequent message.
521 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(chromium_connectable));
524 // Tests a web connectable extension that doesn't receive TLS channel id.
525 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
526 WebConnectableWithoutTlsChannelId) {
527 InitializeTestServer();
529 // Install the web connectable extension. chromium.org can connect to it,
531 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
532 ASSERT_TRUE(chromium_connectable);
534 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
535 // The web connectable extension doesn't request the TLS channel ID, so it
536 // doesn't get it, whether or not the page asks for it.
537 EXPECT_EQ(std::string(),
538 GetTlsChannelIdFromPortConnect(chromium_connectable, false));
539 EXPECT_EQ(std::string(),
540 GetTlsChannelIdFromSendMessage(chromium_connectable, true));
541 EXPECT_EQ(std::string(),
542 GetTlsChannelIdFromPortConnect(chromium_connectable, false));
543 EXPECT_EQ(std::string(),
544 GetTlsChannelIdFromSendMessage(chromium_connectable, true));
547 // Tests a web connectable extension that receives TLS channel id with a site
548 // that can't connect to it.
549 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
550 WebConnectableWithTlsChannelIdWithNonMatchingSite) {
551 InitializeTestServer();
553 const Extension* chromium_connectable =
554 LoadChromiumConnectableExtensionWithTlsChannelId();
555 ASSERT_TRUE(chromium_connectable);
557 ui_test_utils::NavigateToURL(browser(), google_com_url());
558 // The extension requests the TLS channel ID, but it doesn't get it for a
559 // site that can't connect to it, regardless of whether the page asks for it.
560 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED),
561 GetTlsChannelIdFromPortConnect(chromium_connectable, false));
562 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED),
563 GetTlsChannelIdFromSendMessage(chromium_connectable, true));
564 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED),
565 GetTlsChannelIdFromPortConnect(chromium_connectable, false));
566 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED),
567 GetTlsChannelIdFromSendMessage(chromium_connectable, true));
570 // Tests a web connectable extension that receives TLS channel id on a site
571 // that can connect to it, but with no TLS channel ID having been generated.
572 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
573 WebConnectableWithTlsChannelIdWithEmptyTlsChannelId) {
574 InitializeTestServer();
576 const Extension* chromium_connectable =
577 LoadChromiumConnectableExtensionWithTlsChannelId();
578 ASSERT_TRUE(chromium_connectable);
580 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
582 // Since the extension requests the TLS channel ID, it gets it for a site that
583 // can connect to it, but only if the page also asks to include it.
584 EXPECT_EQ(std::string(),
585 GetTlsChannelIdFromPortConnect(chromium_connectable, false));
586 EXPECT_EQ(std::string(),
587 GetTlsChannelIdFromSendMessage(chromium_connectable, false));
588 // If the page does ask for it, it isn't empty.
589 std::string tls_channel_id =
590 GetTlsChannelIdFromPortConnect(chromium_connectable, true);
591 // Because the TLS channel ID has never been generated for this domain,
592 // no TLS channel ID is reported.
593 EXPECT_EQ(std::string(), tls_channel_id);
596 // Flaky on Linux and Windows. http://crbug.com/315264
597 // Tests a web connectable extension that receives TLS channel id, but
598 // immediately closes its background page upon receipt of a message.
599 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
600 DISABLED_WebConnectableWithEmptyTlsChannelIdAndClosedBackgroundPage) {
601 InitializeTestServer();
603 const Extension* chromium_connectable =
604 LoadChromiumConnectableExtensionWithTlsChannelId();
606 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
607 // If the page does ask for it, it isn't empty, even if the background page
608 // closes upon receipt of the connect.
609 std::string tls_channel_id = GetTlsChannelIdFromPortConnect(
610 chromium_connectable, true, close_background_message());
611 // Because the TLS channel ID has never been generated for this domain,
612 // no TLS channel ID is reported.
613 EXPECT_EQ(std::string(), tls_channel_id);
614 // A subsequent connect will still succeed, even if the background page was
615 // previously closed.
616 tls_channel_id = GetTlsChannelIdFromPortConnect(chromium_connectable, true);
617 // And the empty value is still retrieved.
618 EXPECT_EQ(std::string(), tls_channel_id);
621 // Tests that enabling and disabling an extension makes the runtime bindings
622 // appear and disappear.
624 // TODO(kalman): Test with multiple extensions that can be accessed by the same
626 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
627 EnablingAndDisabling) {
628 InitializeTestServer();
630 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
631 const Extension* not_connectable = LoadNotConnectableExtension();
633 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
634 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(chromium_connectable));
635 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
636 CanConnectAndSendMessagesToMainFrame(not_connectable));
638 DisableExtension(chromium_connectable->id());
639 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
640 CanConnectAndSendMessagesToMainFrame(chromium_connectable));
642 EnableExtension(chromium_connectable->id());
643 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(chromium_connectable));
644 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
645 CanConnectAndSendMessagesToMainFrame(not_connectable));
648 // Tests connection from incognito tabs when the user denies the connection
649 // request. Spanning mode only. A separate test for apps and extensions.
651 // TODO(kalman): ensure that we exercise split vs spanning incognito logic
652 // somewhere. This is a test that should be shared with the content script logic
653 // so it's not really our specific concern for web connectable.
655 // TODO(kalman): test messages from incognito extensions too.
656 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
657 FromIncognitoDenyApp) {
658 InitializeTestServer();
660 scoped_refptr<const Extension> app = LoadChromiumConnectableApp();
661 ASSERT_TRUE(app->is_platform_app());
663 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord(
664 profile()->GetOffTheRecordProfile(),
666 content::RenderFrameHost* incognito_frame = incognito_browser->
667 tab_strip_model()->GetActiveWebContents()->GetMainFrame();
670 IncognitoConnectability::ScopedAlertTracker alert_tracker(
671 IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY);
673 // No connection because incognito-enabled hasn't been set for the app, and
674 // the user denied our interactive request.
675 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
676 CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL));
677 EXPECT_EQ(1, alert_tracker.GetAndResetAlertCount());
679 // Try again. User has already denied so alert not shown.
680 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
681 CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL));
682 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount());
685 // It's not possible to allow an app in incognito.
686 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(app->id(), true);
687 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
688 CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL));
691 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
692 FromIncognitoDenyExtension) {
693 InitializeTestServer();
695 scoped_refptr<const Extension> extension = LoadChromiumConnectableExtension();
697 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord(
698 profile()->GetOffTheRecordProfile(), chromium_org_url());
699 content::RenderFrameHost* incognito_frame =
700 incognito_browser->tab_strip_model()
701 ->GetActiveWebContents()
705 IncognitoConnectability::ScopedAlertTracker alert_tracker(
706 IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY);
708 // The alert doesn't show for extensions.
710 COULD_NOT_ESTABLISH_CONNECTION_ERROR,
711 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL));
712 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount());
715 // Allowing the extension in incognito mode will bypass the deny.
716 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(extension->id(), true);
718 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL));
721 // Tests connection from incognito tabs when the user accepts the connection
722 // request. Spanning mode only. Separate tests for apps and extensions.
724 // TODO(kalman): see comment above about split mode.
725 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
726 FromIncognitoAllowApp) {
727 InitializeTestServer();
729 scoped_refptr<const Extension> app = LoadChromiumConnectableApp();
730 ASSERT_TRUE(app->is_platform_app());
732 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord(
733 profile()->GetOffTheRecordProfile(),
735 content::RenderFrameHost* incognito_frame = incognito_browser->
736 tab_strip_model()->GetActiveWebContents()->GetMainFrame();
739 IncognitoConnectability::ScopedAlertTracker alert_tracker(
740 IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW);
742 // Connection allowed even with incognito disabled, because the user
743 // accepted the interactive request.
744 EXPECT_EQ(OK, CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL));
745 EXPECT_EQ(1, alert_tracker.GetAndResetAlertCount());
747 // Try again. User has already allowed.
748 EXPECT_EQ(OK, CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL));
749 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount());
752 // Apps can't be allowed in incognito mode, but it's moot because it's
754 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(app->id(), true);
755 EXPECT_EQ(OK, CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL));
758 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
759 FromIncognitoAllowExtension) {
760 InitializeTestServer();
762 scoped_refptr<const Extension> extension = LoadChromiumConnectableExtension();
764 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord(
765 profile()->GetOffTheRecordProfile(), chromium_org_url());
766 content::RenderFrameHost* incognito_frame =
767 incognito_browser->tab_strip_model()
768 ->GetActiveWebContents()
772 IncognitoConnectability::ScopedAlertTracker alert_tracker(
773 IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW);
775 // No alert is shown.
777 COULD_NOT_ESTABLISH_CONNECTION_ERROR,
778 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL));
779 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount());
782 // Allowing the extension in incognito mode is what allows connections.
783 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(extension->id(), true);
785 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL));
788 // Tests a connection from an iframe within a tab which doesn't have
789 // permission. Iframe should work.
790 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
791 FromIframeWithPermission) {
792 InitializeTestServer();
794 const Extension* extension = LoadChromiumConnectableExtension();
796 ui_test_utils::NavigateToURL(browser(), google_com_url());
797 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
798 CanConnectAndSendMessagesToMainFrame(extension));
799 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
801 ASSERT_TRUE(AppendIframe(chromium_org_url()));
803 EXPECT_EQ(OK, CanConnectAndSendMessagesToIFrame(extension));
804 EXPECT_FALSE(AreAnyNonWebApisDefinedForIFrame());
807 // Tests connection from an iframe without permission within a tab that does.
808 // Iframe shouldn't work.
809 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
810 FromIframeWithoutPermission) {
811 InitializeTestServer();
813 const Extension* extension = LoadChromiumConnectableExtension();
815 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
816 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(extension));
817 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
819 ASSERT_TRUE(AppendIframe(google_com_url()));
821 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
822 CanConnectAndSendMessagesToIFrame(extension));
823 EXPECT_FALSE(AreAnyNonWebApisDefinedForIFrame());
826 // Tests externally_connectable between a web page and an extension with a
827 // TLS channel ID created for the origin.
828 class ExternallyConnectableMessagingWithTlsChannelIdTest :
829 public ExternallyConnectableMessagingTest {
831 ExternallyConnectableMessagingWithTlsChannelIdTest()
832 : tls_channel_id_created_(false, false) {
835 std::string CreateTlsChannelId() {
836 scoped_refptr<net::URLRequestContextGetter> request_context_getter(
837 profile()->GetRequestContext());
838 std::string domain_bound_private_key;
839 std::string domain_bound_cert;
840 net::ServerBoundCertService::RequestHandle request_handle;
841 content::BrowserThread::PostTask(
842 content::BrowserThread::IO,
845 &ExternallyConnectableMessagingWithTlsChannelIdTest::
846 CreateDomainBoundCertOnIOThread,
847 base::Unretained(this),
848 base::Unretained(&domain_bound_private_key),
849 base::Unretained(&domain_bound_cert),
850 base::Unretained(&request_handle),
851 request_context_getter));
852 tls_channel_id_created_.Wait();
853 // Create the expected value.
854 base::StringPiece spki;
855 net::asn1::ExtractSPKIFromDERCert(domain_bound_cert, &spki);
856 base::DictionaryValue jwk_value;
857 net::JwkSerializer::ConvertSpkiFromDerToJwk(spki, &jwk_value);
858 std::string tls_channel_id_value;
859 base::JSONWriter::Write(&jwk_value, &tls_channel_id_value);
860 return tls_channel_id_value;
864 void CreateDomainBoundCertOnIOThread(
865 std::string* domain_bound_private_key,
866 std::string* domain_bound_cert,
867 net::ServerBoundCertService::RequestHandle* request_handle,
868 scoped_refptr<net::URLRequestContextGetter> request_context_getter) {
869 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
870 net::ServerBoundCertService* server_bound_cert_service =
871 request_context_getter->GetURLRequestContext()->
872 server_bound_cert_service();
873 int status = server_bound_cert_service->GetOrCreateDomainBoundCert(
874 chromium_org_url().host(),
875 domain_bound_private_key,
877 base::Bind(&ExternallyConnectableMessagingWithTlsChannelIdTest::
879 base::Unretained(this)),
881 if (status == net::ERR_IO_PENDING)
883 GotDomainBoundCert(status);
886 void GotDomainBoundCert(int status) {
887 ASSERT_TRUE(status == net::OK);
888 tls_channel_id_created_.Signal();
891 base::WaitableEvent tls_channel_id_created_;
894 // Tests a web connectable extension that receives TLS channel id on a site
895 // that can connect to it, with a TLS channel ID having been generated.
896 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingWithTlsChannelIdTest,
897 WebConnectableWithNonEmptyTlsChannelId) {
898 InitializeTestServer();
899 std::string expected_tls_channel_id_value = CreateTlsChannelId();
901 const Extension* chromium_connectable =
902 LoadChromiumConnectableExtensionWithTlsChannelId();
903 ASSERT_TRUE(chromium_connectable);
905 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
907 // Since the extension requests the TLS channel ID, it gets it for a site that
908 // can connect to it, but only if the page also asks to send it.
909 EXPECT_EQ(std::string(),
910 GetTlsChannelIdFromPortConnect(chromium_connectable, false));
911 EXPECT_EQ(std::string(),
912 GetTlsChannelIdFromSendMessage(chromium_connectable, false));
914 // If the page does ask to send the TLS channel ID, it's sent and non-empty.
915 std::string tls_channel_id_from_port_connect =
916 GetTlsChannelIdFromPortConnect(chromium_connectable, true);
917 EXPECT_NE(0u, tls_channel_id_from_port_connect.size());
919 // The same value is received by both connect and sendMessage.
920 std::string tls_channel_id_from_send_message =
921 GetTlsChannelIdFromSendMessage(chromium_connectable, true);
922 EXPECT_EQ(tls_channel_id_from_port_connect, tls_channel_id_from_send_message);
924 // And since a TLS channel ID exists for the domain, the value received is
925 // parseable as a JWK. (In particular, it has the same value we created by
926 // converting the public key to JWK with net::ConvertSpkiFromDerToJwk.)
927 std::string tls_channel_id(tls_channel_id_from_port_connect);
928 EXPECT_EQ(expected_tls_channel_id_value, tls_channel_id);
930 // The TLS channel ID shouldn't change from one connection to the next...
931 std::string tls_channel_id2 =
932 GetTlsChannelIdFromPortConnect(chromium_connectable, true);
933 EXPECT_EQ(tls_channel_id, tls_channel_id2);
934 tls_channel_id2 = GetTlsChannelIdFromSendMessage(chromium_connectable, true);
935 EXPECT_EQ(tls_channel_id, tls_channel_id2);
937 // nor should it change when navigating away, revisiting the page and
938 // requesting it again.
939 ui_test_utils::NavigateToURL(browser(), google_com_url());
940 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
941 tls_channel_id2 = GetTlsChannelIdFromPortConnect(chromium_connectable, true);
942 EXPECT_EQ(tls_channel_id, tls_channel_id2);
943 tls_channel_id2 = GetTlsChannelIdFromSendMessage(chromium_connectable, true);
944 EXPECT_EQ(tls_channel_id, tls_channel_id2);
947 // Tests a web connectable extension that receives TLS channel id, but
948 // immediately closes its background page upon receipt of a message.
949 // Same flakiness seen in http://crbug.com/297866
950 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingWithTlsChannelIdTest,
951 DISABLED_WebConnectableWithNonEmptyTlsChannelIdAndClosedBackgroundPage) {
952 InitializeTestServer();
953 std::string expected_tls_channel_id_value = CreateTlsChannelId();
955 const Extension* chromium_connectable =
956 LoadChromiumConnectableExtensionWithTlsChannelId();
958 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
959 // If the page does ask for it, it isn't empty, even if the background page
960 // closes upon receipt of the connect.
961 std::string tls_channel_id = GetTlsChannelIdFromPortConnect(
962 chromium_connectable, true, close_background_message());
963 EXPECT_EQ(expected_tls_channel_id_value, tls_channel_id);
964 // A subsequent connect will still succeed, even if the background page was
965 // previously closed.
966 tls_channel_id = GetTlsChannelIdFromPortConnect(chromium_connectable, true);
967 // And the expected value is still retrieved.
968 EXPECT_EQ(expected_tls_channel_id_value, tls_channel_id);
971 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingUserGesture) {
972 const char kManifest[] = "{"
973 " \"name\": \"user_gesture\","
974 " \"version\": \"1.0\","
976 " \"scripts\": [\"background.js\"]"
978 " \"manifest_version\": 2"
981 TestExtensionDir receiver_dir;
982 receiver_dir.WriteManifest(kManifest);
983 receiver_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
984 "chrome.runtime.onMessageExternal.addListener(\n"
985 " function(msg, sender, reply) {\n"
986 " reply({result:chrome.test.isProcessingUserGesture()});\n"
988 const Extension* receiver = LoadExtension(receiver_dir.unpacked_path());
989 ASSERT_TRUE(receiver);
991 TestExtensionDir sender_dir;
992 sender_dir.WriteManifest(kManifest);
993 sender_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "");
994 const Extension* sender = LoadExtension(sender_dir.unpacked_path());
998 ExecuteScriptInBackgroundPage(sender->id(),
1000 "chrome.test.runWithoutUserGesture(function() {\n"
1001 " chrome.runtime.sendMessage('%s', {}, function(response) {\n"
1002 " window.domAutomationController.send('' + response.result);\n"
1004 "});", receiver->id().c_str())));
1007 ExecuteScriptInBackgroundPage(sender->id(),
1009 "chrome.test.runWithUserGesture(function() {\n"
1010 " chrome.runtime.sendMessage('%s', {}, function(response) {\n"
1011 " window.domAutomationController.send('' + response.result);\n"
1013 "});", receiver->id().c_str())));
1016 // Tests that a hosted app on a connectable site doesn't interfere with the
1017 // connectability of that site.
1018 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, HostedAppOnWebsite) {
1019 InitializeTestServer();
1021 scoped_refptr<const Extension> app = LoadChromiumHostedApp();
1023 // The presence of the hosted app shouldn't give the ability to send messages.
1024 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
1025 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessagesToMainFrame(app));
1026 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
1028 // Once a connectable extension is installed, it should.
1029 const Extension* extension = LoadChromiumConnectableExtension();
1030 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(extension));
1031 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame());
1034 // Tests that an invalid extension ID specified in a hosted app does not crash
1035 // the hosted app's renderer.
1037 // This is a regression test for http://crbug.com/326250#c12.
1038 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
1039 InvalidExtensionIDFromHostedApp) {
1040 InitializeTestServer();
1042 // The presence of the chromium hosted app triggers this bug. The chromium
1043 // connectable extension needs to be installed to set up the runtime bindings.
1044 LoadChromiumHostedApp();
1045 LoadChromiumConnectableExtension();
1047 scoped_refptr<const Extension> invalid =
1049 // A bit scary that this works...
1051 .SetManifest(DictionaryBuilder()
1052 .Set("name", "Fake extension")
1053 .Set("version", "1")
1054 .Set("manifest_version", 2))
1057 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
1058 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
1059 CanConnectAndSendMessagesToMainFrame(invalid));
1062 #endif // !defined(OS_WIN) - http://crbug.com/350517.
1066 }; // namespace extensions