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/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;
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 {
89 BrowserActionsContainerDelegate(
90 AutocompleteTextField* location_bar,
91 BrowserActionsContainerView* browser_actions_container_view);
92 ~BrowserActionsContainerDelegate() override;
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;
124 @interface ToolbarController (YosemiteSDK)
128 @interface ToolbarController()
129 @property(assign, nonatomic) Browser* browser;
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;
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 {
156 explicit CommandObserverBridge(ToolbarController* observer)
157 : observer_(observer) {
162 // Overridden from CommandObserver
163 void EnabledStateChangedForCommand(int command, bool enabled) override {
164 [observer_ enabledStateChangedForCommand:command enabled:enabled];
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 {
178 explicit NotificationBridge(ToolbarController* controller)
179 : controller_(controller),
180 badge_controller_([controller browser]->profile(), this) {
182 ~NotificationBridge() override {}
184 void UpdateBadgeSeverity() {
185 badge_controller_.UpdateDelegate();
188 void UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
189 WrenchIconPainter::Severity severity,
190 bool animate) override {
191 [controller_ updateWrenchButtonSeverity:severity animate:animate];
194 void OnPreferenceChanged(const std::string& pref_name) {
195 [controller_ prefChanged:pref_name];
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;
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.
241 - (id)initWithCommands:(CommandUpdater*)commands
242 profile:(Profile*)profile
243 browser:(Browser*)browser {
244 if ((self = [self initWithCommands:commands
247 nibFileNamed:@"Toolbar"])) {
248 // Start global error services now so we badge the menu correctly.
249 SyncGlobalErrorFactory::GetForProfile(profile);
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
260 - (void)awakeFromNib {
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());
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,
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]);
370 [[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
371 options:NSTrackingMouseMoved |
372 NSTrackingInVisibleRect |
373 NSTrackingMouseEnteredAndExited |
374 NSTrackingActiveAlways
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]
385 selector:@selector(toolbarFrameChanged)
386 name:NSViewFrameDidChangeNotification
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];
405 - (void)browserWillBeDestroyed {
406 // Pass this call onto other reference counted objects.
407 [backMenuController_ browserWillBeDestroyed];
408 [forwardMenuController_ browserWillBeDestroyed];
409 [browserActionsController_ browserWillBeDestroyed];
410 [wrenchMenuController_ browserWillBeDestroyed];
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.
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();
437 // Destroy owned objects that hold a weak Browser*.
438 locationBarView_.reset();
439 browserActionsContainerDelegate_.reset();
443 - (void)addAccessibilityDescriptions {
444 // Set accessibility descriptions. http://openradar.appspot.com/7496255
445 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
447 accessibilitySetOverrideValue:description
448 forAttribute:NSAccessibilityDescriptionAttribute];
449 NSString* helpTag = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TOOLTIP_BACK);
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);
469 accessibilitySetOverrideValue:description
470 forAttribute:NSAccessibilityDescriptionAttribute];
471 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
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]]) &&
495 respondsToSelector:@selector(setIsMouseInside:)]))
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];
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);
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;
530 button = backButton_;
533 button = forwardButton_;
536 button = homeButton_;
539 [button setEnabled:enabled];
542 // Init the enabled state of the buttons on the toolbar to match the state in
544 - (void)initCommandStatus:(CommandUpdater*)commands {
545 [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
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];
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);
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]);
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]];
636 return autocompleteTextFieldEditor_.get();
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;
657 // Show or hide the home button based on the pref.
658 - (void)showOptionalHomeButton {
659 // Ignore this message if only showing the URL bar.
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;
671 moveX *= -1; // Reverse the direction of the move.
673 [locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
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())
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];
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]
714 selector:@selector(browserActionsContainerDragged:)
715 name:kBrowserActionGrippyDraggingNotification
716 object:browserActionsContainerView_];
717 [[NSNotificationCenter defaultCenter]
719 selector:@selector(browserActionsVisibilityChanged:)
720 name:kBrowserActionVisibilityChangedNotification
721 object:browserActionsController_];
722 [[NSNotificationCenter defaultCenter]
724 selector:@selector(browserActionsContainerWillAnimate:)
725 name:kBrowserActionsContainerWillAnimate
726 object:browserActionsContainerView_];
727 [[NSNotificationCenter defaultCenter]
729 selector:@selector(adjustBrowserActionsContainerForNewWindow:)
730 name:NSWindowDidBecomeKeyNotification
731 object:[[self view] window]];
733 if (![browserActionsContainerView_ isHidden])
734 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
737 - (void)adjustBrowserActionsContainerForNewWindow:
738 (NSNotification*)notification {
739 [self toolbarFrameChanged];
740 [[NSNotificationCenter defaultCenter]
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;
766 leftDistance = NSMinX([browserActionsContainerView_ animationEndFrame]) -
769 if (leftDistance != 0.0)
770 [self adjustLocationSizeBy:leftDistance animate:animate];
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];
784 - (void)toolbarFrameChanged {
785 // Do nothing if the frame changes but no Browser Action Controller is
787 if (!browserActionsController_.get())
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
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];
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
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];
835 [browserActionsContainerView_ setFrame:containerFrame];
836 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
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];
848 [locationBar_ animateToFrame:locationFrame];
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.
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
887 if ([view isKindOfClass:[ToolbarView class]]) {
888 ToolbarView* toolbarView = (ToolbarView*)view;
889 [toolbarView setDividerOpacity:opacity];
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) {
917 // TODO(viettrungluu): dropping multiple URLs?
918 if ([urls count] > 1)
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())));
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 {
957 // (URLDropTargetController protocol)
958 - (void)hideDropURLsIndicatorInView:(NSView*)view {
962 // (URLDropTargetController protocol)
963 - (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
964 return drag_util::IsUnsupportedDropData(profile_, info);