Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_messages_apitest.cc
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   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   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* const 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(&not_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