Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / declarative_content / declarative_content_apitest.cc
blobf21802c626adc8c2804b2c9315ba2ed1f56e75ab
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/strings/stringprintf.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
8 #include "chrome/browser/extensions/extension_action.h"
9 #include "chrome/browser/extensions/extension_action_manager.h"
10 #include "chrome/browser/extensions/extension_action_test_util.h"
11 #include "chrome/browser/extensions/extension_apitest.h"
12 #include "chrome/browser/extensions/extension_tab_util.h"
13 #include "chrome/browser/extensions/test_extension_dir.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "components/bookmarks/browser/bookmark_model.h"
18 #include "content/public/test/browser_test_utils.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/test/extension_test_message_listener.h"
21 #include "testing/gmock/include/gmock/gmock.h"
23 namespace extensions {
24 namespace {
26 const char kDeclarativeContentManifest[] =
27 "{\n"
28 " \"name\": \"Declarative Content apitest\",\n"
29 " \"version\": \"0.1\",\n"
30 " \"manifest_version\": 2,\n"
31 " \"description\": \n"
32 " \"end-to-end browser test for the declarative Content API\",\n"
33 " \"background\": {\n"
34 " \"scripts\": [\"background.js\"]\n"
35 " },\n"
36 " \"page_action\": {},\n"
37 " \"permissions\": [\n"
38 " \"declarativeContent\", \"bookmarks\"\n"
39 " ],\n"
40 " \"incognito\": \"spanning\"\n"
41 "}\n";
43 const char kIncognitoSpecificBackground[] =
44 "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
45 "var ShowPageAction = chrome.declarativeContent.ShowPageAction;\n"
46 "var inIncognitoContext = chrome.extension.inIncognitoContext;\n"
47 "\n"
48 "var hostPrefix = chrome.extension.inIncognitoContext ?\n"
49 " 'test_split' : 'test_normal';\n"
50 "var rule = {\n"
51 " conditions: [new PageStateMatcher({\n"
52 " pageUrl: {hostPrefix: hostPrefix}})],\n"
53 " actions: [new ShowPageAction()]\n"
54 "}\n"
55 "\n"
56 "var onPageChanged = chrome.declarativeContent.onPageChanged;\n"
57 "onPageChanged.removeRules(undefined, function() {\n"
58 " onPageChanged.addRules([rule], function() {\n"
59 " chrome.test.sendMessage(\n"
60 " !inIncognitoContext ? \"ready\" : \"ready (split)\");\n"
61 " });\n"
62 "});\n";
64 const char kBackgroundHelpers[] =
65 "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
66 "var ShowPageAction = chrome.declarativeContent.ShowPageAction;\n"
67 "var onPageChanged = chrome.declarativeContent.onPageChanged;\n"
68 "var reply = window.domAutomationController.send.bind(\n"
69 " window.domAutomationController);\n"
70 "\n"
71 "function setRules(rules, responseString) {\n"
72 " onPageChanged.removeRules(undefined, function() {\n"
73 " onPageChanged.addRules(rules, function() {\n"
74 " if (chrome.runtime.lastError) {\n"
75 " reply(chrome.runtime.lastError.message);\n"
76 " return;\n"
77 " }\n"
78 " reply(responseString);\n"
79 " });\n"
80 " });\n"
81 "};\n"
82 "\n"
83 "function addRules(rules, responseString) {\n"
84 " onPageChanged.addRules(rules, function() {\n"
85 " if (chrome.runtime.lastError) {\n"
86 " reply(chrome.runtime.lastError.message);\n"
87 " return;\n"
88 " }\n"
89 " reply(responseString);\n"
90 " });\n"
91 "};\n"
92 "\n"
93 "function removeRule(id, responseString) {\n"
94 " onPageChanged.removeRules([id], function() {\n"
95 " if (chrome.runtime.lastError) {\n"
96 " reply(chrome.runtime.lastError.message);\n"
97 " return;\n"
98 " }\n"
99 " reply(responseString);\n"
100 " });\n"
101 "};\n";
103 class DeclarativeContentApiTest : public ExtensionApiTest {
104 public:
105 DeclarativeContentApiTest() {}
107 protected:
108 enum IncognitoMode { SPANNING, SPLIT };
110 // Checks that the rules are correctly evaluated for an extension in incognito
111 // mode |mode| when the extension's incognito enable state is
112 // |is_enabled_in_incognito|.
113 void CheckIncognito(IncognitoMode mode, bool is_enabled_in_incognito);
115 // Checks that the rules matching a bookmarked state of |is_bookmarked| are
116 // correctly evaluated on bookmark events.
117 void CheckBookmarkEvents(bool is_bookmarked);
119 TestExtensionDir ext_dir_;
121 private:
122 DISALLOW_COPY_AND_ASSIGN(DeclarativeContentApiTest);
125 void DeclarativeContentApiTest::CheckIncognito(IncognitoMode mode,
126 bool is_enabled_in_incognito) {
127 std::string manifest = kDeclarativeContentManifest;
128 if (mode == SPLIT) {
129 base::ReplaceSubstringsAfterOffset(
130 &manifest, 0, "\"incognito\": \"spanning\"",
131 "\"incognito\": \"split\"");
132 ASSERT_NE(kDeclarativeContentManifest, manifest);
134 ext_dir_.WriteManifest(manifest);
135 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"),
136 kIncognitoSpecificBackground);
138 ExtensionTestMessageListener ready("ready", false);
139 ExtensionTestMessageListener ready_incognito("ready (split)", false);
141 const Extension* extension = is_enabled_in_incognito ?
142 LoadExtensionIncognito(ext_dir_.unpacked_path()) :
143 LoadExtension(ext_dir_.unpacked_path());
144 ASSERT_TRUE(extension);
146 Browser* incognito_browser = CreateIncognitoBrowser();
147 const ExtensionAction* incognito_page_action =
148 ExtensionActionManager::Get(incognito_browser->profile())->
149 GetPageAction(*extension);
150 ASSERT_TRUE(incognito_page_action);
152 ASSERT_TRUE(ready.WaitUntilSatisfied());
153 if (is_enabled_in_incognito && mode == SPLIT)
154 ASSERT_TRUE(ready_incognito.WaitUntilSatisfied());
156 content::WebContents* const incognito_tab =
157 incognito_browser->tab_strip_model()->GetWebContentsAt(0);
158 const int incognito_tab_id = ExtensionTabUtil::GetTabId(incognito_tab);
160 EXPECT_FALSE(incognito_page_action->GetIsVisible(incognito_tab_id));
162 NavigateInRenderer(incognito_tab, GURL("http://test_split/"));
163 if (mode == SPLIT) {
164 EXPECT_EQ(is_enabled_in_incognito,
165 incognito_page_action->GetIsVisible(incognito_tab_id));
166 } else {
167 EXPECT_FALSE(incognito_page_action->GetIsVisible(incognito_tab_id));
170 NavigateInRenderer(incognito_tab, GURL("http://test_normal/"));
171 if (mode != SPLIT) {
172 EXPECT_EQ(is_enabled_in_incognito,
173 incognito_page_action->GetIsVisible(incognito_tab_id));
174 } else {
175 EXPECT_FALSE(incognito_page_action->GetIsVisible(incognito_tab_id));
178 // Verify that everything works as expected in the non-incognito browser.
179 const ExtensionAction* page_action =
180 ExtensionActionManager::Get(browser()->profile())->
181 GetPageAction(*extension);
182 content::WebContents* const tab =
183 browser()->tab_strip_model()->GetWebContentsAt(0);
184 const int tab_id = ExtensionTabUtil::GetTabId(tab);
186 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
188 NavigateInRenderer(tab, GURL("http://test_normal/"));
189 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
191 NavigateInRenderer(tab, GURL("http://test_split/"));
192 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
195 void DeclarativeContentApiTest::CheckBookmarkEvents(bool match_is_bookmarked) {
196 ext_dir_.WriteManifest(kDeclarativeContentManifest);
197 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
199 content::WebContents* const tab =
200 browser()->tab_strip_model()->GetWebContentsAt(0);
201 const int tab_id = ExtensionTabUtil::GetTabId(tab);
203 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
204 ASSERT_TRUE(extension);
205 const ExtensionAction* page_action = ExtensionActionManager::Get(
206 browser()->profile())->GetPageAction(*extension);
207 ASSERT_TRUE(page_action);
209 NavigateInRenderer(tab, GURL("http://test1/"));
210 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
212 static const char kSetIsBookmarkedRule[] =
213 "setRules([{\n"
214 " conditions: [new PageStateMatcher({isBookmarked: %s})],\n"
215 " actions: [new ShowPageAction()]\n"
216 "}], 'test_rule');\n";
218 EXPECT_EQ("test_rule", ExecuteScriptInBackgroundPage(
219 extension->id(),
220 base::StringPrintf(kSetIsBookmarkedRule,
221 match_is_bookmarked ? "true" : "false")));
222 EXPECT_EQ(!match_is_bookmarked, page_action->GetIsVisible(tab_id));
224 // Check rule evaluation on add/remove bookmark.
225 bookmarks::BookmarkModel* bookmark_model =
226 BookmarkModelFactory::GetForProfile(browser()->profile());
227 const bookmarks::BookmarkNode* node =
228 bookmark_model->AddURL(bookmark_model->other_node(), 0,
229 base::ASCIIToUTF16("title"),
230 GURL("http://test1/"));
231 EXPECT_EQ(match_is_bookmarked, page_action->GetIsVisible(tab_id));
233 bookmark_model->Remove(node);
234 EXPECT_EQ(!match_is_bookmarked, page_action->GetIsVisible(tab_id));
236 // Check rule evaluation on navigate to bookmarked and non-bookmarked URL.
237 bookmark_model->AddURL(bookmark_model->other_node(), 0,
238 base::ASCIIToUTF16("title"),
239 GURL("http://test2/"));
241 NavigateInRenderer(tab, GURL("http://test2/"));
242 EXPECT_EQ(match_is_bookmarked, page_action->GetIsVisible(tab_id));
244 NavigateInRenderer(tab, GURL("http://test3/"));
245 EXPECT_EQ(!match_is_bookmarked, page_action->GetIsVisible(tab_id));
249 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, Overview) {
250 ext_dir_.WriteManifest(kDeclarativeContentManifest);
251 ext_dir_.WriteFile(
252 FILE_PATH_LITERAL("background.js"),
253 "var declarative = chrome.declarative;\n"
254 "\n"
255 "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
256 "var ShowPageAction = chrome.declarativeContent.ShowPageAction;\n"
257 "\n"
258 "var rule = {\n"
259 " conditions: [new PageStateMatcher({\n"
260 " pageUrl: {hostPrefix: \"test1\"}}),\n"
261 " new PageStateMatcher({\n"
262 " css: [\"input[type='password']\"]})],\n"
263 " actions: [new ShowPageAction()]\n"
264 "}\n"
265 "\n"
266 "var testEvent = chrome.declarativeContent.onPageChanged;\n"
267 "\n"
268 "testEvent.removeRules(undefined, function() {\n"
269 " testEvent.addRules([rule], function() {\n"
270 " chrome.test.sendMessage(\"ready\")\n"
271 " });\n"
272 "});\n");
273 ExtensionTestMessageListener ready("ready", false);
274 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
275 ASSERT_TRUE(extension);
276 const ExtensionAction* page_action =
277 ExtensionActionManager::Get(browser()->profile())->
278 GetPageAction(*extension);
279 ASSERT_TRUE(page_action);
281 ASSERT_TRUE(ready.WaitUntilSatisfied());
282 content::WebContents* const tab =
283 browser()->tab_strip_model()->GetWebContentsAt(0);
284 const int tab_id = ExtensionTabUtil::GetTabId(tab);
286 NavigateInRenderer(tab, GURL("http://test1/"));
288 // The declarative API should show the page action instantly, rather
289 // than waiting for the extension to run.
290 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
292 // Make sure leaving a matching page unshows the page action.
293 NavigateInRenderer(tab, GURL("http://not_checked/"));
294 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
296 // Insert a password field to make sure that's noticed.
297 // Notice that we touch offsetTop to force a synchronous layout.
298 ASSERT_TRUE(content::ExecuteScript(
299 tab, "document.body.innerHTML = '<input type=\"password\">';"
300 "document.body.offsetTop;"));
302 // Give the style match a chance to run and send back the matching-selector
303 // update.
304 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
306 EXPECT_TRUE(page_action->GetIsVisible(tab_id))
307 << "Adding a matching element should show the page action.";
309 // Remove it again to make sure that reverts the action.
310 // Notice that we touch offsetTop to force a synchronous layout.
311 ASSERT_TRUE(content::ExecuteScript(
312 tab, "document.body.innerHTML = 'Hello world';"
313 "document.body.offsetTop;"));
315 // Give the style match a chance to run and send back the matching-selector
316 // update.
317 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
319 EXPECT_FALSE(page_action->GetIsVisible(tab_id))
320 << "Removing the matching element should hide the page action again.";
323 // Tests that the rules are evaluated at the time they are added or removed.
324 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, RulesEvaluatedOnAddRemove) {
325 ext_dir_.WriteManifest(kDeclarativeContentManifest);
326 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
327 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
328 ASSERT_TRUE(extension);
329 const ExtensionAction* page_action =
330 ExtensionActionManager::Get(browser()->profile())->
331 GetPageAction(*extension);
332 ASSERT_TRUE(page_action);
334 content::WebContents* const tab =
335 browser()->tab_strip_model()->GetWebContentsAt(0);
336 const int tab_id = ExtensionTabUtil::GetTabId(tab);
338 NavigateInRenderer(tab, GURL("http://test1/"));
340 const std::string kAddTestRules =
341 "addRules([{\n"
342 " id: '1',\n"
343 " conditions: [new PageStateMatcher({\n"
344 " pageUrl: {hostPrefix: \"test1\"}})],\n"
345 " actions: [new ShowPageAction()]\n"
346 "}, {\n"
347 " id: '2',\n"
348 " conditions: [new PageStateMatcher({\n"
349 " pageUrl: {hostPrefix: \"test2\"}})],\n"
350 " actions: [new ShowPageAction()]\n"
351 "}], 'add_rules');\n";
352 EXPECT_EQ("add_rules",
353 ExecuteScriptInBackgroundPage(extension->id(), kAddTestRules));
355 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
357 const std::string kRemoveTestRule1 = "removeRule('1', 'remove_rule1');\n";
358 EXPECT_EQ("remove_rule1",
359 ExecuteScriptInBackgroundPage(extension->id(), kRemoveTestRule1));
361 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
363 NavigateInRenderer(tab, GURL("http://test2/"));
365 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
367 const std::string kRemoveTestRule2 = "removeRule('2', 'remove_rule2');\n";
368 EXPECT_EQ("remove_rule2",
369 ExecuteScriptInBackgroundPage(extension->id(), kRemoveTestRule2));
371 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
374 // Tests that rules from manifest are added and evaluated properly.
375 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, RulesAddedFromManifest) {
376 const char manifest[] =
377 "{\n"
378 " \"name\": \"Declarative Content apitest\",\n"
379 " \"version\": \"0.1\",\n"
380 " \"manifest_version\": 2,\n"
381 " \"page_action\": {},\n"
382 " \"permissions\": [\n"
383 " \"declarativeContent\"\n"
384 " ],\n"
385 " \"event_rules\": [{\n"
386 " \"event\": \"declarativeContent.onPageChanged\",\n"
387 " \"actions\": [{\n"
388 " \"type\": \"declarativeContent.ShowPageAction\"\n"
389 " }],\n"
390 " \"conditions\": [{\n"
391 " \"type\": \"declarativeContent.PageStateMatcher\",\n"
392 " \"pageUrl\": {\"hostPrefix\": \"test1\"}\n"
393 " }]\n"
394 " }]\n"
395 "}\n";
396 ext_dir_.WriteManifest(manifest);
397 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
398 ASSERT_TRUE(extension);
399 const ExtensionAction* page_action =
400 ExtensionActionManager::Get(browser()->profile())
401 ->GetPageAction(*extension);
402 ASSERT_TRUE(page_action);
404 content::WebContents* const tab =
405 browser()->tab_strip_model()->GetWebContentsAt(0);
406 const int tab_id = ExtensionTabUtil::GetTabId(tab);
408 NavigateInRenderer(tab, GURL("http://blank/"));
409 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
410 NavigateInRenderer(tab, GURL("http://test1/"));
411 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
412 NavigateInRenderer(tab, GURL("http://test2/"));
413 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
416 // Tests that rules are not evaluated in incognito browser windows when the
417 // extension specifies spanning incognito mode but is not enabled for incognito.
418 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
419 DisabledForSpanningIncognito) {
420 CheckIncognito(SPANNING, false);
423 // Tests that rules are evaluated in incognito browser windows when the
424 // extension specifies spanning incognito mode and is enabled for incognito.
425 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, EnabledForSpanningIncognito) {
426 CheckIncognito(SPANNING, true);
429 // Tests that rules are not evaluated in incognito browser windows when the
430 // extension specifies split incognito mode but is not enabled for incognito.
431 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DisabledForSplitIncognito) {
432 CheckIncognito(SPLIT, false);
435 // Tests that rules are evaluated in incognito browser windows when the
436 // extension specifies split incognito mode and is enabled for incognito.
437 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, EnabledForSplitIncognito) {
438 CheckIncognito(SPLIT, true);
441 // Tests that rules are evaluated for an incognito tab that exists at the time
442 // the rules are added.
443 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
444 RulesEvaluatedForExistingIncognitoTab) {
445 Browser* incognito_browser = CreateIncognitoBrowser();
446 content::WebContents* const incognito_tab =
447 incognito_browser->tab_strip_model()->GetWebContentsAt(0);
448 const int incognito_tab_id = ExtensionTabUtil::GetTabId(incognito_tab);
450 NavigateInRenderer(incognito_tab, GURL("http://test_normal/"));
452 ext_dir_.WriteManifest(kDeclarativeContentManifest);
453 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"),
454 kIncognitoSpecificBackground);
455 ExtensionTestMessageListener ready("ready", false);
456 const Extension* extension = LoadExtensionIncognito(ext_dir_.unpacked_path());
457 ASSERT_TRUE(extension);
458 ASSERT_TRUE(ready.WaitUntilSatisfied());
460 const ExtensionAction* incognito_page_action =
461 ExtensionActionManager::Get(incognito_browser->profile())->
462 GetPageAction(*extension);
463 ASSERT_TRUE(incognito_page_action);
465 // The page action should be shown.
466 EXPECT_TRUE(incognito_page_action->GetIsVisible(incognito_tab_id));
469 // Sets up rules matching http://test1/ in a normal and incognito browser.
470 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, PRE_RulesPersistence) {
471 ExtensionTestMessageListener ready("ready", false);
472 ExtensionTestMessageListener ready_split("ready (split)", false);
473 // An on-disk extension is required so that it can be reloaded later in the
474 // RulesPersistence test.
475 const Extension* extension =
476 LoadExtensionIncognito(test_data_dir_.AppendASCII("declarative_content")
477 .AppendASCII("persistence"));
478 ASSERT_TRUE(extension);
479 ASSERT_TRUE(ready.WaitUntilSatisfied());
481 CreateIncognitoBrowser();
482 ASSERT_TRUE(ready_split.WaitUntilSatisfied());
485 // Reloads the extension from PRE_RulesPersistence and checks that the rules
486 // continue to work as expected after being persisted and reloaded.
487 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, RulesPersistence) {
488 ExtensionTestMessageListener ready("second run ready", false);
489 ExtensionTestMessageListener ready_split("second run ready (split)", false);
490 ASSERT_TRUE(ready.WaitUntilSatisfied());
492 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
493 const Extension* extension =
494 GetExtensionByPath(registry->enabled_extensions(),
495 test_data_dir_.AppendASCII("declarative_content")
496 .AppendASCII("persistence"));
498 // Check non-incognito browser.
499 content::WebContents* const tab =
500 browser()->tab_strip_model()->GetWebContentsAt(0);
501 const int tab_id = ExtensionTabUtil::GetTabId(tab);
503 const ExtensionAction* page_action =
504 ExtensionActionManager::Get(browser()->profile())->
505 GetPageAction(*extension);
506 ASSERT_TRUE(page_action);
507 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
509 NavigateInRenderer(tab, GURL("http://test_normal/"));
510 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
512 NavigateInRenderer(tab, GURL("http://test_split/"));
513 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
515 // Check incognito browser.
516 Browser* incognito_browser = CreateIncognitoBrowser();
517 ASSERT_TRUE(ready_split.WaitUntilSatisfied());
518 content::WebContents* const incognito_tab =
519 incognito_browser->tab_strip_model()->GetWebContentsAt(0);
520 const int incognito_tab_id = ExtensionTabUtil::GetTabId(incognito_tab);
522 const ExtensionAction* incognito_page_action =
523 ExtensionActionManager::Get(incognito_browser->profile())->
524 GetPageAction(*extension);
525 ASSERT_TRUE(incognito_page_action);
527 NavigateInRenderer(incognito_tab, GURL("http://test_split/"));
528 EXPECT_TRUE(incognito_page_action->GetIsVisible(incognito_tab_id));
530 NavigateInRenderer(incognito_tab, GURL("http://test_normal/"));
531 EXPECT_FALSE(incognito_page_action->GetIsVisible(incognito_tab_id));
534 // http://crbug.com/304373
535 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
536 UninstallWhileActivePageAction) {
537 ext_dir_.WriteManifest(kDeclarativeContentManifest);
538 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
539 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
540 ASSERT_TRUE(extension);
541 const std::string extension_id = extension->id();
542 const ExtensionAction* page_action = ExtensionActionManager::Get(
543 browser()->profile())->GetPageAction(*extension);
544 ASSERT_TRUE(page_action);
546 const std::string kTestRule =
547 "setRules([{\n"
548 " conditions: [new PageStateMatcher({\n"
549 " pageUrl: {hostPrefix: \"test\"}})],\n"
550 " actions: [new ShowPageAction()]\n"
551 "}], 'test_rule');\n";
552 EXPECT_EQ("test_rule",
553 ExecuteScriptInBackgroundPage(extension_id, kTestRule));
555 content::WebContents* const tab =
556 browser()->tab_strip_model()->GetWebContentsAt(0);
557 const int tab_id = ExtensionTabUtil::GetTabId(tab);
559 NavigateInRenderer(tab, GURL("http://test/"));
561 EXPECT_TRUE(page_action->GetIsVisible(tab_id));
562 EXPECT_TRUE(WaitForPageActionVisibilityChangeTo(1));
563 EXPECT_EQ(1u, extension_action_test_util::GetVisiblePageActionCount(tab));
564 EXPECT_EQ(1u, extension_action_test_util::GetTotalPageActionCount(tab));
566 ReloadExtension(extension_id); // Invalidates page_action and extension.
567 EXPECT_EQ("test_rule",
568 ExecuteScriptInBackgroundPage(extension_id, kTestRule));
569 // TODO(jyasskin): Apply new rules to existing tabs, without waiting for a
570 // navigation.
571 NavigateInRenderer(tab, GURL("http://test/"));
572 EXPECT_TRUE(WaitForPageActionVisibilityChangeTo(1));
573 EXPECT_EQ(1u, extension_action_test_util::GetVisiblePageActionCount(tab));
574 EXPECT_EQ(1u, extension_action_test_util::GetTotalPageActionCount(tab));
576 UnloadExtension(extension_id);
577 NavigateInRenderer(tab, GURL("http://test/"));
578 EXPECT_TRUE(WaitForPageActionVisibilityChangeTo(0));
579 EXPECT_EQ(0u, extension_action_test_util::GetVisiblePageActionCount(tab));
580 EXPECT_EQ(0u, extension_action_test_util::GetTotalPageActionCount(tab));
583 // This tests against a renderer crash that was present during development.
584 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
585 DISABLED_AddExtensionMatchingExistingTabWithDeadFrames) {
586 ext_dir_.WriteManifest(kDeclarativeContentManifest);
587 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
588 content::WebContents* const tab =
589 browser()->tab_strip_model()->GetWebContentsAt(0);
590 const int tab_id = ExtensionTabUtil::GetTabId(tab);
592 ASSERT_TRUE(content::ExecuteScript(
593 tab, "document.body.innerHTML = '<iframe src=\"http://test2\">';"));
594 // Replace the iframe to destroy its WebFrame.
595 ASSERT_TRUE(content::ExecuteScript(
596 tab, "document.body.innerHTML = '<span class=\"foo\">';"));
598 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
599 ASSERT_TRUE(extension);
600 const ExtensionAction* page_action = ExtensionActionManager::Get(
601 browser()->profile())->GetPageAction(*extension);
602 ASSERT_TRUE(page_action);
603 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
605 EXPECT_EQ("rule0",
606 ExecuteScriptInBackgroundPage(
607 extension->id(),
608 "setRules([{\n"
609 " conditions: [new PageStateMatcher({\n"
610 " css: [\"span[class=foo]\"]})],\n"
611 " actions: [new ShowPageAction()]\n"
612 "}], 'rule0');\n"));
613 // Give the renderer a chance to apply the rules change and notify the
614 // browser. This takes one time through the Blink message loop to receive
615 // the rule change and apply the new stylesheet, and a second to dedupe the
616 // update.
617 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
618 ASSERT_TRUE(content::ExecuteScript(tab, std::string()));
620 EXPECT_FALSE(tab->IsCrashed());
621 EXPECT_TRUE(page_action->GetIsVisible(tab_id))
622 << "Loading an extension when an open page matches its rules "
623 << "should show the page action.";
625 EXPECT_EQ("removed",
626 ExecuteScriptInBackgroundPage(
627 extension->id(),
628 "onPageChanged.removeRules(undefined, function() {\n"
629 " window.domAutomationController.send('removed');\n"
630 "});\n"));
631 EXPECT_FALSE(page_action->GetIsVisible(tab_id));
634 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
635 ShowPageActionWithoutPageAction) {
636 std::string manifest_without_page_action = kDeclarativeContentManifest;
637 base::ReplaceSubstringsAfterOffset(
638 &manifest_without_page_action, 0, "\"page_action\": {},", "");
639 ASSERT_NE(kDeclarativeContentManifest, manifest_without_page_action);
640 ext_dir_.WriteManifest(manifest_without_page_action);
641 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
642 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
643 ASSERT_TRUE(extension);
645 EXPECT_THAT(ExecuteScriptInBackgroundPage(
646 extension->id(),
647 "setRules([{\n"
648 " conditions: [new PageStateMatcher({\n"
649 " pageUrl: {hostPrefix: \"test\"}})],\n"
650 " actions: [new ShowPageAction()]\n"
651 "}], 'test_rule');\n"),
652 testing::HasSubstr("without a page action"));
654 content::WebContents* const tab =
655 browser()->tab_strip_model()->GetWebContentsAt(0);
656 NavigateInRenderer(tab, GURL("http://test/"));
658 EXPECT_EQ(NULL,
659 ExtensionActionManager::Get(browser()->profile())->
660 GetPageAction(*extension));
661 EXPECT_EQ(0u, extension_action_test_util::GetVisiblePageActionCount(tab));
664 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
665 CanonicalizesPageStateMatcherCss) {
666 ext_dir_.WriteManifest(kDeclarativeContentManifest);
667 ext_dir_.WriteFile(
668 FILE_PATH_LITERAL("background.js"),
669 "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
670 "function Return(obj) {\n"
671 " window.domAutomationController.send('' + obj);\n"
672 "}\n");
673 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
674 ASSERT_TRUE(extension);
676 EXPECT_EQ("input[type=\"password\"]",
677 ExecuteScriptInBackgroundPage(
678 extension->id(),
679 "var psm = new PageStateMatcher(\n"
680 " {css: [\"input[type='password']\"]});\n"
681 "Return(psm.css);"));
683 EXPECT_THAT(ExecuteScriptInBackgroundPage(
684 extension->id(),
685 "try {\n"
686 " new PageStateMatcher({css: 'Not-an-array'});\n"
687 " Return('Failed to throw');\n"
688 "} catch (e) {\n"
689 " Return(e.message);\n"
690 "}\n"),
691 testing::ContainsRegex("css.*Expected 'array'"));
692 EXPECT_THAT(ExecuteScriptInBackgroundPage(
693 extension->id(),
694 "try {\n"
695 " new PageStateMatcher({css: [null]});\n" // Not a string.
696 " Return('Failed to throw');\n"
697 "} catch (e) {\n"
698 " Return(e.message);\n"
699 "}\n"),
700 testing::ContainsRegex("css\\.0.*Expected 'string'"));
701 EXPECT_THAT(ExecuteScriptInBackgroundPage(
702 extension->id(),
703 "try {\n"
704 // Invalid CSS:
705 " new PageStateMatcher({css: [\"input''\"]});\n"
706 " Return('Failed to throw');\n"
707 "} catch (e) {\n"
708 " Return(e.message);\n"
709 "}\n"),
710 testing::ContainsRegex("valid.*: input''$"));
711 EXPECT_THAT(ExecuteScriptInBackgroundPage(
712 extension->id(),
713 "try {\n"
714 // "Complex" selector:
715 " new PageStateMatcher({css: ['div input']});\n"
716 " Return('Failed to throw');\n"
717 "} catch (e) {\n"
718 " Return(e.message);\n"
719 "}\n"),
720 testing::ContainsRegex("compound selector.*: div input$"));
723 // Tests that the rules with isBookmarked: true are evaluated when handling
724 // bookmarking events.
725 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
726 IsBookmarkedRulesEvaluatedOnBookmarkEvents) {
727 CheckBookmarkEvents(true);
730 // Tests that the rules with isBookmarked: false are evaluated when handling
731 // bookmarking events.
732 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
733 NotBookmarkedRulesEvaluatedOnBookmarkEvents) {
734 CheckBookmarkEvents(false);
737 // https://crbug.com/497586
738 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
739 WebContentsWithoutTabAddedNotificationAtOnLoaded) {
740 // Add a web contents to the tab strip in a way that doesn't trigger
741 // NOTIFICATION_TAB_ADDED.
742 content::WebContents* contents = content::WebContents::Create(
743 content::WebContents::CreateParams(profile()));
744 browser()->tab_strip_model()->AppendWebContents(contents, false);
746 // The actual extension contents don't matter here -- we're just looking to
747 // trigger OnExtensionLoaded.
748 ext_dir_.WriteManifest(kDeclarativeContentManifest);
749 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
750 ASSERT_TRUE(LoadExtension(ext_dir_.unpacked_path()));
753 // https://crbug.com/501225
754 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
755 PendingWebContentsClearedOnRemoveRules) {
756 ext_dir_.WriteManifest(kDeclarativeContentManifest);
757 ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers);
758 const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
759 ASSERT_TRUE(extension);
760 const ExtensionAction* page_action = ExtensionActionManager::Get(
761 browser()->profile())->GetPageAction(*extension);
762 ASSERT_TRUE(page_action);
764 // Create two tabs.
765 content::WebContents* const tab1 =
766 browser()->tab_strip_model()->GetWebContentsAt(0);
768 AddTabAtIndex(1, GURL("http://test2/"), ui::PAGE_TRANSITION_LINK);
769 scoped_ptr<content::WebContents> tab2(
770 browser()->tab_strip_model()->GetWebContentsAt(1));
772 // Add a rule matching the second tab.
773 const std::string kAddTestRules =
774 "addRules([{\n"
775 " id: '1',\n"
776 " conditions: [new PageStateMatcher({\n"
777 " pageUrl: {hostPrefix: \"test1\"}})],\n"
778 " actions: [new ShowPageAction()]\n"
779 "}, {\n"
780 " id: '2',\n"
781 " conditions: [new PageStateMatcher({\n"
782 " pageUrl: {hostPrefix: \"test2\"}})],\n"
783 " actions: [new ShowPageAction()]\n"
784 "}], 'add_rules');\n";
785 EXPECT_EQ("add_rules",
786 ExecuteScriptInBackgroundPage(extension->id(), kAddTestRules));
787 EXPECT_TRUE(page_action->GetIsVisible(
788 ExtensionTabUtil::GetTabId(tab2.get())));
790 // Remove the rule.
791 const std::string kRemoveTestRule1 = "removeRule('2', 'remove_rule1');\n";
792 EXPECT_EQ("remove_rule1",
793 ExecuteScriptInBackgroundPage(extension->id(), kRemoveTestRule1));
795 // Remove the second tab, then trigger a rule evaluation for the remaining
796 // tab.
797 tab2.reset();
798 NavigateInRenderer(tab1, GURL("http://test1/"));
799 EXPECT_TRUE(page_action->GetIsVisible(ExtensionTabUtil::GetTabId(tab1)));
803 // TODO(wittman): Once ChromeContentRulesRegistry operates on condition and
804 // action interfaces, add a test that checks that a navigation always evaluates
805 // consistent URL state for all conditions. i.e.: if condition1 evaluates to
806 // false on url0 and true on url1, and condition2 evaluates to true on url0 and
807 // false on url1, navigate from url0 to url1 and validate that no action is
808 // triggered. Do the same when navigating back to url0. This kind of test is
809 // unfortunately not feasible with the current implementation and the existing
810 // supported conditions and actions.
812 } // namespace
813 } // namespace extensions