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
43namespace extensions {
44namespace {
45
46class 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.
105IN_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.
111IN_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.
123IN_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.
129class 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
136IN_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.
150class 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(&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
450IN_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.
474IN_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
511IN_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.
531IN_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.
556IN_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.
579IN_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.
606IN_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.
634IN_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.
668IN_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
705IN_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.
740IN_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
776IN_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.
809IN_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.
828IN_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.
847class 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.
915IN_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
972IN_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
994IN_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.
1041IN_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.
1062IN_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