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/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/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" 41 #include "url/gurl.h" 42 43 namespace extensions { 44 namespace { 45 46 class MessageSender : public content::NotificationObserver { 47 public: 48 MessageSender() { 49 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, 50 content::NotificationService::AllSources()); 51 } 52 53 private: 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(); 63 } 64 65 static scoped_ptr<Event> BuildEvent(scoped_ptr<base::ListValue> event_args, 66 Profile* profile, 67 GURL event_url) { 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; 71 return event.Pass(); 72 } 73 74 virtual void Observe(int type, 75 const content::NotificationSource& source, 76 const content::NotificationDetails& details) OVERRIDE { 77 EventRouter* event_router = 78 EventRouter::Get(content::Source<Profile>(source).ptr()); 79 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(), 85 GURL())); 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(), 97 GURL())); 98 } 99 100 content::NotificationRegistrar registrar_; 101 }; 102 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_; 107 } 108 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"))); 116 117 ASSERT_TRUE(RunExtensionTest("messaging/connect_external")) << message_; 118 } 119 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_; 125 } 126 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); 132 } 133 }; 134 135 IN_PROC_BROWSER_TEST_F(PanelMessagingTest, MessagingPanel) { 136 ASSERT_TRUE(RunExtensionTest("messaging/connect_panel")) << message_; 137 } 138 139 // XXX(kalman): All web messaging tests disabled on windows due to extreme 140 // flakiness. See http://crbug.com/350517. 141 #if !defined(OS_WIN) 142 143 // Tests externally_connectable between a web page and an extension. 144 // 145 // TODO(kalman): Test between extensions. This is already tested in this file, 146 // but not with externally_connectable set in the manifest. 147 // 148 // TODO(kalman): Test with host permissions. 149 class ExternallyConnectableMessagingTest : public ExtensionApiTest { 150 protected: 151 // Result codes from the test. These must match up with |results| in 152 // c/t/d/extensions/api_test/externally_connectable/assertions.json. 153 enum Result { 154 OK = 0, 155 NAMESPACE_NOT_DEFINED = 1, 156 FUNCTION_NOT_DEFINED = 2, 157 COULD_NOT_ESTABLISH_CONNECTION_ERROR = 3, 158 OTHER_ERROR = 4, 159 INCORRECT_RESPONSE_SENDER = 5, 160 INCORRECT_RESPONSE_MESSAGE = 6, 161 }; 162 163 bool AppendIframe(const GURL& src) { 164 bool result; 165 CHECK(content::ExecuteScriptAndExtractBool( 166 browser()->tab_strip_model()->GetActiveWebContents(), 167 "actions.appendIframe('" + src.spec() + "');", &result)); 168 return result; 169 } 170 171 Result CanConnectAndSendMessagesToMainFrame(const Extension* extension, 172 const char* message = NULL) { 173 return CanConnectAndSendMessagesToFrame( 174 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(), 175 extension, 176 message); 177 } 178 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); 185 } 186 187 Result CanConnectAndSendMessagesToFrame(content::RenderFrameHost* frame, 188 const Extension* extension, 189 const char* message) { 190 int result; 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); 198 } 199 200 testing::AssertionResult AreAnyNonWebApisDefinedForMainFrame() { 201 return AreAnyNonWebApisDefinedForFrame( 202 browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame()); 203 } 204 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); 210 } 211 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[] = { 216 "getBackgroundPage", 217 "getManifest", 218 "getURL", 219 "reload", 220 "requestUpdateCheck", 221 "restart", 222 "connectNative", 223 "sendNativeMessage", 224 "onStartup", 225 "onInstalled", 226 "onSuspend", 227 "onSuspendCanceled", 228 "onUpdateAvailable", 229 "onBrowserUpdateAvailable", 230 "onConnect", 231 "onConnectExternal", 232 "onMessage", 233 "onMessageExternal", 234 "onRestartRequired", 235 // Note: no "id" here because this test method is used for hosted apps, 236 // which do have access to runtime.id. 237 }; 238 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]); 244 } 245 as_js_array += "]"; 246 247 bool any_defined; 248 CHECK(content::ExecuteScriptAndExtractBool( 249 frame, 250 "assertions.areAnyRuntimePropertiesDefined(" + as_js_array + ")", 251 &any_defined)); 252 return any_defined ? 253 testing::AssertionSuccess() : testing::AssertionFailure(); 254 } 255 256 std::string GetTlsChannelIdFromPortConnect(const Extension* extension, 257 bool include_tls_channel_id, 258 const char* message = NULL) { 259 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromPortConnect", 260 extension, 261 include_tls_channel_id, 262 message); 263 } 264 265 std::string GetTlsChannelIdFromSendMessage(const Extension* extension, 266 bool include_tls_channel_id, 267 const char* message = NULL) { 268 return GetTlsChannelIdFromAssertion("getTlsChannelIdFromSendMessage", 269 extension, 270 include_tls_channel_id, 271 message); 272 } 273 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); 280 } 281 282 GURL chromium_org_url() { 283 return GetURLForPath("www.chromium.org", "/chromium.org.html"); 284 } 285 286 GURL google_com_url() { 287 return GetURLForPath("www.google.com", "/google.com.html"); 288 } 289 290 scoped_refptr<const Extension> LoadChromiumConnectableExtension() { 291 scoped_refptr<const Extension> extension = 292 LoadExtensionIntoDir(&web_connectable_dir_, 293 base::StringPrintf( 294 "{" 295 " \"name\": \"chromium_connectable\"," 296 " %s," 297 " \"externally_connectable\": {" 298 " \"matches\": [\"*://*.chromium.org:*/*\"]" 299 " }" 300 "}", 301 common_manifest())); 302 CHECK(extension.get()); 303 return extension; 304 } 305 306 scoped_refptr<const Extension> LoadChromiumConnectableApp() { 307 scoped_refptr<const Extension> extension = 308 LoadExtensionIntoDir(&web_connectable_dir_, 309 "{" 310 " \"app\": {" 311 " \"background\": {" 312 " \"scripts\": [\"background.js\"]" 313 " }" 314 " }," 315 " \"externally_connectable\": {" 316 " \"matches\": [\"*://*.chromium.org:*/*\"]" 317 " }," 318 " \"manifest_version\": 2," 319 " \"name\": \"app_connectable\"," 320 " \"version\": \"1.0\"" 321 "}"); 322 CHECK(extension.get()); 323 return extension; 324 } 325 326 scoped_refptr<const Extension> LoadNotConnectableExtension() { 327 scoped_refptr<const Extension> extension = 328 LoadExtensionIntoDir(¬_connectable_dir_, 329 base::StringPrintf( 330 "{" 331 " \"name\": \"not_connectable\"," 332 " %s" 333 "}", 334 common_manifest())); 335 CHECK(extension.get()); 336 return extension; 337 } 338 339 scoped_refptr<const Extension> 340 LoadChromiumConnectableExtensionWithTlsChannelId() { 341 return LoadExtensionIntoDir(&tls_channel_id_connectable_dir_, 342 connectable_with_tls_channel_id_manifest()); 343 } 344 345 scoped_refptr<const Extension> LoadChromiumHostedApp() { 346 scoped_refptr<const Extension> hosted_app = 347 LoadExtensionIntoDir(&hosted_app_dir_, 348 base::StringPrintf( 349 "{" 350 " \"name\": \"chromium_hosted_app\"," 351 " \"version\": \"1.0\"," 352 " \"manifest_version\": 2," 353 " \"app\": {" 354 " \"urls\": [\"%s\"]," 355 " \"launch\": {" 356 " \"web_url\": \"%s\"" 357 " }\n" 358 " }\n" 359 "}", 360 chromium_org_url().spec().c_str(), 361 chromium_org_url().spec().c_str())); 362 CHECK(hosted_app.get()); 363 return hosted_app; 364 } 365 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()); 373 } 374 375 const char* close_background_message() { 376 return "closeBackgroundPage"; 377 } 378 379 private: 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"), 385 base::StringPrintf( 386 "function maybeClose(message) {\n" 387 " if (message.indexOf('%s') >= 0)\n" 388 " window.setTimeout(function() { window.close() }, 0);\n" 389 "}\n" 390 "chrome.runtime.onMessageExternal.addListener(\n" 391 " function(message, sender, reply) {\n" 392 " reply({ message: message, sender: sender });\n" 393 " maybeClose(message);\n" 394 "});\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" 399 " });\n" 400 "});\n", 401 close_background_message())); 402 return LoadExtension(dir->unpacked_path()); 403 } 404 405 const char* common_manifest() { 406 return "\"version\": \"1.0\"," 407 "\"background\": {" 408 " \"scripts\": [\"background.js\"]," 409 " \"persistent\": false" 410 "}," 411 "\"manifest_version\": 2"; 412 } 413 414 std::string connectable_with_tls_channel_id_manifest() { 415 return base::StringPrintf( 416 "{" 417 " \"name\": \"chromium_connectable_with_tls_channel_id\"," 418 " %s," 419 " \"externally_connectable\": {" 420 " \"matches\": [\"*://*.chromium.org:*/*\"]," 421 " \"accepts_tls_channel_id\": true" 422 " }" 423 "}", 424 common_manifest()); 425 } 426 427 std::string GetTlsChannelIdFromAssertion(const char* method, 428 const Extension* extension, 429 bool include_tls_channel_id, 430 const char* message) { 431 std::string result; 432 std::string args = "'" + extension->id() + "', "; 433 args += include_tls_channel_id ? "true" : "false"; 434 if (message) 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()), 439 &result)); 440 return result; 441 } 442 443 TestExtensionDir web_connectable_dir_; 444 TestExtensionDir not_connectable_dir_; 445 TestExtensionDir tls_channel_id_connectable_dir_; 446 TestExtensionDir hosted_app_dir_; 447 }; 448 449 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, NotInstalled) { 450 InitializeTestServer(); 451 452 scoped_refptr<const Extension> extension = 453 ExtensionBuilder() 454 .SetID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") 455 .SetManifest(DictionaryBuilder() 456 .Set("name", "Fake extension") 457 .Set("version", "1") 458 .Set("manifest_version", 2)) 459 .Build(); 460 461 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 462 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 463 CanConnectAndSendMessagesToMainFrame(extension)); 464 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 465 466 ui_test_utils::NavigateToURL(browser(), google_com_url()); 467 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 468 CanConnectAndSendMessagesToMainFrame(extension)); 469 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 470 } 471 472 // Tests two extensions on the same sites: one web connectable, one not. 473 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 474 WebConnectableAndNotConnectable) { 475 InitializeTestServer(); 476 477 // Install the web connectable extension. chromium.org can connect to it, 478 // google.com can't. 479 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 480 481 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 482 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(chromium_connectable)); 483 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 484 485 ui_test_utils::NavigateToURL(browser(), google_com_url()); 486 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 487 CanConnectAndSendMessagesToMainFrame(chromium_connectable)); 488 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 489 490 // Install the non-connectable extension. Nothing can connect to it. 491 const Extension* not_connectable = LoadNotConnectableExtension(); 492 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()); 499 500 ui_test_utils::NavigateToURL(browser(), google_com_url()); 501 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 502 CanConnectAndSendMessagesToMainFrame(not_connectable)); 503 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 504 } 505 506 // See http://crbug.com/297866 507 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 508 DISABLED_BackgroundPageClosesOnMessageReceipt) { 509 InitializeTestServer(); 510 511 // Install the web connectable extension. 512 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 513 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... 517 EXPECT_EQ(OK, 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)); 522 } 523 524 // Tests a web connectable extension that doesn't receive TLS channel id. 525 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 526 WebConnectableWithoutTlsChannelId) { 527 InitializeTestServer(); 528 529 // Install the web connectable extension. chromium.org can connect to it, 530 // google.com can't. 531 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 532 ASSERT_TRUE(chromium_connectable); 533 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)); 545 } 546 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(); 552 553 const Extension* chromium_connectable = 554 LoadChromiumConnectableExtensionWithTlsChannelId(); 555 ASSERT_TRUE(chromium_connectable); 556 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)); 568 } 569 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(); 575 576 const Extension* chromium_connectable = 577 LoadChromiumConnectableExtensionWithTlsChannelId(); 578 ASSERT_TRUE(chromium_connectable); 579 580 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 581 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); 594 } 595 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(); 602 603 const Extension* chromium_connectable = 604 LoadChromiumConnectableExtensionWithTlsChannelId(); 605 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); 619 } 620 621 // Tests that enabling and disabling an extension makes the runtime bindings 622 // appear and disappear. 623 // 624 // TODO(kalman): Test with multiple extensions that can be accessed by the same 625 // host. 626 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 627 EnablingAndDisabling) { 628 InitializeTestServer(); 629 630 const Extension* chromium_connectable = LoadChromiumConnectableExtension(); 631 const Extension* not_connectable = LoadNotConnectableExtension(); 632 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)); 637 638 DisableExtension(chromium_connectable->id()); 639 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 640 CanConnectAndSendMessagesToMainFrame(chromium_connectable)); 641 642 EnableExtension(chromium_connectable->id()); 643 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(chromium_connectable)); 644 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 645 CanConnectAndSendMessagesToMainFrame(not_connectable)); 646 } 647 648 // Tests connection from incognito tabs when the user denies the connection 649 // request. Spanning mode only. A separate test for apps and extensions. 650 // 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. 654 // 655 // TODO(kalman): test messages from incognito extensions too. 656 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 657 FromIncognitoDenyApp) { 658 InitializeTestServer(); 659 660 scoped_refptr<const Extension> app = LoadChromiumConnectableApp(); 661 ASSERT_TRUE(app->is_platform_app()); 662 663 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord( 664 profile()->GetOffTheRecordProfile(), 665 chromium_org_url()); 666 content::RenderFrameHost* incognito_frame = incognito_browser-> 667 tab_strip_model()->GetActiveWebContents()->GetMainFrame(); 668 669 { 670 IncognitoConnectability::ScopedAlertTracker alert_tracker( 671 IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY); 672 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()); 678 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()); 683 } 684 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)); 689 } 690 691 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 692 FromIncognitoDenyExtension) { 693 InitializeTestServer(); 694 695 scoped_refptr<const Extension> extension = LoadChromiumConnectableExtension(); 696 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() 702 ->GetMainFrame(); 703 704 { 705 IncognitoConnectability::ScopedAlertTracker alert_tracker( 706 IncognitoConnectability::ScopedAlertTracker::ALWAYS_DENY); 707 708 // The alert doesn't show for extensions. 709 EXPECT_EQ( 710 COULD_NOT_ESTABLISH_CONNECTION_ERROR, 711 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL)); 712 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount()); 713 } 714 715 // Allowing the extension in incognito mode will bypass the deny. 716 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(extension->id(), true); 717 EXPECT_EQ(OK, 718 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL)); 719 } 720 721 // Tests connection from incognito tabs when the user accepts the connection 722 // request. Spanning mode only. Separate tests for apps and extensions. 723 // 724 // TODO(kalman): see comment above about split mode. 725 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 726 FromIncognitoAllowApp) { 727 InitializeTestServer(); 728 729 scoped_refptr<const Extension> app = LoadChromiumConnectableApp(); 730 ASSERT_TRUE(app->is_platform_app()); 731 732 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord( 733 profile()->GetOffTheRecordProfile(), 734 chromium_org_url()); 735 content::RenderFrameHost* incognito_frame = incognito_browser-> 736 tab_strip_model()->GetActiveWebContents()->GetMainFrame(); 737 738 { 739 IncognitoConnectability::ScopedAlertTracker alert_tracker( 740 IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW); 741 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()); 746 747 // Try again. User has already allowed. 748 EXPECT_EQ(OK, CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL)); 749 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount()); 750 } 751 752 // Apps can't be allowed in incognito mode, but it's moot because it's 753 // already allowed. 754 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(app->id(), true); 755 EXPECT_EQ(OK, CanConnectAndSendMessagesToFrame(incognito_frame, app, NULL)); 756 } 757 758 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 759 FromIncognitoAllowExtension) { 760 InitializeTestServer(); 761 762 scoped_refptr<const Extension> extension = LoadChromiumConnectableExtension(); 763 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() 769 ->GetMainFrame(); 770 771 { 772 IncognitoConnectability::ScopedAlertTracker alert_tracker( 773 IncognitoConnectability::ScopedAlertTracker::ALWAYS_ALLOW); 774 775 // No alert is shown. 776 EXPECT_EQ( 777 COULD_NOT_ESTABLISH_CONNECTION_ERROR, 778 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL)); 779 EXPECT_EQ(0, alert_tracker.GetAndResetAlertCount()); 780 } 781 782 // Allowing the extension in incognito mode is what allows connections. 783 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(extension->id(), true); 784 EXPECT_EQ(OK, 785 CanConnectAndSendMessagesToFrame(incognito_frame, extension, NULL)); 786 } 787 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(); 793 794 const Extension* extension = LoadChromiumConnectableExtension(); 795 796 ui_test_utils::NavigateToURL(browser(), google_com_url()); 797 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 798 CanConnectAndSendMessagesToMainFrame(extension)); 799 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 800 801 ASSERT_TRUE(AppendIframe(chromium_org_url())); 802 803 EXPECT_EQ(OK, CanConnectAndSendMessagesToIFrame(extension)); 804 EXPECT_FALSE(AreAnyNonWebApisDefinedForIFrame()); 805 } 806 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(); 812 813 const Extension* extension = LoadChromiumConnectableExtension(); 814 815 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 816 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(extension)); 817 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 818 819 ASSERT_TRUE(AppendIframe(google_com_url())); 820 821 EXPECT_EQ(NAMESPACE_NOT_DEFINED, 822 CanConnectAndSendMessagesToIFrame(extension)); 823 EXPECT_FALSE(AreAnyNonWebApisDefinedForIFrame()); 824 } 825 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 { 830 public: 831 ExternallyConnectableMessagingWithTlsChannelIdTest() 832 : tls_channel_id_created_(false, false) { 833 } 834 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, 843 FROM_HERE, 844 base::Bind( 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; 861 } 862 863 private: 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_CURRENTLY_ON(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, 876 domain_bound_cert, 877 base::Bind(&ExternallyConnectableMessagingWithTlsChannelIdTest:: 878 GotDomainBoundCert, 879 base::Unretained(this)), 880 request_handle); 881 if (status == net::ERR_IO_PENDING) 882 return; 883 GotDomainBoundCert(status); 884 } 885 886 void GotDomainBoundCert(int status) { 887 ASSERT_TRUE(status == net::OK); 888 tls_channel_id_created_.Signal(); 889 } 890 891 base::WaitableEvent tls_channel_id_created_; 892 }; 893 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(); 900 901 const Extension* chromium_connectable = 902 LoadChromiumConnectableExtensionWithTlsChannelId(); 903 ASSERT_TRUE(chromium_connectable); 904 905 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 906 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)); 913 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()); 918 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); 923 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); 929 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); 936 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); 945 } 946 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(); 954 955 const Extension* chromium_connectable = 956 LoadChromiumConnectableExtensionWithTlsChannelId(); 957 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); 969 } 970 971 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingUserGesture) { 972 const char kManifest[] = "{" 973 " \"name\": \"user_gesture\"," 974 " \"version\": \"1.0\"," 975 " \"background\": {" 976 " \"scripts\": [\"background.js\"]" 977 " }," 978 " \"manifest_version\": 2" 979 "}"; 980 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" 987 " });"); 988 const Extension* receiver = LoadExtension(receiver_dir.unpacked_path()); 989 ASSERT_TRUE(receiver); 990 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()); 995 ASSERT_TRUE(sender); 996 997 EXPECT_EQ("false", 998 ExecuteScriptInBackgroundPage(sender->id(), 999 base::StringPrintf( 1000 "chrome.test.runWithoutUserGesture(function() {\n" 1001 " chrome.runtime.sendMessage('%s', {}, function(response) {\n" 1002 " window.domAutomationController.send('' + response.result);\n" 1003 " });\n" 1004 "});", receiver->id().c_str()))); 1005 1006 EXPECT_EQ("true", 1007 ExecuteScriptInBackgroundPage(sender->id(), 1008 base::StringPrintf( 1009 "chrome.test.runWithUserGesture(function() {\n" 1010 " chrome.runtime.sendMessage('%s', {}, function(response) {\n" 1011 " window.domAutomationController.send('' + response.result);\n" 1012 " });\n" 1013 "});", receiver->id().c_str()))); 1014 } 1015 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(); 1020 1021 scoped_refptr<const Extension> app = LoadChromiumHostedApp(); 1022 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()); 1027 1028 // Once a connectable extension is installed, it should. 1029 const Extension* extension = LoadChromiumConnectableExtension(); 1030 EXPECT_EQ(OK, CanConnectAndSendMessagesToMainFrame(extension)); 1031 EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); 1032 } 1033 1034 // Tests that an invalid extension ID specified in a hosted app does not crash 1035 // the hosted app's renderer. 1036 // 1037 // This is a regression test for http://crbug.com/326250#c12. 1038 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, 1039 InvalidExtensionIDFromHostedApp) { 1040 InitializeTestServer(); 1041 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(); 1046 1047 scoped_refptr<const Extension> invalid = 1048 ExtensionBuilder() 1049 // A bit scary that this works... 1050 .SetID("invalid") 1051 .SetManifest(DictionaryBuilder() 1052 .Set("name", "Fake extension") 1053 .Set("version", "1") 1054 .Set("manifest_version", 2)) 1055 .Build(); 1056 1057 ui_test_utils::NavigateToURL(browser(), chromium_org_url()); 1058 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR, 1059 CanConnectAndSendMessagesToMainFrame(invalid)); 1060 } 1061 1062 #endif // !defined(OS_WIN) - http://crbug.com/350517. 1063 1064 } // namespace 1065 1066 }; // namespace extensions 1067