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(
205 extension_ids
, extensions::ExtensionToolbarModel::HIGHLIGHT_WARNING
);
207 // Only two browser actions should be visible.
208 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
209 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
211 // We shouldn't be able to drag in highlight mode.
212 action_view
= container
->GetToolbarActionViewAt(0);
213 EXPECT_FALSE(container
->CanStartDragForView(action_view
, point
, point
));
215 // We should go back to normal after leaving highlight mode.
216 toolbar_model()->StopHighlighting();
217 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
218 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
219 action_view
= container
->GetToolbarActionViewAt(0);
220 EXPECT_TRUE(container
->CanStartDragForView(action_view
, point
, point
));
223 // Test the behavior of the overflow container for Extension Actions.
224 class BrowserActionsContainerOverflowTest
225 : public BrowserActionsBarRedesignBrowserTest
{
227 BrowserActionsContainerOverflowTest() : main_bar_(nullptr),
228 overflow_bar_(nullptr) {
230 ~BrowserActionsContainerOverflowTest() override
{}
233 // Returns true if the order of the ToolbarActionViews in |main_bar_|
234 // and |overflow_bar_| match.
235 bool ViewOrdersMatch();
237 // Returns Success if the visible count matches |expected_visible|. This means
238 // that the number of visible browser actions in |main_bar_| is
239 // |expected_visible| and shows the first icons, and that the overflow bar
240 // shows all (and only) the remainder.
241 testing::AssertionResult
VerifyVisibleCount(size_t expected_visible
)
245 BrowserActionsContainer
* main_bar() { return main_bar_
; }
246 BrowserActionsContainer
* overflow_bar() { return overflow_bar_
; }
249 void SetUpOnMainThread() override
;
250 void TearDownOnMainThread() override
;
252 // The main BrowserActionsContainer (owned by the browser view).
253 BrowserActionsContainer
* main_bar_
;
255 // A parent view for the overflow menu.
256 scoped_ptr
<views::View
> overflow_parent_
;
258 // The overflow BrowserActionsContainer. We manufacture this so that we don't
259 // have to open the wrench menu.
260 // Owned by the |overflow_parent_|.
261 BrowserActionsContainer
* overflow_bar_
;
263 DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerOverflowTest
);
266 void BrowserActionsContainerOverflowTest::SetUpOnMainThread() {
267 BrowserActionsBarBrowserTest::SetUpOnMainThread();
268 main_bar_
= BrowserView::GetBrowserViewForBrowser(browser())
269 ->toolbar()->browser_actions();
270 overflow_parent_
.reset(new views::View());
271 overflow_parent_
->set_owned_by_client();
272 overflow_bar_
= new BrowserActionsContainer(browser(), main_bar_
);
273 overflow_parent_
->AddChildView(overflow_bar_
);
276 void BrowserActionsContainerOverflowTest::TearDownOnMainThread() {
277 overflow_parent_
.reset();
278 BrowserActionsBarBrowserTest::TearDownOnMainThread();
281 bool BrowserActionsContainerOverflowTest::ViewOrdersMatch() {
282 if (main_bar_
->num_toolbar_actions() !=
283 overflow_bar_
->num_toolbar_actions())
285 for (size_t i
= 0; i
< main_bar_
->num_toolbar_actions(); ++i
) {
286 if (main_bar_
->GetIdAt(i
) != overflow_bar_
->GetIdAt(i
))
292 testing::AssertionResult
293 BrowserActionsContainerOverflowTest::VerifyVisibleCount(
294 size_t expected_visible
) {
295 // Views order should always match (as it is based directly off the model).
296 if (!ViewOrdersMatch())
297 return testing::AssertionFailure() << "View orders don't match";
299 // Loop through and check each browser action for proper visibility (which
300 // implicitly also guarantees that the proper number are visible).
301 for (size_t i
= 0; i
< overflow_bar_
->num_toolbar_actions(); ++i
) {
302 bool visible
= i
< expected_visible
;
303 if (main_bar_
->GetToolbarActionViewAt(i
)->visible() != visible
) {
304 return testing::AssertionFailure() << "Index " << i
<<
305 " has improper visibility in main: " << !visible
;
307 if (overflow_bar_
->GetToolbarActionViewAt(i
)->visible() == visible
) {
308 return testing::AssertionFailure() << "Index " << i
<<
309 " has improper visibility in overflow: " << visible
;
312 return testing::AssertionSuccess();
315 // Test the basic functionality of the BrowserActionsContainer in overflow mode.
316 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest
,
317 TestBasicActionOverflow
) {
320 // Since the overflow bar isn't attached to a view, we have to kick it in
321 // order to retrigger layout each time we change the number of icons in the
323 overflow_bar()->Layout();
325 // All actions are showing, and are in the installation order.
326 EXPECT_TRUE(toolbar_model()->all_icons_visible());
327 EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
328 ASSERT_EQ(3u, main_bar()->num_toolbar_actions());
329 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
330 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
331 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
332 EXPECT_TRUE(VerifyVisibleCount(3u));
334 // Reduce the visible count to 2. Order should be unchanged (A B C), but
335 // only A and B should be visible on the main bar.
336 toolbar_model()->SetVisibleIconCount(2u);
337 overflow_bar()->Layout(); // Kick.
338 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
339 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
340 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
341 EXPECT_TRUE(VerifyVisibleCount(2u));
343 // Move extension C to the first position. Order should now be C A B, with
344 // C and A visible in the main bar.
345 toolbar_model()->MoveExtensionIcon(extension_c()->id(), 0);
346 overflow_bar()->Layout(); // Kick.
347 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(0u));
348 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
349 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
350 EXPECT_TRUE(VerifyVisibleCount(2u));
352 // Hide action A. This results in it being sent to overflow, and reducing the
353 // visible size to 1, so the order should be C A B, with only C visible in the
355 extensions::ExtensionActionAPI::Get(profile())->SetBrowserActionVisibility(
358 overflow_bar()->Layout(); // Kick.
359 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(0u));
360 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
361 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
362 EXPECT_TRUE(VerifyVisibleCount(1u));
365 // Test drag and drop between the overflow container and the main container.
366 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest
,
367 TestOverflowDragging
) {
370 // Start with one extension in overflow.
371 toolbar_model()->SetVisibleIconCount(2u);
372 overflow_bar()->Layout();
374 // Verify starting state is A B [C].
375 ASSERT_EQ(3u, main_bar()->num_toolbar_actions());
376 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
377 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
378 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
379 EXPECT_TRUE(VerifyVisibleCount(2u));
381 // Drag extension A (on the main bar) to the left of extension C (in
383 ui::OSExchangeData drop_data
;
384 BrowserActionDragData
browser_action_drag_data(extension_a()->id(), 0u);
385 browser_action_drag_data
.Write(profile(), &drop_data
);
386 ToolbarActionView
* view
= overflow_bar()->GetViewForId(extension_c()->id());
387 gfx::Point
location(view
->x(), view
->y());
388 ui::DropTargetEvent
target_event(
389 drop_data
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
391 overflow_bar()->OnDragUpdated(target_event
);
392 overflow_bar()->OnPerformDrop(target_event
);
393 overflow_bar()->Layout();
395 // Order should now be B [A C].
396 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(0u));
397 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
398 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
399 EXPECT_TRUE(VerifyVisibleCount(1u));
401 // Drag extension A back from overflow to the main bar.
402 ui::OSExchangeData drop_data2
;
403 BrowserActionDragData
browser_action_drag_data2(extension_a()->id(), 1u);
404 browser_action_drag_data2
.Write(profile(), &drop_data2
);
405 view
= main_bar()->GetViewForId(extension_b()->id());
406 location
= gfx::Point(view
->x(), view
->y());
407 ui::DropTargetEvent
target_event2(
408 drop_data2
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
410 main_bar()->OnDragUpdated(target_event2
);
411 main_bar()->OnPerformDrop(target_event2
);
413 // Order should be A B [C] again.
414 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
415 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
416 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
417 EXPECT_TRUE(VerifyVisibleCount(2u));
419 // Drag extension C from overflow to the main bar (before extension B).
420 ui::OSExchangeData drop_data3
;
421 BrowserActionDragData
browser_action_drag_data3(extension_c()->id(), 2u);
422 browser_action_drag_data3
.Write(profile(), &drop_data3
);
423 location
= gfx::Point(view
->x(), view
->y());
424 ui::DropTargetEvent
target_event3(
425 drop_data3
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
427 main_bar()->OnDragUpdated(target_event3
);
428 main_bar()->OnPerformDrop(target_event3
);
430 // Order should be A C B, and there should be no extensions in overflow.
431 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
432 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(1u));
433 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
434 EXPECT_TRUE(VerifyVisibleCount(3u));