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/autocomplete/autocomplete_input.h"
20 #include "chrome/browser/autocomplete/autocomplete_match.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/command_updater.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/search/search.h"
25 #include "chrome/browser/search_engines/template_url_service.h"
26 #include "chrome/browser/themes/theme_service.h"
27 #include "chrome/browser/ui/browser.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.h"
41 #import "chrome/browser/ui/cocoa/toolbar/toolbar_button.h"
42 #import "chrome/browser/ui/cocoa/toolbar/toolbar_view.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/global_error/global_error_service.h"
47 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
48 #include "chrome/browser/ui/omnibox/omnibox_view.h"
49 #include "chrome/browser/ui/tabs/tab_strip_model.h"
50 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
51 #include "chrome/browser/upgrade_detector.h"
52 #include "chrome/common/net/url_fixer_upper.h"
53 #include "chrome/common/pref_names.h"
54 #include "content/public/browser/notification_details.h"
55 #include "content/public/browser/notification_observer.h"
56 #include "content/public/browser/notification_service.h"
57 #include "content/public/browser/web_contents.h"
58 #include "grit/chromium_strings.h"
59 #include "grit/generated_resources.h"
60 #include "grit/theme_resources.h"
61 #import "ui/base/cocoa/menu_controller.h"
62 #include "ui/base/l10n/l10n_util.h"
63 #include "ui/base/l10n/l10n_util_mac.h"
64 #include "ui/base/resource/resource_bundle.h"
65 #include "ui/gfx/image/image.h"
66 #include "ui/gfx/rect.h"
68 using content::OpenURLParams;
69 using content::Referrer;
70 using content::WebContents;
74 // Height of the toolbar in pixels when the bookmark bar is closed.
75 const CGFloat kBaseToolbarHeightNormal = 35.0;
77 // The minimum width of the location bar in pixels.
78 const CGFloat kMinimumLocationBarWidth = 100.0;
80 // The duration of any animation that occurs within the toolbar in seconds.
81 const CGFloat kAnimationDuration = 0.2;
83 // The amount of left padding that the wrench menu should have.
84 const CGFloat kWrenchMenuLeftPadding = 3.0;
88 @interface ToolbarController()
89 @property(assign, nonatomic) Browser* browser;
90 - (void)addAccessibilityDescriptions;
91 - (void)initCommandStatus:(CommandUpdater*)commands;
92 - (void)prefChanged:(const std::string&)prefName;
93 - (BackgroundGradientView*)backgroundGradientView;
94 - (void)toolbarFrameChanged;
95 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate;
96 - (void)maintainMinimumLocationBarWidth;
97 - (void)adjustBrowserActionsContainerForNewWindow:(NSNotification*)notification;
98 - (void)browserActionsContainerDragged:(NSNotification*)notification;
99 - (void)browserActionsContainerDragFinished:(NSNotification*)notification;
100 - (void)browserActionsVisibilityChanged:(NSNotification*)notification;
101 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate;
102 - (void)updateWrenchButtonSeverity;
105 namespace ToolbarControllerInternal {
107 // A class registered for C++ notifications. This is used to detect changes in
108 // preferences and upgrade available notifications. Bridges the notification
109 // back to the ToolbarController.
110 class NotificationBridge
111 : public content::NotificationObserver {
113 explicit NotificationBridge(ToolbarController* controller)
114 : controller_(controller) {
115 registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
116 content::NotificationService::AllSources());
117 registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
118 content::Source<Profile>([controller browser]->profile()));
121 // Overridden from content::NotificationObserver:
122 virtual void Observe(int type,
123 const content::NotificationSource& source,
124 const content::NotificationDetails& details) OVERRIDE {
126 case chrome::NOTIFICATION_UPGRADE_RECOMMENDED:
127 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED:
128 [controller_ updateWrenchButtonSeverity];
135 void OnPreferenceChanged(const std::string& pref_name) {
136 [controller_ prefChanged:pref_name];
140 ToolbarController* controller_; // weak, owns us
142 content::NotificationRegistrar registrar_;
145 } // namespace ToolbarControllerInternal
147 @implementation ToolbarController
149 @synthesize browser = browser_;
151 - (id)initWithCommands:(CommandUpdater*)commands
152 profile:(Profile*)profile
153 browser:(Browser*)browser
154 resizeDelegate:(id<ViewResizer>)resizeDelegate
155 nibFileNamed:(NSString*)nibName {
156 DCHECK(commands && profile && [nibName length]);
157 if ((self = [super initWithNibName:nibName
158 bundle:base::mac::FrameworkBundle()])) {
159 commands_ = commands;
162 resizeDelegate_ = resizeDelegate;
164 hasLocationBar_ = YES;
166 // Register for notifications about state changes for the toolbar buttons
167 commandObserver_.reset(new CommandObserverBridge(self, commands));
168 commandObserver_->ObserveCommand(IDC_BACK);
169 commandObserver_->ObserveCommand(IDC_FORWARD);
170 commandObserver_->ObserveCommand(IDC_RELOAD);
171 commandObserver_->ObserveCommand(IDC_HOME);
172 commandObserver_->ObserveCommand(IDC_BOOKMARK_PAGE);
177 - (id)initWithCommands:(CommandUpdater*)commands
178 profile:(Profile*)profile
179 browser:(Browser*)browser
180 resizeDelegate:(id<ViewResizer>)resizeDelegate {
181 if ((self = [self initWithCommands:commands
184 resizeDelegate:resizeDelegate
185 nibFileNamed:@"Toolbar"])) {
192 // Unset ViewIDs of toolbar elements.
193 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
194 // |browserActionsContainerView_| are handled by themselves.
195 view_id_util::UnsetID(backButton_);
196 view_id_util::UnsetID(forwardButton_);
197 view_id_util::UnsetID(homeButton_);
198 view_id_util::UnsetID(wrenchButton_);
200 // Make sure any code in the base class which assumes [self view] is
201 // the "parent" view continues to work.
203 hasLocationBar_ = YES;
205 [[NSNotificationCenter defaultCenter] removeObserver:self];
207 if (trackingArea_.get())
208 [[self view] removeTrackingArea:trackingArea_.get()];
212 // Called after the view is done loading and the outlets have been hooked up.
213 // Now we can hook up bridges that rely on UI objects such as the location
214 // bar and button state.
215 - (void)awakeFromNib {
216 [[backButton_ cell] setImageID:IDR_BACK
217 forButtonState:image_button_cell::kDefaultState];
218 [[backButton_ cell] setImageID:IDR_BACK_H
219 forButtonState:image_button_cell::kHoverState];
220 [[backButton_ cell] setImageID:IDR_BACK_P
221 forButtonState:image_button_cell::kPressedState];
222 [[backButton_ cell] setImageID:IDR_BACK_D
223 forButtonState:image_button_cell::kDisabledState];
225 [[forwardButton_ cell] setImageID:IDR_FORWARD
226 forButtonState:image_button_cell::kDefaultState];
227 [[forwardButton_ cell] setImageID:IDR_FORWARD_H
228 forButtonState:image_button_cell::kHoverState];
229 [[forwardButton_ cell] setImageID:IDR_FORWARD_P
230 forButtonState:image_button_cell::kPressedState];
231 [[forwardButton_ cell] setImageID:IDR_FORWARD_D
232 forButtonState:image_button_cell::kDisabledState];
234 [[reloadButton_ cell] setImageID:IDR_RELOAD
235 forButtonState:image_button_cell::kDefaultState];
236 [[reloadButton_ cell] setImageID:IDR_RELOAD_H
237 forButtonState:image_button_cell::kHoverState];
238 [[reloadButton_ cell] setImageID:IDR_RELOAD_P
239 forButtonState:image_button_cell::kPressedState];
241 [[homeButton_ cell] setImageID:IDR_HOME
242 forButtonState:image_button_cell::kDefaultState];
243 [[homeButton_ cell] setImageID:IDR_HOME_H
244 forButtonState:image_button_cell::kHoverState];
245 [[homeButton_ cell] setImageID:IDR_HOME_P
246 forButtonState:image_button_cell::kPressedState];
248 [[wrenchButton_ cell] setImageID:IDR_TOOLS
249 forButtonState:image_button_cell::kDefaultState];
250 [[wrenchButton_ cell] setImageID:IDR_TOOLS_H
251 forButtonState:image_button_cell::kHoverState];
252 [[wrenchButton_ cell] setImageID:IDR_TOOLS_P
253 forButtonState:image_button_cell::kPressedState];
255 [self updateWrenchButtonSeverity];
257 [wrenchButton_ setOpenMenuOnClick:YES];
259 [backButton_ setOpenMenuOnRightClick:YES];
260 [forwardButton_ setOpenMenuOnRightClick:YES];
262 [backButton_ setHandleMiddleClick:YES];
263 [forwardButton_ setHandleMiddleClick:YES];
264 [reloadButton_ setHandleMiddleClick:YES];
265 [homeButton_ setHandleMiddleClick:YES];
267 [self initCommandStatus:commands_];
269 locationBarView_.reset(new LocationBarViewMac(locationBar_, commands_,
270 profile_, browser_));
271 [locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
272 // Register pref observers for the optional home and page/options buttons
273 // and then add them to the toolbar based on those prefs.
274 notificationBridge_.reset(
275 new ToolbarControllerInternal::NotificationBridge(self));
276 PrefService* prefs = profile_->GetPrefs();
277 showHomeButton_.Init(
278 prefs::kShowHomeButton, prefs,
280 &ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged,
281 base::Unretained(notificationBridge_.get())));
282 [self showOptionalHomeButton];
283 [self installWrenchMenu];
285 // Create the controllers for the back/forward menus.
286 backMenuController_.reset([[BackForwardMenuController alloc]
287 initWithBrowser:browser_
288 modelType:BACK_FORWARD_MENU_TYPE_BACK
289 button:backButton_]);
290 forwardMenuController_.reset([[BackForwardMenuController alloc]
291 initWithBrowser:browser_
292 modelType:BACK_FORWARD_MENU_TYPE_FORWARD
293 button:forwardButton_]);
295 // For a popup window, the toolbar is really just a location bar
296 // (see override for [ToolbarController view], below). When going
297 // fullscreen, we remove the toolbar controller's view from the view
298 // hierarchy. Calling [locationBar_ removeFromSuperview] when going
299 // fullscreen causes it to get released, making us unhappy
300 // (http://crbug.com/18551). We avoid the problem by incrementing
301 // the retain count of the location bar; use of the scoped object
302 // helps us remember to release it.
303 locationBarRetainer_.reset([locationBar_ retain]);
305 [[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
306 options:NSTrackingMouseMoved |
307 NSTrackingInVisibleRect |
308 NSTrackingMouseEnteredAndExited |
309 NSTrackingActiveAlways
312 NSView* toolbarView = [self view];
313 [toolbarView addTrackingArea:trackingArea_.get()];
315 // If the user has any Browser Actions installed, the container view for them
316 // may have to be resized depending on the width of the toolbar frame.
317 [toolbarView setPostsFrameChangedNotifications:YES];
318 [[NSNotificationCenter defaultCenter]
320 selector:@selector(toolbarFrameChanged)
321 name:NSViewFrameDidChangeNotification
324 // Set ViewIDs for toolbar elements which don't have their dedicated class.
325 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
326 // |browserActionsContainerView_| are handled by themselves.
327 view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON);
328 view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON);
329 view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON);
330 view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU);
332 [self addAccessibilityDescriptions];
335 - (void)addAccessibilityDescriptions {
336 // Set accessibility descriptions. http://openradar.appspot.com/7496255
337 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
339 accessibilitySetOverrideValue:description
340 forAttribute:NSAccessibilityDescriptionAttribute];
341 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_FORWARD);
342 [[forwardButton_ cell]
343 accessibilitySetOverrideValue:description
344 forAttribute:NSAccessibilityDescriptionAttribute];
345 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_RELOAD);
346 [[reloadButton_ cell]
347 accessibilitySetOverrideValue:description
348 forAttribute:NSAccessibilityDescriptionAttribute];
349 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_HOME);
351 accessibilitySetOverrideValue:description
352 forAttribute:NSAccessibilityDescriptionAttribute];
353 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
355 accessibilitySetOverrideValue:description
356 forAttribute:NSAccessibilityDescriptionAttribute];
357 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP);
358 [[wrenchButton_ cell]
359 accessibilitySetOverrideValue:description
360 forAttribute:NSAccessibilityDescriptionAttribute];
363 - (void)mouseExited:(NSEvent*)theEvent {
364 [[hoveredButton_ cell] setMouseInside:NO animate:YES];
365 [hoveredButton_ release];
366 hoveredButton_ = nil;
369 - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent {
370 NSButton* targetView = (NSButton*)[[self view]
371 hitTest:[theEvent locationInWindow]];
373 // Only interpret the view as a hoverButton_ if it's both button and has a
374 // button cell that cares. GradientButtonCell derived cells care.
375 if (([targetView isKindOfClass:[NSButton class]]) &&
377 respondsToSelector:@selector(setMouseInside:animate:)]))
382 - (void)mouseMoved:(NSEvent*)theEvent {
383 NSButton* targetView = [self hoverButtonForEvent:theEvent];
384 if (hoveredButton_ != targetView) {
385 [[hoveredButton_ cell] setMouseInside:NO animate:YES];
386 [[targetView cell] setMouseInside:YES animate:YES];
387 [hoveredButton_ release];
388 hoveredButton_ = [targetView retain];
392 - (void)mouseEntered:(NSEvent*)event {
393 [self mouseMoved:event];
396 - (LocationBarViewMac*)locationBarBridge {
397 return locationBarView_.get();
400 - (void)focusLocationBar:(BOOL)selectAll {
401 if (locationBarView_.get()) {
403 locationBarView_->GetToolbarModel()->WouldOmitURLDueToOriginChip()) {
404 // select_all is true when it's expected that the user may want to copy
405 // the URL to the clipboard. If the origin chip is being displayed (and
406 // thus the URL is not being shown in the Omnibox) show it now to support
407 // the same functionality.
408 locationBarView_->GetOmniboxView()->ShowURL();
410 locationBarView_->FocusLocation(selectAll ? true : false);
415 // Called when the state for a command changes to |enabled|. Update the
416 // corresponding UI element.
417 - (void)enabledStateChangedForCommand:(NSInteger)command enabled:(BOOL)enabled {
418 NSButton* button = nil;
421 button = backButton_;
424 button = forwardButton_;
427 button = homeButton_;
430 [button setEnabled:enabled];
433 // Init the enabled state of the buttons on the toolbar to match the state in
435 - (void)initCommandStatus:(CommandUpdater*)commands {
436 [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
438 setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO];
439 [reloadButton_ setEnabled:YES];
440 [homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO];
443 - (void)updateToolbarWithContents:(WebContents*)tab {
444 locationBarView_->Update(tab);
446 [locationBar_ updateMouseTracking];
448 if (browserActionsController_.get()) {
449 [browserActionsController_ update];
453 - (void)setStarredState:(BOOL)isStarred {
454 locationBarView_->SetStarred(isStarred);
457 - (void)setTranslateIconLit:(BOOL)on {
458 locationBarView_->SetTranslateIconLit(on);
461 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
462 locationBarView_->ZoomChangedForActiveTab(
463 canShowBubble && ![wrenchMenuController_ isMenuOpen]);
466 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
467 [reloadButton_ setIsLoading:isLoading force:force];
470 - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar {
471 [self view]; // Force nib loading.
473 hasToolbar_ = toolbar;
475 // If there's a toolbar, there must be a location bar.
476 DCHECK((toolbar && locBar) || !toolbar);
477 hasLocationBar_ = toolbar ? YES : locBar;
479 // Decide whether to hide/show based on whether there's a location bar.
480 [[self view] setHidden:!hasLocationBar_];
482 // Make location bar not editable when in a pop-up.
483 locationBarView_->SetEditable(toolbar);
492 // (Private) Returns the backdrop to the toolbar.
493 - (BackgroundGradientView*)backgroundGradientView {
494 // We really do mean |[super view]|; see our override of |-view|.
495 DCHECK([[super view] isKindOfClass:[BackgroundGradientView class]]);
496 return (BackgroundGradientView*)[super view];
499 - (id)customFieldEditorForObject:(id)obj {
500 if (obj == locationBar_) {
501 // Lazilly construct Field editor, Cocoa UI code always runs on the
502 // same thread, so there shoudn't be a race condition here.
503 if (autocompleteTextFieldEditor_.get() == nil) {
504 autocompleteTextFieldEditor_.reset(
505 [[AutocompleteTextFieldEditor alloc] init]);
508 // This needs to be called every time, otherwise notifications
509 // aren't sent correctly.
510 DCHECK(autocompleteTextFieldEditor_.get());
511 [autocompleteTextFieldEditor_.get() setFieldEditor:YES];
512 if (base::mac::IsOSSnowLeopard()) {
513 // Manually transferring the drawsBackground and backgroundColor
514 // properties is necessary to ensure anti-aliased text on 10.6.
515 [autocompleteTextFieldEditor_
516 setDrawsBackground:[locationBar_ drawsBackground]];
517 [autocompleteTextFieldEditor_
518 setBackgroundColor:[locationBar_ backgroundColor]];
520 return autocompleteTextFieldEditor_.get();
525 // Returns an array of views in the order of the outlets above.
526 - (NSArray*)toolbarViews {
527 return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_,
528 homeButton_, wrenchButton_, locationBar_,
529 browserActionsContainerView_, nil];
532 // Moves |rect| to the right by |delta|, keeping the right side fixed by
533 // shrinking the width to compensate. Passing a negative value for |deltaX|
534 // moves to the left and increases the width.
535 - (NSRect)adjustRect:(NSRect)rect byAmount:(CGFloat)deltaX {
536 NSRect frame = NSOffsetRect(rect, deltaX, 0);
537 frame.size.width -= deltaX;
541 // Show or hide the home button based on the pref.
542 - (void)showOptionalHomeButton {
543 // Ignore this message if only showing the URL bar.
546 BOOL hide = showHomeButton_.GetValue() ? NO : YES;
547 if (hide == [homeButton_ isHidden])
548 return; // Nothing to do, view state matches pref state.
550 // Always shift the text field by the width of the home button minus one pixel
551 // since the frame edges of each button are right on top of each other. When
552 // hiding the button, reverse the direction of the movement (to the left).
553 CGFloat moveX = [homeButton_ frame].size.width - 1.0;
555 moveX *= -1; // Reverse the direction of the move.
557 [locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
559 [homeButton_ setHidden:hide];
562 // Install the menu wrench buttons. Calling this repeatedly is inexpensive so it
563 // can be done every time the buttons are shown.
564 - (void)installWrenchMenu {
565 if (wrenchMenuController_.get())
568 wrenchMenuController_.reset(
569 [[WrenchMenuController alloc] initWithBrowser:browser_]);
570 [wrenchMenuController_ setUseWithPopUpButtonCell:YES];
571 [wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]];
574 - (WrenchMenuController*)wrenchMenuController {
575 return wrenchMenuController_;
578 - (void)updateWrenchButtonSeverity {
579 WrenchToolbarButtonCell* cell =
580 base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
581 if (UpgradeDetector::GetInstance()->notify_upgrade()) {
582 UpgradeDetector::UpgradeNotificationAnnoyanceLevel level =
583 UpgradeDetector::GetInstance()->upgrade_notification_stage();
584 [cell setSeverity:WrenchIconPainter::SeverityFromUpgradeLevel(level)
585 shouldAnimate:WrenchIconPainter::ShouldAnimateUpgradeLevel(level)];
589 GlobalError* error = GlobalErrorServiceFactory::GetForProfile(
590 browser_->profile())->GetHighestSeverityGlobalErrorWithWrenchMenuItem();
592 [cell setSeverity:WrenchIconPainter::GlobalErrorSeverity()
597 [cell setSeverity:WrenchIconPainter::SEVERITY_NONE shouldAnimate:YES];
600 - (void)prefChanged:(const std::string&)prefName {
601 if (prefName == prefs::kShowHomeButton) {
602 [self showOptionalHomeButton];
606 - (void)createBrowserActionButtons {
607 if (!browserActionsController_.get()) {
608 browserActionsController_.reset([[BrowserActionsController alloc]
609 initWithBrowser:browser_
610 containerView:browserActionsContainerView_]);
611 [[NSNotificationCenter defaultCenter]
613 selector:@selector(browserActionsContainerDragged:)
614 name:kBrowserActionGrippyDraggingNotification
615 object:browserActionsController_];
616 [[NSNotificationCenter defaultCenter]
618 selector:@selector(browserActionsContainerDragFinished:)
619 name:kBrowserActionGrippyDragFinishedNotification
620 object:browserActionsController_];
621 [[NSNotificationCenter defaultCenter]
623 selector:@selector(browserActionsVisibilityChanged:)
624 name:kBrowserActionVisibilityChangedNotification
625 object:browserActionsController_];
626 [[NSNotificationCenter defaultCenter]
628 selector:@selector(adjustBrowserActionsContainerForNewWindow:)
629 name:NSWindowDidBecomeKeyNotification
630 object:[[self view] window]];
632 CGFloat containerWidth = [browserActionsContainerView_ isHidden] ? 0.0 :
633 NSWidth([browserActionsContainerView_ frame]);
634 if (containerWidth > 0.0)
635 [self adjustLocationSizeBy:(containerWidth * -1) animate:NO];
638 - (void)adjustBrowserActionsContainerForNewWindow:
639 (NSNotification*)notification {
640 [self toolbarFrameChanged];
641 [[NSNotificationCenter defaultCenter]
643 name:NSWindowDidBecomeKeyNotification
644 object:[[self view] window]];
647 - (void)browserActionsContainerDragged:(NSNotification*)notification {
648 CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
649 locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
650 [browserActionsContainerView_ setCanDragLeft:!locationBarAtMinSize_];
651 [browserActionsContainerView_ setGrippyPinned:locationBarAtMinSize_];
652 [self adjustLocationSizeBy:
653 [browserActionsContainerView_ resizeDeltaX] animate:NO];
656 - (void)browserActionsContainerDragFinished:(NSNotification*)notification {
657 [browserActionsController_ resizeContainerAndAnimate:YES];
658 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES];
661 - (void)browserActionsVisibilityChanged:(NSNotification*)notification {
662 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
665 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate {
666 CGFloat locationBarXPos = NSMaxX([locationBar_ frame]);
667 CGFloat leftDistance;
669 if ([browserActionsContainerView_ isHidden]) {
670 CGFloat edgeXPos = [wrenchButton_ frame].origin.x;
671 leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding;
673 NSRect containerFrame = animate ?
674 [browserActionsContainerView_ animationEndFrame] :
675 [browserActionsContainerView_ frame];
677 leftDistance = containerFrame.origin.x - locationBarXPos;
679 if (leftDistance != 0.0)
680 [self adjustLocationSizeBy:leftDistance animate:animate];
683 - (void)maintainMinimumLocationBarWidth {
684 CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
685 locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
686 if (locationBarAtMinSize_) {
687 CGFloat dX = kMinimumLocationBarWidth - locationBarWidth;
688 [self adjustLocationSizeBy:dX animate:NO];
692 - (void)toolbarFrameChanged {
693 // Do nothing if the frame changes but no Browser Action Controller is
695 if (!browserActionsController_.get())
698 [self maintainMinimumLocationBarWidth];
700 if (locationBarAtMinSize_) {
701 // Once the grippy is pinned, leave it until it is explicity un-pinned.
702 [browserActionsContainerView_ setGrippyPinned:YES];
703 NSRect containerFrame = [browserActionsContainerView_ frame];
704 // Determine how much the container needs to move in case it's overlapping
705 // with the location bar.
706 CGFloat dX = NSMaxX([locationBar_ frame]) - containerFrame.origin.x;
707 containerFrame = NSOffsetRect(containerFrame, dX, 0);
708 containerFrame.size.width -= dX;
709 [browserActionsContainerView_ setFrame:containerFrame];
710 } else if (!locationBarAtMinSize_ &&
711 [browserActionsContainerView_ grippyPinned]) {
712 // Expand out the container until it hits the saved size, then unpin the
714 // Add 0.1 pixel so that it doesn't hit the minimum width codepath above.
715 CGFloat dX = NSWidth([locationBar_ frame]) -
716 (kMinimumLocationBarWidth + 0.1);
717 NSRect containerFrame = [browserActionsContainerView_ frame];
718 containerFrame = NSOffsetRect(containerFrame, -dX, 0);
719 containerFrame.size.width += dX;
720 CGFloat savedContainerWidth = [browserActionsController_ savedWidth];
721 if (NSWidth(containerFrame) >= savedContainerWidth) {
722 containerFrame = NSOffsetRect(containerFrame,
723 NSWidth(containerFrame) - savedContainerWidth, 0);
724 containerFrame.size.width = savedContainerWidth;
725 [browserActionsContainerView_ setGrippyPinned:NO];
727 [browserActionsContainerView_ setFrame:containerFrame];
728 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
732 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate {
733 // Ensure that the location bar is in its proper place.
734 NSRect locationFrame = [locationBar_ frame];
735 locationFrame.size.width += dX;
738 [locationBar_ setFrame:locationFrame];
742 [NSAnimationContext beginGrouping];
743 [[NSAnimationContext currentContext] setDuration:kAnimationDuration];
744 [[locationBar_ animator] setFrame:locationFrame];
745 [NSAnimationContext endGrouping];
748 - (NSPoint)bookmarkBubblePoint {
749 if (locationBarView_->IsStarEnabled())
750 return locationBarView_->GetBookmarkBubblePoint();
752 // Grab bottom middle of hotdogs.
753 NSRect frame = wrenchButton_.frame;
754 NSPoint point = NSMakePoint(NSMidX(frame), NSMinY(frame));
755 // Inset to account for the whitespace around the hotdogs.
756 point.y += wrench_menu_controller::kWrenchBubblePointOffsetY;
757 return [self.view convertPoint:point toView:nil];
760 - (NSPoint)translateBubblePoint {
761 return locationBarView_->GetTranslateBubblePoint();
764 - (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight {
765 // With no toolbar, just ignore the compression.
767 return NSHeight([locationBar_ frame]);
769 return kBaseToolbarHeightNormal - compressByHeight;
772 - (void)setDividerOpacity:(CGFloat)opacity {
773 BackgroundGradientView* view = [self backgroundGradientView];
774 [view setShowsDivider:(opacity > 0 ? YES : NO)];
776 // We may not have a toolbar view (e.g., popup windows only have a location
778 if ([view isKindOfClass:[ToolbarView class]]) {
779 ToolbarView* toolbarView = (ToolbarView*)view;
780 [toolbarView setDividerOpacity:opacity];
783 [view setNeedsDisplay:YES];
786 - (BrowserActionsController*)browserActionsController {
787 return browserActionsController_.get();
790 - (NSView*)wrenchButton {
791 return wrenchButton_;
794 - (void)activatePageAction:(const std::string&)extension_id {
795 locationBarView_->ActivatePageAction(extension_id);
798 // Activates the browser action for the extension that has the given id.
799 - (void)activateBrowserAction:(const std::string&)extension_id {
800 [browserActionsController_ activateBrowserAction:extension_id];
803 // (URLDropTargetController protocol)
804 - (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point {
805 // TODO(viettrungluu): This code is more or less copied from the code in
806 // |TabStripController|. I'll refactor this soon to make it common and expand
807 // its capabilities (e.g., allow text DnD).
808 if ([urls count] < 1) {
813 // TODO(viettrungluu): dropping multiple URLs?
814 if ([urls count] > 1)
817 // Get the first URL and fix it up.
818 GURL url(URLFixerUpper::FixupURL(
819 base::SysNSStringToUTF8([urls objectAtIndex:0]), std::string()));
821 if (url.SchemeIs(content::kJavaScriptScheme)) {
822 browser_->window()->GetLocationBar()->GetOmniboxView()->SetUserText(
823 OmniboxView::StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())));
825 OpenURLParams params(
826 url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false);
827 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
830 // (URLDropTargetController protocol)
831 - (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point {
832 // TODO(viettrungluu): This code is more or less copied from the code in
833 // |TabStripController|. I'll refactor this soon to make it common and expand
834 // its capabilities (e.g., allow text DnD).
836 // If the input is plain text, classify the input and make the URL.
837 AutocompleteMatch match;
838 AutocompleteClassifierFactory::GetForProfile(browser_->profile())->Classify(
839 base::SysNSStringToUTF16(text), false, false, AutocompleteInput::BLANK,
841 GURL url(match.destination_url);
843 OpenURLParams params(
844 url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false);
845 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
848 // (URLDropTargetController protocol)
849 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
853 // (URLDropTargetController protocol)
854 - (void)hideDropURLsIndicatorInView:(NSView*)view {
858 // (URLDropTargetController protocol)
859 - (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
860 return drag_util::IsUnsupportedDropData(profile_, info);