summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/browser_actions_container.h
blob: 8fb070d91c5d94a22366e625c0583494a69ebeac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_VIEWS_BROWSER_ACTIONS_CONTAINER_H_
#define CHROME_BROWSER_VIEWS_BROWSER_ACTIONS_CONTAINER_H_

#include <set>
#include <string>
#include <vector>

#include "app/slide_animation.h"
#include "base/task.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/extensions/image_loading_tracker.h"
#include "chrome/browser/views/browser_bubble.h"
#include "chrome/browser/views/extensions/browser_action_overflow_menu_controller.h"
#include "chrome/browser/views/extensions/extension_popup.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
#include "views/controls/button/menu_button.h"
#include "views/controls/menu/view_menu_delegate.h"
#include "views/controls/resize_gripper.h"
#include "views/view.h"

class Browser;
class BrowserActionsContainer;
class BrowserActionOverflowMenuController;
class BrowserActionsContainer;
class Extension;
class ExtensionAction;
class ExtensionPopup;
class PrefService;
class Profile;

namespace gfx {
class CanvasSkia;
}

namespace views {
class Menu2;
}

////////////////////////////////////////////////////////////////////////////////
// BrowserActionButton

// The BrowserActionButton is a specialization of the MenuButton class.
// It acts on a ExtensionAction, in this case a BrowserAction and handles
// loading the image for the button asynchronously on the file thread.
class BrowserActionButton : public views::MenuButton,
                            public views::ButtonListener,
                            public ImageLoadingTracker::Observer,
                            public NotificationObserver {
 public:
  BrowserActionButton(Extension* extension, BrowserActionsContainer* panel);

  // Call this instead of delete.
  void Destroy();

  ExtensionAction* browser_action() const { return browser_action_; }
  Extension* extension() { return extension_; }

  // Called to update the display to match the browser action's state.
  void UpdateState();

  // Returns the default icon, if any.
  const SkBitmap& default_icon() const { return default_icon_; }

  // Overridden from views::View. Return a 0-inset so the icon can draw all the
  // way to the edge of the view if it wants.
  virtual gfx::Insets GetInsets() const;

  // Overridden from views::ButtonListener:
  virtual void ButtonPressed(views::Button* sender, const views::Event& event);

  // Overridden from ImageLoadingTracker.
  virtual void OnImageLoaded(
      SkBitmap* image, ExtensionResource resource, int index);

  // Overridden from NotificationObserver:
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

  // MenuButton behavior overrides.  These methods all default to TextButton
  // behavior unless this button is a popup.  In that case, it uses MenuButton
  // behavior.  MenuButton has the notion of a child popup being shown where the
  // button will stay in the pushed state until the "menu" (a popup in this
  // case) is dismissed.
  virtual bool Activate();
  virtual bool OnMousePressed(const views::MouseEvent& e);
  virtual void OnMouseReleased(const views::MouseEvent& e, bool canceled);
  virtual bool OnKeyReleased(const views::KeyEvent& e);
  virtual void OnMouseExited(const views::MouseEvent& event);
  virtual void ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture);

  // Does this button's action have a popup?
  virtual bool IsPopup();
  virtual GURL GetPopupUrl();

  // Notifications when to set button state to pushed/not pushed (for when the
  // popup/context menu is hidden or shown by the container).
  void SetButtonPushed();
  void SetButtonNotPushed();

 private:
  virtual ~BrowserActionButton();

  // The browser action this view represents. The ExtensionAction is not owned
  // by this class.
  ExtensionAction* browser_action_;

  // The extension associated with the browser action we're displaying.
  Extension* extension_;

  // The object that is waiting for the image loading to complete
  // asynchronously.
  ImageLoadingTracker tracker_;

  // Whether we are currently showing/just finished showing a context menu.
  bool showing_context_menu_;

  // The default icon for our browser action. This might be non-empty if the
  // browser action had a value for default_icon in the manifest.
  SkBitmap default_icon_;

  // The browser action shelf.
  BrowserActionsContainer* panel_;

  scoped_refptr<ExtensionContextMenuModel> context_menu_contents_;
  scoped_ptr<views::Menu2> context_menu_menu_;

  NotificationRegistrar registrar_;

  friend class DeleteTask<BrowserActionButton>;

  DISALLOW_COPY_AND_ASSIGN(BrowserActionButton);
};


