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"
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"
62 using content::OpenURLParams;
63 using content::Referrer;
64 using content::WebContents;
68 // Height of the toolbar in pixels when the bookmark bar is closed.
69 const CGFloat kBaseToolbarHeightNormal = 35.0;
71 // The minimum width of the location bar in pixels.
72 const CGFloat kMinimumLocationBarWidth = 100.0;
74 // The duration of any animation that occurs within the toolbar in seconds.
75 const CGFloat kAnimationDuration = 0.2;
77 // The amount of left padding that the wrench menu should have.
78 const CGFloat kWrenchMenuLeftPadding = 3.0;
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;
100 namespace ToolbarControllerInternal {
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 {
107 explicit NotificationBridge(ToolbarController* controller)
108 : controller_(controller),
109 badge_controller_([controller browser]->profile(), this) {
111 virtual ~NotificationBridge() {
114 void UpdateBadgeSeverity() {
115 badge_controller_.UpdateDelegate();
118 virtual void UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
119 WrenchIconPainter::Severity severity,
120 bool animate) OVERRIDE {
121 [controller_ updateWrenchButtonSeverity:severity animate:animate];
124 void OnPreferenceChanged(const std::string& pref_name) {
125 [controller_ prefChanged:pref_name];
129 ToolbarController* controller_; // weak, owns us
131 WrenchMenuBadgeController badge_controller_;
133 DISALLOW_COPY_AND_ASSIGN(NotificationBridge);
136 } // namespace ToolbarControllerInternal
138 @implementation ToolbarController
140 @synthesize browser = browser_;
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;
153 resizeDelegate_ = resizeDelegate;
155 hasLocationBar_ = YES;
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);
168 - (id)initWithCommands:(CommandUpdater*)commands
169 profile:(Profile*)profile
170 browser:(Browser*)browser
171 resizeDelegate:(id<ViewResizer>)resizeDelegate {
172 if ((self = [self initWithCommands:commands
175 resizeDelegate:resizeDelegate
176 nibFileNamed:@"Toolbar"])) {
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_);
191 // Make sure any code in the base class which assumes [self view] is
192 // the "parent" view continues to work.
194 hasLocationBar_ = YES;
196 [[NSNotificationCenter defaultCenter] removeObserver:self];
198 if (trackingArea_.get())
199 [[self view] removeTrackingArea:trackingArea_.get()];
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];
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];
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];
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];
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];
246 notificationBridge_.reset(
247 new ToolbarControllerInternal::NotificationBridge(self));
248 notificationBridge_->UpdateBadgeSeverity();
250 [wrenchButton_ setOpenMenuOnClick:YES];
252 [backButton_ setOpenMenuOnRightClick:YES];
253 [forwardButton_ setOpenMenuOnRightClick:YES];
255 [backButton_ setHandleMiddleClick:YES];
256 [forwardButton_ setHandleMiddleClick:YES];
257 [reloadButton_ setHandleMiddleClick:YES];
258 [homeButton_ setHandleMiddleClick:YES];
260 [self initCommandStatus:commands_];
262 locationBarView_.reset(new LocationBarViewMac(locationBar_, commands_,
263 profile_, browser_));
264 [locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
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,
272 &ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged,
273 base::Unretained(notificationBridge_.get())));
274 [self showOptionalHomeButton];
275 [self installWrenchMenu];
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_]);
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]);
297 [[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
298 options:NSTrackingMouseMoved |
299 NSTrackingInVisibleRect |
300 NSTrackingMouseEnteredAndExited |
301 NSTrackingActiveAlways
304 NSView* toolbarView = [self view];
305 [toolbarView addTrackingArea:trackingArea_.get()];
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]
312 selector:@selector(toolbarFrameChanged)
313 name:NSViewFrameDidChangeNotification
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);
324 [self addAccessibilityDescriptions];
327 - (void)addAccessibilityDescriptions {
328 // Set accessibility descriptions. http://openradar.appspot.com/7496255
329 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
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);
343 accessibilitySetOverrideValue:description
344 forAttribute:NSAccessibilityDescriptionAttribute];
345 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
347 accessibilitySetOverrideValue:description
348 forAttribute:NSAccessibilityDescriptionAttribute];
349 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP);
350 [[wrenchButton_ cell]
351 accessibilitySetOverrideValue:description
352 forAttribute:NSAccessibilityDescriptionAttribute];
355 - (void)mouseExited:(NSEvent*)theEvent {
356 [[hoveredButton_ cell] setIsMouseInside:NO];
357 [hoveredButton_ release];
358 hoveredButton_ = nil;
361 - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent {
362 NSButton* targetView = (NSButton*)[[self view]
363 hitTest:[theEvent locationInWindow]];
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]]) &&
369 respondsToSelector:@selector(setIsMouseInside:)]))
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];
384 - (void)mouseEntered:(NSEvent*)event {
385 [self mouseMoved:event];
388 - (LocationBarViewMac*)locationBarBridge {
389 return locationBarView_.get();
392 - (void)focusLocationBar:(BOOL)selectAll {
393 if (locationBarView_.get()) {
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();
402 locationBarView_->FocusLocation(selectAll ? true : false);
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;
413 button = backButton_;
416 button = forwardButton_;
419 button = homeButton_;
422 [button setEnabled:enabled];
425 // Init the enabled state of the buttons on the toolbar to match the state in
427 - (void)initCommandStatus:(CommandUpdater*)commands {
428 [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
430 setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO];
431 [reloadButton_ setEnabled:YES];
432 [homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO];
435 - (void)updateToolbarWithContents:(WebContents*)tab {
436 locationBarView_->Update(tab);
438 [locationBar_ updateMouseTracking];
440 if (browserActionsController_.get()) {
441 [browserActionsController_ update];
445 - (void)setStarredState:(BOOL)isStarred {
446 locationBarView_->SetStarred(isStarred);
449 - (void)setTranslateIconLit:(BOOL)on {
450 locationBarView_->SetTranslateIconLit(on);
453 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
454 locationBarView_->ZoomChangedForActiveTab(
455 canShowBubble && ![wrenchMenuController_ isMenuOpen]);
458 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
459 [reloadButton_ setIsLoading:isLoading force:force];
462 - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar {
463 [self view]; // Force nib loading.
465 hasToolbar_ = toolbar;
467 // If there's a toolbar, there must be a location bar.
468 DCHECK((toolbar && locBar) || !toolbar);
469 hasLocationBar_ = toolbar ? YES : locBar;
471 // Decide whether to hide/show based on whether there's a location bar.
472 [[self view] setHidden:!hasLocationBar_];
474 // Make location bar not editable when in a pop-up.
475 locationBarView_->SetEditable(toolbar);
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];
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]);
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]];
512 return autocompleteTextFieldEditor_.get();
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];
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;
533 // Show or hide the home button based on the pref.
534 - (void)showOptionalHomeButton {
535 // Ignore this message if only showing the URL bar.
538 BOOL hide = showHomeButton_.GetValue() ? NO : YES;
539 if (hide == [homeButton_ isHidden])
540 return; // Nothing to do, view state matches pref state.
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;
547 moveX *= -1; // Reverse the direction of the move.
549 [locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
551 [homeButton_ setHidden:hide];
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())
560 wrenchMenuController_.reset(
561 [[WrenchMenuController alloc] initWithBrowser:browser_]);
562 [wrenchMenuController_ setUseWithPopUpButtonCell:YES];
563 [wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]];
566 - (WrenchMenuController*)wrenchMenuController {
567 return wrenchMenuController_;
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];
577 - (void)prefChanged:(const std::string&)prefName {
578 if (prefName == prefs::kShowHomeButton) {
579 [self showOptionalHomeButton];
583 - (void)createBrowserActionButtons {
584 if (!browserActionsController_.get()) {
585 browserActionsController_.reset([[BrowserActionsController alloc]
586 initWithBrowser:browser_
587 containerView:browserActionsContainerView_]);
588 [[NSNotificationCenter defaultCenter]
590 selector:@selector(browserActionsContainerDragged:)
591 name:kBrowserActionGrippyDraggingNotification
592 object:browserActionsController_];
593 [[NSNotificationCenter defaultCenter]
595 selector:@selector(browserActionsContainerDragFinished:)
596 name:kBrowserActionGrippyDragFinishedNotification
597 object:browserActionsController_];
598 [[NSNotificationCenter defaultCenter]
600 selector:@selector(browserActionsVisibilityChanged:)
601 name:kBrowserActionVisibilityChangedNotification
602 object:browserActionsController_];
603 [[NSNotificationCenter defaultCenter]
605 selector:@selector(adjustBrowserActionsContainerForNewWindow:)
606 name:NSWindowDidBecomeKeyNotification
607 object:[[self view] window]];
609 CGFloat containerWidth = [browserActionsContainerView_ isHidden] ? 0.0 :
610 NSWidth([browserActionsContainerView_ frame]);
611 if (containerWidth > 0.0)
612 [self adjustLocationSizeBy:(containerWidth * -1) animate:NO];
615 - (void)adjustBrowserActionsContainerForNewWindow:
616 (NSNotification*)notification {
617 [self toolbarFrameChanged];
618 [[NSNotificationCenter defaultCenter]
620 name:NSWindowDidBecomeKeyNotification
621 object:[[self view] window]];
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];
633 - (void)browserActionsContainerDragFinished:(NSNotification*)notification {
634 [browserActionsController_ resizeContainerAndAnimate:YES];
635 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES];
638 - (void)browserActionsVisibilityChanged:(NSNotification*)notification {
639 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
642 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate {
643 CGFloat locationBarXPos = NSMaxX([locationBar_ frame]);
644 CGFloat leftDistance;
646 if ([browserActionsContainerView_ isHidden]) {
647 CGFloat edgeXPos = [wrenchButton_ frame].origin.x;
648 leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding;
650 NSRect containerFrame = animate ?
651 [browserActionsContainerView_ animationEndFrame] :
652 [browserActionsContainerView_ frame];
654 leftDistance = containerFrame.origin.x - locationBarXPos;
656 if (leftDistance != 0.0)
657 [self adjustLocationSizeBy:leftDistance animate:animate];
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];
669 - (void)toolbarFrameChanged {
670 // Do nothing if the frame changes but no Browser Action Controller is
672 if (!browserActionsController_.get())
675 [self maintainMinimumLocationBarWidth];
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
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];
704 [browserActionsContainerView_ setFrame:containerFrame];
705 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
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;
715 [locationBar_ setFrame:locationFrame];
719 [NSAnimationContext beginGrouping];
720 [[NSAnimationContext currentContext] setDuration:kAnimationDuration];
721 [[locationBar_ animator] setFrame:locationFrame];
722 [NSAnimationContext endGrouping];
725 - (NSPoint)bookmarkBubblePoint {
726 if (locationBarView_->IsStarEnabled())
727 return locationBarView_->GetBookmarkBubblePoint();
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];
737 - (NSPoint)managePasswordsBubblePoint {
738 return locationBarView_->GetManagePasswordsBubblePoint();
741 - (NSPoint)translateBubblePoint {
742 return locationBarView_->GetTranslateBubblePoint();
745 - (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight {
746 // With no toolbar, just ignore the compression.
748 return NSHeight([locationBar_ frame]);
750 return kBaseToolbarHeightNormal - compressByHeight;
753 - (void)setDividerOpacity:(CGFloat)opacity {
754 BackgroundGradientView* view = [self backgroundGradientView];
755 [view setShowsDivider:(opacity > 0 ? YES : NO)];
757 // We may not have a toolbar view (e.g., popup windows only have a location
759 if ([view isKindOfClass:[ToolbarView class]]) {
760 ToolbarView* toolbarView = (ToolbarView*)view;
761 [toolbarView setDividerOpacity:opacity];
764 [view setNeedsDisplay:YES];
767 - (BrowserActionsController*)browserActionsController {
768 return browserActionsController_.get();
771 - (NSView*)wrenchButton {
772 return wrenchButton_;
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) {
785 // TODO(viettrungluu): dropping multiple URLs?
786 if ([urls count] > 1)
789 // Get the first URL and fix it up.
790 GURL url(url_fixer::FixupURL(base::SysNSStringToUTF8([urls objectAtIndex:0]),
793 if (url.SchemeIs(url::kJavaScriptScheme)) {
794 browser_->window()->GetLocationBar()->GetOmniboxView()->SetUserText(
795 OmniboxView::StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())));
797 OpenURLParams params(
798 url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
799 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
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).
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);
815 OpenURLParams params(
816 url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
817 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
820 // (URLDropTargetController protocol)
821 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
825 // (URLDropTargetController protocol)
826 - (void)hideDropURLsIndicatorInView:(NSView*)view {
830 // (URLDropTargetController protocol)
831 - (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
832 return drag_util::IsUnsupportedDropData(profile_, info);