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/extensions/browser_action_drag_data.h"
15#include "chrome/browser/ui/views/frame/browser_view.h"
16#include "chrome/browser/ui/views/toolbar/browser_action_view.h"
17#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
18#include "content/public/test/test_utils.h"
19#include "extensions/browser/extension_prefs.h"
20#include "extensions/common/extension.h"
21#include "ui/base/dragdrop/drop_target_event.h"
22#include "ui/base/dragdrop/os_exchange_data.h"
23#include "ui/gfx/geometry/point.h"
24#include "ui/views/view.h"
25
26using extensions::Extension;
27
28class BrowserActionsContainerTest : public ExtensionBrowserTest {
29 public:
30  BrowserActionsContainerTest() {
31  }
32  virtual ~BrowserActionsContainerTest() {}
33
34  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
35    BrowserActionsContainer::disable_animations_during_testing_ = true;
36    ExtensionBrowserTest::SetUpCommandLine(command_line);
37  }
38
39  virtual void SetUpOnMainThread() OVERRIDE {
40    ExtensionBrowserTest::SetUpOnMainThread();
41    browser_actions_bar_.reset(new BrowserActionTestUtil(browser()));
42  }
43
44  virtual void TearDownOnMainThread() OVERRIDE {
45    BrowserActionsContainer::disable_animations_during_testing_ = false;
46  }
47
48  BrowserActionTestUtil* browser_actions_bar() {
49    return browser_actions_bar_.get();
50  }
51
52 private:
53  scoped_ptr<BrowserActionTestUtil> browser_actions_bar_;
54};
55
56// Test the basic functionality.
57// http://crbug.com/120770
58#if defined(OS_WIN)
59IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, DISABLED_Basic) {
60#else
61IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Basic) {
62#endif
63  // Load an extension with no browser action.
64  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
65                                          .AppendASCII("browser_action")
66                                          .AppendASCII("none")));
67  // This extension should not be in the model (has no browser action).
68  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
69
70  // Load an extension with a browser action.
71  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
72                                          .AppendASCII("browser_action")
73                                          .AppendASCII("basics")));
74  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
75  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
76
77
78  // Unload the extension.
79  std::string id = browser_actions_bar()->GetExtensionId(0);
80  UnloadExtension(id);
81  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
82}
83
84// Test moving various browser actions. This is not to check the logic of the
85// move (that's in the toolbar model tests), but just to check our ui.
86IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest,
87                       MoveBrowserActions) {
88  base::FilePath data_dir =
89      test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action");
90  // Load three extensions with browser actions.
91  const extensions::Extension* extension_a =
92      LoadExtension(data_dir.AppendASCII("basics"));
93  ASSERT_TRUE(extension_a);
94  const extensions::Extension* extension_b =
95      LoadExtension(data_dir.AppendASCII("add_popup"));
96  ASSERT_TRUE(extension_b);
97  const extensions::Extension* extension_c =
98      LoadExtension(data_dir.AppendASCII("remove_popup"));
99  ASSERT_TRUE(extension_c);
100
101  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
102  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
103
104  extensions::ExtensionToolbarModel* model =
105      extensions::ExtensionToolbarModel::Get(profile());
106  ASSERT_TRUE(model);
107
108  // Order is now A B C.
109  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0));
110  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1));
111  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2));
112
113  // Move C to first position. Order is C A B.
114  model->MoveExtensionIcon(extension_c, 0);
115  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0));
116  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1));
117  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2));
118
119  // Move B to third position. Order is still C A B.
120  model->MoveExtensionIcon(extension_b, 2);
121  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0));
122  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1));
123  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2));
124
125  // Move B to middle position. Order is C B A.
126  model->MoveExtensionIcon(extension_b, 1);
127  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0));
128  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1));
129  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(2));
130}
131
132// Test that dragging browser actions works, and that dragging a browser action
133// from the overflow menu results in it "popping" out (growing the container
134// size by 1), rather than just reordering the extensions.
135IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, DragBrowserActions) {
136  base::FilePath data_dir =
137      test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action");
138  // Load three extensions with browser actions.
139  const extensions::Extension* extension_a =
140      LoadExtension(data_dir.AppendASCII("basics"));
141  ASSERT_TRUE(extension_a);
142  const extensions::Extension* extension_b =
143      LoadExtension(data_dir.AppendASCII("add_popup"));
144  ASSERT_TRUE(extension_b);
145  const extensions::Extension* extension_c =
146      LoadExtension(data_dir.AppendASCII("remove_popup"));
147  ASSERT_TRUE(extension_c);
148
149  // Sanity check: All extensions showing; order is A B C.
150  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
151  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
152  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0));
153  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1));
154  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2));
155
156  BrowserActionsContainer* container =
157      BrowserView::GetBrowserViewForBrowser(browser())
158          ->toolbar()->browser_actions();
159
160  // Simulate a drag and drop to the right.
161  ui::OSExchangeData drop_data;
162  // Drag extension A from index 0...
163  BrowserActionDragData browser_action_drag_data(extension_a->id(), 0u);
164  browser_action_drag_data.Write(profile(), &drop_data);
165  BrowserActionView* view = container->GetViewForExtension(extension_b);
166  // ...to the right of extension B.
167  gfx::Point location(view->x() + view->width(), view->y());
168  ui::DropTargetEvent target_event(
169      drop_data, location, location, ui::DragDropTypes::DRAG_MOVE);
170
171  // Drag and drop.
172  container->OnDragUpdated(target_event);
173  container->OnPerformDrop(target_event);
174
175  // The order should now be B A C, since A was dragged to the right of B.
176  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(0));
177  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1));
178  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2));
179
180  // This order should be reflected in the underlying model.
181  extensions::ExtensionToolbarModel* model =
182      extensions::ExtensionToolbarModel::Get(profile());
183  EXPECT_EQ(extension_b, model->toolbar_items()[0].get());
184  EXPECT_EQ(extension_a, model->toolbar_items()[1].get());
185  EXPECT_EQ(extension_c, model->toolbar_items()[2].get());
186
187  // Simulate a drag and drop to the left.
188  ui::OSExchangeData drop_data2;
189  // Drag extension A from index 1...
190  BrowserActionDragData browser_action_drag_data2(extension_a->id(), 1u);
191  browser_action_drag_data2.Write(profile(), &drop_data2);
192  // ...to the left of extension B (which is now at index 0).
193  location = gfx::Point(view->x(), view->y());
194  ui::DropTargetEvent target_event2(
195      drop_data2, location, location, ui::DragDropTypes::DRAG_MOVE);
196
197  // Drag and drop.
198  container->OnDragUpdated(target_event2);
199  container->OnPerformDrop(target_event2);
200
201  // Order should be restored to A B C.
202  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0));
203  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1));
204  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2));
205
206  // Shrink the size of the container so we have an overflow menu.
207  model->SetVisibleIconCountForTest(2u);
208  EXPECT_EQ(2u, container->VisibleBrowserActions());
209  ASSERT_TRUE(container->chevron());
210  EXPECT_TRUE(container->chevron()->visible());
211
212  // Simulate a drag and drop from the overflow menu.
213  ui::OSExchangeData drop_data3;
214  // Drag extension C from index 2 (in the overflow menu)...
215  BrowserActionDragData browser_action_drag_data3(extension_c->id(), 2u);
216  browser_action_drag_data3.Write(profile(), &drop_data3);
217  // ...to the left of extension B (which is back in index 1 on the main bar).
218  location = gfx::Point(view->x(), view->y());
219  ui::DropTargetEvent target_event3(
220      drop_data3, location, location, ui::DragDropTypes::DRAG_MOVE);
221
222  // Drag and drop.
223  container->OnDragUpdated(target_event3);
224  container->OnPerformDrop(target_event3);
225
226  // The order should have changed *and* the container should have grown to
227  // accommodate extension C. The new order should be A C B, and all three
228  // extensions should be visible, with no overflow menu.
229  EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0));
230  EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(1));
231  EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2));
232  EXPECT_EQ(3u, container->VisibleBrowserActions());
233  EXPECT_FALSE(container->chevron()->visible());
234  EXPECT_EQ(-1, model->GetVisibleIconCount());
235
236  // TODO(devlin): Ideally, we'd also have tests for dragging from the legacy
237  // overflow menu (i.e., chevron) to the main bar, but this requires either
238  // having a fairly complicated interactive UI test or finding a good way to
239  // mock up the BrowserActionOverflowMenuController.
240}
241
242IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Visibility) {
243  // Load extension A (contains browser action).
244  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
245                                          .AppendASCII("browser_action")
246                                          .AppendASCII("basics")));
247  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
248  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
249  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
250  std::string idA = browser_actions_bar()->GetExtensionId(0);
251
252  // Load extension B (contains browser action).
253  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
254                                          .AppendASCII("browser_action")
255                                          .AppendASCII("add_popup")));
256  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
257  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
258  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
259  std::string idB = browser_actions_bar()->GetExtensionId(1);
260
261  EXPECT_NE(idA, idB);
262
263  // Load extension C (contains browser action).
264  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
265                                          .AppendASCII("browser_action")
266                                          .AppendASCII("remove_popup")));
267  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
268  EXPECT_TRUE(browser_actions_bar()->HasIcon(2));
269  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
270  std::string idC = browser_actions_bar()->GetExtensionId(2);
271
272  // Change container to show only one action, rest in overflow: A, [B, C].
273  browser_actions_bar()->SetIconVisibilityCount(1);
274  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
275
276  // Disable extension A (should disappear). State becomes: B [C].
277  DisableExtension(idA);
278  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
279  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
280  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
281
282  // Enable A again. A should get its spot in the same location and the bar
283  // should not grow (chevron is showing). For details: http://crbug.com/35349.
284  // State becomes: A, [B, C].
285  EnableExtension(idA);
286  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
287  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
288  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
289
290  // Disable C (in overflow). State becomes: A, [B].
291  DisableExtension(idC);
292  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
293  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
294  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
295
296  // Enable C again. State becomes: A, [B, C].
297  EnableExtension(idC);
298  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
299  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
300  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
301
302  // Now we have 3 extensions. Make sure they are all visible. State: A, B, C.
303  browser_actions_bar()->SetIconVisibilityCount(3);
304  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
305
306  // Disable extension A (should disappear). State becomes: B, C.
307  DisableExtension(idA);
308  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
309  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
310  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
311
312  // Disable extension B (should disappear). State becomes: C.
313  DisableExtension(idB);
314  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
315  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
316  EXPECT_EQ(idC, browser_actions_bar()->GetExtensionId(0));
317
318  // Enable B. State becomes: B, C.
319  EnableExtension(idB);
320  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
321  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
322  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
323
324  // Enable A. State becomes: A, B, C.
325  EnableExtension(idA);
326  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
327  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
328  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
329
330  // Shrink the browser actions bar to zero visible icons.
331  // No icons should be visible, but we *should* show the chevron and have a
332  // non-empty size.
333  browser_actions_bar()->SetIconVisibilityCount(0);
334  EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions());
335  BrowserActionsContainer* container =
336      BrowserView::GetBrowserViewForBrowser(browser())
337          ->toolbar()->browser_actions();
338  ASSERT_TRUE(container->chevron());
339  EXPECT_TRUE(container->chevron()->visible());
340  EXPECT_FALSE(container->GetPreferredSize().IsEmpty());
341
342  // Reset visibility count to 2. State should be A, B, [C], and the chevron
343  // should be visible.
344  browser_actions_bar()->SetIconVisibilityCount(2);
345  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
346  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
347  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(1));
348  EXPECT_TRUE(container->chevron()->visible());
349
350  // Disable C (the overflowed extension). State should now be A, B, and the
351  // chevron should be hidden.
352  DisableExtension(idC);
353  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
354  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
355  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(1));
356  EXPECT_FALSE(container->chevron()->visible());
357
358  // Re-enable C. We should still only have 2 visible icons, and the chevron
359  // should be visible.
360  EnableExtension(idC);
361  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
362  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
363  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(1));
364  EXPECT_TRUE(container->chevron()->visible());
365}
366
367IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, ForceHide) {
368  // Load extension A (contains browser action).
369  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
370                                          .AppendASCII("browser_action")
371                                          .AppendASCII("basics")));
372  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
373  EXPECT_TRUE(browser_actions_bar()->HasIcon(0));
374  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
375  std::string idA = browser_actions_bar()->GetExtensionId(0);
376
377  // Force hide this browser action.
378  extensions::ExtensionActionAPI::SetBrowserActionVisibility(
379      extensions::ExtensionPrefs::Get(browser()->profile()), idA, false);
380  EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions());
381}
382
383// Test that the BrowserActionsContainer responds correctly when the underlying
384// model enters highlight mode, and that browser actions are undraggable in
385// highlight mode. (Highlight mode itself it tested more thoroughly in the
386// ExtensionToolbarModel browsertests).
387IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, HighlightMode) {
388  // Load three extensions with browser actions.
389  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
390                                          .AppendASCII("browser_action")
391                                          .AppendASCII("basics")));
392  std::string id_a = browser_actions_bar()->GetExtensionId(0);
393  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
394                                          .AppendASCII("browser_action")
395                                          .AppendASCII("add_popup")));
396  std::string id_b = browser_actions_bar()->GetExtensionId(1);
397  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
398                                          .AppendASCII("browser_action")
399                                          .AppendASCII("remove_popup")));
400
401  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
402  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
403
404  BrowserActionsContainer* container = browser()
405                                           ->window()
406                                           ->GetBrowserWindowTesting()
407                                           ->GetToolbarView()
408                                           ->browser_actions();
409
410  // Currently, dragging should be enabled.
411  BrowserActionView* action_view = container->GetBrowserActionViewAt(0);
412  ASSERT_TRUE(action_view);
413  gfx::Point point(action_view->x(), action_view->y());
414  EXPECT_TRUE(container->CanStartDragForView(action_view, point, point));
415
416  extensions::ExtensionToolbarModel* model =
417      extensions::ExtensionToolbarModel::Get(profile());
418
419  extensions::ExtensionIdList extension_ids;
420  extension_ids.push_back(id_a);
421  extension_ids.push_back(id_b);
422  model->HighlightExtensions(extension_ids);
423
424  // Only two browser actions should be visible.
425  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
426  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
427
428  // We shouldn't be able to drag in highlight mode.
429  action_view = container->GetBrowserActionViewAt(0);
430  EXPECT_FALSE(container->CanStartDragForView(action_view, point, point));
431
432  // We should go back to normal after leaving highlight mode.
433  model->StopHighlighting();
434  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
435  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
436  action_view = container->GetBrowserActionViewAt(0);
437  EXPECT_TRUE(container->CanStartDragForView(action_view, point, point));
438}
439
440// Test the behavior of the overflow container for Extension Actions.
441class BrowserActionsContainerOverflowTest : public BrowserActionsContainerTest {
442 public:
443  BrowserActionsContainerOverflowTest() : main_bar_(NULL), model_(NULL) {
444  }
445  virtual ~BrowserActionsContainerOverflowTest() {
446  }
447
448 protected:
449  // Returns true if the order of the BrowserActionViews in |main_bar_|
450  // and |overflow_bar_| match.
451  bool ViewOrdersMatch();
452
453  // Returns Success if the visible count matches |expected_visible|. This means
454  // that the number of visible browser actions in |main_bar_| is
455  // |expected_visible| and shows the first icons, and that the overflow bar
456  // shows all (and only) the remainder.
457  testing::AssertionResult VerifyVisibleCount(size_t expected_visible);
458
459  // Accessors.
460  BrowserActionsContainer* main_bar() { return main_bar_; }
461  BrowserActionsContainer* overflow_bar() { return overflow_bar_.get(); }
462  extensions::ExtensionToolbarModel* model() { return model_; }
463
464 private:
465  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE;
466  virtual void SetUpOnMainThread() OVERRIDE;
467  virtual void TearDownOnMainThread() OVERRIDE;
468
469  // The main BrowserActionsContainer (owned by the browser view).
470  BrowserActionsContainer* main_bar_;
471
472  // The overflow BrowserActionsContainer. We manufacture this so that we don't
473  // have to open the wrench menu.
474  scoped_ptr<BrowserActionsContainer> overflow_bar_;
475
476  // The associated toolbar model.
477  extensions::ExtensionToolbarModel* model_;
478
479  // Enable the feature redesign switch.
480  scoped_ptr<extensions::FeatureSwitch::ScopedOverride> enable_redesign_;
481
482  DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerOverflowTest);
483};
484
485void BrowserActionsContainerOverflowTest::SetUpCommandLine(
486    base::CommandLine* command_line) {
487  BrowserActionsContainerTest::SetUpCommandLine(command_line);
488  enable_redesign_.reset(new extensions::FeatureSwitch::ScopedOverride(
489      extensions::FeatureSwitch::extension_action_redesign(),
490      true));
491}
492
493void BrowserActionsContainerOverflowTest::SetUpOnMainThread() {
494  BrowserActionsContainerTest::SetUpOnMainThread();
495  main_bar_ = BrowserView::GetBrowserViewForBrowser(browser())
496                  ->toolbar()->browser_actions();
497  overflow_bar_.reset(new BrowserActionsContainer(browser(), NULL, main_bar_));
498  overflow_bar_->set_owned_by_client();
499  model_ = extensions::ExtensionToolbarModel::Get(profile());
500}
501
502void BrowserActionsContainerOverflowTest::TearDownOnMainThread() {
503  overflow_bar_.reset();
504  enable_redesign_.reset();
505  BrowserActionsContainerTest::TearDownOnMainThread();
506}
507
508bool BrowserActionsContainerOverflowTest::ViewOrdersMatch() {
509  if (main_bar_->num_browser_actions() !=
510      overflow_bar_->num_browser_actions())
511    return false;
512  for (size_t i = 0; i < main_bar_->num_browser_actions(); ++i) {
513    if (main_bar_->GetBrowserActionViewAt(i)->extension() !=
514        overflow_bar_->GetBrowserActionViewAt(i)->extension())
515      return false;
516  }
517  return true;
518}
519
520testing::AssertionResult
521BrowserActionsContainerOverflowTest::VerifyVisibleCount(
522    size_t expected_visible) {
523  // Views order should always match (as it is based directly off the model).
524  if (!ViewOrdersMatch())
525    return testing::AssertionFailure() << "View orders don't match";
526
527  // Loop through and check each browser action for proper visibility (which
528  // implicitly also guarantees that the proper number are visible).
529  for (size_t i = 0; i < overflow_bar_->num_browser_actions(); ++i) {
530    bool visible = i < expected_visible;
531    if (main_bar_->GetBrowserActionViewAt(i)->visible() != visible) {
532      return testing::AssertionFailure() << "Index " << i <<
533          " has improper visibility in main: " << !visible;
534    }
535    if (overflow_bar_->GetBrowserActionViewAt(i)->visible() == visible) {
536      return testing::AssertionFailure() << "Index " << i <<
537          " has improper visibility in overflow: " << visible;
538    }
539  }
540  return testing::AssertionSuccess();
541}
542
543// Test the basic functionality of the BrowserActionsContainer in overflow mode.
544IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest,
545                       TestBasicActionOverflow) {
546  // Load three extensions with browser actions.
547  // TODO(devlin): Make a method to load these, and generate them rather than
548  // using files.
549  base::FilePath test_data_path =
550      test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action");
551  const extensions::Extension* extension_a =
552      LoadExtension(test_data_path.AppendASCII("basics"));
553  const extensions::Extension* extension_b =
554      LoadExtension(test_data_path.AppendASCII("add_popup"));
555  const extensions::Extension* extension_c =
556      LoadExtension(test_data_path.AppendASCII("remove_popup"));
557
558  // Since the overflow bar isn't attached to a view, we have to kick it in
559  // order to retrigger layout each time we change the number of icons in the
560  // bar.
561  overflow_bar()->Layout();
562
563  // Sanity checks:
564  // All extensions loaded.
565  ASSERT_TRUE(extension_a);
566  ASSERT_TRUE(extension_b);
567  ASSERT_TRUE(extension_c);
568
569  // All actions are showing, and are in the installation order.
570  EXPECT_EQ(-1, model()->GetVisibleIconCount());
571  ASSERT_EQ(3u, main_bar()->num_browser_actions());
572  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension());
573  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension());
574  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension());
575  EXPECT_TRUE(VerifyVisibleCount(3u));
576
577  // Reduce the visible count to 2. Order should be unchanged (A B C), but
578  // only A and B should be visible on the main bar.
579  model()->SetVisibleIconCountForTest(2u);
580  overflow_bar()->Layout();  // Kick.
581  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension());
582  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension());
583  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension());
584  EXPECT_TRUE(VerifyVisibleCount(2u));
585
586  // Move extension C to the first position. Order should now be C A B, with
587  // C and A visible in the main bar.
588  model()->MoveExtensionIcon(extension_c, 0);
589  overflow_bar()->Layout();  // Kick.
590  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension());
591  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension());
592  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension());
593  EXPECT_TRUE(VerifyVisibleCount(2u));
594
595  // Hide action A. This results in it being sent to overflow, and reducing the
596  // visible size to 1, so the order should be C A B, with only C visible in the
597  // main bar.
598  extensions::ExtensionActionAPI::SetBrowserActionVisibility(
599      extensions::ExtensionPrefs::Get(profile()),
600      extension_a->id(),
601      false);
602  overflow_bar()->Layout();  // Kick.
603  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension());
604  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension());
605  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension());
606  EXPECT_TRUE(VerifyVisibleCount(1u));
607}
608
609// Test drag and drop between the overflow container and the main container.
610IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest,
611                       TestOverflowDragging) {
612  base::FilePath test_data_path =
613      test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action");
614  const extensions::Extension* extension_a =
615      LoadExtension(test_data_path.AppendASCII("basics"));
616  const extensions::Extension* extension_b =
617      LoadExtension(test_data_path.AppendASCII("add_popup"));
618  const extensions::Extension* extension_c =
619      LoadExtension(test_data_path.AppendASCII("remove_popup"));
620
621  // Start with one extension in overflow.
622  model()->SetVisibleIconCountForTest(2u);
623  overflow_bar()->Layout();
624
625  // Verify starting state is A B [C].
626  ASSERT_EQ(3u, main_bar()->num_browser_actions());
627  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension());
628  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension());
629  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension());
630  EXPECT_TRUE(VerifyVisibleCount(2u));
631
632  // Drag extension A (on the main bar) to the left of extension C (in
633  // overflow).
634  ui::OSExchangeData drop_data;
635  BrowserActionDragData browser_action_drag_data(extension_a->id(), 0u);
636  browser_action_drag_data.Write(profile(), &drop_data);
637  BrowserActionView* view = overflow_bar()->GetViewForExtension(extension_c);
638  gfx::Point location(view->x(), view->y());
639  ui::DropTargetEvent target_event(
640      drop_data, location, location, ui::DragDropTypes::DRAG_MOVE);
641
642  overflow_bar()->OnDragUpdated(target_event);
643  overflow_bar()->OnPerformDrop(target_event);
644  overflow_bar()->Layout();
645
646  // Order should now be B [A C].
647  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(0)->extension());
648  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension());
649  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension());
650  VerifyVisibleCount(1u);
651
652  // Drag extension A back from overflow to the main bar.
653  ui::OSExchangeData drop_data2;
654  BrowserActionDragData browser_action_drag_data2(extension_a->id(), 1u);
655  browser_action_drag_data2.Write(profile(), &drop_data2);
656  view = main_bar()->GetViewForExtension(extension_b);
657  location = gfx::Point(view->x(), view->y());
658  ui::DropTargetEvent target_event2(
659      drop_data2, location, location, ui::DragDropTypes::DRAG_MOVE);
660
661  main_bar()->OnDragUpdated(target_event2);
662  main_bar()->OnPerformDrop(target_event2);
663
664  // Order should be A B [C] again.
665  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension());
666  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension());
667  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension());
668  VerifyVisibleCount(2u);
669
670  // Drag extension C from overflow to the main bar (before extension B).
671  ui::OSExchangeData drop_data3;
672  BrowserActionDragData browser_action_drag_data3(extension_c->id(), 2u);
673  browser_action_drag_data3.Write(profile(), &drop_data3);
674  location = gfx::Point(view->x(), view->y());
675  ui::DropTargetEvent target_event3(
676      drop_data3, location, location, ui::DragDropTypes::DRAG_MOVE);
677
678  main_bar()->OnDragUpdated(target_event3);
679  main_bar()->OnPerformDrop(target_event3);
680
681  // Order should be A C B, and there should be no extensions in overflow.
682  EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension());
683  EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(1)->extension());
684  EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension());
685  VerifyVisibleCount(3u);
686}
687