////////////////////////////////////////////////////////////////////////////////
// BrowserActionView
// A single section in the browser action container. This contains the actual
// BrowserActionButton, as well as the logic to paint the badge.

class BrowserActionView : public views::View {
 public:
  BrowserActionView(Extension* extension, BrowserActionsContainer* panel);
  virtual ~BrowserActionView();

  BrowserActionButton* button() { return button_; }

  // Allocates a canvas object on the heap and draws into it the icon for the
  // view as well as the badge (if any). Caller is responsible for deleting the
  // returned object.
  gfx::Canvas* GetIconWithBadge();

  // Accessibility accessors, overridden from View.
  virtual bool GetAccessibleRole(AccessibilityTypes::Role* role);

 private:
  virtual void Layout();

  // Override PaintChildren so that we can paint the badge on top of children.
  virtual void PaintChildren(gfx::Canvas* canvas);

  // The container for this view.
  BrowserActionsContainer* panel_;

  // The button this view contains.
  BrowserActionButton* button_;

  DISALLOW_COPY_AND_ASSIGN(BrowserActionView);
};

////////////////////////////////////////////////////////////////////////////////
//
// The BrowserActionsContainer is a container view, responsible for drawing the
// browser action icons (extensions that add icons to the toolbar).
//
// The BrowserActionsContainer (when it contains one or more icons) consists of
// the following elements, numbered as seen below the line:
//
//    || _ Icon _ Icon _ Icon _ [chevron] _ | _
//    -----------------------------------------
//     1 2   3  4                   5     6 7 8
//
// 1) The ResizeGripper view.
// 2) Padding  (kHorizontalPadding).
// 3) The browser action icon button (BrowserActionView).
// 4) Padding to visually separate icons from one another
//    (kBrowserActionButtonPadding). Not included if only one icon visible.
// 5) The chevron menu (MenuButton), shown when there is not enough room to show
//    all the icons.
// 6) Padding  (kDividerHorizontalMargin).
// 7) A thin vertical divider drawn during Paint to create visual separation for
//    the container from the Page and Wrench menus.
// 8) Padding  (kChevronRightMargin).
//
// The BrowserActionsContainer follows a few rules, in terms of user experience:
//
// 1) The container can never grow beyond the space needed to show all icons
// (hereby referred to as the max width).
// 2) The container can never shrink below the space needed to show just the
// resize gripper and the chevron (ignoring the case where there are no icons to
// show, in which case the container won't be visible anyway).
// 3) The container snaps into place (to the pixel count that fits the visible
// icons) to make sure there is no wasted space at the edges of the container.
// 4) If the user adds or removes icons (read: installs/uninstalls browser
// actions) we grow and shrink the container as needed - but ONLY if the
// container was at max width to begin with.
// 5) If the container is NOT at max width (has an overflow menu), we respect
// that size when adding and removing icons and DON'T grow/shrink the container.
// This means that new icons (which always appear at the far right) will show up
// in the overflow menu. The install bubble for extensions points to the chevron
// menu in this case.
//
// Resizing the BrowserActionsContainer:
//
// The ResizeGripper view sends OnResize messages to the BrowserActionsContainer
// class as the user drags the gripper. This modifies the value for
// |resize_amount_|. That indicates to the container that a resize is in
// progress and is used to calculate the size in GetPreferredSize(), though
// that function never exceeds the defined minimum and maximum size of the
// container.
//
// When the user releases the mouse (ends the resize), we calculate a target
// size for the container (animation_target_size_), clamp that value to the
// containers min and max and then animate from the *current* position (that the
// user has dragged the view to) to the target size.
//
// Animating the BrowserActionsContainer:
//
// Animations are used when snapping the container to a value that fits all
// visible icons. This can be triggered when the user finishes resizing the
// container or when Browser Actions are added/removed.
//
// We always animate from the current width (container_size_.width()) to the
// target size (animation_target_size_), using |resize_amount| to keep track of
// the animation progress.
//
// NOTE: When adding Browser Actions to a maximum width container (no overflow)
// we make sure to suppress the chevron menu if it wasn't visible. This is
// because we won't have enough space to show the new Browser Action until the
// animation ends and we don't want the chevron to flash into view while we are
// growing the container.
//
////////////////////////////////////////////////////////////////////////////////
class BrowserActionsContainer
  : public views::View,
    public views::ViewMenuDelegate,
    public views::DragController,
    public views::ResizeGripper::ResizeGripperDelegate,
    public AnimationDelegate,
    public ExtensionToolbarModel::Observer,
    public BrowserActionOverflowMenuController::Observer,
    public ExtensionContextMenuModel::PopupDelegate,
    public ExtensionPopup::Observer {
 public:
  BrowserActionsContainer(Browser* browser, views::View* owner_view);
  virtual ~BrowserActionsContainer();

  static void RegisterUserPrefs(PrefService* prefs);

  // Get the number of browser actions being displayed.
  int num_browser_actions() const { return browser_action_views_.size(); }

  // Whether we are performing resize animation on the container.
  bool animating() const { return animation_target_size_ > 0; }

  // Returns the chevron, if any.
  const views::View* chevron() const { return chevron_; }

  // Returns the profile this container is associated with.
  Profile* profile() const { return profile_; }

  // Returns the browser this container is associated with.
  Browser* browser() const { return browser_; }

  // Returns the current tab's ID, or -1 if there is no current tab.
  int GetCurrentTabId() const;

  // Get a particular browser action view.
  BrowserActionView* GetBrowserActionViewAt(int index) {
    return browser_action_views_[index];
  }

  // Retrieve the BrowserActionView for |extension|.
  BrowserActionView* GetBrowserActionView(ExtensionAction* action);

  // Update the views to reflect the state of the browser action icons.
  void RefreshBrowserActionViews();

  // Sets up the browser action view vector.
  void CreateBrowserActionViews();

  // Delete all browser action views.
  void DeleteBrowserActionViews();

  // Called when a browser action becomes visible/hidden.
  void OnBrowserActionVisibilityChanged();

  // Returns how many browser actions are visible.
  size_t VisibleBrowserActions() const;

  // Called when the user clicks on the browser action icon.
  void OnBrowserActionExecuted(BrowserActionButton* button,
                               bool inspect_with_devtools);

  // Overridden from views::View:
  virtual gfx::Size GetPreferredSize();
  virtual void Layout();
  virtual void Paint(gfx::Canvas* canvas);
  virtual void ViewHierarchyChanged(bool is_add,
                                    views::View* parent,
                                    views::View* child);
  virtual bool GetDropFormats(
      int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats);
  virtual bool AreDropTypesRequired();
  virtual bool CanDrop(const OSExchangeData& data);
  virtual void OnDragEntered(const views::DropTargetEvent& event);
  virtual int OnDragUpdated(const views::DropTargetEvent& event);
  virtual void OnDragExited();
  virtual int OnPerformDrop(const views::DropTargetEvent& event);
  virtual bool GetAccessibleRole(AccessibilityTypes::Role* role);

  // Overridden from views::ViewMenuDelegate:
  virtual void RunMenu(View* source, const gfx::Point& pt);

  // Overridden from views::DragController:
  virtual void WriteDragData(View* sender,
                             const gfx::Point& press_pt,
                             OSExchangeData* data);
  virtual int GetDragOperations(View* sender, const gfx::Point& p);
  virtual bool CanStartDrag(View* sender,
                            const gfx::Point& press_pt,
                            const gfx::Point& p);

  // Overridden from ResizeGripper::ResizeGripperDelegate:
  virtual void OnResize(int resize_amount, bool done_resizing);

  // Overridden from AnimationDelegate:
  virtual void AnimationProgressed(const Animation* animation);
  virtual void AnimationEnded(const Animation* animation);

  // Overridden from BrowserActionOverflowMenuController::Observer:
  virtual void NotifyMenuDeleted(
      BrowserActionOverflowMenuController* controller);

  // Overridden from ExtensionContextMenuModel::PopupDelegate
  virtual void InspectPopup(ExtensionAction* action);

  // Overriden from ExtensionPopup::Delegate
  virtual void ExtensionPopupIsClosing(ExtensionPopup* popup);

  // Moves a browser action with |id| to |new_index|.
  void MoveBrowserAction(const std::string& extension_id, size_t new_index);

  // Hide the current popup.
  void HidePopup();

  // Simulate a click on a browser action button.  This should only be
  // used by unit tests.
  void TestExecuteBrowserAction(int index);

  // Retrieve the current popup.  This should only be used by unit tests.
  ExtensionPopup* TestGetPopup() { return popup_; }

  // Set how many icons the container should show. This should only be used by
  // unit tests.
  void TestSetIconVisibilityCount(size_t icons);

  // During testing we can disable animations by setting this flag to true,
  // so that the bar resizes instantly, instead of having to poll it while it
  // animates to open/closed status.
  static bool disable_animations_during_testing_;

 private:
  friend class ShowFolderMenuTask;

  typedef std::vector<BrowserActionView*> BrowserActionViews;

  // ExtensionToolbarModel::Observer implementation.
  virtual void BrowserActionAdded(Extension* extension, int index);
  virtual void BrowserActionRemoved(Extension* extension);
  virtual void BrowserActionMoved(Extension* extension, int index);
  virtual void ModelLoaded();

  // Sets the initial container width.
  void SetContainerWidth();

  // Closes the overflow menu if open.
  void CloseOverflowMenu();

  // Cancels the timer for showing the drop down menu.
  void StopShowFolderDropMenuTimer();

  // Show the drop down folder after a slight delay.
  void StartShowFolderDropMenuTimer();

  // Show the overflow menu.
  void ShowDropFolder();

  // Sets the drop indicator position (and schedules paint if the position has
  // changed).
  void SetDropIndicator(int x_pos);

  // Takes a width in pixels, calculates how many icons fit within that space
  // (up to the maximum number of icons in our vector) and shaves off the
  // excess pixels. |allow_shrink_to_minimum| specifies whether this function
  // clamps the size down further (down to ContainerMinSize()) if there is not
  // room for even one icon. When determining how large the container should be
  // this should be |true|. When determining where to place items, such as the
  // drop indicator, this should be |false|.
  int ClampToNearestIconCount(int pixels, bool allow_shrink_to_minimum) const;

  // Calculates the width of the container area NOT used to show the icons (the
  // controls to the left and to the right of the icons).
  int WidthOfNonIconArea() const;

  // Given a number of |icons| return the amount of pixels needed to draw it,
  // including the controls (chevron if visible and resize gripper).
  int IconCountToWidth(int icons) const;

  // Returns the absolute minimum size you can shrink the container down to and
  // still show it. We account for the chevron and the resize gripper, but not
  // all the padding that we normally show if there are icons.
  int ContainerMinSize() const;

  // Animate to the target value (unless testing, in which case we go straight
  // to the target size).
  void Animate(Tween::Type type, int target_size);

  // Returns true if this extension should be shown in this toolbar. This can
  // return false if we are in an incognito window and the extension is disabled
  // for incognito.
  bool ShouldDisplayBrowserAction(Extension* extension);

  // The vector of browser actions (icons/image buttons for each action). Note
  // that not every BrowserAction in the ToolbarModel will necessarily be in
  // this collection. Some extensions may be disabled in incognito windows.
  BrowserActionViews browser_action_views_;

  Profile* profile_;

  // The Browser object the container is associated with.
  Browser* browser_;

  // The view that owns us.
  views::View* owner_view_;

  // The current popup and the button it came from.  NULL if no popup.
  ExtensionPopup* popup_;

  // The button that triggered the current popup (just a reference to a button
  // from browser_action_views_).
  BrowserActionButton* popup_button_;

  // The model that tracks the order of the toolbar icons.
  ExtensionToolbarModel* model_;

  // The current size of the container.
  gfx::Size container_size_;

  // The resize gripper for the container.
  views::ResizeGripper* resize_gripper_;

  // The chevron for accessing the overflow items.
  views::MenuButton* chevron_;

  // The menu to show for the overflow button (chevron). This class manages its
  // own lifetime so that it can stay alive during drag and drop operations.
  BrowserActionOverflowMenuController* overflow_menu_;

  // The animation that happens when the container snaps to place.
  scoped_ptr<SlideAnimation> resize_animation_;

  // Don't show the chevron while animating.
  bool suppress_chevron_;

  // This is used while the user is resizing (and when the animations are in
  // progress) to know how wide the delta is between the current state and what
  // we should draw.
  int resize_amount_;

  // Keeps track of the absolute pixel width the container should have when we
  // are done animating.
  int animation_target_size_;

  // The x position for where to draw the drop indicator. -1 if no indicator.
  int drop_indicator_position_;

  ScopedRunnableMethodFactory<BrowserActionsContainer> task_factory_;

  // Handles delayed showing of the overflow menu when hovering.
  ScopedRunnableMethodFactory<BrowserActionsContainer> show_menu_task_factory_;

  DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainer);
};

#endif  // CHROME_BROWSER_VIEWS_BROWSER_ACTIONS_CONTAINER_H_