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.
5 #include "chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h"
7 #include "base/run_loop.h"
8 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
9 #include "chrome/browser/extensions/browser_action_test_util.h"
10 #include "chrome/browser/extensions/extension_action.h"
11 #include "chrome/browser/extensions/extension_action_manager.h"
12 #include "chrome/browser/extensions/extension_action_test_util.h"
13 #include "chrome/browser/extensions/extension_browsertest.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_toolbar_model.h"
16 #include "chrome/browser/sessions/session_tab_helper.h"
17 #include "chrome/browser/ui/extensions/extension_action_view_controller.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
20 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
21 #include "components/crx_file/id_util.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/test/test_utils.h"
24 #include "extensions/browser/extension_prefs.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/notification_types.h"
27 #include "extensions/common/extension.h"
28 #include "extensions/common/extension_builder.h"
29 #include "extensions/common/value_builder.h"
33 scoped_refptr
<const extensions::Extension
> CreateExtension(
34 const std::string
& name
,
35 bool has_browser_action
) {
36 extensions::DictionaryBuilder manifest
;
37 manifest
.Set("name", name
).
38 Set("description", "an extension").
39 Set("manifest_version", 2).
40 Set("version", "1.0");
41 if (has_browser_action
)
42 manifest
.Set("browser_action", extensions::DictionaryBuilder().Pass());
43 return extensions::ExtensionBuilder().
44 SetManifest(manifest
.Pass()).
45 SetID(crx_file::id_util::GenerateId(name
)).
51 // BrowserActionsBarBrowserTest:
53 BrowserActionsBarBrowserTest::BrowserActionsBarBrowserTest()
54 : toolbar_model_(nullptr) {
57 BrowserActionsBarBrowserTest::~BrowserActionsBarBrowserTest() {
60 void BrowserActionsBarBrowserTest::SetUpCommandLine(
61 base::CommandLine
* command_line
) {
62 ToolbarActionsBar::disable_animations_for_testing_
= true;
63 ExtensionBrowserTest::SetUpCommandLine(command_line
);
66 void BrowserActionsBarBrowserTest::SetUpOnMainThread() {
67 ExtensionBrowserTest::SetUpOnMainThread();
68 browser_actions_bar_
.reset(new BrowserActionTestUtil(browser()));
69 toolbar_model_
= extensions::ExtensionToolbarModel::Get(profile());
72 void BrowserActionsBarBrowserTest::TearDownOnMainThread() {
73 ToolbarActionsBar::disable_animations_for_testing_
= false;
74 ExtensionBrowserTest::TearDownOnMainThread();
77 void BrowserActionsBarBrowserTest::LoadExtensions() {
78 // Create three extensions with browser actions.
79 extension_a_
= CreateExtension("alpha", true);
80 extension_b_
= CreateExtension("beta", true);
81 extension_c_
= CreateExtension("gamma", true);
83 const extensions::Extension
* extensions
[] =
84 { extension_a(), extension_b(), extension_c() };
85 extensions::ExtensionRegistry
* registry
=
86 extensions::ExtensionRegistry::Get(profile());
87 // Add each, and verify that it is both correctly added to the extension
88 // registry and to the browser actions container.
89 for (size_t i
= 0; i
< arraysize(extensions
); ++i
) {
90 extension_service()->AddExtension(extensions
[i
]);
91 EXPECT_TRUE(registry
->enabled_extensions().GetByID(extensions
[i
]->id())) <<
92 extensions
[i
]->name();
93 EXPECT_EQ(static_cast<int>(i
+ 1),
94 browser_actions_bar_
->NumberOfBrowserActions());
95 EXPECT_TRUE(browser_actions_bar_
->HasIcon(i
));
96 EXPECT_EQ(static_cast<int>(i
+ 1),
97 browser_actions_bar()->VisibleBrowserActions());
101 // BrowserActionsBarRedesignBrowserTest:
103 BrowserActionsBarRedesignBrowserTest::BrowserActionsBarRedesignBrowserTest() {
106 BrowserActionsBarRedesignBrowserTest::~BrowserActionsBarRedesignBrowserTest() {
109 void BrowserActionsBarRedesignBrowserTest::SetUpCommandLine(
110 base::CommandLine
* command_line
) {
111 BrowserActionsBarBrowserTest::SetUpCommandLine(command_line
);
112 enable_redesign_
.reset(new extensions::FeatureSwitch::ScopedOverride(
113 extensions::FeatureSwitch::extension_action_redesign(),
117 // Test the basic functionality.
118 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, Basic
) {
119 // Load an extension with no browser action.
120 extension_service()->AddExtension(CreateExtension("alpha", false).get());
121 // This extension should not be in the model (has no browser action).
122 EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
124 // Load an extension with a browser action.
125 extension_service()->AddExtension(CreateExtension("beta", true).get());
126 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
127 EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
129 // Unload the extension.
130 std::string id
= browser_actions_bar()->GetExtensionId(0);
132 EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
135 // Test moving various browser actions. This is not to check the logic of the
136 // move (that's in the toolbar model tests), but just to check our ui.
137 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, MoveBrowserActions
) {
140 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
141 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
143 // Order is now A B C.
144 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
145 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
146 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
148 // Move C to first position. Order is C A B.
149 toolbar_model()->MoveExtensionIcon(extension_c()->id(), 0);
150 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(0));
151 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(1));
152 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(2));
154 // Move B to third position. Order is still C A B.
155 toolbar_model()->MoveExtensionIcon(extension_b()->id(), 2);
156 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(0));
157 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(1));
158 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(2));
160 // Move B to middle position. Order is C B A.
161 toolbar_model()->MoveExtensionIcon(extension_b()->id(), 1);
162 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(0));
163 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
164 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(2));
167 // Test that explicitly hiding an extension action results in it disappearing
168 // from the browser actions bar.
169 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, ForceHide
) {
172 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
173 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
174 // Force hide one of the extensions' browser action.
175 extensions::ExtensionActionAPI::SetBrowserActionVisibility(
176 extensions::ExtensionPrefs::Get(browser()->profile()),
179 // The browser action for Extension A should be removed.
180 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
181 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(0));
184 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, Visibility
) {
187 // Change container to show only one action, rest in overflow: A, [B, C].
188 toolbar_model()->SetVisibleIconCount(1);
189 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
191 // Disable extension A (should disappear). State becomes: B [C].
192 DisableExtension(extension_a()->id());
193 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
194 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
195 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(0));
197 // Enable A again. A should get its spot in the same location and the bar
198 // should not grow (chevron is showing). For details: http://crbug.com/35349.
199 // State becomes: A, [B, C].
200 EnableExtension(extension_a()->id());
201 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
202 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
203 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
205 // Disable C (in overflow). State becomes: A, [B].
206 DisableExtension(extension_c()->id());
207 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
208 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
209 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
211 // Enable C again. State becomes: A, [B, C].
212 EnableExtension(extension_c()->id());
213 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
214 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
215 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
217 // Now we have 3 extensions. Make sure they are all visible. State: A, B, C.
218 toolbar_model()->SetVisibleIconCount(3);
219 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
221 // Disable extension A (should disappear). State becomes: B, C.
222 DisableExtension(extension_a()->id());
223 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
224 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
225 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(0));
227 // Disable extension B (should disappear). State becomes: C.
228 DisableExtension(extension_b()->id());
229 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
230 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
231 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(0));
233 // Enable B. State becomes: B, C.
234 EnableExtension(extension_b()->id());
235 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
236 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
237 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(0));
239 // Enable A. State becomes: A, B, C.
240 EnableExtension(extension_a()->id());
241 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
242 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
243 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
245 // Shrink the browser actions bar to zero visible icons.
246 // No icons should be visible, but we *should* show the chevron and have a
248 toolbar_model()->SetVisibleIconCount(0);
249 EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions());
250 EXPECT_TRUE(browser_actions_bar()->IsChevronShowing());
252 // Reset visibility count to 2. State should be A, B, [C], and the chevron
253 // should be visible.
254 toolbar_model()->SetVisibleIconCount(2);
255 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
256 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
257 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
258 EXPECT_TRUE(browser_actions_bar()->IsChevronShowing());
260 // Disable C (the overflowed extension). State should now be A, B, and the
261 // chevron should be hidden.
262 DisableExtension(extension_c()->id());
263 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
264 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
265 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
266 EXPECT_FALSE(browser_actions_bar()->IsChevronShowing());
268 // Re-enable C. We should still only have 2 visible icons, and the chevron
269 // should be visible.
270 EnableExtension(extension_c()->id());
271 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
272 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
273 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
274 EXPECT_TRUE(browser_actions_bar()->IsChevronShowing());
277 // Test that, with the toolbar action redesign, actions that want to run have
278 // the proper appearance.
279 IN_PROC_BROWSER_TEST_F(BrowserActionsBarRedesignBrowserTest
,
280 TestUiForActionsWantToRun
) {
282 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
284 // Load an extension with a page action.
285 scoped_refptr
<const extensions::Extension
> page_action_extension
=
286 extensions::extension_action_test_util::CreateActionExtension(
287 "page action", extensions::extension_action_test_util::PAGE_ACTION
);
288 extension_service()->AddExtension(page_action_extension
.get());
290 // Verify that the extension was added at the last index.
291 EXPECT_EQ(4, browser_actions_bar()->VisibleBrowserActions());
292 EXPECT_EQ(page_action_extension
->id(),
293 browser_actions_bar()->GetExtensionId(3));
294 EXPECT_FALSE(browser_actions_bar()->ActionButtonWantsToRun(3));
296 // Make the extension want to run on the current page.
297 ExtensionAction
* action
= extensions::ExtensionActionManager::Get(profile())->
298 GetExtensionAction(*page_action_extension
);
300 content::WebContents
* web_contents
=
301 browser()->tab_strip_model()->GetActiveWebContents();
302 int tab_id
= SessionTabHelper::IdForTab(web_contents
);
303 action
->SetIsVisible(tab_id
, true);
304 extensions::ExtensionActionAPI
* extension_action_api
=
305 extensions::ExtensionActionAPI::Get(profile());
306 extension_action_api
->NotifyChange(action
, web_contents
, profile());
307 // Verify that the extension's button has the proper UI.
308 EXPECT_TRUE(browser_actions_bar()->ActionButtonWantsToRun(3));
310 // Make the extension not want to run, and check that the special UI goes
312 action
->SetIsVisible(tab_id
, false);
313 extension_action_api
->NotifyChange(action
, web_contents
, profile());
314 EXPECT_FALSE(browser_actions_bar()->ActionButtonWantsToRun(3));
316 // Reduce the visible icon count so that the extension is hidden.
317 toolbar_model()->SetVisibleIconCount(3);
318 EXPECT_FALSE(browser_actions_bar()->OverflowedActionButtonWantsToRun());
320 // Make the extension want to run, and verify that the overflow button (the
321 // wrench) has the correct UI. Then, make the extension not want to run and
322 // verify it goes away.
323 action
->SetIsVisible(tab_id
, true);
324 extension_action_api
->NotifyChange(action
, web_contents
, profile());
325 EXPECT_TRUE(browser_actions_bar()->OverflowedActionButtonWantsToRun());
326 action
->SetIsVisible(tab_id
, false);
327 extension_action_api
->NotifyChange(action
, web_contents
, profile());
328 EXPECT_FALSE(browser_actions_bar()->OverflowedActionButtonWantsToRun());
330 // Make the extension want to run again, and then move it out of the overflow
331 // menu. This should stop the wrench menu from having the special UI.
332 action
->SetIsVisible(tab_id
, true);
333 extension_action_api
->NotifyChange(action
, web_contents
, profile());
334 EXPECT_TRUE(browser_actions_bar()->OverflowedActionButtonWantsToRun());
335 toolbar_model()->SetVisibleIconCount(4);
336 EXPECT_FALSE(browser_actions_bar()->OverflowedActionButtonWantsToRun());
339 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, BrowserActionPopupTest
) {
340 // Load up two extensions that have browser action popups.
341 base::FilePath data_dir
=
342 test_data_dir_
.AppendASCII("api_test").AppendASCII("browser_action");
343 const extensions::Extension
* first_extension
=
344 LoadExtension(data_dir
.AppendASCII("open_popup"));
345 ASSERT_TRUE(first_extension
);
346 const extensions::Extension
* second_extension
=
347 LoadExtension(data_dir
.AppendASCII("remove_popup"));
348 ASSERT_TRUE(second_extension
);
350 // Verify state: two actions, in the order of [first, second].
351 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
352 EXPECT_EQ(first_extension
->id(), browser_actions_bar()->GetExtensionId(0));
353 EXPECT_EQ(second_extension
->id(), browser_actions_bar()->GetExtensionId(1));
355 // Do a little piping to get at the underlying ExtensionActionViewControllers.
356 ToolbarActionsBar
* toolbar_actions_bar
=
357 browser_actions_bar()->GetToolbarActionsBar();
358 const std::vector
<ToolbarActionViewController
*>& toolbar_actions
=
359 toolbar_actions_bar
->toolbar_actions();
360 ASSERT_EQ(2u, toolbar_actions
.size());
361 EXPECT_EQ(first_extension
->id(), toolbar_actions
[0]->GetId());
362 EXPECT_EQ(second_extension
->id(), toolbar_actions
[1]->GetId());
363 ExtensionActionViewController
* first_controller
=
364 static_cast<ExtensionActionViewController
*>(toolbar_actions
[0]);
365 ExtensionActionViewController
* second_controller
=
366 static_cast<ExtensionActionViewController
*>(toolbar_actions
[1]);
368 // Neither should yet be showing a popup.
369 EXPECT_FALSE(browser_actions_bar()->HasPopup());
370 EXPECT_FALSE(first_controller
->is_showing_popup());
371 EXPECT_FALSE(second_controller
->is_showing_popup());
373 // Click on the first extension's browser action. This should open a popup.
374 browser_actions_bar()->Press(0);
375 EXPECT_TRUE(browser_actions_bar()->HasPopup());
376 EXPECT_TRUE(first_controller
->is_showing_popup());
377 EXPECT_FALSE(second_controller
->is_showing_popup());
380 content::WindowedNotificationObserver
observer(
381 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
382 content::NotificationService::AllSources());
383 // Clicking on the second extension's browser action should open the
384 // second's popup. Since we only allow one extension popup at a time, this
385 // should also close the first popup.
386 browser_actions_bar()->Press(1);
387 // Closing an extension popup isn't always synchronous; wait for a
390 EXPECT_TRUE(browser_actions_bar()->HasPopup());
391 EXPECT_FALSE(first_controller
->is_showing_popup());
392 EXPECT_TRUE(second_controller
->is_showing_popup());
396 // Clicking on the second extension's browser action a second time should
397 // result in closing the popup.
398 content::WindowedNotificationObserver
observer(
399 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
400 content::NotificationService::AllSources());
401 browser_actions_bar()->Press(1);
403 EXPECT_FALSE(browser_actions_bar()->HasPopup());
404 EXPECT_FALSE(first_controller
->is_showing_popup());
405 EXPECT_FALSE(second_controller
->is_showing_popup());