Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / toolbar / toolbar_controller.mm
blob12feb683f90301b2cddbb84137f1387628ad4eb7
1 // Copyright (c) 2012 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 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
7 #include <algorithm>
9 #include "base/mac/bundle_locations.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/mac_util.h"
12 #include "base/memory/singleton.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/app/chrome_command_ids.h"
18 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
19 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/command_updater.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/search/search.h"
24 #include "chrome/browser/themes/theme_service.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_commands.h"
27 #include "chrome/browser/ui/browser_window.h"
28 #import "chrome/browser/ui/cocoa/background_gradient_view.h"
29 #include "chrome/browser/ui/cocoa/drag_util.h"
30 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
31 #import "chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h"
32 #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
33 #import "chrome/browser/ui/cocoa/gradient_button_cell.h"
34 #import "chrome/browser/ui/cocoa/image_button_cell.h"
35 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
36 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
37 #import "chrome/browser/ui/cocoa/menu_button.h"
38 #import "chrome/browser/ui/cocoa/toolbar/back_forward_menu_controller.h"
39 #import "chrome/browser/ui/cocoa/toolbar/reload_button_cocoa.h"
40 #import "chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.h"
41 #import "chrome/browser/ui/cocoa/toolbar/toolbar_view_cocoa.h"
42 #import "chrome/browser/ui/cocoa/toolbar/wrench_toolbar_button_cell.h"
43 #import "chrome/browser/ui/cocoa/view_id_util.h"
44 #import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.h"
45 #include "chrome/browser/ui/omnibox/omnibox_view.h"
46 #include "chrome/browser/ui/tabs/tab_strip_model.h"
47 #include "chrome/browser/ui/toolbar/wrench_menu_badge_controller.h"
48 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
49 #include "chrome/common/pref_names.h"
50 #include "chrome/grit/chromium_strings.h"
51 #include "chrome/grit/generated_resources.h"
52 #include "components/metrics/proto/omnibox_event.pb.h"
53 #include "components/omnibox/autocomplete_match.h"
54 #include "components/search_engines/template_url_service.h"
55 #include "components/url_fixer/url_fixer.h"
56 #include "content/public/browser/web_contents.h"
57 #include "grit/theme_resources.h"
58 #import "ui/base/cocoa/menu_controller.h"
59 #include "ui/base/l10n/l10n_util.h"
60 #include "ui/base/l10n/l10n_util_mac.h"
61 #include "ui/gfx/geometry/rect.h"
62 #include "ui/gfx/image/image.h"
64 using content::OpenURLParams;
65 using content::Referrer;
66 using content::WebContents;
68 namespace {
70 // Height of the toolbar in pixels when the bookmark bar is closed.
71 const CGFloat kBaseToolbarHeightNormal = 35.0;
73 // The padding above the toolbar elements. This is calculated from the values
74 // in Toolbar.xib: the height of the toolbar (35) minus the height of the child
75 // elements (29) minus the y-origin of the elements (4).
76 const CGFloat kToolbarElementTopPadding = 2.0;
78 // The minimum width of the location bar in pixels.
79 const CGFloat kMinimumLocationBarWidth = 100.0;
81 // The amount of left padding that the wrench menu should have.
82 const CGFloat kWrenchMenuLeftPadding = 3.0;
84 class BrowserActionsContainerDelegate :
85     public BrowserActionsContainerViewSizeDelegate {
86  public:
87   BrowserActionsContainerDelegate(
88       AutocompleteTextField* location_bar,
89       BrowserActionsContainerView* browser_actions_container_view);
90   ~BrowserActionsContainerDelegate() override;
92  private:
93   // BrowserActionsContainerSizeDelegate:
94   CGFloat GetMaxAllowedWidth() override;
96   AutocompleteTextField* location_bar_;
97   BrowserActionsContainerView* browser_actions_container_;
99   DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerDelegate);
102 BrowserActionsContainerDelegate::BrowserActionsContainerDelegate(
103     AutocompleteTextField* location_bar,
104     BrowserActionsContainerView* browser_actions_container_view)
105     : location_bar_(location_bar),
106       browser_actions_container_(browser_actions_container_view) {
107   [browser_actions_container_ setDelegate:this];
110 BrowserActionsContainerDelegate::~BrowserActionsContainerDelegate() {
111   [browser_actions_container_ setDelegate:nil];
114 CGFloat BrowserActionsContainerDelegate::GetMaxAllowedWidth() {
115   CGFloat location_bar_flex =
116       NSWidth([location_bar_ frame]) - kMinimumLocationBarWidth;
117   return NSWidth([browser_actions_container_ frame]) + location_bar_flex;
120 }  // namespace
122 @interface ToolbarController()
123 @property(assign, nonatomic) Browser* browser;
124 - (void)addAccessibilityDescriptions;
125 - (void)initCommandStatus:(CommandUpdater*)commands;
126 - (void)prefChanged:(const std::string&)prefName;
127 - (BackgroundGradientView*)backgroundGradientView;
128 - (void)toolbarFrameChanged;
129 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate;
130 - (void)maintainMinimumLocationBarWidth;
131 - (void)adjustBrowserActionsContainerForNewWindow:(NSNotification*)notification;
132 - (void)browserActionsContainerDragged:(NSNotification*)notification;
133 - (void)browserActionsVisibilityChanged:(NSNotification*)notification;
134 - (void)browserActionsContainerWillAnimate:(NSNotification*)notification;
135 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate;
136 - (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
137                            animate:(BOOL)animate;
138 @end
140 namespace ToolbarControllerInternal {
142 // A class registered for C++ notifications. This is used to detect changes in
143 // preferences and upgrade available notifications. Bridges the notification
144 // back to the ToolbarController.
145 class NotificationBridge : public WrenchMenuBadgeController::Delegate {
146  public:
147   explicit NotificationBridge(ToolbarController* controller)
148       : controller_(controller),
149         badge_controller_([controller browser]->profile(), this) {
150   }
151   ~NotificationBridge() override {}
153   void UpdateBadgeSeverity() {
154     badge_controller_.UpdateDelegate();
155   }
157   void UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
158                            WrenchIconPainter::Severity severity,
159                            bool animate) override {
160     [controller_ updateWrenchButtonSeverity:severity animate:animate];
161   }
163   void OnPreferenceChanged(const std::string& pref_name) {
164     [controller_ prefChanged:pref_name];
165   }
167  private:
168   ToolbarController* controller_;  // weak, owns us
170   WrenchMenuBadgeController badge_controller_;
172   DISALLOW_COPY_AND_ASSIGN(NotificationBridge);
175 }  // namespace ToolbarControllerInternal
177 @implementation ToolbarController
179 @synthesize browser = browser_;
181 - (id)initWithCommands:(CommandUpdater*)commands
182                profile:(Profile*)profile
183                browser:(Browser*)browser
184         resizeDelegate:(id<ViewResizer>)resizeDelegate
185           nibFileNamed:(NSString*)nibName {
186   DCHECK(commands && profile && [nibName length]);
187   if ((self = [super initWithNibName:nibName
188                               bundle:base::mac::FrameworkBundle()])) {
189     commands_ = commands;
190     profile_ = profile;
191     browser_ = browser;
192     resizeDelegate_ = resizeDelegate;
193     hasToolbar_ = YES;
194     hasLocationBar_ = YES;
196     // Register for notifications about state changes for the toolbar buttons
197     commandObserver_.reset(new CommandObserverBridge(self, commands));
198     commandObserver_->ObserveCommand(IDC_BACK);
199     commandObserver_->ObserveCommand(IDC_FORWARD);
200     commandObserver_->ObserveCommand(IDC_RELOAD);
201     commandObserver_->ObserveCommand(IDC_HOME);
202     commandObserver_->ObserveCommand(IDC_BOOKMARK_PAGE);
203   }
204   return self;
207 - (id)initWithCommands:(CommandUpdater*)commands
208                profile:(Profile*)profile
209                browser:(Browser*)browser
210         resizeDelegate:(id<ViewResizer>)resizeDelegate {
211   if ((self = [self initWithCommands:commands
212                              profile:profile
213                              browser:browser
214                       resizeDelegate:resizeDelegate
215                         nibFileNamed:@"Toolbar"])) {
216   }
217   return self;
221 - (void)dealloc {
222   browserActionsContainerDelegate_.reset();
224   // Unset ViewIDs of toolbar elements.
225   // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
226   // |browserActionsContainerView_| are handled by themselves.
227   view_id_util::UnsetID(backButton_);
228   view_id_util::UnsetID(forwardButton_);
229   view_id_util::UnsetID(homeButton_);
230   view_id_util::UnsetID(wrenchButton_);
232   // Make sure any code in the base class which assumes [self view] is
233   // the "parent" view continues to work.
234   hasToolbar_ = YES;
235   hasLocationBar_ = YES;
237   [[NSNotificationCenter defaultCenter] removeObserver:self];
239   if (trackingArea_.get())
240     [[self view] removeTrackingArea:trackingArea_.get()];
241   [super dealloc];
244 // Called after the view is done loading and the outlets have been hooked up.
245 // Now we can hook up bridges that rely on UI objects such as the location
246 // bar and button state.
247 - (void)awakeFromNib {
248   [[backButton_ cell] setImageID:IDR_BACK
249                   forButtonState:image_button_cell::kDefaultState];
250   [[backButton_ cell] setImageID:IDR_BACK_H
251                   forButtonState:image_button_cell::kHoverState];
252   [[backButton_ cell] setImageID:IDR_BACK_P
253                   forButtonState:image_button_cell::kPressedState];
254   [[backButton_ cell] setImageID:IDR_BACK_D
255                   forButtonState:image_button_cell::kDisabledState];
257   [[forwardButton_ cell] setImageID:IDR_FORWARD
258                      forButtonState:image_button_cell::kDefaultState];
259   [[forwardButton_ cell] setImageID:IDR_FORWARD_H
260                      forButtonState:image_button_cell::kHoverState];
261   [[forwardButton_ cell] setImageID:IDR_FORWARD_P
262                      forButtonState:image_button_cell::kPressedState];
263   [[forwardButton_ cell] setImageID:IDR_FORWARD_D
264                      forButtonState:image_button_cell::kDisabledState];
266   [[reloadButton_ cell] setImageID:IDR_RELOAD
267                     forButtonState:image_button_cell::kDefaultState];
268   [[reloadButton_ cell] setImageID:IDR_RELOAD_H
269                     forButtonState:image_button_cell::kHoverState];
270   [[reloadButton_ cell] setImageID:IDR_RELOAD_P
271                     forButtonState:image_button_cell::kPressedState];
273   [[homeButton_ cell] setImageID:IDR_HOME
274                   forButtonState:image_button_cell::kDefaultState];
275   [[homeButton_ cell] setImageID:IDR_HOME_H
276                   forButtonState:image_button_cell::kHoverState];
277   [[homeButton_ cell] setImageID:IDR_HOME_P
278                   forButtonState:image_button_cell::kPressedState];
280   [[wrenchButton_ cell] setImageID:IDR_TOOLS
281                     forButtonState:image_button_cell::kDefaultState];
282   [[wrenchButton_ cell] setImageID:IDR_TOOLS_H
283                     forButtonState:image_button_cell::kHoverState];
284   [[wrenchButton_ cell] setImageID:IDR_TOOLS_P
285                     forButtonState:image_button_cell::kPressedState];
287   notificationBridge_.reset(
288       new ToolbarControllerInternal::NotificationBridge(self));
289   notificationBridge_->UpdateBadgeSeverity();
291   [wrenchButton_ setOpenMenuOnClick:YES];
293   [backButton_ setOpenMenuOnRightClick:YES];
294   [forwardButton_ setOpenMenuOnRightClick:YES];
296   [backButton_ setHandleMiddleClick:YES];
297   [forwardButton_ setHandleMiddleClick:YES];
298   [reloadButton_ setHandleMiddleClick:YES];
299   [homeButton_ setHandleMiddleClick:YES];
301   [self initCommandStatus:commands_];
302   [reloadButton_ setCommandUpdater:commands_];
304   locationBarView_.reset(new LocationBarViewMac(locationBar_, commands_,
305                                                 profile_, browser_));
306   [locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
308   // Register pref observers for the optional home and page/options buttons
309   // and then add them to the toolbar based on those prefs.
310   PrefService* prefs = profile_->GetPrefs();
311   showHomeButton_.Init(
312       prefs::kShowHomeButton, prefs,
313       base::Bind(
314           &ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged,
315           base::Unretained(notificationBridge_.get())));
316   [self showOptionalHomeButton];
317   [self installWrenchMenu];
319   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
321   // Create the controllers for the back/forward menus.
322   backMenuController_.reset([[BackForwardMenuController alloc]
323           initWithBrowser:browser_
324                 modelType:BACK_FORWARD_MENU_TYPE_BACK
325                    button:backButton_]);
326   forwardMenuController_.reset([[BackForwardMenuController alloc]
327           initWithBrowser:browser_
328                 modelType:BACK_FORWARD_MENU_TYPE_FORWARD
329                    button:forwardButton_]);
331   // For a popup window, the toolbar is really just a location bar
332   // (see override for [ToolbarController view], below).  When going
333   // fullscreen, we remove the toolbar controller's view from the view
334   // hierarchy.  Calling [locationBar_ removeFromSuperview] when going
335   // fullscreen causes it to get released, making us unhappy
336   // (http://crbug.com/18551).  We avoid the problem by incrementing
337   // the retain count of the location bar; use of the scoped object
338   // helps us remember to release it.
339   locationBarRetainer_.reset([locationBar_ retain]);
340   trackingArea_.reset(
341       [[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
342                                    options:NSTrackingMouseMoved |
343                                            NSTrackingInVisibleRect |
344                                            NSTrackingMouseEnteredAndExited |
345                                            NSTrackingActiveAlways
346                                      owner:self
347                                   userInfo:nil]);
348   NSView* toolbarView = [self view];
349   [toolbarView addTrackingArea:trackingArea_.get()];
351   // If the user has any Browser Actions installed, the container view for them
352   // may have to be resized depending on the width of the toolbar frame.
353   [toolbarView setPostsFrameChangedNotifications:YES];
354   [[NSNotificationCenter defaultCenter]
355       addObserver:self
356          selector:@selector(toolbarFrameChanged)
357              name:NSViewFrameDidChangeNotification
358            object:toolbarView];
360   // Set ViewIDs for toolbar elements which don't have their dedicated class.
361   // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
362   // |browserActionsContainerView_| are handled by themselves.
363   view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON);
364   view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON);
365   view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON);
366   view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU);
368   [self addAccessibilityDescriptions];
371 - (void)addAccessibilityDescriptions {
372   // Set accessibility descriptions. http://openradar.appspot.com/7496255
373   NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
374   [[backButton_ cell]
375       accessibilitySetOverrideValue:description
376                        forAttribute:NSAccessibilityDescriptionAttribute];
377   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_FORWARD);
378   [[forwardButton_ cell]
379       accessibilitySetOverrideValue:description
380                        forAttribute:NSAccessibilityDescriptionAttribute];
381   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_RELOAD);
382   [[reloadButton_ cell]
383       accessibilitySetOverrideValue:description
384                        forAttribute:NSAccessibilityDescriptionAttribute];
385   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_HOME);
386   [[homeButton_ cell]
387       accessibilitySetOverrideValue:description
388                        forAttribute:NSAccessibilityDescriptionAttribute];
389   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
390   [[locationBar_ cell]
391       accessibilitySetOverrideValue:description
392                        forAttribute:NSAccessibilityDescriptionAttribute];
393   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP);
394   [[wrenchButton_ cell]
395       accessibilitySetOverrideValue:description
396                        forAttribute:NSAccessibilityDescriptionAttribute];
399 - (void)mouseExited:(NSEvent*)theEvent {
400   [[hoveredButton_ cell] setIsMouseInside:NO];
401   [hoveredButton_ release];
402   hoveredButton_ = nil;
405 - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent {
406   NSButton* targetView = (NSButton*)[[self view]
407                                      hitTest:[theEvent locationInWindow]];
409   // Only interpret the view as a hoverButton_ if it's both button and has a
410   // button cell that cares.  GradientButtonCell derived cells care.
411   if (([targetView isKindOfClass:[NSButton class]]) &&
412       ([[targetView cell]
413          respondsToSelector:@selector(setIsMouseInside:)]))
414     return targetView;
415   return nil;
418 - (void)mouseMoved:(NSEvent*)theEvent {
419   NSButton* targetView = [self hoverButtonForEvent:theEvent];
420   if (hoveredButton_ != targetView) {
421     [[hoveredButton_ cell] setIsMouseInside:NO];
422     [[targetView cell] setIsMouseInside:YES];
423     [hoveredButton_ release];
424     hoveredButton_ = [targetView retain];
425   }
428 - (void)mouseEntered:(NSEvent*)event {
429   [self mouseMoved:event];
432 - (LocationBarViewMac*)locationBarBridge {
433   return locationBarView_.get();
436 - (void)focusLocationBar:(BOOL)selectAll {
437   if (locationBarView_.get()) {
438     locationBarView_->FocusLocation(selectAll ? true : false);
439   }
442 // Called when the state for a command changes to |enabled|. Update the
443 // corresponding UI element.
444 - (void)enabledStateChangedForCommand:(NSInteger)command enabled:(BOOL)enabled {
445   NSButton* button = nil;
446   switch (command) {
447     case IDC_BACK:
448       button = backButton_;
449       break;
450     case IDC_FORWARD:
451       button = forwardButton_;
452       break;
453     case IDC_HOME:
454       button = homeButton_;
455       break;
456   }
457   [button setEnabled:enabled];
460 // Init the enabled state of the buttons on the toolbar to match the state in
461 // the controller.
462 - (void)initCommandStatus:(CommandUpdater*)commands {
463   [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
464   [forwardButton_
465       setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO];
466   [reloadButton_ setEnabled:YES];
467   [homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO];
470 - (void)updateToolbarWithContents:(WebContents*)tab {
471   locationBarView_->Update(tab);
473   [locationBar_ updateMouseTracking];
475   if (browserActionsController_.get()) {
476     [browserActionsController_ update];
477   }
479   BOOL needReloadMenu = chrome::IsDebuggerAttachedToCurrentTab(browser_);
480   [reloadButton_ setMenuEnabled:needReloadMenu];
483 - (void)resetTabState:(WebContents*)tab {
484   locationBarView_->ResetTabState(tab);
487 - (void)setStarredState:(BOOL)isStarred {
488   locationBarView_->SetStarred(isStarred);
491 - (void)setTranslateIconLit:(BOOL)on {
492   locationBarView_->SetTranslateIconLit(on);
495 - (void)setOverflowedToolbarActionWantsToRun:(BOOL)overflowedActionWantsToRun {
496   WrenchToolbarButtonCell* cell =
497       base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
498   [cell setOverflowedToolbarActionWantsToRun:overflowedActionWantsToRun];
501 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
502   locationBarView_->ZoomChangedForActiveTab(
503       canShowBubble && ![wrenchMenuController_ isMenuOpen]);
506 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
507   [reloadButton_ setIsLoading:isLoading force:force];
510 - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar {
511   [self view];  // Force nib loading.
513   hasToolbar_ = toolbar;
515   // If there's a toolbar, there must be a location bar.
516   DCHECK((toolbar && locBar) || !toolbar);
517   hasLocationBar_ = toolbar ? YES : locBar;
519   // Decide whether to hide/show based on whether there's a location bar.
520   [[self view] setHidden:!hasLocationBar_];
522   // Make location bar not editable when in a pop-up.
523   locationBarView_->SetEditable(toolbar);
526 - (NSView*)view {
527   if (hasToolbar_)
528     return [super view];
529   return locationBar_;
532 // (Private) Returns the backdrop to the toolbar.
533 - (BackgroundGradientView*)backgroundGradientView {
534   // We really do mean |[super view]|; see our override of |-view|.
535   DCHECK([[super view] isKindOfClass:[BackgroundGradientView class]]);
536   return (BackgroundGradientView*)[super view];
539 - (id)customFieldEditorForObject:(id)obj {
540   if (obj == locationBar_) {
541     // Lazilly construct Field editor, Cocoa UI code always runs on the
542     // same thread, so there shoudn't be a race condition here.
543     if (autocompleteTextFieldEditor_.get() == nil) {
544       autocompleteTextFieldEditor_.reset(
545           [[AutocompleteTextFieldEditor alloc] init]);
546     }
548     // This needs to be called every time, otherwise notifications
549     // aren't sent correctly.
550     DCHECK(autocompleteTextFieldEditor_.get());
551     [autocompleteTextFieldEditor_.get() setFieldEditor:YES];
552     if (base::mac::IsOSSnowLeopard()) {
553       // Manually transferring the drawsBackground and backgroundColor
554       // properties is necessary to ensure anti-aliased text on 10.6.
555       [autocompleteTextFieldEditor_
556           setDrawsBackground:[locationBar_ drawsBackground]];
557       [autocompleteTextFieldEditor_
558           setBackgroundColor:[locationBar_ backgroundColor]];
559     }
560     return autocompleteTextFieldEditor_.get();
561   }
562   return nil;
565 // Returns an array of views in the order of the outlets above.
566 - (NSArray*)toolbarViews {
567   return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_,
568              homeButton_, wrenchButton_, locationBar_,
569              browserActionsContainerView_, nil];
572 // Moves |rect| to the right by |delta|, keeping the right side fixed by
573 // shrinking the width to compensate. Passing a negative value for |deltaX|
574 // moves to the left and increases the width.
575 - (NSRect)adjustRect:(NSRect)rect byAmount:(CGFloat)deltaX {
576   NSRect frame = NSOffsetRect(rect, deltaX, 0);
577   frame.size.width -= deltaX;
578   return frame;
581 // Show or hide the home button based on the pref.
582 - (void)showOptionalHomeButton {
583   // Ignore this message if only showing the URL bar.
584   if (!hasToolbar_)
585     return;
586   BOOL hide = showHomeButton_.GetValue() ? NO : YES;
587   if (hide == [homeButton_ isHidden])
588     return;  // Nothing to do, view state matches pref state.
590   // Always shift the text field by the width of the home button minus one pixel
591   // since the frame edges of each button are right on top of each other. When
592   // hiding the button, reverse the direction of the movement (to the left).
593   CGFloat moveX = [homeButton_ frame].size.width - 1.0;
594   if (hide)
595     moveX *= -1;  // Reverse the direction of the move.
597   [locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
598                                  byAmount:moveX]];
599   [homeButton_ setHidden:hide];
602 // Install the menu wrench buttons. Calling this repeatedly is inexpensive so it
603 // can be done every time the buttons are shown.
604 - (void)installWrenchMenu {
605   if (wrenchMenuController_.get())
606     return;
608   wrenchMenuController_.reset(
609       [[WrenchMenuController alloc] initWithBrowser:browser_]);
610   [wrenchMenuController_ setUseWithPopUpButtonCell:YES];
611   [wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]];
614 - (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
615                            animate:(BOOL)animate {
616   WrenchToolbarButtonCell* cell =
617       base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
618   [cell setSeverity:severity shouldAnimate:animate];
621 - (void)prefChanged:(const std::string&)prefName {
622   if (prefName == prefs::kShowHomeButton) {
623     [self showOptionalHomeButton];
624   }
627 - (void)createBrowserActionButtons {
628   if (!browserActionsController_.get()) {
629     browserActionsContainerDelegate_.reset(
630         new BrowserActionsContainerDelegate(locationBar_,
631                                             browserActionsContainerView_));
632     browserActionsController_.reset([[BrowserActionsController alloc]
633             initWithBrowser:browser_
634               containerView:browserActionsContainerView_
635              mainController:nil]);
636     [[NSNotificationCenter defaultCenter]
637         addObserver:self
638            selector:@selector(browserActionsContainerDragged:)
639                name:kBrowserActionGrippyDraggingNotification
640              object:browserActionsContainerView_];
641     [[NSNotificationCenter defaultCenter]
642         addObserver:self
643            selector:@selector(browserActionsVisibilityChanged:)
644                name:kBrowserActionVisibilityChangedNotification
645              object:browserActionsController_];
646     [[NSNotificationCenter defaultCenter]
647         addObserver:self
648            selector:@selector(browserActionsContainerWillAnimate:)
649                name:kBrowserActionsContainerWillAnimate
650              object:browserActionsContainerView_];
651     [[NSNotificationCenter defaultCenter]
652         addObserver:self
653            selector:@selector(adjustBrowserActionsContainerForNewWindow:)
654                name:NSWindowDidBecomeKeyNotification
655              object:[[self view] window]];
656   }
657   if (![browserActionsContainerView_ isHidden])
658     [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
661 - (void)adjustBrowserActionsContainerForNewWindow:
662     (NSNotification*)notification {
663   [self toolbarFrameChanged];
664   [[NSNotificationCenter defaultCenter]
665       removeObserver:self
666                 name:NSWindowDidBecomeKeyNotification
667               object:[[self view] window]];
670 - (void)browserActionsContainerDragged:(NSNotification*)notification {
671   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
674 - (void)browserActionsVisibilityChanged:(NSNotification*)notification {
675   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
678 - (void)browserActionsContainerWillAnimate:(NSNotification*)notification {
679   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES];
682 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate {
683   CGFloat locationBarXPos = NSMaxX([locationBar_ frame]);
684   CGFloat leftDistance = 0.0;
686   if ([browserActionsContainerView_ isHidden]) {
687     CGFloat edgeXPos = [wrenchButton_ frame].origin.x;
688     leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding;
689   } else {
690     leftDistance = NSMinX([browserActionsContainerView_ animationEndFrame]) -
691         locationBarXPos;
692   }
693   if (leftDistance != 0.0)
694     [self adjustLocationSizeBy:leftDistance animate:animate];
695   else
696     [locationBar_ stopAnimation];
699 - (void)maintainMinimumLocationBarWidth {
700   CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
701   locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
702   if (locationBarAtMinSize_) {
703     CGFloat dX = kMinimumLocationBarWidth - locationBarWidth;
704     [self adjustLocationSizeBy:dX animate:NO];
705   }
708 - (void)toolbarFrameChanged {
709   // Do nothing if the frame changes but no Browser Action Controller is
710   // present.
711   if (!browserActionsController_.get())
712     return;
714   if ([browserActionsContainerView_ isAnimating]) {
715     // If the browser actions container is animating, we need to stop it first,
716     // because the frame it's animating for could be incorrect with the new
717     // bounds (if, for instance, the bookmark bar was added).
718     // This will advance to the end of the animation, so we also need to adjust
719     // it afterwards.
720     [browserActionsContainerView_ stopAnimation];
721     NSRect containerFrame = [browserActionsContainerView_ frame];
722     containerFrame.origin.y =
723         NSHeight([[self view] frame]) - NSHeight(containerFrame) -
724         kToolbarElementTopPadding;
725     [browserActionsContainerView_ setFrame:containerFrame];
726     [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
727   }
729   [self maintainMinimumLocationBarWidth];
731   if (locationBarAtMinSize_) {
732     // Once the grippy is pinned, leave it until it is explicity un-pinned.
733     [browserActionsContainerView_ setGrippyPinned:YES];
734     NSRect containerFrame = [browserActionsContainerView_ frame];
735     // Determine how much the container needs to move in case it's overlapping
736     // with the location bar.
737     CGFloat dX = NSMaxX([locationBar_ frame]) - containerFrame.origin.x;
738     containerFrame = NSOffsetRect(containerFrame, dX, 0);
739     containerFrame.size.width -= dX;
740     [browserActionsContainerView_ setFrame:containerFrame];
741   } else if (!locationBarAtMinSize_ &&
742       [browserActionsContainerView_ grippyPinned]) {
743     // Expand out the container until it hits the saved size, then unpin the
744     // grippy.
745     // Add 0.1 pixel so that it doesn't hit the minimum width codepath above.
746     CGFloat dX = NSWidth([locationBar_ frame]) -
747         (kMinimumLocationBarWidth + 0.1);
748     NSRect containerFrame = [browserActionsContainerView_ frame];
749     containerFrame = NSOffsetRect(containerFrame, -dX, 0);
750     containerFrame.size.width += dX;
751     CGFloat savedContainerWidth =
752         [browserActionsController_ preferredSize].width();
753     if (NSWidth(containerFrame) >= savedContainerWidth) {
754       containerFrame = NSOffsetRect(containerFrame,
755           NSWidth(containerFrame) - savedContainerWidth, 0);
756       containerFrame.size.width = savedContainerWidth;
757       [browserActionsContainerView_ setGrippyPinned:NO];
758     }
759     [browserActionsContainerView_ setFrame:containerFrame];
760     [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
761   }
764 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate {
765   // Ensure that the location bar is in its proper place.
766   NSRect locationFrame = [locationBar_ frame];
767   locationFrame.size.width += dX;
769   [locationBar_ stopAnimation];
771   if (animate)
772     [locationBar_ animateToFrame:locationFrame];
773   else
774     [locationBar_ setFrame:locationFrame];
777 - (NSPoint)bookmarkBubblePoint {
778   if (locationBarView_->IsStarEnabled())
779     return locationBarView_->GetBookmarkBubblePoint();
781   // Grab bottom middle of hotdogs.
782   NSRect frame = wrenchButton_.frame;
783   NSPoint point = NSMakePoint(NSMidX(frame), NSMinY(frame));
784   // Inset to account for the whitespace around the hotdogs.
785   point.y += wrench_menu_controller::kWrenchBubblePointOffsetY;
786   return [self.view convertPoint:point toView:nil];
789 - (NSPoint)managePasswordsBubblePoint {
790   return locationBarView_->GetManagePasswordsBubblePoint();
793 - (NSPoint)translateBubblePoint {
794   return locationBarView_->GetTranslateBubblePoint();
797 - (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight {
798   // With no toolbar, just ignore the compression.
799   if (!hasToolbar_)
800     return NSHeight([locationBar_ frame]);
802   return kBaseToolbarHeightNormal - compressByHeight;
805 - (void)setDividerOpacity:(CGFloat)opacity {
806   BackgroundGradientView* view = [self backgroundGradientView];
807   [view setShowsDivider:(opacity > 0 ? YES : NO)];
809   // We may not have a toolbar view (e.g., popup windows only have a location
810   // bar).
811   if ([view isKindOfClass:[ToolbarView class]]) {
812     ToolbarView* toolbarView = (ToolbarView*)view;
813     [toolbarView setDividerOpacity:opacity];
814   }
816   [view setNeedsDisplay:YES];
819 - (BrowserActionsController*)browserActionsController {
820   return browserActionsController_.get();
823 - (NSView*)wrenchButton {
824   return wrenchButton_;
827 - (WrenchMenuController*)wrenchMenuController {
828   return wrenchMenuController_.get();
831 // (URLDropTargetController protocol)
832 - (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point {
833   // TODO(viettrungluu): This code is more or less copied from the code in
834   // |TabStripController|. I'll refactor this soon to make it common and expand
835   // its capabilities (e.g., allow text DnD).
836   if ([urls count] < 1) {
837     NOTREACHED();
838     return;
839   }
841   // TODO(viettrungluu): dropping multiple URLs?
842   if ([urls count] > 1)
843     NOTIMPLEMENTED();
845   // Get the first URL and fix it up.
846   GURL url(url_fixer::FixupURL(base::SysNSStringToUTF8([urls objectAtIndex:0]),
847                                std::string()));
849   if (url.SchemeIs(url::kJavaScriptScheme)) {
850     browser_->window()->GetLocationBar()->GetOmniboxView()->SetUserText(
851           OmniboxView::StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())));
852   }
853   OpenURLParams params(
854       url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
855   browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
858 // (URLDropTargetController protocol)
859 - (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point {
860   // TODO(viettrungluu): This code is more or less copied from the code in
861   // |TabStripController|. I'll refactor this soon to make it common and expand
862   // its capabilities (e.g., allow text DnD).
864   // If the input is plain text, classify the input and make the URL.
865   AutocompleteMatch match;
866   AutocompleteClassifierFactory::GetForProfile(browser_->profile())->Classify(
867       base::SysNSStringToUTF16(text), false, false,
868       metrics::OmniboxEventProto::BLANK, &match, NULL);
869   GURL url(match.destination_url);
871   OpenURLParams params(
872       url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
873   browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
876 // (URLDropTargetController protocol)
877 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
878   // Do nothing.
881 // (URLDropTargetController protocol)
882 - (void)hideDropURLsIndicatorInView:(NSView*)view {
883   // Do nothing.
886 // (URLDropTargetController protocol)
887 - (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
888   return drag_util::IsUnsupportedDropData(profile_, info);
891 @end