app_list: Re-enable people search.
[chromium-blink-merge.git] / chrome / browser / extensions / api / declarative_content / declarative_content_apitest.cc
blob4c727062ed816e3bd56c7f00068f321cbea890b4
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 "chrome/browser/extensions/extension_action.h"
6 #include "chrome/browser/extensions/extension_action_manager.h"
7 #include "chrome/browser/extensions/extension_action_test_util.h"
8 #include "chrome/browser/extensions/extension_apitest.h"
9 #include "chrome/browser/extensions/extension_tab_util.h"
10 #include "chrome/browser/extensions/test_extension_dir.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_window.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/common/extensions/features/feature_channel.h"
15 #include "content/public/test/browser_test_utils.h"
16 #include "extensions/test/extension_test_message_listener.h"
17 #include "testing/gmock/include/gmock/gmock.h"
19 namespace extensions {
20 namespace {
22 const char kDeclarativeContentManifest[] =
23 "{\n"
24 " \"name\": \"Declarative Content apitest\",\n"
25 " \"version\": \"0.1\",\n"
26 " \"manifest_version\": 2,\n"
27 " \"description\": \n"
28 " \"end-to-end browser test for the declarative Content API\",\n"
29 " \"background\": {\n"
30 " \"scripts\": [\"background.js\"]\n"
31 " },\n"
32 " \"page_action\": {},\n"
33 " \"permissions\": [\n"
34 " \"declarativeContent\"\n"
35 " ]\n"
36 "}\n";
38 const char kBackgroundHelpers[] =
39 "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
40 "var ShowPageAction = chrome.declarativeContent.ShowPageAction;\n"
41 "var onPageChanged = chrome.declarativeContent.onPageChanged;\n"
42 "var Reply = window.domAutomationController.send.bind(\n"
43 " window.domAutomationController);\n"
44 "\n"
45 "function setRules(rules, responseString) {\n"
46 " onPageChanged.removeRules(undefined, function() {\n"
47 " onPageChanged.addRules(rules, function() {\n"
48 " if (chrome.runtime.lastError) {\n"
49 " Reply(chrome.runtime.lastError.message);\n"
50 " return;\n"
51 " }\n"
52 " Reply(responseString);\n"
53 " });\n"
54 " });\n"
55 "};\n";
57 class DeclarativeContentApiTest : public ExtensionApiTest {
58 public:
59 DeclarativeContentApiTest()
60 // Set the channel to "trunk" since declarativeContent is restricted
61 // to trunk.
62 : current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {
64 ~DeclarativeContentApiTest() override {}
66 extensions::ScopedCurrentChannel current_channel_;
67 TestExtensionDir ext_dir_;
70 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, Overview) {
71 ext_dir_.WriteManifest(kDeclarativeContentManifest);
72 ext_dir_.WriteFile(
73 FILE_PATH_LITERAL("background.js"),
74 "var declarative = chrome.declarative;\n"
75 "\n"
76 "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
77 "var ShowPageAction = chrome.declarativeContent.ShowPageAction;\n"
78 "\n"
79 "var rule0 = {\n"
80 " conditions: [new PageStateMatcher({\n"
81 " pageUrl: {hostPrefix: \"test1\"}}),\n"
82 " new PageStateMatcher({\n"
83 " css: [\"input[type='password']\"]})],\n"
84 " actions: [new ShowPageAction()]\n"
85 "}\n"
86 "\n"
87 "var testEvent = chrome.declarativeContent.onPageChanged;\n"
88 "\n"
89 "testEvent.removeRules(undefined, function() {\n"
90 " testEvent.addRules([rule0], function() {\n"
91 " chrome.test.sendMessage(\"ready\", function(reply) {\n"
92 " })\n"
93 " });\n"
94 "});\n");
95 ExtensionTestMessageListener ready("ready", true);
96 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
97 ASSERT_TRUE(extension);
98 const ExtensionAction* page_action =
99 ExtensionActionManager::Get(browser()->profile())->
100 GetPageAction(*extension);
101 ASSERT_TRUE(page_action);
103 ASSERT_TRUE(ready.WaitUntilSatisfied());
104 content::WebContents* const tab =
105 browser()->tab_strip_model()->GetWebContentsAt(0);
106 const int tab_id = ExtensionTabUtil::GetTabId(tab);
108 NavigateInRenderer(tab, GURL("http://test1/"));
110 // The declarative API should show the page action instantly, rather
111 // than waiting for the extension to run.
112 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
114 // Make sure leaving a matching page unshows the page action.
115 NavigateInRenderer(tab, GURL("http://not_checked/"));
116 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
118 // Insert a password field to make sure that's noticed.
119 // Notice that we touch offsetTop to force a synchronous layout.
120 ASSERT_TRUE(content::ExecuteScript(
121 tab, "document.body.innerHTML = '<input type=\"password\">';"
122 "document.body.offsetTop;"));
124 // Give the style match a chance to run and send back the matching-selector
125 // update. This takes one time through the Blink message loop to apply the
126 // style to the new element, and a second to dedupe updates.
127 // FIXME: Remove this after https://codereview.chromium.org/145663012/
128 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
129 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
131 EXPECT_TRUE(page_action->GetIsVisible(tab_id))
132 << "Adding a matching element should show the page action.";
134 // Remove it again to make sure that reverts the action.
135 // Notice that we touch offsetTop to force a synchronous layout.
136 ASSERT_TRUE(content::ExecuteScript(
137 tab, "document.body.innerHTML = 'Hello world';"
138 "document.body.offsetTop;"));
140 // Give the style match a chance to run and send back the matching-selector
141 // update. This takes one time through the Blink message loop to apply the
142 // style to the new element, and a second to dedupe updates.
143 // FIXME: Remove this after https://codereview.chromium.org/145663012/
144 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
145 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
147 EXPECT_FALSE(page_action->GetIsVisible(tab_id))
148 << "Removing the matching element should hide the page action again.";
151 // http://crbug.com/304373
152 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
153 UninstallWhileActivePageAction) {
154 ext_dir_.WriteManifest(kDeclarativeContentManifest);
155 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
156 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
157 ASSERT_TRUE(extension);
158 const std::string extension_id = extension->id();
159 const ExtensionAction* page_action = ExtensionActionManager::Get(
160 browser()->profile())->GetPageAction(*extension);
161 ASSERT_TRUE(page_action);
163 const std::string kTestRule =
164 "setRules([{\n"
165 " conditions: [new PageStateMatcher({\n"
166 " pageUrl: {hostPrefix: \"test\"}})],\n"
167 " actions: [new ShowPageAction()]\n"
168 "}], 'test_rule');\n";
169 EXPECT_EQ("test_rule",
170 ExecuteScriptInBackgroundPage(extension_id, kTestRule));
172 content::WebContents* const tab =
173 browser()->tab_strip_model()->GetWebContentsAt(0);
174 const int tab_id = ExtensionTabUtil::GetTabId(tab);
176 NavigateInRenderer(tab, GURL("http://test/"));
178 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
179 EXPECT_TRUE(WaitForPageActionVisibilityChangeTo(1));
180 EXPECT_EQ(1u, extension_action_test_util::GetVisiblePageActionCount(tab));
181 EXPECT_EQ(1u, extension_action_test_util::GetTotalPageActionCount(tab));
183 ReloadExtension(extension_id); // Invalidates page_action and extension.
184 EXPECT_EQ("test_rule",
185 ExecuteScriptInBackgroundPage(extension_id, kTestRule));
186 // TODO(jyasskin): Apply new rules to existing tabs, without waiting for a
187 // navigation.
188 NavigateInRenderer(tab, GURL("http://test/"));
189 EXPECT_TRUE(WaitForPageActionVisibilityChangeTo(1));
190 EXPECT_EQ(1u, extension_action_test_util::GetVisiblePageActionCount(tab));
191 EXPECT_EQ(1u, extension_action_test_util::GetTotalPageActionCount(tab));
193 UnloadExtension(extension_id);
194 NavigateInRenderer(tab, GURL("http://test/"));
195 EXPECT_TRUE(WaitForPageActionVisibilityChangeTo(0));
196 EXPECT_EQ(0u, extension_action_test_util::GetVisiblePageActionCount(tab));
197 EXPECT_EQ(0u, extension_action_test_util::GetTotalPageActionCount(tab));
200 // This tests against a renderer crash that was present during development.
201 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
202 DISABLED_AddExtensionMatchingExistingTabWithDeadFrames) {
203 ext_dir_.WriteManifest(kDeclarativeContentManifest);
204 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
205 content::WebContents* const tab =
206 browser()->tab_strip_model()->GetWebContentsAt(0);
207 const int tab_id = ExtensionTabUtil::GetTabId(tab);
209 ASSERT_TRUE(content::ExecuteScript(
210 tab, "document.body.innerHTML = '<iframe src=\"http://test2\">';"));
211 // Replace the iframe to destroy its WebFrame.
212 ASSERT_TRUE(content::ExecuteScript(
213 tab, "document.body.innerHTML = '<span class=\"foo\">';"));
215 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
216 ASSERT_TRUE(extension);
217 const ExtensionAction* page_action = ExtensionActionManager::Get(
218 browser()->profile())->GetPageAction(*extension);
219 ASSERT_TRUE(page_action);
220 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
222 EXPECT_EQ("rule0",
223 ExecuteScriptInBackgroundPage(
224 extension->id(),
225 "setRules([{\n"
226 " conditions: [new PageStateMatcher({\n"
227 " css: [\"span[class=foo]\"]})],\n"
228 " actions: [new ShowPageAction()]\n"
229 "}], 'rule0');\n"));
230 // Give the renderer a chance to apply the rules change and notify the
231 // browser. This takes one time through the Blink message loop to receive
232 // the rule change and apply the new stylesheet, and a second to dedupe the
233 // update.
234 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
235 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
237 EXPECT_FALSE(tab->IsCrashed());
238 EXPECT_TRUE(page_action->GetIsVisible(tab_id))
239 << "Loading an extension when an open page matches its rules "
240 << "should show the page action.";
242 EXPECT_EQ("removed",
243 ExecuteScriptInBackgroundPage(
244 extension->id(),
245 "onPageChanged.removeRules(undefined, function() {\n"
246 " window.domAutomationController.send('removed');\n"
247 "});\n"));
248 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
251 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
252 ShowPageActionWithoutPageAction) {
253 std::string manifest_without_page_action = kDeclarativeContentManifest;
254 ReplaceSubstringsAfterOffset(
255 &manifest_without_page_action, 0, "\"page_action\": {},", "");
256 ext_dir_.WriteManifest(manifest_without_page_action);
257 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
258 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
259 ASSERT_TRUE(extension);
261 EXPECT_THAT(ExecuteScriptInBackgroundPage(
262 extension->id(),
263 "setRules([{\n"
264 " conditions: [new PageStateMatcher({\n"
265 " pageUrl: {hostPrefix: \"test\"}})],\n"
266 " actions: [new ShowPageAction()]\n"
267 "}], 'test_rule');\n"),
268 testing::HasSubstr("without a page action"));
270 content::WebContents* const tab =
271 browser()->tab_strip_model()->GetWebContentsAt(0);
272 NavigateInRenderer(tab, GURL("http://test/"));
274 EXPECT_EQ(NULL,
275 ExtensionActionManager::Get(browser()->profile())->
276 GetPageAction(*extension));
277 EXPECT_EQ(0u, extension_action_test_util::GetVisiblePageActionCount(tab));
280 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
281 CanonicalizesPageStateMatcherCss) {
282 ext_dir_.WriteManifest(kDeclarativeContentManifest);
283 ext_dir_.WriteFile(
284 FILE_PATH_LITERAL("background.js"),
285 "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
286 "function Return(obj) {\n"
287 " window.domAutomationController.send('' + obj);\n"
288 "}\n");
289 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
290 ASSERT_TRUE(extension);
292 EXPECT_EQ("input[type=\"password\"]",
293 ExecuteScriptInBackgroundPage(
294 extension->id(),
295 "var psm = new PageStateMatcher(\n"
296 " {css: [\"input[type='password']\"]});\n"
297 "Return(psm.css);"));
299 EXPECT_THAT(ExecuteScriptInBackgroundPage(
300 extension->id(),
301 "try {\n"
302 " new PageStateMatcher({css: 'Not-an-array'});\n"
303 " Return('Failed to throw');\n"
304 "} catch (e) {\n"
305 " Return(e.message);\n"
306 "}\n"),
307 testing::ContainsRegex("css.*Expected 'array'"));
308 EXPECT_THAT(ExecuteScriptInBackgroundPage(
309 extension->id(),
310 "try {\n"
311 " new PageStateMatcher({css: [null]});\n" // Not a string.
312 " Return('Failed to throw');\n"
313 "} catch (e) {\n"
314 " Return(e.message);\n"
315 "}\n"),
316 testing::ContainsRegex("css\\.0.*Expected 'string'"));
317 EXPECT_THAT(ExecuteScriptInBackgroundPage(
318 extension->id(),
319 "try {\n"
320 // Invalid CSS:
321 " new PageStateMatcher({css: [\"input''\"]});\n"
322 " Return('Failed to throw');\n"
323 "} catch (e) {\n"
324 " Return(e.message);\n"
325 "}\n"),
326 testing::ContainsRegex("valid.*: input''$"));
327 EXPECT_THAT(ExecuteScriptInBackgroundPage(
328 extension->id(),
329 "try {\n"
330 // "Complex" selector:
331 " new PageStateMatcher({css: ['div input']});\n"
332 " Return('Failed to throw');\n"
333 "} catch (e) {\n"
334 " Return(e.message);\n"
335 "}\n"),
336 testing::ContainsRegex("compound selector.*: div input$"));
339 } // namespace
340 } // namespace extensions