browser_actions_container_browsertest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
1// Copyright 2013 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/ui/views/toolbar/browser_actions_container.h"
6
7#include "chrome/browser/chrome_notification_types.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_browsertest.h"
11#include "chrome/browser/extensions/extension_toolbar_model.h"
12#include "chrome/browser/ui/browser_window.h"
13#include "chrome/browser/ui/browser_window_testing_views.h"
14#include "chrome/browser/ui/views/frame/browser_view.h"
15#include "chrome/browser/ui/views/toolbar/browser_action_view.h"
16#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
17#include "content/public/test/test_utils.h"
18#include "extensions/browser/extension_prefs.h"
19#include "extensions/common/extension.h"
20#include "ui/gfx/geometry/point.h"
21#include "ui/views/view.h"
22
23using extensions::Extension;
24
25class BrowserActionsContainerTest : public ExtensionBrowserTest {
26 public:
27  BrowserActionsContainerTest() {
28  }
29  virtual ~BrowserActionsContainerTest() {}
30
31  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
32    BrowserActionsContainer::disable_animations_during_testing_ = true;
33    ExtensionBrowserTest::SetUpCommandLine(command_line);
34  }
35
36  virtual void SetUpOnMainThread() OVERRIDE {
37    ExtensionBrowserTest::SetUpOnMainThread();
38    browser_actions_bar_.reset(new BrowserActionTestUtil(browser()));
39  }
40
41  virtual void TearDownOnMainThread() OVERRIDE {
42    BrowserActionsContainer::disable_animations_during_testing_ = false;
43  }
44
45  BrowserActionTestUtil* browser_actions_bar() {
46    return browser_actions_bar_.get();
47  }
48
49 private:
50  scoped_ptr<BrowserActionTestUtil> browser_actions_bar_;
51};
52
53// Test the basic functionality.
54// http://crbug.com/120770
55#if defined(OS_WIN)
56IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, DISABLED_Basic) {
57#else
58IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Basic) {
59#endif
60  // Load an extension with no browser action.
61  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
62                                          .AppendASCII("browser_action")
63                                          .AppendASCII("none")));
64  // This extension should not be in the model (has no browser action).
65  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
66
67  // Load an extension with a browser action.
68  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
69                                          .AppendASCII("browser_action")
70                                          .AppendASCII("basics")));
71  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
72  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
73
74
75  // Unload the extension.
76  std::string id = browser_actions_bar()->GetExtensionId(0);
77  UnloadExtension(id);
78  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
79}
80
81// Test moving various browser actions. This is not to check the logic of the
82// move (that's in the toolbar model tests), but just to check our ui.
83IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest,
84                       MoveBrowserActions) {
85  // Load three extensions with browser actions.
86  const extensions::Extension* extension_a =
87      LoadExtension(test_data_dir_.AppendASCII("api_test")
88                                  .AppendASCII("browser_action")
89                                  .AppendASCII("basics"));
90  ASSERT_TRUE(extension_a);
91  const extensions::Extension* extension_b =
92      LoadExtension(test_data_dir_.AppendASCII("api_test")
93                                  .AppendASCII("browser_action")
94                                  .AppendASCII("add_popup"));
95  ASSERT_TRUE(extension_b);
96  const extensions::Extension* extension_c =
97      LoadExtension(test_data_dir_.AppendASCII("api_test")
98                                  .AppendASCII("browser_action")
99                                  .AppendASCII("remove_popup"));
100  ASSERT_TRUE(extension_c);
101
102  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
103  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
104
105  extensions::ExtensionToolbarModel* model =
106      extensions::ExtensionToolbarModel::Get(profile());
107  ASSERT_TRUE(model);
108
109  // Order is now A B C.
110  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0));
111  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1));
112  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2));
113
114  // Move C to first position. Order is C A B.
115  model->MoveExtensionIcon(extension_c, 0);
116  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0));
117  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1));
118  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2));
119
120  // Move B to third position. Order is still C A B.
121  model->MoveExtensionIcon(extension_b, 2);
122  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0));
123  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1));
124  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2));
125
126  // Move B to middle position. Order is C B A.
127  model->MoveExtensionIcon(extension_b, 1);
128  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0));
129  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1));
130  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(2));
131}
132
133IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Visibility) {
134  // Load extension A (contains browser action).
135  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
136                                          .AppendASCII("browser_action")
137                                          .AppendASCII("basics")));
138  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
139  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
140  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
141  std::string idA = browser_actions_bar()->GetExtensionId(0);
142
143  // Load extension B (contains browser action).
144  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
145                                          .AppendASCII("browser_action")
146                                          .AppendASCII("add_popup")));
147  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
148  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
149  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
150  std::string idB = browser_actions_bar()->GetExtensionId(1);
151
152  EXPECT_NE(idA, idB);
153
154  // Load extension C (contains browser action).
155  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
156                                          .AppendASCII("browser_action")
157                                          .AppendASCII("remove_popup")));
158  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
159  EXPECT_TRUE(browser_actions_bar()->HasIcon(2));
160  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
161  std::string idC = browser_actions_bar()->GetExtensionId(2);
162
163  // Change container to show only one action, rest in overflow: A, [B, C].
164  browser_actions_bar()->SetIconVisibilityCount(1);
165  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
166
167  // Disable extension A (should disappear). State becomes: B [C].
168  DisableExtension(idA);
169  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
170  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
171  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
172
173  // Enable A again. A should get its spot in the same location and the bar
174  // should not grow (chevron is showing). For details: http://crbug.com/35349.
175  // State becomes: A, [B, C].
176  EnableExtension(idA);
177  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
178  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
179  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
180
181  // Disable C (in overflow). State becomes: A, [B].
182  DisableExtension(idC);
183  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
184  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
185  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
186
187  // Enable C again. State becomes: A, [B, C].
188  EnableExtension(idC);
189  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
190  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
191  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
192
193  // Now we have 3 extensions. Make sure they are all visible. State: A, B, C.
194  browser_actions_bar()->SetIconVisibilityCount(3);
195  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
196
197  // Disable extension A (should disappear). State becomes: B, C.
198  DisableExtension(idA);
199  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
200  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
201  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
202
203  // Disable extension B (should disappear). State becomes: C.
204  DisableExtension(idB);
205  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
206  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
207  EXPECT_EQ(idC, browser_actions_bar()->GetExtensionId(0));
208
209  // Enable B. State becomes: B, C.
210  EnableExtension(idB);
211  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
212  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
213  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
214
215  // Enable A. State becomes: A, B, C.
216  EnableExtension(idA);
217  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
218  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
219  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
220}
221
222IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, ForceHide) {
223  // Load extension A (contains browser action).
224  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
225                                          .AppendASCII("browser_action")
226                                          .AppendASCII("basics")));
227  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
228  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
229  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
230  std::string idA = browser_actions_bar()->GetExtensionId(0);
231
232  // Force hide this browser action.
233  extensions::ExtensionActionAPI::SetBrowserActionVisibility(
234      extensions::ExtensionPrefs::Get(browser()->profile()), idA, false);
235  EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions());
236}
237
238// Test that the BrowserActionsContainer responds correctly when the underlying
239// model enters highlight mode, and that browser actions are undraggable in
240// highlight mode. (Highlight mode itself it tested more thoroughly in the
241// ExtensionToolbarModel browsertests).
242IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, HighlightMode) {
243  // Load three extensions with browser actions.
244  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
245                                          .AppendASCII("browser_action")
246                                          .AppendASCII("basics")));
247  std::string id_a = browser_actions_bar()->GetExtensionId(0);
248  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
249                                          .AppendASCII("browser_action")
250                                          .AppendASCII("add_popup")));
251  std::string id_b = browser_actions_bar()->GetExtensionId(1);
252  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
253                                          .AppendASCII("browser_action")
254                                          .AppendASCII("remove_popup")));
255
256  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
257  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
258
259  BrowserActionsContainer* container = browser()
260                                           ->window()
261                                           ->GetBrowserWindowTesting()
262                                           ->GetToolbarView()
263                                           ->browser_actions();
264
265  // Currently, dragging should be enabled.
266  BrowserActionView* action_view = container->GetBrowserActionViewAt(0);
267  ASSERT_TRUE(action_view);
268  gfx::Point point(action_view->x(), action_view->y());
269  EXPECT_TRUE(container->CanStartDragForView(action_view, point, point));
270
271  extensions::ExtensionToolbarModel* model =
272      extensions::ExtensionToolbarModel::Get(profile());
273
274  extensions::ExtensionIdList extension_ids;
275  extension_ids.push_back(id_a);
276  extension_ids.push_back(id_b);
277  model->HighlightExtensions(extension_ids);
278
279  // Only two browser actions should be visible.
280  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
281  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
282
283  // We shouldn't be able to drag in highlight mode.
284  action_view = container->GetBrowserActionViewAt(0);
285  EXPECT_FALSE(container->CanStartDragForView(action_view, point, point));
286
287  // We should go back to normal after leaving highlight mode.
288  model->StopHighlighting();
289  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
290  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
291  action_view = container->GetBrowserActionViewAt(0);
292  EXPECT_TRUE(container->CanStartDragForView(action_view, point, point));
293}
294
295// Test the behavior of the overflow container for Extension Actions.
296class BrowserActionsContainerOverflowTest : public BrowserActionsContainerTest {
297 public:
298  BrowserActionsContainerOverflowTest() : main_bar_(NULL), model_(NULL) {
299  }
300  virtual ~BrowserActionsContainerOverflowTest() {
301  }
302
303 protected:
304  // Returns true if the order of the BrowserActionViews in |main_bar_|
305  // and |overflow_bar_| match.
306  bool ViewOrdersMatch();
307
308  // Returns Success if the visible count matches |expected_visible|. This means
309  // that the number of visible browser actions in |main_bar_| is
310  // |expected_visible| and shows the first icons, and that the overflow bar
311  // shows all (and only) the remainder.
312  testing::AssertionResult VerifyVisibleCount(size_t expected_visible);
313
314  // Accessors.
315  BrowserActionsContainer* main_bar() { return main_bar_; }
316  BrowserActionsContainer* overflow_bar() { return overflow_bar_.get(); }
317  extensions::ExtensionToolbarModel* model() { return model_; }
318
319 private:
320  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE;
321  virtual void SetUpOnMainThread() OVERRIDE;
322  virtual void TearDownOnMainThread() OVERRIDE;
323
324  // The main BrowserActionsContainer (owned by the browser view).
325  BrowserActionsContainer* main_bar_;
326
327  // The overflow BrowserActionsContainer. We manufacture this so that we don't
328  // have to open the wrench menu.
329  scoped_ptr<BrowserActionsContainer> overflow_bar_;
330
331  // The associated toolbar model.
332  extensions::ExtensionToolbarModel* model_;
333
334  // Enable the feature redesign switch.
335  scoped_ptr<extensions::FeatureSwitch::ScopedOverride> enable_redesign_;
336
337  DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerOverflowTest);
338};
339
340void BrowserActionsContainerOverflowTest::SetUpCommandLine(
341    base::CommandLine* command_line) {
342  BrowserActionsContainerTest::SetUpCommandLine(command_line);
343  enable_redesign_.reset(new extensions::FeatureSwitch::ScopedOverride(
344      extensions::FeatureSwitch::extension_action_redesign(),
345      true));
346}
347
348void BrowserActionsContainerOverflowTest::SetUpOnMainThread() {
349  BrowserActionsContainerTest::SetUpOnMainThread();
350  main_bar_ = BrowserView::GetBrowserViewForBrowser(browser())
351                  ->toolbar()->browser_actions();
352  overflow_bar_.reset(new BrowserActionsContainer(browser(), NULL, main_bar_));
353  overflow_bar_->set_owned_by_client();
354  model_ = extensions::ExtensionToolbarModel::Get(profile());
355}
356
357void BrowserActionsContainerOverflowTest::TearDownOnMainThread() {
358  overflow_bar_.reset();
359  enable_redesign_.reset();
360  BrowserActionsContainerTest::TearDownOnMainThread();
361}
362
363bool BrowserActionsContainerOverflowTest::ViewOrdersMatch() {
364  if (main_bar_->num_browser_actions() !=
365      overflow_bar_->num_browser_actions())
366    return false;
367  for (size_t i = 0; i < main_bar_->num_browser_actions(); ++i) {
368    if (main_bar_->GetBrowserActionViewAt(i)->extension() !=
369        overflow_bar_->GetBrowserActionViewAt(i)->extension())
370      return false;
371  }
372  return true;
373}
374
375testing::AssertionResult
376BrowserActionsContainerOverflowTest::VerifyVisibleCount(
377    size_t expected_visible) {
378  // Views order should always match (as it is based directly off the model).
379  if (!ViewOrdersMatch())
380    return testing::AssertionFailure() << "View orders don't match";
381
382  // Loop through and check each browser action for proper visibility (which
383  // implicitly also guarantees that the proper number are visible).
384  for (size_t i = 0; i < overflow_bar_->num_browser_actions(); ++i) {
385    bool visible = i < expected_visible;
386    if (main_bar_->GetBrowserActionViewAt(i)->visible() != visible) {
387      return testing::AssertionFailure() << "Index " << i <<
388          " has improper visibility in main: " << !visible;
389    }
390    if (overflow_bar_->GetBrowserActionViewAt(i)->visible() == visible) {
391      return testing::AssertionFailure() << "Index " << i <<
392          " has improper visibility in overflow: " << visible;
393    }
394  }
395  return testing::AssertionSuccess();
396}
397
398// Test the basic functionality of the BrowserActionsContainer in overflow mode.
399IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest,
400                       TestBasicActionOverflow) {
401  // Load three extensions with browser actions.
402  // TODO(devlin): Make a method to load these, and generate them rather than
403  // using files.
404  base::FilePath test_data_path =
405      test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action");
406  const extensions::Extension* extension_a =
407      LoadExtension(test_data_path.AppendASCII("basics"));
408  const extensions::Extension* extension_b =
409      LoadExtension(test_data_path.AppendASCII("add_popup"));
410  const extensions::Extension* extension_c =
411      LoadExtension(test_data_path.AppendASCII("remove_popup"));
412
413  // Since the overflow bar isn't attached to a view, we have to kick it in
414  // order to retrigger layout each time we change the number of icons in the
415  // bar.
416  overflow_bar()->Layout();
417
418  // Sanity checks:
419  // All extensions loaded.
420  ASSERT_TRUE(extension_a);
421  ASSERT_TRUE(extension_b);
422  ASSERT_TRUE(extension_c);
423
424  // All actions are showing, and are in the installation order.
425  EXPECT_EQ(-1, model()->GetVisibleIconCount());
426  ASSERT_EQ(3u, main_bar()->num_browser_actions());
427  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension());
428  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension());
429  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension());
430  EXPECT_TRUE(VerifyVisibleCount(3u));
431
432  // Reduce the visible count to 2. Order should be unchanged (A B C), but
433  // only A and B should be visible on the main bar.
434  model()->SetVisibleIconCountForTest(2u);
435  overflow_bar()->Layout();  // Kick.
436  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension());
437  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension());
438  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension());
439  EXPECT_TRUE(VerifyVisibleCount(2u));
440
441  // Move extension C to the first position. Order should now be C A B, with
442  // C and A visible in the main bar.
443  model()->MoveExtensionIcon(extension_c, 0);
444  overflow_bar()->Layout();  // Kick.
445  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension());
446  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension());
447  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension());
448  EXPECT_TRUE(VerifyVisibleCount(2u));
449
450  // Hide action A. This results in it being sent to overflow, and reducing the
451  // visible size to 1, so the order should be C A B, with only C visible in the
452  // main bar.
453  extensions::ExtensionActionAPI::SetBrowserActionVisibility(
454      extensions::ExtensionPrefs::Get(profile()),
455      extension_a->id(),
456      false);
457  overflow_bar()->Layout();  // Kick.
458  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension());
459  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension());
460  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension());
461  EXPECT_TRUE(VerifyVisibleCount(1u));
462}
463