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