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.
5 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
7 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
8 #include "chrome/browser/extensions/browser_action_test_util.h"
9 #include "chrome/browser/extensions/extension_toolbar_model.h"
10 #include "chrome/browser/ui/browser_window.h"
11 #include "chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h"
12 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
13 #include "chrome/browser/ui/views/frame/browser_view.h"
14 #include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
15 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
16 #include "extensions/browser/extension_prefs.h"
17 #include "extensions/common/extension.h"
18 #include "ui/base/dragdrop/drop_target_event.h"
19 #include "ui/base/dragdrop/os_exchange_data.h"
20 #include "ui/gfx/geometry/point.h"
21 #include "ui/views/view.h"
23 // TODO(devlin): Continue moving any tests that should be platform independent
24 // from this file to the crossplatform tests in
25 // chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc.
27 // Test that dragging browser actions works, and that dragging a browser action
28 // from the overflow menu results in it "popping" out (growing the container
29 // size by 1), rather than just reordering the extensions.
30 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, DragBrowserActions
) {
33 // Sanity check: All extensions showing; order is A B C.
34 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
35 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
36 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
37 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
38 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
40 BrowserActionsContainer
* container
=
41 BrowserView::GetBrowserViewForBrowser(browser())
42 ->toolbar()->browser_actions();
44 // Simulate a drag and drop to the right.
45 ui::OSExchangeData drop_data
;
46 // Drag extension A from index 0...
47 BrowserActionDragData
browser_action_drag_data(extension_a()->id(), 0u);
48 browser_action_drag_data
.Write(profile(), &drop_data
);
49 ToolbarActionView
* view
= container
->GetViewForId(extension_b()->id());
50 // ...to the right of extension B.
51 gfx::Point
location(view
->x() + view
->width(), view
->y());
52 ui::DropTargetEvent
target_event(
53 drop_data
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
56 container
->OnDragUpdated(target_event
);
57 container
->OnPerformDrop(target_event
);
59 // The order should now be B A C, since A was dragged to the right of B.
60 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(0));
61 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(1));
62 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
64 // This order should be reflected in the underlying model.
65 EXPECT_EQ(extension_b(), toolbar_model()->toolbar_items()[0].get());
66 EXPECT_EQ(extension_a(), toolbar_model()->toolbar_items()[1].get());
67 EXPECT_EQ(extension_c(), toolbar_model()->toolbar_items()[2].get());
69 // Simulate a drag and drop to the left.
70 ui::OSExchangeData drop_data2
;
71 // Drag extension A from index 1...
72 BrowserActionDragData
browser_action_drag_data2(extension_a()->id(), 1u);
73 browser_action_drag_data2
.Write(profile(), &drop_data2
);
74 // ...to the left of extension B (which is now at index 0).
75 location
= gfx::Point(view
->x(), view
->y());
76 ui::DropTargetEvent
target_event2(
77 drop_data2
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
80 container
->OnDragUpdated(target_event2
);
81 container
->OnPerformDrop(target_event2
);
83 // Order should be restored to A B C.
84 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
85 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
86 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
88 // Shrink the size of the container so we have an overflow menu.
89 toolbar_model()->SetVisibleIconCount(2u);
90 EXPECT_EQ(2u, container
->VisibleBrowserActions());
91 ASSERT_TRUE(container
->chevron());
92 EXPECT_TRUE(container
->chevron()->visible());
94 // Simulate a drag and drop from the overflow menu.
95 ui::OSExchangeData drop_data3
;
96 // Drag extension C from index 2 (in the overflow menu)...
97 BrowserActionDragData
browser_action_drag_data3(extension_c()->id(), 2u);
98 browser_action_drag_data3
.Write(profile(), &drop_data3
);
99 // ...to the left of extension B (which is back in index 1 on the main bar).
100 location
= gfx::Point(view
->x(), view
->y());
101 ui::DropTargetEvent
target_event3(
102 drop_data3
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
105 container
->OnDragUpdated(target_event3
);
106 container
->OnPerformDrop(target_event3
);
108 // The order should have changed *and* the container should have grown to
109 // accommodate extension C. The new order should be A C B, and all three
110 // extensions should be visible, with no overflow menu.
111 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
112 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(1));
113 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(2));
114 EXPECT_EQ(3u, container
->VisibleBrowserActions());
115 EXPECT_FALSE(container
->chevron()->visible());
116 EXPECT_TRUE(toolbar_model()->all_icons_visible());
118 // TODO(devlin): Ideally, we'd also have tests for dragging from the legacy
119 // overflow menu (i.e., chevron) to the main bar, but this requires either
120 // having a fairly complicated interactive UI test or finding a good way to
121 // mock up the BrowserActionOverflowMenuController.
124 // Test that changes performed in one container affect containers in other
125 // windows so that it is consistent.
126 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, MultipleWindows
) {
128 BrowserActionsContainer
* first
=
129 BrowserView::GetBrowserViewForBrowser(browser())->toolbar()->
132 // Create a second browser.
133 Browser
* second_browser
= new Browser(
134 Browser::CreateParams(profile(), browser()->host_desktop_type()));
135 BrowserActionsContainer
* second
=
136 BrowserView::GetBrowserViewForBrowser(second_browser
)->toolbar()->
139 // Both containers should have the same order and visible actions, which
140 // is right now A B C.
141 EXPECT_EQ(3u, first
->VisibleBrowserActions());
142 EXPECT_EQ(3u, second
->VisibleBrowserActions());
143 EXPECT_EQ(extension_a()->id(), first
->GetIdAt(0u));
144 EXPECT_EQ(extension_a()->id(), second
->GetIdAt(0u));
145 EXPECT_EQ(extension_b()->id(), first
->GetIdAt(1u));
146 EXPECT_EQ(extension_b()->id(), second
->GetIdAt(1u));
147 EXPECT_EQ(extension_c()->id(), first
->GetIdAt(2u));
148 EXPECT_EQ(extension_c()->id(), second
->GetIdAt(2u));
150 // Simulate a drag and drop to the right.
151 ui::OSExchangeData drop_data
;
152 // Drag extension A from index 0...
153 BrowserActionDragData
browser_action_drag_data(extension_a()->id(), 0u);
154 browser_action_drag_data
.Write(profile(), &drop_data
);
155 ToolbarActionView
* view
= first
->GetViewForId(extension_b()->id());
156 // ...to the right of extension B.
157 gfx::Point
location(view
->x() + view
->width(), view
->y());
158 ui::DropTargetEvent
target_event(
159 drop_data
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
162 first
->OnDragUpdated(target_event
);
163 first
->OnPerformDrop(target_event
);
165 // The new order, B A C, should be reflected in *both* containers, even
166 // though the drag only happened in the first one.
167 EXPECT_EQ(extension_b()->id(), first
->GetIdAt(0u));
168 EXPECT_EQ(extension_b()->id(), second
->GetIdAt(0u));
169 EXPECT_EQ(extension_a()->id(), first
->GetIdAt(1u));
170 EXPECT_EQ(extension_a()->id(), second
->GetIdAt(1u));
171 EXPECT_EQ(extension_c()->id(), first
->GetIdAt(2u));
172 EXPECT_EQ(extension_c()->id(), second
->GetIdAt(2u));
174 // Next, simulate a resize by shrinking the container.
175 first
->OnResize(1, true);
176 // The first and second container should each have resized.
177 EXPECT_EQ(2u, first
->VisibleBrowserActions());
178 EXPECT_EQ(2u, second
->VisibleBrowserActions());
181 // Test that the BrowserActionsContainer responds correctly when the underlying
182 // model enters highlight mode, and that browser actions are undraggable in
183 // highlight mode. (Highlight mode itself it tested more thoroughly in the
184 // ExtensionToolbarModel browsertests).
185 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, HighlightMode
) {
188 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
189 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
191 BrowserActionsContainer
* container
=
192 BrowserView::GetBrowserViewForBrowser(browser())
193 ->toolbar()->browser_actions();
195 // Currently, dragging should be enabled.
196 ToolbarActionView
* action_view
= container
->GetToolbarActionViewAt(0);
197 ASSERT_TRUE(action_view
);
198 gfx::Point
point(action_view
->x(), action_view
->y());
199 EXPECT_TRUE(container
->CanStartDragForView(action_view
, point
, point
));
201 extensions::ExtensionIdList extension_ids
;
202 extension_ids
.push_back(extension_a()->id());
203 extension_ids
.push_back(extension_b()->id());
204 toolbar_model()->HighlightExtensions(extension_ids
);
206 // Only two browser actions should be visible.
207 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
208 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
210 // We shouldn't be able to drag in highlight mode.
211 action_view
= container
->GetToolbarActionViewAt(0);
212 EXPECT_FALSE(container
->CanStartDragForView(action_view
, point
, point
));
214 // We should go back to normal after leaving highlight mode.
215 toolbar_model()->StopHighlighting();
216 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
217 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
218 action_view
= container
->GetToolbarActionViewAt(0);
219 EXPECT_TRUE(container
->CanStartDragForView(action_view
, point
, point
));
222 // Test the behavior of the overflow container for Extension Actions.
223 class BrowserActionsContainerOverflowTest
224 : public BrowserActionsBarRedesignBrowserTest
{
226 BrowserActionsContainerOverflowTest() : main_bar_(nullptr),
227 overflow_bar_(nullptr) {
229 ~BrowserActionsContainerOverflowTest() override
{}
232 // Returns true if the order of the ToolbarActionViews in |main_bar_|
233 // and |overflow_bar_| match.
234 bool ViewOrdersMatch();
236 // Returns Success if the visible count matches |expected_visible|. This means
237 // that the number of visible browser actions in |main_bar_| is
238 // |expected_visible| and shows the first icons, and that the overflow bar
239 // shows all (and only) the remainder.
240 testing::AssertionResult
VerifyVisibleCount(size_t expected_visible
)
244 BrowserActionsContainer
* main_bar() { return main_bar_
; }
245 BrowserActionsContainer
* overflow_bar() { return overflow_bar_
; }
248 void SetUpOnMainThread() override
;
249 void TearDownOnMainThread() override
;
251 // The main BrowserActionsContainer (owned by the browser view).
252 BrowserActionsContainer
* main_bar_
;
254 // A parent view for the overflow menu.
255 scoped_ptr
<views::View
> overflow_parent_
;
257 // The overflow BrowserActionsContainer. We manufacture this so that we don't
258 // have to open the wrench menu.
259 // Owned by the |overflow_parent_|.
260 BrowserActionsContainer
* overflow_bar_
;
262 // The associated toolbar model.
263 extensions::ExtensionToolbarModel
* model_
;
265 DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerOverflowTest
);
268 void BrowserActionsContainerOverflowTest::SetUpOnMainThread() {
269 BrowserActionsBarBrowserTest::SetUpOnMainThread();
270 main_bar_
= BrowserView::GetBrowserViewForBrowser(browser())
271 ->toolbar()->browser_actions();
272 overflow_parent_
.reset(new views::View());
273 overflow_parent_
->set_owned_by_client();
274 overflow_bar_
= new BrowserActionsContainer(browser(), main_bar_
);
275 overflow_parent_
->AddChildView(overflow_bar_
);
278 void BrowserActionsContainerOverflowTest::TearDownOnMainThread() {
279 overflow_parent_
.reset();
280 BrowserActionsBarBrowserTest::TearDownOnMainThread();
283 bool BrowserActionsContainerOverflowTest::ViewOrdersMatch() {
284 if (main_bar_
->num_toolbar_actions() !=
285 overflow_bar_
->num_toolbar_actions())
287 for (size_t i
= 0; i
< main_bar_
->num_toolbar_actions(); ++i
) {
288 if (main_bar_
->GetIdAt(i
) != overflow_bar_
->GetIdAt(i
))
294 testing::AssertionResult
295 BrowserActionsContainerOverflowTest::VerifyVisibleCount(
296 size_t expected_visible
) {
297 // Views order should always match (as it is based directly off the model).
298 if (!ViewOrdersMatch())
299 return testing::AssertionFailure() << "View orders don't match";
301 // Loop through and check each browser action for proper visibility (which
302 // implicitly also guarantees that the proper number are visible).
303 for (size_t i
= 0; i
< overflow_bar_
->num_toolbar_actions(); ++i
) {
304 bool visible
= i
< expected_visible
;
305 if (main_bar_
->GetToolbarActionViewAt(i
)->visible() != visible
) {
306 return testing::AssertionFailure() << "Index " << i
<<
307 " has improper visibility in main: " << !visible
;
309 if (overflow_bar_
->GetToolbarActionViewAt(i
)->visible() == visible
) {
310 return testing::AssertionFailure() << "Index " << i
<<
311 " has improper visibility in overflow: " << visible
;
314 return testing::AssertionSuccess();
317 // Test the basic functionality of the BrowserActionsContainer in overflow mode.
318 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest
,
319 TestBasicActionOverflow
) {
322 // Since the overflow bar isn't attached to a view, we have to kick it in
323 // order to retrigger layout each time we change the number of icons in the
325 overflow_bar()->Layout();
327 // All actions are showing, and are in the installation order.
328 EXPECT_TRUE(toolbar_model()->all_icons_visible());
329 EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
330 ASSERT_EQ(3u, main_bar()->num_toolbar_actions());
331 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
332 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
333 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
334 EXPECT_TRUE(VerifyVisibleCount(3u));
336 // Reduce the visible count to 2. Order should be unchanged (A B C), but
337 // only A and B should be visible on the main bar.
338 toolbar_model()->SetVisibleIconCount(2u);
339 overflow_bar()->Layout(); // Kick.
340 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
341 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
342 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
343 EXPECT_TRUE(VerifyVisibleCount(2u));
345 // Move extension C to the first position. Order should now be C A B, with
346 // C and A visible in the main bar.
347 toolbar_model()->MoveExtensionIcon(extension_c()->id(), 0);
348 overflow_bar()->Layout(); // Kick.
349 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(0u));
350 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
351 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
352 EXPECT_TRUE(VerifyVisibleCount(2u));
354 // Hide action A. This results in it being sent to overflow, and reducing the
355 // visible size to 1, so the order should be C A B, with only C visible in the
357 extensions::ExtensionActionAPI::SetBrowserActionVisibility(
358 extensions::ExtensionPrefs::Get(profile()),
361 overflow_bar()->Layout(); // Kick.
362 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(0u));
363 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
364 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
365 EXPECT_TRUE(VerifyVisibleCount(1u));
368 // Test drag and drop between the overflow container and the main container.
369 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest
,
370 TestOverflowDragging
) {
373 // Start with one extension in overflow.
374 toolbar_model()->SetVisibleIconCount(2u);
375 overflow_bar()->Layout();
377 // Verify starting state is A B [C].
378 ASSERT_EQ(3u, main_bar()->num_toolbar_actions());
379 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
380 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
381 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
382 EXPECT_TRUE(VerifyVisibleCount(2u));
384 // Drag extension A (on the main bar) to the left of extension C (in
386 ui::OSExchangeData drop_data
;
387 BrowserActionDragData
browser_action_drag_data(extension_a()->id(), 0u);
388 browser_action_drag_data
.Write(profile(), &drop_data
);
389 ToolbarActionView
* view
= overflow_bar()->GetViewForId(extension_c()->id());
390 gfx::Point
location(view
->x(), view
->y());
391 ui::DropTargetEvent
target_event(
392 drop_data
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
394 overflow_bar()->OnDragUpdated(target_event
);
395 overflow_bar()->OnPerformDrop(target_event
);
396 overflow_bar()->Layout();
398 // Order should now be B [A C].
399 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(0u));
400 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
401 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
402 EXPECT_TRUE(VerifyVisibleCount(1u));
404 // Drag extension A back from overflow to the main bar.
405 ui::OSExchangeData drop_data2
;
406 BrowserActionDragData
browser_action_drag_data2(extension_a()->id(), 1u);
407 browser_action_drag_data2
.Write(profile(), &drop_data2
);
408 view
= main_bar()->GetViewForId(extension_b()->id());
409 location
= gfx::Point(view
->x(), view
->y());
410 ui::DropTargetEvent
target_event2(
411 drop_data2
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
413 main_bar()->OnDragUpdated(target_event2
);
414 main_bar()->OnPerformDrop(target_event2
);
416 // Order should be A B [C] again.
417 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
418 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
419 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
420 EXPECT_TRUE(VerifyVisibleCount(2u));
422 // Drag extension C from overflow to the main bar (before extension B).
423 ui::OSExchangeData drop_data3
;
424 BrowserActionDragData
browser_action_drag_data3(extension_c()->id(), 2u);
425 browser_action_drag_data3
.Write(profile(), &drop_data3
);
426 location
= gfx::Point(view
->x(), view
->y());
427 ui::DropTargetEvent
target_event3(
428 drop_data3
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
430 main_bar()->OnDragUpdated(target_event3
);
431 main_bar()->OnPerformDrop(target_event3
);
433 // Order should be A C B, and there should be no extensions in overflow.
434 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
435 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(1u));
436 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
437 EXPECT_TRUE(VerifyVisibleCount(3u));