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 "chrome/browser/extensions/api/extension_action/extension_action_api.h"
6 #include "chrome/browser/extensions/extension_action.h"
7 #include "chrome/browser/extensions/extension_action_icon_factory.h"
8 #include "chrome/browser/extensions/extension_action_manager.h"
9 #include "chrome/browser/extensions/extension_action_runner.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_service.h"
13 #include "chrome/browser/extensions/extension_tab_util.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_commands.h"
17 #include "chrome/browser/ui/browser_window.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "components/sessions/content/session_tab_helper.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/test/browser_test_utils.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/test/result_catcher.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h"
27 
28 using content::WebContents;
29 
30 namespace extensions {
31 namespace {
32 
33 class PageActionApiTest : public ExtensionApiTest {
34  protected:
GetPageAction(const Extension & extension)35   ExtensionAction* GetPageAction(const Extension& extension) {
36     ExtensionAction* extension_action =
37         ExtensionActionManager::Get(browser()->profile())
38             ->GetExtensionAction(extension);
39     return extension_action->action_type() == ActionInfo::TYPE_PAGE
40                ? extension_action
41                : nullptr;
42   }
43 };
44 
IN_PROC_BROWSER_TEST_F(PageActionApiTest,Basic)45 IN_PROC_BROWSER_TEST_F(PageActionApiTest, Basic) {
46   ASSERT_TRUE(embedded_test_server()->Start());
47   ASSERT_TRUE(RunExtensionTest("page_action/basics")) << message_;
48   const Extension* extension = GetSingleLoadedExtension();
49   ASSERT_TRUE(extension) << message_;
50   {
51     // Tell the extension to update the page action state.
52     ResultCatcher catcher;
53     ui_test_utils::NavigateToURL(browser(),
54         GURL(extension->GetResourceURL("update.html")));
55     ASSERT_TRUE(catcher.GetNextResult());
56   }
57 
58   // Test that we received the changes.
59   int tab_id = sessions::SessionTabHelper::FromWebContents(
60                    browser()->tab_strip_model()->GetActiveWebContents())
61                    ->session_id()
62                    .id();
63   ExtensionAction* action = GetPageAction(*extension);
64   ASSERT_TRUE(action);
65   EXPECT_EQ("Modified", action->GetTitle(tab_id));
66 
67   {
68     // Simulate the page action being clicked.
69     ResultCatcher catcher;
70     ExtensionActionRunner::GetForWebContents(
71         browser()->tab_strip_model()->GetActiveWebContents())
72         ->RunAction(extension, true);
73     EXPECT_TRUE(catcher.GetNextResult());
74   }
75 
76   {
77     // Tell the extension to update the page action state again.
78     ResultCatcher catcher;
79     ui_test_utils::NavigateToURL(browser(),
80         GURL(extension->GetResourceURL("update2.html")));
81     ASSERT_TRUE(catcher.GetNextResult());
82   }
83 
84   // We should not be creating icons asynchronously, so we don't need an
85   // observer.
86   ExtensionActionIconFactory icon_factory(profile(), extension, action, NULL);
87 
88   // Test that we received the changes.
89   tab_id = sessions::SessionTabHelper::FromWebContents(
90                browser()->tab_strip_model()->GetActiveWebContents())
91                ->session_id()
92                .id();
93   EXPECT_FALSE(icon_factory.GetIcon(tab_id).IsEmpty());
94 }
95 
96 // Test that calling chrome.pageAction.setPopup() can enable a popup.
IN_PROC_BROWSER_TEST_F(PageActionApiTest,AddPopup)97 IN_PROC_BROWSER_TEST_F(PageActionApiTest, AddPopup) {
98   // Load the extension, which has no default popup.
99   ASSERT_TRUE(RunExtensionTest("page_action/add_popup")) << message_;
100   const Extension* extension = GetSingleLoadedExtension();
101   ASSERT_TRUE(extension) << message_;
102 
103   int tab_id = ExtensionTabUtil::GetTabId(
104       browser()->tab_strip_model()->GetActiveWebContents());
105 
106   ExtensionAction* page_action = GetPageAction(*extension);
107   ASSERT_TRUE(page_action)
108       << "Page action test extension should have a page action.";
109 
110   ASSERT_FALSE(page_action->HasPopup(tab_id));
111 
112   // Simulate the page action being clicked.  The resulting event should
113   // install a page action popup.
114   {
115     ResultCatcher catcher;
116     ExtensionActionRunner::GetForWebContents(
117         browser()->tab_strip_model()->GetActiveWebContents())
118         ->RunAction(extension, true);
119     ASSERT_TRUE(catcher.GetNextResult());
120   }
121 
122   ASSERT_TRUE(page_action->HasPopup(tab_id))
123       << "Clicking on the page action should have caused a popup to be added.";
124 
125   ASSERT_STREQ("/a_popup.html",
126                page_action->GetPopupUrl(tab_id).path().c_str());
127 
128   // Now change the popup from a_popup.html to a_second_popup.html .
129   // Load a page which removes the popup using chrome.pageAction.setPopup().
130   {
131     ResultCatcher catcher;
132     ui_test_utils::NavigateToURL(
133         browser(),
134         GURL(extension->GetResourceURL("change_popup.html")));
135     ASSERT_TRUE(catcher.GetNextResult());
136   }
137 
138   ASSERT_TRUE(page_action->HasPopup(tab_id));
139   ASSERT_STREQ("/another_popup.html",
140                page_action->GetPopupUrl(tab_id).path().c_str());
141 }
142 
143 // Test that calling chrome.pageAction.setPopup() can remove a popup.
IN_PROC_BROWSER_TEST_F(PageActionApiTest,RemovePopup)144 IN_PROC_BROWSER_TEST_F(PageActionApiTest, RemovePopup) {
145   // Load the extension, which has a page action with a default popup.
146   ASSERT_TRUE(RunExtensionTest("page_action/remove_popup")) << message_;
147   const Extension* extension = GetSingleLoadedExtension();
148   ASSERT_TRUE(extension) << message_;
149 
150   int tab_id = ExtensionTabUtil::GetTabId(
151       browser()->tab_strip_model()->GetActiveWebContents());
152 
153   ExtensionAction* page_action = GetPageAction(*extension);
154   ASSERT_TRUE(page_action)
155       << "Page action test extension should have a page action.";
156 
157   ASSERT_TRUE(page_action->HasPopup(tab_id))
158       << "Expect a page action popup before the test removes it.";
159 
160   // Load a page which removes the popup using chrome.pageAction.setPopup().
161   {
162     ResultCatcher catcher;
163     ui_test_utils::NavigateToURL(
164         browser(),
165         GURL(extension->GetResourceURL("remove_popup.html")));
166     ASSERT_TRUE(catcher.GetNextResult());
167   }
168 
169   ASSERT_FALSE(page_action->HasPopup(tab_id))
170       << "Page action popup should have been removed.";
171 }
172 
173 // Tests popups in page actions.
174 // Flaky on the trybots. See http://crbug.com/96725.
IN_PROC_BROWSER_TEST_F(PageActionApiTest,DISABLED_ShowPageActionPopup)175 IN_PROC_BROWSER_TEST_F(PageActionApiTest, DISABLED_ShowPageActionPopup) {
176   ASSERT_TRUE(RunExtensionTest("page_action/popup")) << message_;
177   const Extension* extension = GetSingleLoadedExtension();
178   ASSERT_TRUE(extension) << message_;
179 
180   ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
181 
182   {
183     ResultCatcher catcher;
184     ExtensionActionAPI::Get(browser()->profile())->ShowExtensionActionPopup(
185         extension, browser(), true);
186     ASSERT_TRUE(catcher.GetNextResult());
187   }
188 }
189 
190 // Test http://crbug.com/57333: that two page action extensions using the same
191 // icon for the page action icon and the extension icon do not crash.
IN_PROC_BROWSER_TEST_F(PageActionApiTest,TestCrash57333)192 IN_PROC_BROWSER_TEST_F(PageActionApiTest, TestCrash57333) {
193   // Load extension A.
194   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("page_action")
195                                           .AppendASCII("crash_57333")
196                                           .AppendASCII("Extension1")));
197   // Load extension B.
198   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("page_action")
199                                           .AppendASCII("crash_57333")
200                                           .AppendASCII("Extension2")));
201 }
202 
IN_PROC_BROWSER_TEST_F(PageActionApiTest,Getters)203 IN_PROC_BROWSER_TEST_F(PageActionApiTest, Getters) {
204   ASSERT_TRUE(RunExtensionTest("page_action/getters")) << message_;
205   const Extension* extension = GetSingleLoadedExtension();
206   ASSERT_TRUE(extension) << message_;
207 
208   ResultCatcher catcher;
209   ui_test_utils::NavigateToURL(browser(),
210       GURL(extension->GetResourceURL("update.html")));
211   ASSERT_TRUE(catcher.GetNextResult());
212 }
213 
214 // Verify triggering page action.
IN_PROC_BROWSER_TEST_F(PageActionApiTest,TestTriggerPageAction)215 IN_PROC_BROWSER_TEST_F(PageActionApiTest, TestTriggerPageAction) {
216   ASSERT_TRUE(embedded_test_server()->Start());
217 
218   ASSERT_TRUE(RunExtensionTest("trigger_actions/page_action")) << message_;
219   const Extension* extension = GetSingleLoadedExtension();
220   ASSERT_TRUE(extension) << message_;
221 
222   // Page action icon is displayed when a tab is created.
223   ui_test_utils::NavigateToURL(browser(),
224                                embedded_test_server()->GetURL("/simple.html"));
225   chrome::NewTab(browser());
226   browser()->tab_strip_model()->ActivateTabAt(
227       0, {TabStripModel::GestureType::kOther});
228 
229   // Give the extension time to show the page action on the tab.
230   WaitForPageActionVisibilityChangeTo(1);
231 
232   ExtensionAction* page_action = GetPageAction(*extension);
233   ASSERT_TRUE(page_action);
234 
235   WebContents* tab =
236       browser()->tab_strip_model()->GetActiveWebContents();
237   ASSERT_TRUE(tab);
238 
239   EXPECT_TRUE(page_action->GetIsVisible(ExtensionTabUtil::GetTabId(tab)));
240 
241   {
242     // Simulate the page action being clicked.
243     ResultCatcher catcher;
244     ExtensionActionRunner::GetForWebContents(
245         browser()->tab_strip_model()->GetActiveWebContents())
246         ->RunAction(extension, true);
247     EXPECT_TRUE(catcher.GetNextResult());
248   }
249 
250   // Verify that the browser action turned the background color red.
251   const std::string script =
252       "window.domAutomationController.send(document.body.style."
253       "backgroundColor);";
254   std::string result;
255   EXPECT_TRUE(content::ExecuteScriptAndExtractString(tab, script, &result));
256   EXPECT_EQ(result, "red");
257 }
258 
259 }  // namespace
260 }  // namespace extensions
261