1// Copyright 2014 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 "base/memory/scoped_ptr.h"
6#include "base/run_loop.h"
7#include "base/strings/stringprintf.h"
8#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
9#include "chrome/browser/extensions/extension_action.h"
10#include "chrome/browser/extensions/extension_action_manager.h"
11#include "chrome/browser/extensions/extension_browsertest.h"
12#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/browser/extensions/test_extension_dir.h"
14#include "chrome/browser/sessions/session_tab_helper.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_window.h"
17#include "chrome/browser/ui/location_bar/location_bar.h"
18#include "chrome/browser/ui/tabs/tab_strip_model.h"
19#include "extensions/common/extension.h"
20#include "extensions/common/extension_builder.h"
21#include "extensions/common/feature_switch.h"
22#include "extensions/common/value_builder.h"
23#include "extensions/test/extension_test_message_listener.h"
24
25namespace {
26
27const char kBackgroundScriptSource[] =
28    "chrome.pageAction.onClicked.addListener(function() {\n"
29    "  chrome.test.sendMessage('clicked');\n"
30    "});\n"
31    "chrome.test.sendMessage('registered');\n";
32const char kManifestSource[] =
33      "{"
34      " \"name\": \"%s\","
35      " \"version\": \"1.0\","
36      " \"manifest_version\": 2,"
37      " \"background\": { \"scripts\": [\"background.js\"] },"
38      " \"page_action\": { }"
39      "}";
40
41}  // namespace
42
43class LocationBarBrowserTest : public ExtensionBrowserTest {
44 public:
45  LocationBarBrowserTest() {}
46  virtual ~LocationBarBrowserTest() {}
47
48 protected:
49  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE;
50
51  // Load an extension with a PageAction that sends a message when clicked.
52  const extensions::Extension* LoadPageActionExtension(
53      extensions::TestExtensionDir* dir);
54
55 private:
56  scoped_ptr<extensions::FeatureSwitch::ScopedOverride> enable_override_;
57
58  DISALLOW_COPY_AND_ASSIGN(LocationBarBrowserTest);
59};
60
61void LocationBarBrowserTest::SetUpCommandLine(base::CommandLine* command_line) {
62  // In order to let a vanilla extension override the bookmark star, we have to
63  // enable the switch.
64  enable_override_.reset(new extensions::FeatureSwitch::ScopedOverride(
65      extensions::FeatureSwitch::enable_override_bookmarks_ui(), true));
66  ExtensionBrowserTest::SetUpCommandLine(command_line);
67}
68
69const extensions::Extension* LocationBarBrowserTest::LoadPageActionExtension(
70    extensions::TestExtensionDir* dir) {
71  DCHECK(dir);
72
73  dir->WriteManifest(base::StringPrintf(kManifestSource, "page_action1"));
74  dir->WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundScriptSource);
75
76  ExtensionTestMessageListener registered_listener("registered", false);
77  const extensions::Extension* extension = LoadExtension(dir->unpacked_path());
78  registered_listener.WaitUntilSatisfied();
79
80  return extension;
81}
82
83// Test that page actions show up properly in the location bar. Since the
84// page action logic is more fully tested as part of the extensions system, this
85// only needs to check that they are displayed and clicking on them triggers
86// the action.
87// TODO(devlin): This flakily times out on Mac. crbug.com/410866
88#if defined(OS_MACOSX)
89#define MAYBE_PageActionUITest DISABLED_PageActionUITeset
90#else
91#define MAYBE_PageActionUITest PageActionUITest
92#endif
93IN_PROC_BROWSER_TEST_F(LocationBarBrowserTest, MAYBE_PageActionUITest) {
94  LocationBarTesting* location_bar =
95      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
96
97  // At the start, no page actions should exist.
98  EXPECT_EQ(0, location_bar->PageActionCount());
99  EXPECT_EQ(0, location_bar->PageActionVisibleCount());
100
101  // Load two extensions with page actions.
102  extensions::TestExtensionDir test_dir1;
103  const extensions::Extension* page_action1 =
104      LoadPageActionExtension(&test_dir1);
105  ASSERT_TRUE(page_action1);
106
107  extensions::TestExtensionDir test_dir2;
108  const extensions::Extension* page_action2 =
109      LoadPageActionExtension(&test_dir2);
110  ASSERT_TRUE(page_action2);
111
112  // Now there should be two page actions, but neither should be visible.
113  EXPECT_EQ(2, location_bar->PageActionCount());
114  EXPECT_EQ(0, location_bar->PageActionVisibleCount());
115
116  // Make the first page action visible.
117  ExtensionAction* action = extensions::ExtensionActionManager::Get(
118                                profile())->GetPageAction(*page_action1);
119  content::WebContents* tab =
120      browser()->tab_strip_model()->GetActiveWebContents();
121  int tab_id = SessionTabHelper::IdForTab(tab);
122  action->SetIsVisible(tab_id, true);
123  extensions::ExtensionActionAPI::Get(profile())->NotifyChange(
124      action, tab, profile());
125
126  // Verify that only one action is visible and that it's the proper one.
127  EXPECT_EQ(2, location_bar->PageActionCount());
128  EXPECT_EQ(1, location_bar->PageActionVisibleCount());
129  EXPECT_EQ(action, location_bar->GetVisiblePageAction(0u));
130
131  // Trigger the visible page action, and ensure it executes.
132  ExtensionTestMessageListener clicked_listener("clicked", false);
133  clicked_listener.set_extension_id(page_action1->id());
134  location_bar->TestPageActionPressed(0u);
135  EXPECT_TRUE(clicked_listener.WaitUntilSatisfied());
136}
137
138// Test that installing an extension that overrides the bookmark star
139// successfully hides the star.
140IN_PROC_BROWSER_TEST_F(LocationBarBrowserTest,
141                       ExtensionCanOverrideBookmarkStar) {
142  LocationBarTesting* location_bar =
143      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
144  // By default, we should show the star.
145  EXPECT_TRUE(location_bar->GetBookmarkStarVisibility());
146
147  // Create and install an extension that overrides the bookmark star.
148  extensions::DictionaryBuilder chrome_ui_overrides;
149  chrome_ui_overrides.Set(
150      "bookmarks_ui",
151      extensions::DictionaryBuilder().SetBoolean("remove_button", true));
152  scoped_refptr<const extensions::Extension> extension =
153      extensions::ExtensionBuilder().
154          SetManifest(extensions::DictionaryBuilder().
155                          Set("name", "overrides star").
156                          Set("manifest_version", 2).
157                          Set("version", "0.1").
158                          Set("description", "override the star").
159                          Set("chrome_ui_overrides",
160                              chrome_ui_overrides.Pass())).Build();
161  extension_service()->AddExtension(extension.get());
162
163  // The star should now be hidden.
164  EXPECT_FALSE(location_bar->GetBookmarkStarVisibility());
165}
166
167class LocationBarBrowserTestWithRedesign : public LocationBarBrowserTest {
168 public:
169  LocationBarBrowserTestWithRedesign() {}
170  virtual ~LocationBarBrowserTestWithRedesign() {}
171
172 private:
173  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE;
174
175  scoped_ptr<extensions::FeatureSwitch::ScopedOverride> enable_redesign_;
176
177  DISALLOW_COPY_AND_ASSIGN(LocationBarBrowserTestWithRedesign);
178};
179
180void LocationBarBrowserTestWithRedesign::SetUpCommandLine(
181    base::CommandLine* command_line) {
182  LocationBarBrowserTest::SetUpCommandLine(command_line);
183  enable_redesign_.reset(new extensions::FeatureSwitch::ScopedOverride(
184      extensions::FeatureSwitch::extension_action_redesign(), true));
185}
186
187// Test that page actions are not displayed in the location bar if the
188// extension action redesign switch is enabled.
189IN_PROC_BROWSER_TEST_F(LocationBarBrowserTestWithRedesign,
190                       PageActionUITestWithRedesign) {
191  LocationBarTesting* location_bar =
192      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
193  EXPECT_EQ(0, location_bar->PageActionCount());
194  EXPECT_EQ(0, location_bar->PageActionVisibleCount());
195
196  // Load an extension with a page action.
197  extensions::TestExtensionDir test_dir1;
198  const extensions::Extension* page_action1 =
199      LoadPageActionExtension(&test_dir1);
200  ASSERT_TRUE(page_action1);
201
202  // We should still have no page actions.
203  EXPECT_EQ(0, location_bar->PageActionCount());
204  EXPECT_EQ(0, location_bar->PageActionVisibleCount());
205
206  // Set the page action to be visible.
207  ExtensionAction* action = extensions::ExtensionActionManager::Get(
208                                profile())->GetPageAction(*page_action1);
209  content::WebContents* tab =
210      browser()->tab_strip_model()->GetActiveWebContents();
211  int tab_id = SessionTabHelper::IdForTab(tab);
212  action->SetIsVisible(tab_id, true);
213  extensions::ExtensionActionAPI::Get(profile())->NotifyChange(
214      action, tab, profile());
215
216  // We should still have no page actions.
217  EXPECT_EQ(0, location_bar->PageActionCount());
218  EXPECT_EQ(0, location_bar->PageActionVisibleCount());
219}
220