Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / toolbar / toolbar_controller.mm
blobe83dd26656dfca24e929f25c7b0b758963ae2d7a
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_factory.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/command_observer.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/sync/sync_global_error_factory.h"
25 #include "chrome/browser/themes/theme_service.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/browser/ui/browser_commands.h"
28 #include "chrome/browser/ui/browser_window.h"
29 #import "chrome/browser/ui/cocoa/background_gradient_view.h"
30 #include "chrome/browser/ui/cocoa/drag_util.h"
31 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
32 #import "chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h"
33 #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
34 #import "chrome/browser/ui/cocoa/gradient_button_cell.h"
35 #import "chrome/browser/ui/cocoa/image_button_cell.h"
36 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
37 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
38 #import "chrome/browser/ui/cocoa/menu_button.h"
39 #import "chrome/browser/ui/cocoa/toolbar/back_forward_menu_controller.h"
40 #import "chrome/browser/ui/cocoa/toolbar/reload_button_cocoa.h"
41 #import "chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.h"
42 #import "chrome/browser/ui/cocoa/toolbar/toolbar_view_cocoa.h"
43 #import "chrome/browser/ui/cocoa/toolbar/wrench_toolbar_button_cell.h"
44 #import "chrome/browser/ui/cocoa/view_id_util.h"
45 #import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.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/browser/autocomplete_classifier.h"
54 #include "components/omnibox/browser/autocomplete_match.h"
55 #include "components/omnibox/browser/omnibox_view.h"
56 #include "components/search_engines/template_url_service.h"
57 #include "components/url_formatter/url_fixer.h"
58 #include "content/public/browser/web_contents.h"
59 #include "grit/theme_resources.h"
60 #import "ui/base/cocoa/menu_controller.h"
61 #include "ui/base/l10n/l10n_util.h"
62 #include "ui/base/l10n/l10n_util_mac.h"
63 #include "ui/gfx/geometry/rect.h"
64 #include "ui/gfx/image/image.h"
66 using content::OpenURLParams;
67 using content::Referrer;
68 using content::WebContents;
70 namespace {
72 // Height of the toolbar in pixels when the bookmark bar is closed.
73 const CGFloat kBaseToolbarHeightNormal = 35.0;
75 // The padding above the toolbar elements. This is calculated from the values
76 // in Toolbar.xib: the height of the toolbar (35) minus the height of the child
77 // elements (29) minus the y-origin of the elements (4).
78 const CGFloat kToolbarElementTopPadding = 2.0;
80 // The minimum width of the location bar in pixels.
81 const CGFloat kMinimumLocationBarWidth = 100.0;
83 // The amount of left padding that the wrench menu should have.
84 const CGFloat kWrenchMenuLeftPadding = 3.0;
86 class BrowserActionsContainerDelegate :
87     public BrowserActionsContainerViewSizeDelegate {
88  public:
89   BrowserActionsContainerDelegate(
90       AutocompleteTextField* location_bar,
91       BrowserActionsContainerView* browser_actions_container_view);
92   ~BrowserActionsContainerDelegate() override;
94  private:
95   // BrowserActionsContainerSizeDelegate:
96   CGFloat GetMaxAllowedWidth() override;
98   AutocompleteTextField* location_bar_;
99   BrowserActionsContainerView* browser_actions_container_;
101   DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerDelegate);
104 BrowserActionsContainerDelegate::BrowserActionsContainerDelegate(
105     AutocompleteTextField* location_bar,
106     BrowserActionsContainerView* browser_actions_container_view)
107     : location_bar_(location_bar),
108       browser_actions_container_(browser_actions_container_view) {
109   [browser_actions_container_ setDelegate:this];
112 BrowserActionsContainerDelegate::~BrowserActionsContainerDelegate() {
113   [browser_actions_container_ setDelegate:nil];
116 CGFloat BrowserActionsContainerDelegate::GetMaxAllowedWidth() {
117   CGFloat location_bar_flex =
118       NSWidth([location_bar_ frame]) - kMinimumLocationBarWidth;
119   return NSWidth([browser_actions_container_ frame]) + location_bar_flex;
122 }  // namespace
124 @interface ToolbarController (YosemiteSDK)
125 - (void)viewDidLoad;
126 @end
128 @interface ToolbarController()
129 @property(assign, nonatomic) Browser* browser;
130 - (void)cleanUp;
131 - (void)addAccessibilityDescriptions;
132 - (void)initCommandStatus:(CommandUpdater*)commands;
133 - (void)prefChanged:(const std::string&)prefName;
134 - (BackgroundGradientView*)backgroundGradientView;
135 - (void)toolbarFrameChanged;
136 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate;
137 - (void)maintainMinimumLocationBarWidth;
138 - (void)adjustBrowserActionsContainerForNewWindow:(NSNotification*)notification;
139 - (void)browserActionsContainerDragged:(NSNotification*)notification;
140 - (void)browserActionsVisibilityChanged:(NSNotification*)notification;
141 - (void)browserActionsContainerWillAnimate:(NSNotification*)notification;
142 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate;
143 - (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
144                            animate:(BOOL)animate;
145 @end
147 namespace ToolbarControllerInternal {
149 // A C++ bridge class that handles listening for updates to commands and
150 // passing them back to ToolbarController. ToolbarController will create one of
151 // these bridges, pass them to CommandUpdater::AddCommandObserver, and then wait
152 // for update notifications, delivered via
153 // -enabledStateChangedForCommand:enabled:.
154 class CommandObserverBridge : public CommandObserver {
155  public:
156   explicit CommandObserverBridge(ToolbarController* observer)
157       : observer_(observer) {
158     DCHECK(observer_);
159   }
161  protected:
162   // Overridden from CommandObserver
163   void EnabledStateChangedForCommand(int command, bool enabled) override {
164     [observer_ enabledStateChangedForCommand:command enabled:enabled];
165   }
167  private:
168   ToolbarController* observer_;  // weak, owns me
170   DISALLOW_COPY_AND_ASSIGN(CommandObserverBridge);
173 // A class registered for C++ notifications. This is used to detect changes in
174 // preferences and upgrade available notifications. Bridges the notification
175 // back to the ToolbarController.
176 class NotificationBridge : public WrenchMenuBadgeController::Delegate {
177  public:
178   explicit NotificationBridge(ToolbarController* controller)
179       : controller_(controller),
180         badge_controller_([controller browser]->profile(), this) {
181   }
182   ~NotificationBridge() override {}
184   void UpdateBadgeSeverity() {
185     badge_controller_.UpdateDelegate();
186   }
188   void UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
189                            WrenchIconPainter::Severity severity,
190                            bool animate) override {
191     [controller_ updateWrenchButtonSeverity:severity animate:animate];
192   }
194   void OnPreferenceChanged(const std::string& pref_name) {
195     [controller_ prefChanged:pref_name];
196   }
198  private:
199   ToolbarController* controller_;  // weak, owns us
201   WrenchMenuBadgeController badge_controller_;
203   DISALLOW_COPY_AND_ASSIGN(NotificationBridge);
206 }  // namespace ToolbarControllerInternal
208 @implementation ToolbarController
210 @synthesize browser = browser_;
212 - (id)initWithCommands:(CommandUpdater*)commands
213                profile:(Profile*)profile
214                browser:(Browser*)browser
215           nibFileNamed:(NSString*)nibName {
216   DCHECK(commands && profile && [nibName length]);
217   if ((self = [super initWithNibName:nibName
218                               bundle:base::mac::FrameworkBundle()])) {
219     commands_ = commands;
220     profile_ = profile;
221     browser_ = browser;
222     hasToolbar_ = YES;
223     hasLocationBar_ = YES;
225     // Register for notifications about state changes for the toolbar buttons
226     commandObserver_.reset(
227         new ToolbarControllerInternal::CommandObserverBridge(self));
229     commands->AddCommandObserver(IDC_BACK, commandObserver_.get());
230     commands->AddCommandObserver(IDC_FORWARD, commandObserver_.get());
231     commands->AddCommandObserver(IDC_RELOAD, commandObserver_.get());
232     commands->AddCommandObserver(IDC_HOME, commandObserver_.get());
233     commands->AddCommandObserver(IDC_BOOKMARK_PAGE, commandObserver_.get());
234     // NOTE: Don't remove the command observers. ToolbarController is
235     // autoreleased at about the same time as the CommandUpdater (owned by the
236     // Browser), so |commands_| may not be valid any more.
237   }
238   return self;
241 - (id)initWithCommands:(CommandUpdater*)commands
242                profile:(Profile*)profile
243                browser:(Browser*)browser {
244   if ((self = [self initWithCommands:commands
245                              profile:profile
246                              browser:browser
247                         nibFileNamed:@"Toolbar"])) {
248     // Start global error services now so we badge the menu correctly.
249     SyncGlobalErrorFactory::GetForProfile(profile);
250   }
251   return self;
254 // Called after the view is done loading and the outlets have been hooked up.
255 // Now we can hook up bridges that rely on UI objects such as the location bar
256 // and button state. -viewDidLoad is the recommended way to do this in 10.10
257 // SDK. When running on 10.10 or above -awakeFromNib still works but for some
258 // reason is not guaranteed to be called (http://crbug.com/526276), so implement
259 // both.
260 - (void)awakeFromNib {
261   [self viewDidLoad];
264 - (void)viewDidLoad {
265   // When linking and running on 10.10+, both -awakeFromNib and -viewDidLoad may
266   // be called, don't initialize twice.
267   if (locationBarView_) {
268 #if defined(MAC_OS_X_VERSION_10_10) && \
269     MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10
270     DCHECK(base::mac::IsOSYosemiteOrLater());
271 #else
272     NOTREACHED();
273 #endif
274     return;
275   }
277   [[backButton_ cell] setImageID:IDR_BACK
278                   forButtonState:image_button_cell::kDefaultState];
279   [[backButton_ cell] setImageID:IDR_BACK_H
280                   forButtonState:image_button_cell::kHoverState];
281   [[backButton_ cell] setImageID:IDR_BACK_P
282                   forButtonState:image_button_cell::kPressedState];
283   [[backButton_ cell] setImageID:IDR_BACK_D
284                   forButtonState:image_button_cell::kDisabledState];
286   [[forwardButton_ cell] setImageID:IDR_FORWARD
287                      forButtonState:image_button_cell::kDefaultState];
288   [[forwardButton_ cell] setImageID:IDR_FORWARD_H
289                      forButtonState:image_button_cell::kHoverState];
290   [[forwardButton_ cell] setImageID:IDR_FORWARD_P
291                      forButtonState:image_button_cell::kPressedState];
292   [[forwardButton_ cell] setImageID:IDR_FORWARD_D
293                      forButtonState:image_button_cell::kDisabledState];
295   [[reloadButton_ cell] setImageID:IDR_RELOAD
296                     forButtonState:image_button_cell::kDefaultState];
297   [[reloadButton_ cell] setImageID:IDR_RELOAD_H
298                     forButtonState:image_button_cell::kHoverState];
299   [[reloadButton_ cell] setImageID:IDR_RELOAD_P
300                     forButtonState:image_button_cell::kPressedState];
302   [[homeButton_ cell] setImageID:IDR_HOME
303                   forButtonState:image_button_cell::kDefaultState];
304   [[homeButton_ cell] setImageID:IDR_HOME_H
305                   forButtonState:image_button_cell::kHoverState];
306   [[homeButton_ cell] setImageID:IDR_HOME_P
307                   forButtonState:image_button_cell::kPressedState];
309   [[wrenchButton_ cell] setImageID:IDR_TOOLS
310                     forButtonState:image_button_cell::kDefaultState];
311   [[wrenchButton_ cell] setImageID:IDR_TOOLS_H
312                     forButtonState:image_button_cell::kHoverState];
313   [[wrenchButton_ cell] setImageID:IDR_TOOLS_P
314                     forButtonState:image_button_cell::kPressedState];
316   notificationBridge_.reset(
317       new ToolbarControllerInternal::NotificationBridge(self));
318   notificationBridge_->UpdateBadgeSeverity();
320   [wrenchButton_ setOpenMenuOnClick:YES];
322   [backButton_ setOpenMenuOnRightClick:YES];
323   [forwardButton_ setOpenMenuOnRightClick:YES];
325   [backButton_ setHandleMiddleClick:YES];
326   [forwardButton_ setHandleMiddleClick:YES];
327   [reloadButton_ setHandleMiddleClick:YES];
328   [homeButton_ setHandleMiddleClick:YES];
330   [self initCommandStatus:commands_];
331   [reloadButton_ setCommandUpdater:commands_];
333   locationBarView_.reset(new LocationBarViewMac(locationBar_, commands_,
334                                                 profile_, browser_));
335   [locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
337   // Register pref observers for the optional home and page/options buttons
338   // and then add them to the toolbar based on those prefs.
339   PrefService* prefs = profile_->GetPrefs();
340   showHomeButton_.Init(
341       prefs::kShowHomeButton, prefs,
342       base::Bind(
343           &ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged,
344           base::Unretained(notificationBridge_.get())));
345   [self showOptionalHomeButton];
346   [self installWrenchMenu];
348   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
350   // Create the controllers for the back/forward menus.
351   backMenuController_.reset([[BackForwardMenuController alloc]
352           initWithBrowser:browser_
353                 modelType:BACK_FORWARD_MENU_TYPE_BACK
354                    button:backButton_]);
355   forwardMenuController_.reset([[BackForwardMenuController alloc]
356           initWithBrowser:browser_
357                 modelType:BACK_FORWARD_MENU_TYPE_FORWARD
358                    button:forwardButton_]);
360   // For a popup window, the toolbar is really just a location bar
361   // (see override for [ToolbarController view], below).  When going
362   // fullscreen, we remove the toolbar controller's view from the view
363   // hierarchy.  Calling [locationBar_ removeFromSuperview] when going
364   // fullscreen causes it to get released, making us unhappy
365   // (http://crbug.com/18551).  We avoid the problem by incrementing
366   // the retain count of the location bar; use of the scoped object
367   // helps us remember to release it.
368   locationBarRetainer_.reset([locationBar_ retain]);
369   trackingArea_.reset(
370       [[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
371                                    options:NSTrackingMouseMoved |
372                                            NSTrackingInVisibleRect |
373                                            NSTrackingMouseEnteredAndExited |
374                                            NSTrackingActiveAlways
375                                      owner:self
376                                   userInfo:nil]);
377   NSView* toolbarView = [self view];
378   [toolbarView addTrackingArea:trackingArea_.get()];
380   // If the user has any Browser Actions installed, the container view for them
381   // may have to be resized depending on the width of the toolbar frame.
382   [toolbarView setPostsFrameChangedNotifications:YES];
383   [[NSNotificationCenter defaultCenter]
384       addObserver:self
385          selector:@selector(toolbarFrameChanged)
386              name:NSViewFrameDidChangeNotification
387            object:toolbarView];
389   // Set ViewIDs for toolbar elements which don't have their dedicated class.
390   // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
391   // |browserActionsContainerView_| are handled by themselves.
392   view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON);
393   view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON);
394   view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON);
395   view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU);
397   [self addAccessibilityDescriptions];
400 - (void)dealloc {
401   [self cleanUp];
402   [super dealloc];
405 - (void)browserWillBeDestroyed {
406   // Pass this call onto other reference counted objects.
407   [backMenuController_ browserWillBeDestroyed];
408   [forwardMenuController_ browserWillBeDestroyed];
409   [browserActionsController_ browserWillBeDestroyed];
410   [wrenchMenuController_ browserWillBeDestroyed];
412   [self cleanUp];
415 - (void)cleanUp {
416   // Unset ViewIDs of toolbar elements.
417   // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
418   // |browserActionsContainerView_| are handled by themselves.
419   view_id_util::UnsetID(backButton_);
420   view_id_util::UnsetID(forwardButton_);
421   view_id_util::UnsetID(homeButton_);
422   view_id_util::UnsetID(wrenchButton_);
424   // Make sure any code in the base class which assumes [self view] is
425   // the "parent" view continues to work.
426   hasToolbar_ = YES;
427   hasLocationBar_ = YES;
429   [[NSNotificationCenter defaultCenter] removeObserver:self];
431   if (trackingArea_.get()) {
432     [[self view] removeTrackingArea:trackingArea_.get()];
433     [trackingArea_.get() clearOwner];
434     trackingArea_.reset();
435   }
437   // Destroy owned objects that hold a weak Browser*.
438   locationBarView_.reset();
439   browserActionsContainerDelegate_.reset();
440   browser_ = nullptr;
443 - (void)addAccessibilityDescriptions {
444   // Set accessibility descriptions. http://openradar.appspot.com/7496255
445   NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
446   [[backButton_ cell]
447       accessibilitySetOverrideValue:description
448                        forAttribute:NSAccessibilityDescriptionAttribute];
449   NSString* helpTag = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TOOLTIP_BACK);
450   [[backButton_ cell]
451       accessibilitySetOverrideValue:helpTag
452                        forAttribute:NSAccessibilityHelpAttribute];
454   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_FORWARD);
455   [[forwardButton_ cell]
456       accessibilitySetOverrideValue:description
457                        forAttribute:NSAccessibilityDescriptionAttribute];
458   helpTag = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TOOLTIP_FORWARD);
459   [[forwardButton_ cell]
460       accessibilitySetOverrideValue:helpTag
461                        forAttribute:NSAccessibilityHelpAttribute];
463   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_RELOAD);
464   [[reloadButton_ cell]
465       accessibilitySetOverrideValue:description
466                        forAttribute:NSAccessibilityDescriptionAttribute];
467   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_HOME);
468   [[homeButton_ cell]
469       accessibilitySetOverrideValue:description
470                        forAttribute:NSAccessibilityDescriptionAttribute];
471   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
472   [[locationBar_ cell]
473       accessibilitySetOverrideValue:description
474                        forAttribute:NSAccessibilityDescriptionAttribute];
475   description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP);
476   [[wrenchButton_ cell]
477       accessibilitySetOverrideValue:description
478                        forAttribute:NSAccessibilityDescriptionAttribute];
481 - (void)mouseExited:(NSEvent*)theEvent {
482   [[hoveredButton_ cell] setIsMouseInside:NO];
483   [hoveredButton_ release];
484   hoveredButton_ = nil;
487 - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent {
488   NSButton* targetView = (NSButton*)[[self view]
489                                      hitTest:[theEvent locationInWindow]];
491   // Only interpret the view as a hoverButton_ if it's both button and has a
492   // button cell that cares.  GradientButtonCell derived cells care.
493   if (([targetView isKindOfClass:[NSButton class]]) &&
494       ([[targetView cell]
495          respondsToSelector:@selector(setIsMouseInside:)]))
496     return targetView;
497   return nil;
500 - (void)mouseMoved:(NSEvent*)theEvent {
501   NSButton* targetView = [self hoverButtonForEvent:theEvent];
502   if (hoveredButton_ != targetView) {
503     [[hoveredButton_ cell] setIsMouseInside:NO];
504     [[targetView cell] setIsMouseInside:YES];
505     [hoveredButton_ release];
506     hoveredButton_ = [targetView retain];
507   }
510 - (void)mouseEntered:(NSEvent*)event {
511   [self mouseMoved:event];
514 - (LocationBarViewMac*)locationBarBridge {
515   return locationBarView_.get();
518 - (void)focusLocationBar:(BOOL)selectAll {
519   if (locationBarView_.get()) {
520     locationBarView_->FocusLocation(selectAll ? true : false);
521   }
524 // Called when the state for a command changes to |enabled|. Update the
525 // corresponding UI element.
526 - (void)enabledStateChangedForCommand:(int)command enabled:(bool)enabled {
527   NSButton* button = nil;
528   switch (command) {
529     case IDC_BACK:
530       button = backButton_;
531       break;
532     case IDC_FORWARD:
533       button = forwardButton_;
534       break;
535     case IDC_HOME:
536       button = homeButton_;
537       break;
538   }
539   [button setEnabled:enabled];
542 // Init the enabled state of the buttons on the toolbar to match the state in
543 // the controller.
544 - (void)initCommandStatus:(CommandUpdater*)commands {
545   [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
546   [forwardButton_
547       setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO];
548   [reloadButton_ setEnabled:YES];
549   [homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO];
552 - (void)updateToolbarWithContents:(WebContents*)tab {
553   locationBarView_->Update(tab);
555   [locationBar_ updateMouseTracking];
557   if (browserActionsController_.get()) {
558     [browserActionsController_ update];
559   }
561   BOOL needReloadMenu = chrome::IsDebuggerAttachedToCurrentTab(browser_);
562   [reloadButton_ setMenuEnabled:needReloadMenu];
565 - (void)resetTabState:(WebContents*)tab {
566   locationBarView_->ResetTabState(tab);
569 - (void)setStarredState:(BOOL)isStarred {
570   locationBarView_->SetStarred(isStarred);
573 - (void)setTranslateIconLit:(BOOL)on {
574   locationBarView_->SetTranslateIconLit(on);
577 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
578   locationBarView_->ZoomChangedForActiveTab(
579       canShowBubble && ![wrenchMenuController_ isMenuOpen]);
582 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
583   [reloadButton_ setIsLoading:isLoading force:force];
586 - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar {
587   [self view];  // Force nib loading.
589   hasToolbar_ = toolbar;
591   // If there's a toolbar, there must be a location bar.
592   DCHECK((toolbar && locBar) || !toolbar);
593   hasLocationBar_ = toolbar ? YES : locBar;
595   // Decide whether to hide/show based on whether there's a location bar.
596   [[self view] setHidden:!hasLocationBar_];
598   // Make location bar not editable when in a pop-up.
599   locationBarView_->SetEditable(toolbar);
602 - (NSView*)view {
603   if (hasToolbar_)
604     return [super view];
605   return locationBar_;
608 // (Private) Returns the backdrop to the toolbar.
609 - (BackgroundGradientView*)backgroundGradientView {
610   // We really do mean |[super view]|; see our override of |-view|.
611   DCHECK([[super view] isKindOfClass:[BackgroundGradientView class]]);
612   return (BackgroundGradientView*)[super view];
615 - (id)customFieldEditorForObject:(id)obj {
616   if (obj == locationBar_) {
617     // Lazilly construct Field editor, Cocoa UI code always runs on the
618     // same thread, so there shoudn't be a race condition here.
619     if (autocompleteTextFieldEditor_.get() == nil) {
620       autocompleteTextFieldEditor_.reset(
621           [[AutocompleteTextFieldEditor alloc] init]);
622     }
624     // This needs to be called every time, otherwise notifications
625     // aren't sent correctly.
626     DCHECK(autocompleteTextFieldEditor_.get());
627     [autocompleteTextFieldEditor_.get() setFieldEditor:YES];
628     if (base::mac::IsOSSnowLeopard()) {
629       // Manually transferring the drawsBackground and backgroundColor
630       // properties is necessary to ensure anti-aliased text on 10.6.
631       [autocompleteTextFieldEditor_
632           setDrawsBackground:[locationBar_ drawsBackground]];
633       [autocompleteTextFieldEditor_
634           setBackgroundColor:[locationBar_ backgroundColor]];
635     }
636     return autocompleteTextFieldEditor_.get();
637   }
638   return nil;
641 // Returns an array of views in the order of the outlets above.
642 - (NSArray*)toolbarViews {
643   return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_,
644              homeButton_, wrenchButton_, locationBar_,
645              browserActionsContainerView_, nil];
648 // Moves |rect| to the right by |delta|, keeping the right side fixed by
649 // shrinking the width to compensate. Passing a negative value for |deltaX|
650 // moves to the left and increases the width.
651 - (NSRect)adjustRect:(NSRect)rect byAmount:(CGFloat)deltaX {
652   NSRect frame = NSOffsetRect(rect, deltaX, 0);
653   frame.size.width -= deltaX;
654   return frame;
657 // Show or hide the home button based on the pref.
658 - (void)showOptionalHomeButton {
659   // Ignore this message if only showing the URL bar.
660   if (!hasToolbar_)
661     return;
662   BOOL hide = showHomeButton_.GetValue() ? NO : YES;
663   if (hide == [homeButton_ isHidden])
664     return;  // Nothing to do, view state matches pref state.
666   // Always shift the text field by the width of the home button minus one pixel
667   // since the frame edges of each button are right on top of each other. When
668   // hiding the button, reverse the direction of the movement (to the left).
669   CGFloat moveX = [homeButton_ frame].size.width - 1.0;
670   if (hide)
671     moveX *= -1;  // Reverse the direction of the move.
673   [locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
674                                  byAmount:moveX]];
675   [homeButton_ setHidden:hide];
678 // Install the menu wrench buttons. Calling this repeatedly is inexpensive so it
679 // can be done every time the buttons are shown.
680 - (void)installWrenchMenu {
681   if (wrenchMenuController_.get())
682     return;
684   wrenchMenuController_.reset(
685       [[WrenchMenuController alloc] initWithBrowser:browser_]);
686   [wrenchMenuController_ setUseWithPopUpButtonCell:YES];
687   [wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]];
690 - (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
691                            animate:(BOOL)animate {
692   WrenchToolbarButtonCell* cell =
693       base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
694   [cell setSeverity:severity shouldAnimate:animate];
697 - (void)prefChanged:(const std::string&)prefName {
698   if (prefName == prefs::kShowHomeButton) {
699     [self showOptionalHomeButton];
700   }
703 - (void)createBrowserActionButtons {
704   if (!browserActionsController_.get()) {
705     browserActionsContainerDelegate_.reset(
706         new BrowserActionsContainerDelegate(locationBar_,
707                                             browserActionsContainerView_));
708     browserActionsController_.reset([[BrowserActionsController alloc]
709             initWithBrowser:browser_
710               containerView:browserActionsContainerView_
711              mainController:nil]);
712     [[NSNotificationCenter defaultCenter]
713         addObserver:self
714            selector:@selector(browserActionsContainerDragged:)
715                name:kBrowserActionGrippyDraggingNotification
716              object:browserActionsContainerView_];
717     [[NSNotificationCenter defaultCenter]
718         addObserver:self
719            selector:@selector(browserActionsVisibilityChanged:)
720                name:kBrowserActionVisibilityChangedNotification
721              object:browserActionsController_];
722     [[NSNotificationCenter defaultCenter]
723         addObserver:self
724            selector:@selector(browserActionsContainerWillAnimate:)
725                name:kBrowserActionsContainerWillAnimate
726              object:browserActionsContainerView_];
727     [[NSNotificationCenter defaultCenter]
728         addObserver:self
729            selector:@selector(adjustBrowserActionsContainerForNewWindow:)
730                name:NSWindowDidBecomeKeyNotification
731              object:[[self view] window]];
732   }
733   if (![browserActionsContainerView_ isHidden])
734     [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
737 - (void)adjustBrowserActionsContainerForNewWindow:
738     (NSNotification*)notification {
739   [self toolbarFrameChanged];
740   [[NSNotificationCenter defaultCenter]
741       removeObserver:self
742                 name:NSWindowDidBecomeKeyNotification
743               object:[[self view] window]];
746 - (void)browserActionsContainerDragged:(NSNotification*)notification {
747   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
750 - (void)browserActionsVisibilityChanged:(NSNotification*)notification {
751   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
754 - (void)browserActionsContainerWillAnimate:(NSNotification*)notification {
755   [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES];
758 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate {
759   CGFloat locationBarXPos = NSMaxX([locationBar_ frame]);
760   CGFloat leftDistance = 0.0;
762   if ([browserActionsContainerView_ isHidden]) {
763     CGFloat edgeXPos = [wrenchButton_ frame].origin.x;
764     leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding;
765   } else {
766     leftDistance = NSMinX([browserActionsContainerView_ animationEndFrame]) -
767         locationBarXPos;
768   }
769   if (leftDistance != 0.0)
770     [self adjustLocationSizeBy:leftDistance animate:animate];
771   else
772     [locationBar_ stopAnimation];
775 - (void)maintainMinimumLocationBarWidth {
776   CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
777   locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
778   if (locationBarAtMinSize_) {
779     CGFloat dX = kMinimumLocationBarWidth - locationBarWidth;
780     [self adjustLocationSizeBy:dX animate:NO];
781   }
784 - (void)toolbarFrameChanged {
785   // Do nothing if the frame changes but no Browser Action Controller is
786   // present.
787   if (!browserActionsController_.get())
788     return;
790   if ([browserActionsContainerView_ isAnimating]) {
791     // If the browser actions container is animating, we need to stop it first,
792     // because the frame it's animating for could be incorrect with the new
793     // bounds (if, for instance, the bookmark bar was added).
794     // This will advance to the end of the animation, so we also need to adjust
795     // it afterwards.
796     [browserActionsContainerView_ stopAnimation];
797     NSRect containerFrame = [browserActionsContainerView_ frame];
798     containerFrame.origin.y =
799         NSHeight([[self view] frame]) - NSHeight(containerFrame) -
800         kToolbarElementTopPadding;
801     [browserActionsContainerView_ setFrame:containerFrame];
802     [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
803   }
805   [self maintainMinimumLocationBarWidth];
807   if (locationBarAtMinSize_) {
808     // Once the grippy is pinned, leave it until it is explicity un-pinned.
809     [browserActionsContainerView_ setGrippyPinned:YES];
810     NSRect containerFrame = [browserActionsContainerView_ frame];
811     // Determine how much the container needs to move in case it's overlapping
812     // with the location bar.
813     CGFloat dX = NSMaxX([locationBar_ frame]) - containerFrame.origin.x;
814     containerFrame = NSOffsetRect(containerFrame, dX, 0);
815     containerFrame.size.width -= dX;
816     [browserActionsContainerView_ setFrame:containerFrame];
817   } else if (!locationBarAtMinSize_ &&
818       [browserActionsContainerView_ grippyPinned]) {
819     // Expand out the container until it hits the saved size, then unpin the
820     // grippy.
821     // Add 0.1 pixel so that it doesn't hit the minimum width codepath above.
822     CGFloat dX = NSWidth([locationBar_ frame]) -
823         (kMinimumLocationBarWidth + 0.1);
824     NSRect containerFrame = [browserActionsContainerView_ frame];
825     containerFrame = NSOffsetRect(containerFrame, -dX, 0);
826     containerFrame.size.width += dX;
827     CGFloat savedContainerWidth =
828         [browserActionsController_ preferredSize].width();
829     if (NSWidth(containerFrame) >= savedContainerWidth) {
830       containerFrame = NSOffsetRect(containerFrame,
831           NSWidth(containerFrame) - savedContainerWidth, 0);
832       containerFrame.size.width = savedContainerWidth;
833       [browserActionsContainerView_ setGrippyPinned:NO];
834     }
835     [browserActionsContainerView_ setFrame:containerFrame];
836     [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
837   }
840 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate {
841   // Ensure that the location bar is in its proper place.
842   NSRect locationFrame = [locationBar_ frame];
843   locationFrame.size.width += dX;
845   [locationBar_ stopAnimation];
847   if (animate)
848     [locationBar_ animateToFrame:locationFrame];
849   else
850     [locationBar_ setFrame:locationFrame];
853 - (NSPoint)bookmarkBubblePoint {
854   if (locationBarView_->IsStarEnabled())
855     return locationBarView_->GetBookmarkBubblePoint();
857   // Grab bottom middle of hotdogs.
858   NSRect frame = wrenchButton_.frame;
859   NSPoint point = NSMakePoint(NSMidX(frame), NSMinY(frame));
860   // Inset to account for the whitespace around the hotdogs.
861   point.y += wrench_menu_controller::kWrenchBubblePointOffsetY;
862   return [self.view convertPoint:point toView:nil];
865 - (NSPoint)managePasswordsBubblePoint {
866   return locationBarView_->GetManagePasswordsBubblePoint();
869 - (NSPoint)translateBubblePoint {
870   return locationBarView_->GetTranslateBubblePoint();
873 - (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight {
874   // With no toolbar, just ignore the compression.
875   if (!hasToolbar_)
876     return NSHeight([locationBar_ frame]);
878   return kBaseToolbarHeightNormal - compressByHeight;
881 - (void)setDividerOpacity:(CGFloat)opacity {
882   BackgroundGradientView* view = [self backgroundGradientView];
883   [view setShowsDivider:(opacity > 0 ? YES : NO)];
885   // We may not have a toolbar view (e.g., popup windows only have a location
886   // bar).
887   if ([view isKindOfClass:[ToolbarView class]]) {
888     ToolbarView* toolbarView = (ToolbarView*)view;
889     [toolbarView setDividerOpacity:opacity];
890   }
892   [view setNeedsDisplay:YES];
895 - (BrowserActionsController*)browserActionsController {
896   return browserActionsController_.get();
899 - (NSView*)wrenchButton {
900   return wrenchButton_;
903 - (WrenchMenuController*)wrenchMenuController {
904   return wrenchMenuController_.get();
907 // (URLDropTargetController protocol)
908 - (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point {
909   // TODO(viettrungluu): This code is more or less copied from the code in
910   // |TabStripController|. I'll refactor this soon to make it common and expand
911   // its capabilities (e.g., allow text DnD).
912   if ([urls count] < 1) {
913     NOTREACHED();
914     return;
915   }
917   // TODO(viettrungluu): dropping multiple URLs?
918   if ([urls count] > 1)
919     NOTIMPLEMENTED();
921   // Get the first URL and fix it up.
922   GURL url(url_formatter::FixupURL(
923       base::SysNSStringToUTF8([urls objectAtIndex:0]), std::string()));
925   if (url.SchemeIs(url::kJavaScriptScheme)) {
926     browser_->window()->GetLocationBar()->GetOmniboxView()->SetUserText(
927           OmniboxView::StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())));
928   }
929   OpenURLParams params(
930       url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
931   browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
934 // (URLDropTargetController protocol)
935 - (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point {
936   // TODO(viettrungluu): This code is more or less copied from the code in
937   // |TabStripController|. I'll refactor this soon to make it common and expand
938   // its capabilities (e.g., allow text DnD).
940   // If the input is plain text, classify the input and make the URL.
941   AutocompleteMatch match;
942   AutocompleteClassifierFactory::GetForProfile(browser_->profile())->Classify(
943       base::SysNSStringToUTF16(text), false, false,
944       metrics::OmniboxEventProto::BLANK, &match, NULL);
945   GURL url(match.destination_url);
947   OpenURLParams params(
948       url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
949   browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
952 // (URLDropTargetController protocol)
953 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
954   // Do nothing.
957 // (URLDropTargetController protocol)
958 - (void)hideDropURLsIndicatorInView:(NSView*)view {
959   // Do nothing.
962 // (URLDropTargetController protocol)
963 - (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
964   return drag_util::IsUnsupportedDropData(profile_, info);
967 @end