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/ui/browser_window.h"
10 #include "chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h"
11 #include "chrome/browser/ui/toolbar/toolbar_actions_model.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/browser/extension_registry.h"
18 #include "extensions/common/extension.h"
19 #include "ui/base/dragdrop/drop_target_event.h"
20 #include "ui/base/dragdrop/os_exchange_data.h"
21 #include "ui/gfx/geometry/point.h"
22 #include "ui/views/view.h"
24 // TODO(devlin): Continue moving any tests that should be platform independent
25 // from this file to the crossplatform tests in
26 // chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc.
28 // Test that dragging browser actions works, and that dragging a browser action
29 // from the overflow menu results in it "popping" out (growing the container
30 // size by 1), rather than just reordering the extensions.
31 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, DragBrowserActions
) {
34 // Sanity check: All extensions showing; order is A B C.
35 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
36 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
37 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
38 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
39 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
41 BrowserActionsContainer
* container
=
42 BrowserView::GetBrowserViewForBrowser(browser())
43 ->toolbar()->browser_actions();
45 // Simulate a drag and drop to the right.
46 ui::OSExchangeData drop_data
;
47 // Drag extension A from index 0...
48 BrowserActionDragData
browser_action_drag_data(extension_a()->id(), 0u);
49 browser_action_drag_data
.Write(profile(), &drop_data
);
50 ToolbarActionView
* view
= container
->GetViewForId(extension_b()->id());
51 // ...to the right of extension B.
52 gfx::Point
location(view
->x() + view
->width(), view
->y());
53 ui::DropTargetEvent
target_event(
54 drop_data
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
57 container
->OnDragUpdated(target_event
);
58 container
->OnPerformDrop(target_event
);
60 // The order should now be B A C, since A was dragged to the right of B.
61 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(0));
62 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(1));
63 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
65 const extensions::ExtensionSet
& extension_set
=
66 extensions::ExtensionRegistry::Get(profile())->enabled_extensions();
67 const std::vector
<ToolbarActionsModel::ToolbarItem
>& toolbar_items
=
68 toolbar_model()->toolbar_items();
70 // This order should be reflected in the underlying model.
71 EXPECT_EQ(extension_b(), extension_set
.GetByID(toolbar_items
[0].id
));
72 EXPECT_EQ(extension_a(), extension_set
.GetByID(toolbar_items
[1].id
));
73 EXPECT_EQ(extension_c(), extension_set
.GetByID(toolbar_items
[2].id
));
75 // Simulate a drag and drop to the left.
76 ui::OSExchangeData drop_data2
;
77 // Drag extension A from index 1...
78 BrowserActionDragData
browser_action_drag_data2(extension_a()->id(), 1u);
79 browser_action_drag_data2
.Write(profile(), &drop_data2
);
80 // ...to the left of extension B (which is now at index 0).
81 location
= gfx::Point(view
->x(), view
->y());
82 ui::DropTargetEvent
target_event2(
83 drop_data2
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
86 container
->OnDragUpdated(target_event2
);
87 container
->OnPerformDrop(target_event2
);
89 // Order should be restored to A B C.
90 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
91 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
92 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
94 // Shrink the size of the container so we have an overflow menu.
95 toolbar_model()->SetVisibleIconCount(2u);
96 EXPECT_EQ(2u, container
->VisibleBrowserActions());
97 ASSERT_TRUE(container
->chevron());
98 EXPECT_TRUE(container
->chevron()->visible());
100 // Simulate a drag and drop from the overflow menu.
101 ui::OSExchangeData drop_data3
;
102 // Drag extension C from index 2 (in the overflow menu)...
103 BrowserActionDragData
browser_action_drag_data3(extension_c()->id(), 2u);
104 browser_action_drag_data3
.Write(profile(), &drop_data3
);
105 // ...to the left of extension B (which is back in index 1 on the main bar).
106 location
= gfx::Point(view
->x(), view
->y());
107 ui::DropTargetEvent
target_event3(
108 drop_data3
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
111 container
->OnDragUpdated(target_event3
);
112 container
->OnPerformDrop(target_event3
);
114 // The order should have changed *and* the container should have grown to
115 // accommodate extension C. The new order should be A C B, and all three
116 // extensions should be visible, with no overflow menu.
117 EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(0));
118 EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(1));
119 EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(2));
120 EXPECT_EQ(3u, container
->VisibleBrowserActions());
121 EXPECT_FALSE(container
->chevron()->visible());
122 EXPECT_TRUE(toolbar_model()->all_icons_visible());
124 // TODO(devlin): Ideally, we'd also have tests for dragging from the legacy
125 // overflow menu (i.e., chevron) to the main bar, but this requires either
126 // having a fairly complicated interactive UI test or finding a good way to
127 // mock up the BrowserActionOverflowMenuController.
130 // Test that changes performed in one container affect containers in other
131 // windows so that it is consistent.
132 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, MultipleWindows
) {
134 BrowserActionsContainer
* first
=
135 BrowserView::GetBrowserViewForBrowser(browser())->toolbar()->
138 // Create a second browser.
139 Browser
* second_browser
= new Browser(
140 Browser::CreateParams(profile(), browser()->host_desktop_type()));
141 BrowserActionsContainer
* second
=
142 BrowserView::GetBrowserViewForBrowser(second_browser
)->toolbar()->
145 // Both containers should have the same order and visible actions, which
146 // is right now A B C.
147 EXPECT_EQ(3u, first
->VisibleBrowserActions());
148 EXPECT_EQ(3u, second
->VisibleBrowserActions());
149 EXPECT_EQ(extension_a()->id(), first
->GetIdAt(0u));
150 EXPECT_EQ(extension_a()->id(), second
->GetIdAt(0u));
151 EXPECT_EQ(extension_b()->id(), first
->GetIdAt(1u));
152 EXPECT_EQ(extension_b()->id(), second
->GetIdAt(1u));
153 EXPECT_EQ(extension_c()->id(), first
->GetIdAt(2u));
154 EXPECT_EQ(extension_c()->id(), second
->GetIdAt(2u));
156 // Simulate a drag and drop to the right.
157 ui::OSExchangeData drop_data
;
158 // Drag extension A from index 0...
159 BrowserActionDragData
browser_action_drag_data(extension_a()->id(), 0u);
160 browser_action_drag_data
.Write(profile(), &drop_data
);
161 ToolbarActionView
* view
= first
->GetViewForId(extension_b()->id());
162 // ...to the right of extension B.
163 gfx::Point
location(view
->x() + view
->width(), view
->y());
164 ui::DropTargetEvent
target_event(
165 drop_data
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
168 first
->OnDragUpdated(target_event
);
169 first
->OnPerformDrop(target_event
);
171 // The new order, B A C, should be reflected in *both* containers, even
172 // though the drag only happened in the first one.
173 EXPECT_EQ(extension_b()->id(), first
->GetIdAt(0u));
174 EXPECT_EQ(extension_b()->id(), second
->GetIdAt(0u));
175 EXPECT_EQ(extension_a()->id(), first
->GetIdAt(1u));
176 EXPECT_EQ(extension_a()->id(), second
->GetIdAt(1u));
177 EXPECT_EQ(extension_c()->id(), first
->GetIdAt(2u));
178 EXPECT_EQ(extension_c()->id(), second
->GetIdAt(2u));
180 // Next, simulate a resize by shrinking the container.
181 first
->OnResize(1, true);
182 // The first and second container should each have resized.
183 EXPECT_EQ(2u, first
->VisibleBrowserActions());
184 EXPECT_EQ(2u, second
->VisibleBrowserActions());
187 // Test that the BrowserActionsContainer responds correctly when the underlying
188 // model enters highlight mode, and that browser actions are undraggable in
189 // highlight mode. (Highlight mode itself it tested more thoroughly in the
190 // ToolbarActionsModel browsertests).
191 IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest
, HighlightMode
) {
194 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
195 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
197 BrowserActionsContainer
* container
=
198 BrowserView::GetBrowserViewForBrowser(browser())
199 ->toolbar()->browser_actions();
201 // Currently, dragging should be enabled.
202 ToolbarActionView
* action_view
= container
->GetToolbarActionViewAt(0);
203 ASSERT_TRUE(action_view
);
204 gfx::Point
point(action_view
->x(), action_view
->y());
205 EXPECT_TRUE(container
->CanStartDragForView(action_view
, point
, point
));
207 std::vector
<std::string
> action_ids
;
208 action_ids
.push_back(extension_a()->id());
209 action_ids
.push_back(extension_b()->id());
210 toolbar_model()->HighlightActions(action_ids
,
211 ToolbarActionsModel::HIGHLIGHT_WARNING
);
213 // Only two browser actions should be visible.
214 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
215 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
217 // We shouldn't be able to drag in highlight mode.
218 action_view
= container
->GetToolbarActionViewAt(0);
219 EXPECT_FALSE(container
->CanStartDragForView(action_view
, point
, point
));
221 // We should go back to normal after leaving highlight mode.
222 toolbar_model()->StopHighlighting();
223 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
224 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
225 action_view
= container
->GetToolbarActionViewAt(0);
226 EXPECT_TRUE(container
->CanStartDragForView(action_view
, point
, point
));
229 // Test the behavior of the overflow container for Extension Actions.
230 class BrowserActionsContainerOverflowTest
231 : public BrowserActionsBarRedesignBrowserTest
{
233 BrowserActionsContainerOverflowTest() : main_bar_(nullptr),
234 overflow_bar_(nullptr) {
236 ~BrowserActionsContainerOverflowTest() override
{}
239 // Returns true if the order of the ToolbarActionViews in |main_bar_|
240 // and |overflow_bar_| match.
241 bool ViewOrdersMatch();
243 // Returns Success if the visible count matches |expected_visible|. This means
244 // that the number of visible browser actions in |main_bar_| is
245 // |expected_visible| and shows the first icons, and that the overflow bar
246 // shows all (and only) the remainder.
247 testing::AssertionResult
VerifyVisibleCount(size_t expected_visible
)
251 BrowserActionsContainer
* main_bar() { return main_bar_
; }
252 BrowserActionsContainer
* overflow_bar() { return overflow_bar_
; }
255 void SetUpOnMainThread() override
;
256 void TearDownOnMainThread() override
;
258 // The main BrowserActionsContainer (owned by the browser view).
259 BrowserActionsContainer
* main_bar_
;
261 // A parent view for the overflow menu.
262 scoped_ptr
<views::View
> overflow_parent_
;
264 // The overflow BrowserActionsContainer. We manufacture this so that we don't
265 // have to open the wrench menu.
266 // Owned by the |overflow_parent_|.
267 BrowserActionsContainer
* overflow_bar_
;
269 DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerOverflowTest
);
272 void BrowserActionsContainerOverflowTest::SetUpOnMainThread() {
273 BrowserActionsBarBrowserTest::SetUpOnMainThread();
274 main_bar_
= BrowserView::GetBrowserViewForBrowser(browser())
275 ->toolbar()->browser_actions();
276 overflow_parent_
.reset(new views::View());
277 overflow_parent_
->set_owned_by_client();
278 overflow_bar_
= new BrowserActionsContainer(browser(), main_bar_
);
279 overflow_parent_
->AddChildView(overflow_bar_
);
282 void BrowserActionsContainerOverflowTest::TearDownOnMainThread() {
283 overflow_parent_
.reset();
284 BrowserActionsBarBrowserTest::TearDownOnMainThread();
287 bool BrowserActionsContainerOverflowTest::ViewOrdersMatch() {
288 if (main_bar_
->num_toolbar_actions() !=
289 overflow_bar_
->num_toolbar_actions())
291 for (size_t i
= 0; i
< main_bar_
->num_toolbar_actions(); ++i
) {
292 if (main_bar_
->GetIdAt(i
) != overflow_bar_
->GetIdAt(i
))
298 testing::AssertionResult
299 BrowserActionsContainerOverflowTest::VerifyVisibleCount(
300 size_t expected_visible
) {
301 // Views order should always match (as it is based directly off the model).
302 if (!ViewOrdersMatch())
303 return testing::AssertionFailure() << "View orders don't match";
305 // Loop through and check each browser action for proper visibility (which
306 // implicitly also guarantees that the proper number are visible).
307 for (size_t i
= 0; i
< overflow_bar_
->num_toolbar_actions(); ++i
) {
308 bool visible
= i
< expected_visible
;
309 if (main_bar_
->GetToolbarActionViewAt(i
)->visible() != visible
) {
310 return testing::AssertionFailure() << "Index " << i
<<
311 " has improper visibility in main: " << !visible
;
313 if (overflow_bar_
->GetToolbarActionViewAt(i
)->visible() == visible
) {
314 return testing::AssertionFailure() << "Index " << i
<<
315 " has improper visibility in overflow: " << visible
;
318 return testing::AssertionSuccess();
321 // Test the basic functionality of the BrowserActionsContainer in overflow mode.
322 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest
,
323 TestBasicActionOverflow
) {
326 // Since the overflow bar isn't attached to a view, we have to kick it in
327 // order to retrigger layout each time we change the number of icons in the
329 overflow_bar()->Layout();
331 // All actions are showing, and are in the installation order.
332 EXPECT_TRUE(toolbar_model()->all_icons_visible());
333 EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
334 ASSERT_EQ(3u, main_bar()->num_toolbar_actions());
335 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
336 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
337 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
338 EXPECT_TRUE(VerifyVisibleCount(3u));
340 // Reduce the visible count to 2. Order should be unchanged (A B C), but
341 // only A and B should be visible on the main bar.
342 toolbar_model()->SetVisibleIconCount(2u);
343 overflow_bar()->Layout(); // Kick.
344 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
345 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
346 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
347 EXPECT_TRUE(VerifyVisibleCount(2u));
349 // Move extension C to the first position. Order should now be C A B, with
350 // C and A visible in the main bar.
351 toolbar_model()->MoveActionIcon(extension_c()->id(), 0);
352 overflow_bar()->Layout(); // Kick.
353 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(0u));
354 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
355 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
356 EXPECT_TRUE(VerifyVisibleCount(2u));
358 // Hide action A. This results in it being sent to overflow, and reducing the
359 // visible size to 1, so the order should be C A B, with only C visible in the
361 extensions::ExtensionActionAPI::Get(profile())->SetBrowserActionVisibility(
364 overflow_bar()->Layout(); // Kick.
365 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(0u));
366 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
367 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
368 EXPECT_TRUE(VerifyVisibleCount(1u));
371 // Test drag and drop between the overflow container and the main container.
372 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest
,
373 TestOverflowDragging
) {
376 // Start with one extension in overflow.
377 toolbar_model()->SetVisibleIconCount(2u);
378 overflow_bar()->Layout();
380 // Verify starting state is A B [C].
381 ASSERT_EQ(3u, main_bar()->num_toolbar_actions());
382 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
383 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
384 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
385 EXPECT_TRUE(VerifyVisibleCount(2u));
387 // Drag extension A (on the main bar) to the left of extension C (in
389 ui::OSExchangeData drop_data
;
390 BrowserActionDragData
browser_action_drag_data(extension_a()->id(), 0u);
391 browser_action_drag_data
.Write(profile(), &drop_data
);
392 ToolbarActionView
* view
= overflow_bar()->GetViewForId(extension_c()->id());
393 gfx::Point
location(view
->x(), view
->y());
394 ui::DropTargetEvent
target_event(
395 drop_data
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
397 overflow_bar()->OnDragUpdated(target_event
);
398 overflow_bar()->OnPerformDrop(target_event
);
399 overflow_bar()->Layout();
401 // Order should now be B [A C].
402 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(0u));
403 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
404 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
405 EXPECT_TRUE(VerifyVisibleCount(1u));
407 // Drag extension A back from overflow to the main bar.
408 ui::OSExchangeData drop_data2
;
409 BrowserActionDragData
browser_action_drag_data2(extension_a()->id(), 1u);
410 browser_action_drag_data2
.Write(profile(), &drop_data2
);
411 view
= main_bar()->GetViewForId(extension_b()->id());
412 location
= gfx::Point(view
->x(), view
->y());
413 ui::DropTargetEvent
target_event2(
414 drop_data2
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
416 main_bar()->OnDragUpdated(target_event2
);
417 main_bar()->OnPerformDrop(target_event2
);
419 // Order should be A B [C] again.
420 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
421 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(1u));
422 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(2u));
423 EXPECT_TRUE(VerifyVisibleCount(2u));
425 // Drag extension C from overflow to the main bar (before extension B).
426 ui::OSExchangeData drop_data3
;
427 BrowserActionDragData
browser_action_drag_data3(extension_c()->id(), 2u);
428 browser_action_drag_data3
.Write(profile(), &drop_data3
);
429 location
= gfx::Point(view
->x(), view
->y());
430 ui::DropTargetEvent
target_event3(
431 drop_data3
, location
, location
, ui::DragDropTypes::DRAG_MOVE
);
433 main_bar()->OnDragUpdated(target_event3
);
434 main_bar()->OnPerformDrop(target_event3
);
436 // Order should be A C B, and there should be no extensions in overflow.
437 EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(0u));
438 EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(1u));
439 EXPECT_EQ(extension_b()->id(), main_bar()->GetIdAt(2u));
440 EXPECT_TRUE(VerifyVisibleCount(3u));