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