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