Implement chrome.runtime.restart for cros kiosk app.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_messages_apitest.cc
blobe6557df4b0645f3365ae6d8af199f2da47b838a4
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.
5 #include "base/files/file_path.h"
6 #include "base/path_service.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/values.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/event_router.h"
12 #include "chrome/browser/extensions/extension_apitest.h"
13 #include "chrome/browser/extensions/extension_prefs.h"
14 #include "chrome/browser/extensions/extension_system.h"
15 #include "chrome/browser/extensions/test_extension_dir.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "content/public/browser/notification_registrar.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/test/browser_test_utils.h"
25 #include "net/dns/mock_host_resolver.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h"
27 #include "url/gurl.h"
29 namespace extensions {
30 namespace {
32 class MessageSender : public content::NotificationObserver {
33 public:
34 MessageSender() {
35 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
36 content::NotificationService::AllSources());
39 private:
40 static scoped_ptr<base::ListValue> BuildEventArguments(
41 const bool last_message,
42 const std::string& data) {
43 DictionaryValue* event = new DictionaryValue();
44 event->SetBoolean("lastMessage", last_message);
45 event->SetString("data", data);
46 scoped_ptr<base::ListValue> arguments(new base::ListValue());
47 arguments->Append(event);
48 return arguments.Pass();
51 static scoped_ptr<Event> BuildEvent(scoped_ptr<base::ListValue> event_args,
52 Profile* profile,
53 GURL event_url) {
54 scoped_ptr<Event> event(new Event("test.onMessage", event_args.Pass()));
55 event->restrict_to_profile = profile;
56 event->event_url = event_url;
57 return event.Pass();
60 virtual void Observe(int type,
61 const content::NotificationSource& source,
62 const content::NotificationDetails& details) OVERRIDE {
63 EventRouter* event_router = ExtensionSystem::Get(
64 content::Source<Profile>(source).ptr())->event_router();
66 // Sends four messages to the extension. All but the third message sent
67 // from the origin http://b.com/ are supposed to arrive.
68 event_router->BroadcastEvent(BuildEvent(
69 BuildEventArguments(false, "no restriction"),
70 content::Source<Profile>(source).ptr(),
71 GURL()));
72 event_router->BroadcastEvent(BuildEvent(
73 BuildEventArguments(false, "http://a.com/"),
74 content::Source<Profile>(source).ptr(),
75 GURL("http://a.com/")));
76 event_router->BroadcastEvent(BuildEvent(
77 BuildEventArguments(false, "http://b.com/"),
78 content::Source<Profile>(source).ptr(),
79 GURL("http://b.com/")));
80 event_router->BroadcastEvent(BuildEvent(
81 BuildEventArguments(true, "last message"),
82 content::Source<Profile>(source).ptr(),
83 GURL()));
86 content::NotificationRegistrar registrar_;
89 // Tests that message passing between extensions and content scripts works.
90 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Messaging) {
91 ASSERT_TRUE(StartEmbeddedTestServer());
92 ASSERT_TRUE(RunExtensionTest("messaging/connect")) << message_;
95 // Tests that message passing from one extension to another works.
96 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingExternal) {
97 ASSERT_TRUE(LoadExtension(
98 test_data_dir_.AppendASCII("..").AppendASCII("good")
99 .AppendASCII("Extensions")
100 .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa")
101 .AppendASCII("1.0")));
103 ASSERT_TRUE(RunExtensionTest("messaging/connect_external")) << message_;
106 // Tests that messages with event_urls are only passed to extensions with
107 // appropriate permissions.
108 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingEventURL) {
109 MessageSender sender;
110 ASSERT_TRUE(RunExtensionTest("messaging/event_url")) << message_;
113 // Tests connecting from a panel to its extension.
114 class PanelMessagingTest : public ExtensionApiTest {
115 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
116 ExtensionApiTest::SetUpCommandLine(command_line);
117 command_line->AppendSwitch(switches::kEnablePanels);
121 IN_PROC_BROWSER_TEST_F(PanelMessagingTest, MessagingPanel) {
122 ASSERT_TRUE(RunExtensionTest("messaging/connect_panel")) << message_;
125 // Tests externally_connectable between a web page and an extension.
127 // TODO(kalman): Test between extensions. This is already tested in this file,
128 // but not with externally_connectable set in the manifest.
130 // TODO(kalman): Test with host permissions.
131 class ExternallyConnectableMessagingTest : public ExtensionApiTest {
132 protected:
133 // Result codes from the test. These must match up with |results| in
134 // c/t/d/extensions/api_test/externally_connectable/assertions.json.
135 enum Result {
136 OK = 0,
137 NAMESPACE_NOT_DEFINED = 1,
138 FUNCTION_NOT_DEFINED = 2,
139 COULD_NOT_ESTABLISH_CONNECTION_ERROR = 3,
140 OTHER_ERROR = 4,
141 INCORRECT_RESPONSE_SENDER = 5,
142 INCORRECT_RESPONSE_MESSAGE = 6,
145 bool AppendIframe(const GURL& src) {
146 bool result;
147 CHECK(content::ExecuteScriptAndExtractBool(
148 browser()->tab_strip_model()->GetActiveWebContents(),
149 "actions.appendIframe('" + src.spec() + "');", &result));
150 return result;
153 Result CanConnectAndSendMessages(const std::string& extension_id) {
154 return CanConnectAndSendMessages(browser(), extension_id, "");
157 Result CanConnectAndSendMessages(const std::string& extension_id,
158 const char* frame_xpath,
159 const char* message) {
160 return CanConnectAndSendMessages(browser(), extension_id, frame_xpath,
161 message);
164 Result CanConnectAndSendMessages(Browser* browser,
165 const std::string& extension_id) {
166 return CanConnectAndSendMessages(browser, extension_id, "");
169 Result CanConnectAndSendMessages(const std::string& extension_id,
170 const char* frame_xpath) {
171 return CanConnectAndSendMessages(browser(), extension_id, frame_xpath);
174 Result CanConnectAndSendMessages(Browser* browser,
175 const std::string& extension_id,
176 const char* frame_xpath,
177 const char* message = NULL) {
178 int result;
179 std::string args = "'" + extension_id + "'";
180 if (message)
181 args += std::string(", '") + message + "'";
182 CHECK(content::ExecuteScriptInFrameAndExtractInt(
183 browser->tab_strip_model()->GetActiveWebContents(),
184 frame_xpath,
185 base::StringPrintf("assertions.canConnectAndSendMessages(%s)",
186 args.c_str()).c_str(),
187 &result));
188 return static_cast<Result>(result);
191 testing::AssertionResult AreAnyNonWebApisDefined() {
192 return AreAnyNonWebApisDefined("");
195 testing::AssertionResult AreAnyNonWebApisDefined(const char* frame_xpath) {
196 // All runtime API methods are non-web except for sendRequest and connect.
197 const char* non_messaging_apis[] = {
198 "getBackgroundPage",
199 "getManifest",
200 "getURL",
201 "reload",
202 "requestUpdateCheck",
203 "restart",
204 "connectNative",
205 "sendNativeMessage",
206 "onStartup",
207 "onInstalled",
208 "onSuspend",
209 "onSuspendCanceled",
210 "onUpdateAvailable",
211 "onBrowserUpdateAvailable",
212 "onConnect",
213 "onConnectExternal",
214 "onMessage",
215 "onMessageExternal",
216 "onRestartRequired",
217 "id",
220 // Turn the array into a JS array, which effectively gets eval()ed.
221 std::string as_js_array;
222 for (size_t i = 0; i < arraysize(non_messaging_apis); ++i) {
223 as_js_array += as_js_array.empty() ? "[" : ",";
224 as_js_array += base::StringPrintf("'%s'", non_messaging_apis[i]);
226 as_js_array += "]";
228 bool any_defined;
229 CHECK(content::ExecuteScriptInFrameAndExtractBool(
230 browser()->tab_strip_model()->GetActiveWebContents(),
231 frame_xpath,
232 "assertions.areAnyRuntimePropertiesDefined(" + as_js_array + ")",
233 &any_defined));
234 return any_defined ?
235 testing::AssertionSuccess() : testing::AssertionFailure();
238 GURL GetURLForPath(const std::string& host, const std::string& path) {
239 std::string port = base::IntToString(embedded_test_server()->port());
240 GURL::Replacements replacements;
241 replacements.SetHostStr(host);
242 replacements.SetPortStr(port);
243 return embedded_test_server()->GetURL(path).ReplaceComponents(replacements);
246 GURL chromium_org_url() {
247 return GetURLForPath("www.chromium.org", "/chromium.org.html");
250 GURL google_com_url() {
251 return GetURLForPath("www.google.com", "/google.com.html");
254 const Extension* LoadChromiumConnectableExtension() {
255 const Extension* extension =
256 LoadExtensionIntoDir(&web_connectable_dir_, base::StringPrintf(
258 " \"name\": \"chromium_connectable\","
259 " %s,"
260 " \"externally_connectable\": {"
261 " \"matches\": [\"*://*.chromium.org:*/*\"]"
262 " }"
263 "}",
264 common_manifest()));
265 CHECK(extension);
266 return extension;
269 const Extension* LoadNotConnectableExtension() {
270 const Extension* extension =
271 LoadExtensionIntoDir(&not_connectable_dir_, base::StringPrintf(
273 " \"name\": \"not_connectable\","
274 " %s"
275 "}",
276 common_manifest()));
277 CHECK(extension);
278 return extension;
281 void InitializeTestServer() {
282 base::FilePath test_data;
283 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data));
284 embedded_test_server()->ServeFilesFromDirectory(test_data.AppendASCII(
285 "extensions/api_test/messaging/externally_connectable/sites"));
286 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
287 host_resolver()->AddRule("*", embedded_test_server()->base_url().host());
290 const char* close_background_message() {
291 return "closeBackgroundPage";
294 private:
295 const Extension* LoadExtensionIntoDir(TestExtensionDir* dir,
296 const std::string& manifest) {
297 dir->WriteManifest(manifest);
298 dir->WriteFile(FILE_PATH_LITERAL("background.js"),
299 base::StringPrintf(
300 "function maybeClose(message) {\n"
301 " if (message.indexOf('%s') >= 0)\n"
302 " window.setTimeout(function() { window.close() }, 0);\n"
303 "}\n"
304 "chrome.runtime.onMessageExternal.addListener(\n"
305 " function(message, sender, reply) {\n"
306 " reply({ message: message, sender: sender });\n"
307 " maybeClose(message);\n"
308 "});\n"
309 "chrome.runtime.onConnectExternal.addListener(function(port) {\n"
310 " port.onMessage.addListener(function(message) {\n"
311 " port.postMessage({ message: message, sender: port.sender });\n"
312 " maybeClose(message);\n"
313 " });\n"
314 "});\n",
315 close_background_message()));
316 return LoadExtension(dir->unpacked_path());
319 const char* common_manifest() {
320 return "\"version\": \"1.0\","
321 "\"background\": {"
322 " \"scripts\": [\"background.js\"],"
323 " \"persistent\": false"
324 "},"
325 "\"manifest_version\": 2";
328 TestExtensionDir web_connectable_dir_;
329 TestExtensionDir not_connectable_dir_;
332 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, NotInstalled) {
333 InitializeTestServer();
335 const char kFakeId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
337 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
338 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessages(kFakeId));
339 EXPECT_FALSE(AreAnyNonWebApisDefined());
341 ui_test_utils::NavigateToURL(browser(), google_com_url());
342 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessages(kFakeId));
343 EXPECT_FALSE(AreAnyNonWebApisDefined());
346 // Tests two extensions on the same sites: one web connectable, one not.
347 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
348 WebConnectableAndNotConnectable) {
349 InitializeTestServer();
351 // Install the web connectable extension. chromium.org can connect to it,
352 // google.com can't.
353 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
355 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
356 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id()));
357 EXPECT_FALSE(AreAnyNonWebApisDefined());
359 ui_test_utils::NavigateToURL(browser(), google_com_url());
360 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
361 CanConnectAndSendMessages(chromium_connectable->id()));
362 EXPECT_FALSE(AreAnyNonWebApisDefined());
364 // Install the non-connectable extension. Nothing can connect to it.
365 const Extension* not_connectable = LoadNotConnectableExtension();
367 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
368 // Namespace will be defined here because |chromium_connectable| can connect
369 // to it - so this will be the "cannot establish connection" error.
370 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
371 CanConnectAndSendMessages(not_connectable->id()));
372 EXPECT_FALSE(AreAnyNonWebApisDefined());
374 ui_test_utils::NavigateToURL(browser(), google_com_url());
375 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
376 CanConnectAndSendMessages(not_connectable->id()));
377 EXPECT_FALSE(AreAnyNonWebApisDefined());
380 // See http://crbug.com/297866
381 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
382 DISABLED_BackgroundPageClosesOnMessageReceipt) {
383 InitializeTestServer();
385 // Install the web connectable extension.
386 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
388 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
389 // If the background page closes after receipt of the message, it will still
390 // reply to this message...
391 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id(),
393 close_background_message()));
394 // and be re-opened by receipt of a subsequent message.
395 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id()));
398 // Tests that enabling and disabling an extension makes the runtime bindings
399 // appear and disappear.
401 // TODO(kalman): Test with multiple extensions that can be accessed by the same
402 // host.
403 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
404 EnablingAndDisabling) {
405 InitializeTestServer();
407 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
408 const Extension* not_connectable = LoadNotConnectableExtension();
410 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
411 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id()));
412 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
413 CanConnectAndSendMessages(not_connectable->id()));
415 DisableExtension(chromium_connectable->id());
416 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
417 CanConnectAndSendMessages(chromium_connectable->id()));
419 EnableExtension(chromium_connectable->id());
420 EXPECT_EQ(OK, CanConnectAndSendMessages(chromium_connectable->id()));
421 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
422 CanConnectAndSendMessages(not_connectable->id()));
425 // Tests connection from incognito tabs. Spanning mode only.
427 // TODO(kalman): ensure that we exercise split vs spanning incognito logic
428 // somewhere. This is a test that should be shared with the content script logic
429 // so it's not really our specific concern for web connectable.
430 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, FromIncognito) {
431 InitializeTestServer();
433 const Extension* chromium_connectable = LoadChromiumConnectableExtension();
435 Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord(
436 profile()->GetOffTheRecordProfile(),
437 chromium_org_url());
439 // No connection because incognito enabled hasn't been set.
440 const std::string& id = chromium_connectable->id();
441 EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
442 CanConnectAndSendMessages(incognito_browser, id));
444 // Then yes.
445 ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(id, true);
446 EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id));
449 // Tests a connection from an iframe within a tab which doesn't have
450 // permission. Iframe should work.
451 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
452 FromIframeWithPermission) {
453 InitializeTestServer();
455 const Extension* extension = LoadChromiumConnectableExtension();
457 ui_test_utils::NavigateToURL(browser(), google_com_url());
458 EXPECT_EQ(NAMESPACE_NOT_DEFINED, CanConnectAndSendMessages(extension->id()));
459 EXPECT_FALSE(AreAnyNonWebApisDefined());
461 ASSERT_TRUE(AppendIframe(chromium_org_url()));
463 const char* frame_xpath = "//iframe[1]";
464 EXPECT_EQ(OK, CanConnectAndSendMessages(extension->id(), frame_xpath));
465 EXPECT_FALSE(AreAnyNonWebApisDefined(frame_xpath));
468 // Tests connection from an iframe without permission within a tab that does.
469 // Iframe shouldn't work.
470 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest,
471 FromIframeWithoutPermission) {
472 InitializeTestServer();
474 const Extension* extension = LoadChromiumConnectableExtension();
476 ui_test_utils::NavigateToURL(browser(), chromium_org_url());
477 EXPECT_EQ(OK, CanConnectAndSendMessages(extension->id()));
478 EXPECT_FALSE(AreAnyNonWebApisDefined());
480 ASSERT_TRUE(AppendIframe(google_com_url()));
482 const char* frame_xpath = "//iframe[1]";
483 EXPECT_EQ(NAMESPACE_NOT_DEFINED,
484 CanConnectAndSendMessages(extension->id(), frame_xpath));
485 EXPECT_FALSE(AreAnyNonWebApisDefined(frame_xpath));
488 } // namespace
489 }; // namespace extensions