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. 4 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/extension_prefs.h" 19 #include "chrome/browser/extensions/extension_system.h" 20 #include "chrome/browser/extensions/test_extension_dir.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/ui/browser.h" 23 #include "chrome/browser/ui/tabs/tab_strip_model.h" 24 #include "chrome/common/chrome_paths.h" 25 #include "chrome/common/chrome_switches.h" 26 #include "chrome/common/extensions/api/runtime.h" 27 #include "chrome/test/base/ui_test_utils.h" 28 #include "content/public/browser/notification_registrar.h" 29 #include "content/public/browser/notification_service.h" 30 #include "content/public/test/browser_test_utils.h" 31 #include "extensions/browser/event_router.h" 32 #include "net/cert/asn1_util.h" 33 #include "net/cert/jwk_serializer.h" 34 #include "net/dns/mock_host_resolver.h" 35 #include "net/ssl/server_bound_cert_service.h" 36 #include "net/test/embedded_test_server/embedded_test_server.h" 37 #include "net/url_request/url_request_context.h" 38 #include "net/url_request/url_request_context_getter.h" 39 #include "url/gurl.h" 40 41 namespace extensions { 42 namespace { 43 44 class MessageSender : public content::NotificationObserver { 45 public: 46 MessageSender() { 47 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, 48 content::NotificationService::AllSources()); 49 } 50 51 private: 52 static scoped_ptr<base::ListValue> BuildEventArguments( 53 const bool last_message, 54 const std::string& data) { 55 DictionaryValue* event = new DictionaryValue(); 56 event->SetBoolean("lastMessage", last_message); 57 event->SetString("data", data); 58 scoped_ptr<base::ListValue> arguments(new base::ListValue()); 59 arguments->Append(event); 60 return arguments.Pass(); 61 } 62 63 static scoped_ptr<Event> BuildEvent(scoped_ptr<base::ListValue> event_args, 64 Profile* profile, 65 GURL event_url) { 66 scoped_ptr<Event> event(new Event("test.onMessage", event_args.Pass())); 67 event->restrict_to_browser_context = profile; 68 event->event_url = event_url; 69 return event.Pass(); 70 } 71 72 virtual void Observe(int type, 73 const content::NotificationSource& source, 74 const content::NotificationDetails& details) OVERRIDE { 75 EventRouter* event_router = ExtensionSystem::Get( 76 content::Source<Profile>(source).ptr())->event_router(); 77 78 // Sends four messages to the extension. All but the third message sent 79 // from the origin http://b.com/ are supposed to arrive. 80 event_router->BroadcastEvent(BuildEvent( 81 BuildEventArguments(false, "no restriction"), 82 content::Source<Profile>(source).ptr(), 83 GURL())); 84 event_router->BroadcastEvent(BuildEvent( 85 BuildEventArguments(false, "http://a.com/"), 86 content::Source<Profile>(source).ptr(), 87 GURL("http://a.com/"))); 88 event_router->BroadcastEvent(BuildEvent( 89 BuildEventArguments(false, "http://b.com/"), 90 content::Source<Profile>(source).ptr(), 91 GURL("http://b.com/"))); 92 event_router->BroadcastEvent(BuildEvent( 93 BuildEventArguments(true, "last message"), 94 content::Source<Profile>(source).ptr(), 95 GURL())); 96 } 97 98 content::NotificationRegistrar registrar_; 99 }; 100 101 // Tests that message passing between extensions and content scripts works. 102 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Messaging) { 103 ASSERT_TRUE(StartEmbeddedTestServer()); 104 ASSERT_TRUE(RunExtensionTest("messaging/connect")) << message_; 105 } 106 107 // Tests that message passing from one extension to another works. 108 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingExternal) { 109 ASSERT_TRUE(LoadExtension( 110 test_data_dir_.AppendASCII("..").AppendASCII("good") 111 .AppendASCII("Extensions") 112 .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa") 113 .AppendASCII("1.0"))); 114 115 ASSERT_TRUE(RunExtensionTest("messaging/connect_external")) << message_; 116 } 117 118 // Tests that messages with event_urls are only passed to extensions with 119 // appropriate permissions. 120 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingEventURL) { 121 MessageSender sender; 122 ASSERT_TRUE(RunExtensionTest("messaging/event_url")) << message_; 123 } 124 125 // Tests connecting from a panel to its extension. 126 class PanelMessagingTest : public ExtensionApiTest { 127 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 128 ExtensionApiTest::SetUpCommandLine(command_line); 129 command_line->AppendSwitch(switches::kEnablePanels); 130 } 131 }; 132 133 IN_PROC_BROWSER_TEST_F(PanelMessagingTest, MessagingPanel) { 134 ASSERT_TRUE(RunExtensionTest("messaging/connect_panel")) << message_; 135 } 136 137 // Tests externally_connectable between a web page and an extension. 138 // 139 // TODO(kalman): Test between extensions. This is already tested in this file, 140 // but not with externally_connectable set in the manifest. 141 // 142 // TODO(kalman): Test with host permissions. 143 class ExternallyConnectableMessagingTest : public ExtensionApiTest { 144 protected: 145 // Result codes from the test. These must match up with |results| in 146 // c/t/d/extensions/api_test/externally_connectable/assertions.json. 147 enum Result { 148 OK = 0, 149 NAMESPACE_NOT_DEFINED = 1, 150 FUNCTION_NOT_DEFINED = 2, 151 COULD_NOT_ESTABLISH_CONNECTION_ERROR = 3, 152 OTHER_ERROR = 4, 153 INCORRECT_RESPONSE_SENDER = 5, 154 INCORRECT_RESPONSE_MESSAGE = 6, 155 }; 156 157 bool AppendIframe(const GURL& src) { 158 bool result; 159 CHECK(content::ExecuteScriptAndExtractBool( 160 browser()->tab_strip_model()->GetActiveWebContents(), 161 "actions.appendIframe('" + src.spec() + "');", &result)); 162 return result; 163 } 164 165 Result CanConnectAndSendMessages(const std::string& extension_id) { 166 return CanConnectAndSendMessages(browser(), extension_id, ""); 167 } 168 169 Result CanConnectAndSendMessages(const std::string& extension_id, 170 const char* frame_xpath, 171 const char* message) { 172 return CanConnectAndSendMessages(browser(), extension_id, frame_xpath, 173 message); 174 } 175 176 Result CanConnectAndSendMessages(Browser* browser, 177 const std::string& extension_id) { 178 return CanConnectAndSendMessages(browser, extension_id, ""); 179 } 180 181 Result CanConnectAndSendMessages(const std::string& extension_id, 182 const char* frame_xpath) { 183 return CanConnectAndSendMessages(browser(), extension_id, frame_xpath); 184 } 185 186 Result CanConnectAndSendMessages(Browser* browser, 187 const std::string& extension_id, 188 const char* frame_xpath, 189 const char* message = NULL) { 190 int result; 191 std::string args = "'" + extension_id + "'"; 192 if (message) 193 args += std::string(", '") + message + "'"; 194 CHECK(content::ExecuteScriptInFrameAndExtractInt( 195 browser->tab_strip_model()->GetActiveWebContents(), 196 frame_xpath, 197 base::StringPrintf("assertions.canConnectAndSendMessages(%s)", 198 args.c_str()), 199 &result)); 200 return static_cast<Result>(result); 201 } 202 203 testing::AssertionResult AreAnyNonWebApisDefined() { 204 return AreAnyNonWebApisDefined(""); 205 } 206 207 testing::AssertionResult AreAnyNonWebApisDefined(const char* frame_xpath) { 208 // All runtime API methods are non-web except for sendRequest and connect. 209 const char* non_messaging_apis[] = { 210 "getBackgroundPage", 211 "getManifest", 212 "getURL", 213 "reload", 214 "requestUpdateCheck", 215 "restart", 216 "connectNative", 217 "sendNativeMessage", 218 "onStartup", 219 "onInstalled", 220 "onSuspend", 221 "onSuspendCanceled", 222 "onUpdateAvailable", 223 "onBrowserUpdateAvailable", 224 "onConnect", 225 "onConnectExternal", 226 "onMessage", 227 "onMessageExternal", 228 "onRestartRequired", 229 // Note: no "id" here because this test method is used for hosted apps, 230 // which do have access to runtime.id. 231 }; 232 233 // Turn the array into a JS array, which effectively gets eval()ed. 234 std::string as_js_array; 235 for (size_t i = 0; i < arraysize(non_messaging_apis); ++i) { 236 as_js_array += as_js_array.empty() ? "[" : ","; 237 as_js_array += base::StringPrintf("'%s'", non_messaging_apis[i]); 238 } 239 as_js_array += "]"; 240 241 bool any_defined; 242 CHECK(content::ExecuteScriptInFrameAndExtractBool( 243 browser()->tab_strip_model()->GetActiveWebContents(), 244 frame_xpath, 245 "assertions.areAnyRuntimePropertiesDefined(" + as_js_array + ")", 246 &any_defined)); 247 return any_defined ? 248 testing::AssertionSuccess() : testing::AssertionFailure(); 249 } 250 251 std::string GetTlsChannelIdFromPortConnect(const std::string& extension_id, 252 bool include_tls_channel_id, 253 const char* message = NULL) { 254 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromPortConnect", 255 extension_id, 256 include_tls_channel_id, 257 message); 258 } 259 260 std::string GetTlsChannelIdFromSendMessage(const std::string& extension_id, 261 bool include_tls_channel_id, 262 const char* message = NULL) { 263 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromSendMessage", 264 extension_id, 265 include_tls_channel_id, 266 message); 267 } 268 269 GURL GetURLForPath(const std::string& host, const std::string& path) { 270 std::string port = base::IntToString(embedded_test_server()->port()); 271 GURL::Replacements replacements; 272 replacements.SetHostStr(host); 273 replacements.SetPortStr(port); 274 return embedded_test_server()->GetURL(path).ReplaceComponents(replacements); 275 } 276 277 GURL chromium_org_url() { 278 return GetURLForPath("www.chromium.org", "/chromium.org.html"); 279 } 280 281 GURL google_com_url() { 282 return GetURLForPath("www.google.com", "/google.com.html"); 283 } 284 285 const Extension* LoadChromiumConnectableExtension() { 286 const Extension* extension = 287 LoadExtensionIntoDir(&web_connectable_dir_, base::StringPrintf( 288 "{" 289 " \"name\": \"chromium_connectable\"," 290 " %s," 291 " \"externally_connectable\": {" 292 " \"matches\": [\"*://*.chromium.org:*/*\"]" 293 " }" 294 "}", 295 common_manifest())); 296 CHECK(extension); 297 return extension; 298 } 299 300 const Extension* LoadNotConnectableExtension() { 301 const Extension* extension = 302 LoadExtensionIntoDir(¬_connectable_dir_, base::StringPrintf( 303 "{" 304 " \"name\": \"not_connectable\"," 305 " %s" 306 "}", 307 common_manifest())); 308 CHECK(extension); 309 return extension; 310 } 311 312 const Extension* LoadChromiumConnectableExtensionWithTlsChannelId() { 313 return LoadExtensionIntoDir(&tls_channel_id_connectable_dir_, 314 connectable_with_tls_channel_id_manifest()); 315 } 316 317 const Extension* LoadChromiumHostedApp() { 318 const Extension* hosted_app = 319 LoadExtensionIntoDir(&hosted_app_dir_, base::StringPrintf( 320 "{" 321 " \"name\": \"chromium_hosted_app\"," 322 " \"version\": \"1.0\"," 323 " \"manifest_version\": 2," 324 " \"app\": {" 325 " \"urls\": [\"%s\"]," 326 " \"launch\": {" 327 " \"web_url\": \"%s\"" 328 " }\n" 329 " }\n" 330 "}", chromium_org_url().spec().c_str(), 331 chromium_org_url().spec().c_str())); 332 CHECK(hosted_app); 333 return hosted_app; 334 } 335 336 void InitializeTestServer() { 337 base::FilePath test_data; 338 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data)); 339 embedded_test_server()->ServeFilesFromDirectory(test_data.AppendASCII( 340 "extensions/api_test/messaging/externally_connectable/sites")); 341 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 342 host_resolver()->AddRule("*", embedded_test_server()->base_url().host()); 343 } 344 345 const char* close_background_message() { 346 return "closeBackgroundPage"; 347 } 348 349 private: 350 const Extension* LoadExtensionIntoDir(TestExtensionDir* dir, 351 const std::string& manifest) { 352 dir->WriteManifest(manifest); 353 dir->WriteFile(FILE_PATH_LITERAL("background.js"), 354 base::StringPrintf( 355 "function maybeClose(message) {\n" 356 " if (message.indexOf('%s') >= 0)\n" 357 " window.setTimeout(function() { window.close() }, 0);\n" 358 "}\n" 359 "chrome.runtime.onMessageExternal.addListener(\n" 360 " function(message, sender, reply) {\n" 361 " reply({ message: message, sender: sender });\n" 362 " maybeClose(message);\n" 363 "});\n" 364 "chrome.runtime.onConnectExternal.addListener(function(port) {\n" 365 " port.onMessage.addListener(function(message) {\n" 366 " port.postMessage({ message: message, sender: port.sender });\n" 367 " maybeClose(message);\n" 368 " });\n" 369 "});\n", 370 close_background_message())); 371 return LoadExtension(dir->unpacked_path()); 372 } 373 374 const char* common_manifest() { 375 return "\"version\": \"1.0\"," 376 "\"background\": {" 377 " \"scripts\": [\"background.js\"]," 378 " \"persistent\": false" 379 "}," 380 "\"manifest_version\": 2"; 381 } 382 383 std::string connectable_with_tls_channel_id_manifest() { 384 return base::StringPrintf( 385 "{" 386 " \"name\": \"chromium_connectable_with_tls_channel_id\"," 387 " %s," 388 " \"externally_connectable\": {" 389 " \"matches\": [\"*://*.chromium.org:*/*\"]," 390 " \"accepts_tls_channel_id\": true" 391 " }" 392 "}", 393 common_manifest()); 394 } 395 396 std::string GetTlsChannelIdFromAssertion(const char* method, 397 const std::string& extension_id, 398 bool include_tls_channel_id, 399 const char* message) { 400 std::string result; 401 std::string args = "'" + extension_id + "', "; 402 args += include_tls_channel_id ? "true" : "false"; 403 if (message) 404 args += std::string(", '") + message + "'"; 405 CHECK(content::ExecuteScriptAndExtractString( 406 browser()->tab_strip_model()->GetActiveWebContents(), 407 base::StringPrintf("assertions.%s(%s)", method, args.c_str()), 408 &result)); 409 return result; 410 } 411 412 TestExtensionDir web_connectable_dir_; 413 TestExtensionDir not_connectable_dir_; 414 TestExtensionDir tls_channel_id_connectable_dir_; 415 TestExtensionDir hosted_app_dir_; 416 }; 417 418 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, NotInstalled) { 419 InitializeTestServer(); 420 421 const char kFakeId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 422 423 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 424 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessages(kFakeId)); 425 EXPECT_FALSE(AreAnyNonWebApisDefined()); 426 427 ui_test_utils::NavigateToURL(browser(), google_com_url()); 428 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessages(kFakeId)); 429 EXPECT_FALSE(AreAnyNonWebApisDefined()); 430 } 431 432 // Tests two extensions on the same sites: one web connectable, one not. 433 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 434 WebConnectableAndNotConnectable) { 435 InitializeTestServer(); 436 437 // Install the web connectable extension. chromium.org can connect to it, 438 // google.com can't. 439 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 440 441 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 442 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id())); 443 EXPECT_FALSE(AreAnyNonWebApisDefined()); 444 445 ui_test_utils::NavigateToURL(browser(), google_com_url()); 446 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 447 CanConnectAndSendMessages(chromium_connectable->id())); 448 EXPECT_FALSE(AreAnyNonWebApisDefined()); 449 450 // Install the non-connectable extension. Nothing can connect to it. 451 const Extension* not_connectable = LoadNotConnectableExtension(); 452 453 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 454 // Namespace will be defined here because |chromium_connectable| can connect 455 // to it - so this will be the "cannot establish connection" error. 456 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 457 CanConnectAndSendMessages(not_connectable->id())); 458 EXPECT_FALSE(AreAnyNonWebApisDefined()); 459 460 ui_test_utils::NavigateToURL(browser(), google_com_url()); 461 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 462 CanConnectAndSendMessages(not_connectable->id())); 463 EXPECT_FALSE(AreAnyNonWebApisDefined()); 464 } 465 466 // See http://crbug.com/297866 467 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 468 DISABLED_BackgroundPageClosesOnMessageReceipt) { 469 InitializeTestServer(); 470 471 // Install the web connectable extension. 472 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 473 474 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 475 // If the background page closes after receipt of the message, it will still 476 // reply to this message... 477 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id(), 478 "", 479 close_background_message())); 480 // and be re-opened by receipt of a subsequent message. 481 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id())); 482 } 483 484 // Tests a web connectable extension that doesn't receive TLS channel id. 485 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 486 WebConnectableWithoutTlsChannelId) { 487 InitializeTestServer(); 488 489 // Install the web connectable extension. chromium.org can connect to it, 490 // google.com can't. 491 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 492 ASSERT_TRUE(chromium_connectable); 493 494 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 495 // The web connectable extension doesn't request the TLS channel ID, so it 496 // doesn't get it, whether or not the page asks for it. 497 EXPECT_EQ(std::string(), 498 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), false)); 499 EXPECT_EQ(std::string(), 500 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), true)); 501 EXPECT_EQ(std::string(), 502 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), false)); 503 EXPECT_EQ(std::string(), 504 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), true)); 505 } 506 507 // Tests a web connectable extension that receives TLS channel id with a site 508 // that can't connect to it. 509 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 510 WebConnectableWithTlsChannelIdWithNonMatchingSite) { 511 InitializeTestServer(); 512 513 const Extension* chromium_connectable = 514 LoadChromiumConnectableExtensionWithTlsChannelId(); 515 ASSERT_TRUE(chromium_connectable); 516 517 ui_test_utils::NavigateToURL(browser(), google_com_url()); 518 // The extension requests the TLS channel ID, but it doesn't get it for a 519 // site that can't connect to it, regardless of whether the page asks for it. 520 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED), 521 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), false)); 522 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED), 523 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), true)); 524 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED), 525 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), false)); 526 EXPECT_EQ(base::StringPrintf("%d", NAMESPACE_NOT_DEFINED), 527 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), true)); 528 } 529 530 // Tests a web connectable extension that receives TLS channel id on a site 531 // that can connect to it, but with no TLS channel ID having been generated. 532 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 533 WebConnectableWithTlsChannelIdWithEmptyTlsChannelId) { 534 InitializeTestServer(); 535 536 const Extension* chromium_connectable = 537 LoadChromiumConnectableExtensionWithTlsChannelId(); 538 ASSERT_TRUE(chromium_connectable); 539 540 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 541 542 // Since the extension requests the TLS channel ID, it gets it for a site that 543 // can connect to it, but only if the page also asks to include it. 544 EXPECT_EQ(std::string(), 545 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), false)); 546 EXPECT_EQ(std::string(), 547 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), false)); 548 // If the page does ask for it, it isn't empty. 549 std::string tls_channel_id = 550 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), true); 551 // Because the TLS channel ID has never been generated for this domain, 552 // no TLS channel ID is reported. 553 EXPECT_EQ(std::string(), tls_channel_id); 554 } 555 556 // Flaky on Linux. http://crbug.com/315264 557 #if defined(OS_LINUX) 558 #define MAYBE_WebConnectableWithEmptyTlsChannelIdAndClosedBackgroundPage \ 559 DISABLED_WebConnectableWithEmptyTlsChannelIdAndClosedBackgroundPage 560 #else 561 #define MAYBE_WebConnectableWithEmptyTlsChannelIdAndClosedBackgroundPage \ 562 WebConnectableWithEmptyTlsChannelIdAndClosedBackgroundPage 563 #endif 564 // Tests a web connectable extension that receives TLS channel id, but 565 // immediately closes its background page upon receipt of a message. 566 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 567 MAYBE_WebConnectableWithEmptyTlsChannelIdAndClosedBackgroundPage) { 568 InitializeTestServer(); 569 570 const Extension* chromium_connectable = 571 LoadChromiumConnectableExtensionWithTlsChannelId(); 572 573 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 574 // If the page does ask for it, it isn't empty, even if the background page 575 // closes upon receipt of the connect. 576 std::string tls_channel_id = 577 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), 578 true, 579 close_background_message()); 580 // Because the TLS channel ID has never been generated for this domain, 581 // no TLS channel ID is reported. 582 EXPECT_EQ(std::string(), tls_channel_id); 583 // A subsequent connect will still succeed, even if the background page was 584 // previously closed. 585 tls_channel_id = 586 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), 587 true); 588 // And the empty value is still retrieved. 589 EXPECT_EQ(std::string(), tls_channel_id); 590 } 591 592 // Tests that enabling and disabling an extension makes the runtime bindings 593 // appear and disappear. 594 // 595 // TODO(kalman): Test with multiple extensions that can be accessed by the same 596 // host. 597 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 598 EnablingAndDisabling) { 599 InitializeTestServer(); 600 601 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 602 const Extension* not_connectable = LoadNotConnectableExtension(); 603 604 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 605 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id())); 606 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 607 CanConnectAndSendMessages(not_connectable->id())); 608 609 DisableExtension(chromium_connectable->id()); 610 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 611 CanConnectAndSendMessages(chromium_connectable->id())); 612 613 EnableExtension(chromium_connectable->id()); 614 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id())); 615 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 616 CanConnectAndSendMessages(not_connectable->id())); 617 } 618 619 // Tests connection from incognito tabs when the user denies the connection 620 // request. Spanning mode only. 621 // 622 // TODO(kalman): ensure that we exercise split vs spanning incognito logic 623 // somewhere. This is a test that should be shared with the content script logic 624 // so it's not really our specific concern for web connectable. 625 // 626 // TODO(kalman): test messages from incognito extensions too. 627 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, FromIncognitoDeny) { 628 InitializeTestServer(); 629 630 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 631 const std::string& id = chromium_connectable->id(); 632 633 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord( 634 profile()->GetOffTheRecordProfile(), 635 chromium_org_url()); 636 637 // No connection because incognito-enabled hasn't been set for the extension, 638 // and the user denied our interactive request. 639 { 640 IncognitoConnectability::ScopedAlertTracker alert_tracker( 641 IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY); 642 643 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 644 CanConnectAndSendMessages(incognito_browser, id)); 645 EXPECT_EQ(1, alert_tracker.GetAndResetAlertCount()); 646 647 // Try again. User has already denied. 648 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 649 CanConnectAndSendMessages(incognito_browser, id)); 650 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount()); 651 } 652 653 // Allowing the extension in incognito mode will bypass the deny. 654 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(id, true); 655 EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); 656 } 657 658 // Tests connection from incognito tabs when the user accepts the connection 659 // request. Spanning mode only. 660 // 661 // TODO(kalman): see comment above about split mode. 662 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, FromIncognitoAllow) { 663 InitializeTestServer(); 664 665 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 666 const std::string& id = chromium_connectable->id(); 667 668 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord( 669 profile()->GetOffTheRecordProfile(), 670 chromium_org_url()); 671 672 // Connection allowed even with incognito disabled, because the user accepted 673 // the interactive request. 674 { 675 IncognitoConnectability::ScopedAlertTracker alert_tracker( 676 IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW); 677 678 EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); 679 EXPECT_EQ(1, alert_tracker.GetAndResetAlertCount()); 680 681 // Try again. User has already allowed. 682 EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); 683 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount()); 684 } 685 686 // Allowing the extension in incognito mode will continue to allow. 687 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(id, true); 688 EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id)); 689 } 690 691 // Tests a connection from an iframe within a tab which doesn't have 692 // permission. Iframe should work. 693 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 694 FromIframeWithPermission) { 695 InitializeTestServer(); 696 697 const Extension* extension = LoadChromiumConnectableExtension(); 698 699 ui_test_utils::NavigateToURL(browser(), google_com_url()); 700 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessages(extension->id())); 701 EXPECT_FALSE(AreAnyNonWebApisDefined()); 702 703 ASSERT_TRUE(AppendIframe(chromium_org_url())); 704 705 const char* frame_xpath = "//iframe[1]"; 706 EXPECT_EQ(OK, CanConnectAndSendMessages(extension->id(), frame_xpath)); 707 EXPECT_FALSE(AreAnyNonWebApisDefined(frame_xpath)); 708 } 709 710 // Tests connection from an iframe without permission within a tab that does. 711 // Iframe shouldn't work. 712 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 713 FromIframeWithoutPermission) { 714 InitializeTestServer(); 715 716 const Extension* extension = LoadChromiumConnectableExtension(); 717 718 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 719 EXPECT_EQ(OK, CanConnectAndSendMessages(extension->id())); 720 EXPECT_FALSE(AreAnyNonWebApisDefined()); 721 722 ASSERT_TRUE(AppendIframe(google_com_url())); 723 724 const char* frame_xpath = "//iframe[1]"; 725 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 726 CanConnectAndSendMessages(extension->id(), frame_xpath)); 727 EXPECT_FALSE(AreAnyNonWebApisDefined(frame_xpath)); 728 } 729 730 // Tests externally_connectable between a web page and an extension with a 731 // TLS channel ID created for the origin. 732 class ExternallyConnectableMessagingWithTlsChannelIdTest : 733 public ExternallyConnectableMessagingTest { 734 public: 735 ExternallyConnectableMessagingWithTlsChannelIdTest() 736 : tls_channel_id_created_(false, false) { 737 } 738 739 std::string CreateTlsChannelId() { 740 scoped_refptr<net::URLRequestContextGetter> request_context_getter( 741 profile()->GetRequestContext()); 742 std::string domain_bound_private_key; 743 std::string domain_bound_cert; 744 net::ServerBoundCertService::RequestHandle request_handle; 745 content::BrowserThread::PostTask( 746 content::BrowserThread::IO, 747 FROM_HERE, 748 base::Bind( 749 &ExternallyConnectableMessagingWithTlsChannelIdTest:: 750 CreateDomainBoundCertOnIOThread, 751 base::Unretained(this), 752 base::Unretained(&domain_bound_private_key), 753 base::Unretained(&domain_bound_cert), 754 base::Unretained(&request_handle), 755 request_context_getter)); 756 tls_channel_id_created_.Wait(); 757 // Create the expected value. 758 base::StringPiece spki; 759 net::asn1::ExtractSPKIFromDERCert(domain_bound_cert, &spki); 760 base::DictionaryValue jwk_value; 761 net::JwkSerializer::ConvertSpkiFromDerToJwk(spki, &jwk_value); 762 std::string tls_channel_id_value; 763 base::JSONWriter::Write(&jwk_value, &tls_channel_id_value); 764 return tls_channel_id_value; 765 } 766 767 private: 768 void CreateDomainBoundCertOnIOThread( 769 std::string* domain_bound_private_key, 770 std::string* domain_bound_cert, 771 net::ServerBoundCertService::RequestHandle* request_handle, 772 scoped_refptr<net::URLRequestContextGetter> request_context_getter) { 773 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 774 net::ServerBoundCertService* server_bound_cert_service = 775 request_context_getter->GetURLRequestContext()-> 776 server_bound_cert_service(); 777 int status = server_bound_cert_service->GetOrCreateDomainBoundCert( 778 chromium_org_url().host(), 779 domain_bound_private_key, 780 domain_bound_cert, 781 base::Bind(&ExternallyConnectableMessagingWithTlsChannelIdTest:: 782 GotDomainBoundCert, 783 base::Unretained(this)), 784 request_handle); 785 if (status == net::ERR_IO_PENDING) 786 return; 787 GotDomainBoundCert(status); 788 } 789 790 void GotDomainBoundCert(int status) { 791 ASSERT_TRUE(status == net::OK); 792 tls_channel_id_created_.Signal(); 793 } 794 795 base::WaitableEvent tls_channel_id_created_; 796 }; 797 798 // Tests a web connectable extension that receives TLS channel id on a site 799 // that can connect to it, with a TLS channel ID having been generated. 800 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingWithTlsChannelIdTest, 801 WebConnectableWithNonEmptyTlsChannelId) { 802 InitializeTestServer(); 803 std::string expected_tls_channel_id_value = CreateTlsChannelId(); 804 805 const Extension* chromium_connectable = 806 LoadChromiumConnectableExtensionWithTlsChannelId(); 807 ASSERT_TRUE(chromium_connectable); 808 809 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 810 811 // Since the extension requests the TLS channel ID, it gets it for a site that 812 // can connect to it, but only if the page also asks to send it. 813 EXPECT_EQ(std::string(), 814 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), false)); 815 EXPECT_EQ(std::string(), 816 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), false)); 817 818 // If the page does ask to send the TLS channel ID, it's sent and non-empty. 819 std::string tls_channel_id_from_port_connect = 820 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), true); 821 EXPECT_NE(0u, tls_channel_id_from_port_connect.size()); 822 823 // The same value is received by both connect and sendMessage. 824 std::string tls_channel_id_from_send_message = 825 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), true); 826 EXPECT_EQ(tls_channel_id_from_port_connect, tls_channel_id_from_send_message); 827 828 // And since a TLS channel ID exists for the domain, the value received is 829 // parseable as a JWK. (In particular, it has the same value we created by 830 // converting the public key to JWK with net::ConvertSpkiFromDerToJwk.) 831 std::string tls_channel_id(tls_channel_id_from_port_connect); 832 EXPECT_EQ(expected_tls_channel_id_value, tls_channel_id); 833 834 // The TLS channel ID shouldn't change from one connection to the next... 835 std::string tls_channel_id2 = 836 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), true); 837 EXPECT_EQ(tls_channel_id, tls_channel_id2); 838 tls_channel_id2 = 839 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), true); 840 EXPECT_EQ(tls_channel_id, tls_channel_id2); 841 842 // nor should it change when navigating away, revisiting the page and 843 // requesting it again. 844 ui_test_utils::NavigateToURL(browser(), google_com_url()); 845 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 846 tls_channel_id2 = 847 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), true); 848 EXPECT_EQ(tls_channel_id, tls_channel_id2); 849 tls_channel_id2 = 850 GetTlsChannelIdFromSendMessage(chromium_connectable->id(), true); 851 EXPECT_EQ(tls_channel_id, tls_channel_id2); 852 } 853 854 // Tests a web connectable extension that receives TLS channel id, but 855 // immediately closes its background page upon receipt of a message. 856 // Same flakiness seen in http://crbug.com/297866 857 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingWithTlsChannelIdTest, 858 DISABLED_WebConnectableWithNonEmptyTlsChannelIdAndClosedBackgroundPage) { 859 InitializeTestServer(); 860 std::string expected_tls_channel_id_value = CreateTlsChannelId(); 861 862 const Extension* chromium_connectable = 863 LoadChromiumConnectableExtensionWithTlsChannelId(); 864 865 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 866 // If the page does ask for it, it isn't empty, even if the background page 867 // closes upon receipt of the connect. 868 std::string tls_channel_id = 869 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), 870 true, 871 close_background_message()); 872 EXPECT_EQ(expected_tls_channel_id_value, tls_channel_id); 873 // A subsequent connect will still succeed, even if the background page was 874 // previously closed. 875 tls_channel_id = 876 GetTlsChannelIdFromPortConnect(chromium_connectable->id(), 877 true); 878 // And the expected value is still retrieved. 879 EXPECT_EQ(expected_tls_channel_id_value, tls_channel_id); 880 } 881 882 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingUserGesture) { 883 const char kManifest[] = "{" 884 " \"name\": \"user_gesture\"," 885 " \"version\": \"1.0\"," 886 " \"background\": {" 887 " \"scripts\": [\"background.js\"]" 888 " }," 889 " \"manifest_version\": 2" 890 "}"; 891 892 TestExtensionDir receiver_dir; 893 receiver_dir.WriteManifest(kManifest); 894 receiver_dir.WriteFile(FILE_PATH_LITERAL("background.js"), 895 "chrome.runtime.onMessageExternal.addListener(\n" 896 " function(msg, sender, reply) {\n" 897 " reply({result:chrome.test.isProcessingUserGesture()});\n" 898 " });"); 899 const Extension* receiver = LoadExtension(receiver_dir.unpacked_path()); 900 ASSERT_TRUE(receiver); 901 902 TestExtensionDir sender_dir; 903 sender_dir.WriteManifest(kManifest); 904 sender_dir.WriteFile(FILE_PATH_LITERAL("background.js"), ""); 905 const Extension* sender = LoadExtension(sender_dir.unpacked_path()); 906 ASSERT_TRUE(sender); 907 908 EXPECT_EQ("false", 909 ExecuteScriptInBackgroundPage(sender->id(), 910 base::StringPrintf( 911 "chrome.test.runWithoutUserGesture(function() {\n" 912 " chrome.runtime.sendMessage('%s', {}, function(response) {\n" 913 " window.domAutomationController.send('' + response.result);\n" 914 " });\n" 915 "});", receiver->id().c_str()))); 916 917 EXPECT_EQ("true", 918 ExecuteScriptInBackgroundPage(sender->id(), 919 base::StringPrintf( 920 "chrome.test.runWithUserGesture(function() {\n" 921 " chrome.runtime.sendMessage('%s', {}, function(response) {\n" 922 " window.domAutomationController.send('' + response.result);\n" 923 " });\n" 924 "});", receiver->id().c_str()))); 925 } 926 927 // Tests that a hosted app on a connectable site doesn't interfere with the 928 // connectability of that site. 929 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, HostedAppOnWebsite) { 930 InitializeTestServer(); 931 932 LoadChromiumHostedApp(); 933 934 // The presence of the hosted app shouldn't give the ability to send messages. 935 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 936 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessages("")); 937 EXPECT_FALSE(AreAnyNonWebApisDefined()); 938 939 // Once a connectable extension is installed, it should. 940 const Extension* extension = LoadChromiumConnectableExtension(); 941 EXPECT_EQ(OK, CanConnectAndSendMessages(extension->id())); 942 EXPECT_FALSE(AreAnyNonWebApisDefined()); 943 } 944 945 // Tests that an invalid extension ID specified in a hosted app does not crash 946 // the hosted app's renderer. 947 // 948 // This is a regression test for http://crbug.com/326250#c12. 949 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 950 InvalidExtensionIDFromHostedApp) { 951 InitializeTestServer(); 952 953 // The presence of the chromium hosted app triggers this bug. The chromium 954 // connectable extension needs to be installed to set up the runtime bindings. 955 LoadChromiumHostedApp(); 956 LoadChromiumConnectableExtension(); 957 958 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 959 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR , 960 CanConnectAndSendMessages("invalid")); 961 } 962 963 } // namespace 964 965 }; // namespace extensions 966