Home | History | Annotate | Download | only in tabs
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
      6 
      7 #import <QuartzCore/QuartzCore.h>
      8 
      9 #include <limits>
     10 #include <string>
     11 
     12 #include "app/mac/nsimage_cache.h"
     13 #include "base/command_line.h"
     14 #include "base/mac/mac_util.h"
     15 #include "base/sys_string_conversions.h"
     16 #include "chrome/app/chrome_command_ids.h"
     17 #include "chrome/browser/autocomplete/autocomplete.h"
     18 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
     19 #include "chrome/browser/autocomplete/autocomplete_match.h"
     20 #include "chrome/browser/extensions/extension_tab_helper.h"
     21 #include "chrome/browser/metrics/user_metrics.h"
     22 #include "chrome/browser/prefs/pref_service.h"
     23 #include "chrome/browser/profiles/profile.h"
     24 #include "chrome/browser/debugger/devtools_window.h"
     25 #include "chrome/browser/net/url_fixer_upper.h"
     26 #include "chrome/browser/sidebar/sidebar_container.h"
     27 #include "chrome/browser/sidebar/sidebar_manager.h"
     28 #include "chrome/browser/tabs/tab_strip_model.h"
     29 #include "chrome/browser/ui/browser.h"
     30 #include "chrome/browser/ui/browser_navigator.h"
     31 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
     32 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
     33 #import "chrome/browser/ui/cocoa/constrained_window_mac.h"
     34 #import "chrome/browser/ui/cocoa/new_tab_button.h"
     35 #import "chrome/browser/ui/cocoa/profile_menu_button.h"
     36 #import "chrome/browser/ui/cocoa/tab_contents/favicon_util.h"
     37 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h"
     38 #import "chrome/browser/ui/cocoa/tabs/tab_strip_model_observer_bridge.h"
     39 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
     40 #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
     41 #import "chrome/browser/ui/cocoa/tabs/throbber_view.h"
     42 #import "chrome/browser/ui/cocoa/tracking_area.h"
     43 #include "chrome/browser/ui/find_bar/find_bar.h"
     44 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
     45 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     46 #include "chrome/common/chrome_switches.h"
     47 #include "chrome/common/pref_names.h"
     48 #include "content/browser/tab_contents/navigation_controller.h"
     49 #include "content/browser/tab_contents/navigation_entry.h"
     50 #include "content/browser/tab_contents/tab_contents.h"
     51 #include "content/browser/tab_contents/tab_contents_view.h"
     52 #include "content/common/notification_service.h"
     53 #include "grit/app_resources.h"
     54 #include "grit/generated_resources.h"
     55 #include "grit/theme_resources.h"
     56 #include "skia/ext/skia_utils_mac.h"
     57 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
     58 #include "ui/base/l10n/l10n_util.h"
     59 #include "ui/base/resource/resource_bundle.h"
     60 #include "ui/gfx/image.h"
     61 
     62 NSString* const kTabStripNumberOfTabsChanged = @"kTabStripNumberOfTabsChanged";
     63 
     64 // 10.7 adds public APIs for full-screen support. Provide the declaration so it
     65 // can be called below when building with the 10.5 SDK.
     66 #if !defined(MAC_OS_X_VERSION_10_7) || \
     67 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
     68 
     69 @interface NSWindow (LionSDKDeclarations)
     70 - (void)toggleFullScreen:(id)sender;
     71 @end
     72 
     73 enum {
     74   NSWindowFullScreenButton = 7
     75 };
     76 
     77 #endif  // MAC_OS_X_VERSION_10_7
     78 
     79 namespace {
     80 
     81 // The images names used for different states of the new tab button.
     82 NSString* const kNewTabHoverImage = @"newtab_h.pdf";
     83 NSString* const kNewTabImage = @"newtab.pdf";
     84 NSString* const kNewTabPressedImage = @"newtab_p.pdf";
     85 
     86 // A value to indicate tab layout should use the full available width of the
     87 // view.
     88 const CGFloat kUseFullAvailableWidth = -1.0;
     89 
     90 // The amount by which tabs overlap.
     91 const CGFloat kTabOverlap = 20.0;
     92 
     93 // The width and height for a tab's icon.
     94 const CGFloat kIconWidthAndHeight = 16.0;
     95 
     96 // The amount by which the new tab button is offset (from the tabs).
     97 const CGFloat kNewTabButtonOffset = 8.0;
     98 
     99 // The amount by which to shrink the tab strip (on the right) when the
    100 // incognito badge is present.
    101 const CGFloat kIncognitoBadgeTabStripShrink = 18;
    102 
    103 // Time (in seconds) in which tabs animate to their final position.
    104 const NSTimeInterval kAnimationDuration = 0.125;
    105 
    106 // The amount by wich the profile menu button is offset (from tab tabs or new
    107 // tab button).
    108 const CGFloat kProfileMenuButtonOffset = 6.0;
    109 
    110 // Helper class for doing NSAnimationContext calls that takes a bool to disable
    111 // all the work.  Useful for code that wants to conditionally animate.
    112 class ScopedNSAnimationContextGroup {
    113  public:
    114   explicit ScopedNSAnimationContextGroup(bool animate)
    115       : animate_(animate) {
    116     if (animate_) {
    117       [NSAnimationContext beginGrouping];
    118     }
    119   }
    120 
    121   ~ScopedNSAnimationContextGroup() {
    122     if (animate_) {
    123       [NSAnimationContext endGrouping];
    124     }
    125   }
    126 
    127   void SetCurrentContextDuration(NSTimeInterval duration) {
    128     if (animate_) {
    129       [[NSAnimationContext currentContext] gtm_setDuration:duration
    130                                                  eventMask:NSLeftMouseUpMask];
    131     }
    132   }
    133 
    134   void SetCurrentContextShortestDuration() {
    135     if (animate_) {
    136       // The minimum representable time interval.  This used to stop an
    137       // in-progress animation as quickly as possible.
    138       const NSTimeInterval kMinimumTimeInterval =
    139           std::numeric_limits<NSTimeInterval>::min();
    140       // Directly set the duration to be short, avoiding the Steve slowmotion
    141       // ettect the gtm_setDuration: provides.
    142       [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval];
    143     }
    144   }
    145 
    146 private:
    147   bool animate_;
    148   DISALLOW_COPY_AND_ASSIGN(ScopedNSAnimationContextGroup);
    149 };
    150 
    151 }  // namespace
    152 
    153 @interface TabStripController (Private)
    154 - (void)addSubviewToPermanentList:(NSView*)aView;
    155 - (void)regenerateSubviewList;
    156 - (NSInteger)indexForContentsView:(NSView*)view;
    157 - (void)updateFaviconForContents:(TabContents*)contents
    158                          atIndex:(NSInteger)modelIndex;
    159 - (void)layoutTabsWithAnimation:(BOOL)animate
    160              regenerateSubviews:(BOOL)doUpdate;
    161 - (void)animationDidStopForController:(TabController*)controller
    162                              finished:(BOOL)finished;
    163 - (NSInteger)indexFromModelIndex:(NSInteger)index;
    164 - (NSInteger)numberOfOpenTabs;
    165 - (NSInteger)numberOfOpenMiniTabs;
    166 - (NSInteger)numberOfOpenNonMiniTabs;
    167 - (void)mouseMoved:(NSEvent*)event;
    168 - (void)setTabTrackingAreasEnabled:(BOOL)enabled;
    169 - (void)droppingURLsAt:(NSPoint)point
    170             givesIndex:(NSInteger*)index
    171            disposition:(WindowOpenDisposition*)disposition;
    172 - (void)setNewTabButtonHoverState:(BOOL)showHover;
    173 - (BOOL)shouldShowProfileMenuButton;
    174 - (void)updateProfileMenuButton;
    175 @end
    176 
    177 // A simple view class that prevents the Window Server from dragging the area
    178 // behind tabs. Sometimes core animation confuses it. Unfortunately, it can also
    179 // falsely pick up clicks during rapid tab closure, so we have to account for
    180 // that.
    181 @interface TabStripControllerDragBlockingView : NSView {
    182   TabStripController* controller_;  // weak; owns us
    183 }
    184 
    185 - (id)initWithFrame:(NSRect)frameRect
    186          controller:(TabStripController*)controller;
    187 @end
    188 
    189 @implementation TabStripControllerDragBlockingView
    190 - (BOOL)mouseDownCanMoveWindow {return NO;}
    191 - (void)drawRect:(NSRect)rect {}
    192 
    193 - (id)initWithFrame:(NSRect)frameRect
    194          controller:(TabStripController*)controller {
    195   if ((self = [super initWithFrame:frameRect]))
    196     controller_ = controller;
    197   return self;
    198 }
    199 
    200 // In "rapid tab closure" mode (i.e., the user is clicking close tab buttons in
    201 // rapid succession), the animations confuse Cocoa's hit testing (which appears
    202 // to use cached results, among other tricks), so this view can somehow end up
    203 // getting a mouse down event. Thus we do an explicit hit test during rapid tab
    204 // closure, and if we find that we got a mouse down we shouldn't have, we send
    205 // it off to the appropriate view.
    206 - (void)mouseDown:(NSEvent*)event {
    207   if ([controller_ inRapidClosureMode]) {
    208     NSView* superview = [self superview];
    209     NSPoint hitLocation =
    210         [[superview superview] convertPoint:[event locationInWindow]
    211                                    fromView:nil];
    212     NSView* hitView = [superview hitTest:hitLocation];
    213     if (hitView != self) {
    214       [hitView mouseDown:event];
    215       return;
    216     }
    217   }
    218   [super mouseDown:event];
    219 }
    220 @end
    221 
    222 #pragma mark -
    223 
    224 // A delegate, owned by the CAAnimation system, that is alerted when the
    225 // animation to close a tab is completed. Calls back to the given tab strip
    226 // to let it know that |controller_| is ready to be removed from the model.
    227 // Since we only maintain weak references, the tab strip must call -invalidate:
    228 // to prevent the use of dangling pointers.
    229 @interface TabCloseAnimationDelegate : NSObject {
    230  @private
    231   TabStripController* strip_;  // weak; owns us indirectly
    232   TabController* controller_;  // weak
    233 }
    234 
    235 // Will tell |strip| when the animation for |controller|'s view has completed.
    236 // These should not be nil, and will not be retained.
    237 - (id)initWithTabStrip:(TabStripController*)strip
    238          tabController:(TabController*)controller;
    239 
    240 // Invalidates this object so that no further calls will be made to
    241 // |strip_|.  This should be called when |strip_| is released, to
    242 // prevent attempts to call into the released object.
    243 - (void)invalidate;
    244 
    245 // CAAnimation delegate method
    246 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished;
    247 
    248 @end
    249 
    250 @implementation TabCloseAnimationDelegate
    251 
    252 - (id)initWithTabStrip:(TabStripController*)strip
    253          tabController:(TabController*)controller {
    254   if ((self = [super init])) {
    255     DCHECK(strip && controller);
    256     strip_ = strip;
    257     controller_ = controller;
    258   }
    259   return self;
    260 }
    261 
    262 - (void)invalidate {
    263   strip_ = nil;
    264   controller_ = nil;
    265 }
    266 
    267 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished {
    268   [strip_ animationDidStopForController:controller_ finished:finished];
    269 }
    270 
    271 @end
    272 
    273 namespace TabStripControllerInternal {
    274 
    275 // Bridges C++ notifications back to the TabStripController.
    276 class NotificationBridge : public NotificationObserver {
    277  public:
    278   explicit NotificationBridge(TabStripController* controller,
    279                               PrefService* prefService)
    280       : controller_(controller) {
    281     DCHECK(prefService);
    282     usernamePref_.Init(prefs::kGoogleServicesUsername, prefService, this);
    283   }
    284 
    285   // Overridden from NotificationObserver:
    286   virtual void Observe(NotificationType type,
    287                        const NotificationSource& source,
    288                        const NotificationDetails& details) {
    289     DCHECK_EQ(NotificationType::PREF_CHANGED, type.value);
    290     std::string* name = Details<std::string>(details).ptr();
    291     if (prefs::kGoogleServicesUsername == *name) {
    292       [controller_ updateProfileMenuButton];
    293       [controller_ layoutTabsWithAnimation:NO regenerateSubviews:NO];
    294     }
    295   }
    296 
    297  private:
    298   TabStripController* controller_;  // weak, owns us
    299 
    300   // The Google services user name associated with this BrowserView's profile.
    301   StringPrefMember usernamePref_;
    302 };
    303 
    304 } // namespace TabStripControllerInternal
    305 
    306 #pragma mark -
    307 
    308 // In general, there is a one-to-one correspondence between TabControllers,
    309 // TabViews, TabContentsControllers, and the TabContents in the TabStripModel.
    310 // In the steady-state, the indices line up so an index coming from the model
    311 // is directly mapped to the same index in the parallel arrays holding our
    312 // views and controllers. This is also true when new tabs are created (even
    313 // though there is a small period of animation) because the tab is present
    314 // in the model while the TabView is animating into place. As a result, nothing
    315 // special need be done to handle "new tab" animation.
    316 //
    317 // This all goes out the window with the "close tab" animation. The animation
    318 // kicks off in |-tabDetachedWithContents:atIndex:| with the notification that
    319 // the tab has been removed from the model. The simplest solution at this
    320 // point would be to remove the views and controllers as well, however once
    321 // the TabView is removed from the view list, the tab z-order code takes care of
    322 // removing it from the tab strip and we'll get no animation. That means if
    323 // there is to be any visible animation, the TabView needs to stay around until
    324 // its animation is complete. In order to maintain consistency among the
    325 // internal parallel arrays, this means all structures are kept around until
    326 // the animation completes. At this point, though, the model and our internal
    327 // structures are out of sync: the indices no longer line up. As a result,
    328 // there is a concept of a "model index" which represents an index valid in
    329 // the TabStripModel. During steady-state, the "model index" is just the same
    330 // index as our parallel arrays (as above), but during tab close animations,
    331 // it is different, offset by the number of tabs preceding the index which
    332 // are undergoing tab closing animation. As a result, the caller needs to be
    333 // careful to use the available conversion routines when accessing the internal
    334 // parallel arrays (e.g., -indexFromModelIndex:). Care also needs to be taken
    335 // during tab layout to ignore closing tabs in the total width calculations and
    336 // in individual tab positioning (to avoid moving them right back to where they
    337 // were).
    338 //
    339 // In order to prevent actions being taken on tabs which are closing, the tab
    340 // itself gets marked as such so it no longer will send back its select action
    341 // or allow itself to be dragged. In addition, drags on the tab strip as a
    342 // whole are disabled while there are tabs closing.
    343 
    344 @implementation TabStripController
    345 
    346 @synthesize indentForControls = indentForControls_;
    347 
    348 - (id)initWithView:(TabStripView*)view
    349         switchView:(NSView*)switchView
    350            browser:(Browser*)browser
    351           delegate:(id<TabStripControllerDelegate>)delegate {
    352   DCHECK(view && switchView && browser && delegate);
    353   if ((self = [super init])) {
    354     tabStripView_.reset([view retain]);
    355     switchView_ = switchView;
    356     browser_ = browser;
    357     tabStripModel_ = browser_->tabstrip_model();
    358     delegate_ = delegate;
    359     bridge_.reset(new TabStripModelObserverBridge(tabStripModel_, self));
    360     tabContentsArray_.reset([[NSMutableArray alloc] init]);
    361     tabArray_.reset([[NSMutableArray alloc] init]);
    362     NSWindow* browserWindow = [view window];
    363 
    364     // Important note: any non-tab subviews not added to |permanentSubviews_|
    365     // (see |-addSubviewToPermanentList:|) will be wiped out.
    366     permanentSubviews_.reset([[NSMutableArray alloc] init]);
    367 
    368     defaultFavicon_.reset(
    369         [app::mac::GetCachedImageWithName(@"nav.pdf") retain]);
    370 
    371     [self setIndentForControls:[[self class] defaultIndentForControls]];
    372 
    373     // TODO(viettrungluu): WTF? "For some reason, if the view is present in the
    374     // nib a priori, it draws correctly. If we create it in code and add it to
    375     // the tab view, it draws with all sorts of crazy artifacts."
    376     newTabButton_ = [view newTabButton];
    377     [self addSubviewToPermanentList:newTabButton_];
    378     [newTabButton_ setTarget:nil];
    379     [newTabButton_ setAction:@selector(commandDispatch:)];
    380     [newTabButton_ setTag:IDC_NEW_TAB];
    381 
    382     profileMenuButton_ = [view profileMenuButton];
    383     [self addSubviewToPermanentList:profileMenuButton_];
    384     [self updateProfileMenuButton];
    385     // Register pref observers for profile name.
    386     notificationBridge_.reset(
    387         new TabStripControllerInternal::NotificationBridge(
    388             self, browser_->profile()->GetPrefs()));
    389 
    390     // Set the images from code because Cocoa fails to find them in our sub
    391     // bundle during tests.
    392     [newTabButton_ setImage:app::mac::GetCachedImageWithName(kNewTabImage)];
    393     [newTabButton_ setAlternateImage:
    394         app::mac::GetCachedImageWithName(kNewTabPressedImage)];
    395     newTabButtonShowingHoverImage_ = NO;
    396     newTabTrackingArea_.reset(
    397         [[CrTrackingArea alloc] initWithRect:[newTabButton_ bounds]
    398                                      options:(NSTrackingMouseEnteredAndExited |
    399                                               NSTrackingActiveAlways)
    400                                 proxiedOwner:self
    401                                     userInfo:nil]);
    402     if (browserWindow)  // Nil for Browsers without a tab strip (e.g. popups).
    403       [newTabTrackingArea_ clearOwnerWhenWindowWillClose:browserWindow];
    404     [newTabButton_ addTrackingArea:newTabTrackingArea_.get()];
    405     targetFrames_.reset([[NSMutableDictionary alloc] init]);
    406 
    407     dragBlockingView_.reset(
    408         [[TabStripControllerDragBlockingView alloc] initWithFrame:NSZeroRect
    409                                                        controller:self]);
    410     [self addSubviewToPermanentList:dragBlockingView_];
    411 
    412     newTabTargetFrame_ = NSMakeRect(0, 0, 0, 0);
    413     availableResizeWidth_ = kUseFullAvailableWidth;
    414 
    415     closingControllers_.reset([[NSMutableSet alloc] init]);
    416 
    417     // Install the permanent subviews.
    418     [self regenerateSubviewList];
    419 
    420     // Watch for notifications that the tab strip view has changed size so
    421     // we can tell it to layout for the new size.
    422     [[NSNotificationCenter defaultCenter]
    423         addObserver:self
    424            selector:@selector(tabViewFrameChanged:)
    425                name:NSViewFrameDidChangeNotification
    426              object:tabStripView_];
    427 
    428     trackingArea_.reset([[CrTrackingArea alloc]
    429         initWithRect:NSZeroRect  // Ignored by NSTrackingInVisibleRect
    430              options:NSTrackingMouseEnteredAndExited |
    431                      NSTrackingMouseMoved |
    432                      NSTrackingActiveAlways |
    433                      NSTrackingInVisibleRect
    434         proxiedOwner:self
    435             userInfo:nil]);
    436     if (browserWindow)  // Nil for Browsers without a tab strip (e.g. popups).
    437       [trackingArea_ clearOwnerWhenWindowWillClose:browserWindow];
    438     [tabStripView_ addTrackingArea:trackingArea_.get()];
    439 
    440     // Check to see if the mouse is currently in our bounds so we can
    441     // enable the tracking areas.  Otherwise we won't get hover states
    442     // or tab gradients if we load the window up under the mouse.
    443     NSPoint mouseLoc = [[view window] mouseLocationOutsideOfEventStream];
    444     mouseLoc = [view convertPoint:mouseLoc fromView:nil];
    445     if (NSPointInRect(mouseLoc, [view bounds])) {
    446       [self setTabTrackingAreasEnabled:YES];
    447       mouseInside_ = YES;
    448     }
    449 
    450     // Set accessibility descriptions. http://openradar.appspot.com/7496255
    451     NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_NEWTAB);
    452     [[newTabButton_ cell]
    453         accessibilitySetOverrideValue:description
    454                          forAttribute:NSAccessibilityDescriptionAttribute];
    455 
    456     // Controller may have been (re-)created by switching layout modes, which
    457     // means the tab model is already fully formed with tabs. Need to walk the
    458     // list and create the UI for each.
    459     const int existingTabCount = tabStripModel_->count();
    460     const TabContentsWrapper* selection =
    461         tabStripModel_->GetSelectedTabContents();
    462     for (int i = 0; i < existingTabCount; ++i) {
    463       TabContentsWrapper* currentContents = tabStripModel_->GetTabContentsAt(i);
    464       [self insertTabWithContents:currentContents
    465                           atIndex:i
    466                      inForeground:NO];
    467       if (selection == currentContents) {
    468         // Must manually force a selection since the model won't send
    469         // selection messages in this scenario.
    470         [self selectTabWithContents:currentContents
    471                    previousContents:NULL
    472                             atIndex:i
    473                         userGesture:NO];
    474       }
    475     }
    476     // Don't lay out the tabs until after the controller has been fully
    477     // constructed. The |verticalLayout_| flag has not been initialized by
    478     // subclasses at this point, which would cause layout to potentially use
    479     // the wrong mode.
    480     if (existingTabCount) {
    481       [self performSelectorOnMainThread:@selector(layoutTabs)
    482                              withObject:nil
    483                           waitUntilDone:NO];
    484     }
    485   }
    486   return self;
    487 }
    488 
    489 - (void)dealloc {
    490   if (trackingArea_.get())
    491     [tabStripView_ removeTrackingArea:trackingArea_.get()];
    492 
    493   [newTabButton_ removeTrackingArea:newTabTrackingArea_.get()];
    494   // Invalidate all closing animations so they don't call back to us after
    495   // we're gone.
    496   for (TabController* controller in closingControllers_.get()) {
    497     NSView* view = [controller view];
    498     [[[view animationForKey:@"frameOrigin"] delegate] invalidate];
    499   }
    500   [[NSNotificationCenter defaultCenter] removeObserver:self];
    501   [super dealloc];
    502 }
    503 
    504 + (CGFloat)defaultTabHeight {
    505   return 25.0;
    506 }
    507 
    508 + (CGFloat)defaultIndentForControls {
    509   // Default indentation leaves enough room so tabs don't overlap with the
    510   // window controls.
    511   return 70.0;
    512 }
    513 
    514 // Finds the TabContentsController associated with the given index into the tab
    515 // model and swaps out the sole child of the contentArea to display its
    516 // contents.
    517 - (void)swapInTabAtIndex:(NSInteger)modelIndex {
    518   DCHECK(modelIndex >= 0 && modelIndex < tabStripModel_->count());
    519   NSInteger index = [self indexFromModelIndex:modelIndex];
    520   TabContentsController* controller = [tabContentsArray_ objectAtIndex:index];
    521 
    522   // Resize the new view to fit the window. Calling |view| may lazily
    523   // instantiate the TabContentsController from the nib. Until we call
    524   // |-ensureContentsVisible|, the controller doesn't install the RWHVMac into
    525   // the view hierarchy. This is in order to avoid sending the renderer a
    526   // spurious default size loaded from the nib during the call to |-view|.
    527   NSView* newView = [controller view];
    528 
    529   // Turns content autoresizing off, so removing and inserting views won't
    530   // trigger unnecessary content relayout.
    531   [controller ensureContentsSizeDoesNotChange];
    532 
    533   // Remove the old view from the view hierarchy. We know there's only one
    534   // child of |switchView_| because we're the one who put it there. There
    535   // may not be any children in the case of a tab that's been closed, in
    536   // which case there's no swapping going on.
    537   NSArray* subviews = [switchView_ subviews];
    538   if ([subviews count]) {
    539     NSView* oldView = [subviews objectAtIndex:0];
    540     // Set newView frame to the oldVew frame to prevent NSSplitView hosting
    541     // sidebar and tab content from resizing sidebar's content view.
    542     // ensureContentsVisible (see below) sets content size and autoresizing
    543     // properties.
    544     [newView setFrame:[oldView frame]];
    545     [switchView_ replaceSubview:oldView with:newView];
    546   } else {
    547     [newView setFrame:[switchView_ bounds]];
    548     [switchView_ addSubview:newView];
    549   }
    550 
    551   // New content is in place, delegate should adjust itself accordingly.
    552   [delegate_ onSelectTabWithContents:[controller tabContents]];
    553 
    554   // It also restores content autoresizing properties.
    555   [controller ensureContentsVisible];
    556 
    557   // Tell per-tab sheet manager about currently selected tab.
    558   if (sheetController_.get()) {
    559     [sheetController_ setActiveView:newView];
    560   }
    561 
    562   // Make sure the new tabs's sheets are visible (necessary when a background
    563   // tab opened a sheet while it was in the background and now becomes active).
    564   TabContentsWrapper* newTab = tabStripModel_->GetTabContentsAt(modelIndex);
    565   DCHECK(newTab);
    566   if (newTab) {
    567     TabContents::ConstrainedWindowList::iterator it, end;
    568     end = newTab->tab_contents()->constrained_window_end();
    569     NSWindowController* controller = [[newView window] windowController];
    570     DCHECK([controller isKindOfClass:[BrowserWindowController class]]);
    571 
    572     for (it = newTab->tab_contents()->constrained_window_begin();
    573          it != end;
    574          ++it) {
    575       ConstrainedWindow* constrainedWindow = *it;
    576       static_cast<ConstrainedWindowMac*>(constrainedWindow)->Realize(
    577           static_cast<BrowserWindowController*>(controller));
    578     }
    579   }
    580 }
    581 
    582 // Create a new tab view and set its cell correctly so it draws the way we want
    583 // it to. It will be sized and positioned by |-layoutTabs| so there's no need to
    584 // set the frame here. This also creates the view as hidden, it will be
    585 // shown during layout.
    586 - (TabController*)newTab {
    587   TabController* controller = [[[TabController alloc] init] autorelease];
    588   [controller setTarget:self];
    589   [controller setAction:@selector(selectTab:)];
    590   [[controller view] setHidden:YES];
    591 
    592   return controller;
    593 }
    594 
    595 // (Private) Returns the number of open tabs in the tab strip. This is the
    596 // number of TabControllers we know about (as there's a 1-to-1 mapping from
    597 // these controllers to a tab) less the number of closing tabs.
    598 - (NSInteger)numberOfOpenTabs {
    599   return static_cast<NSInteger>(tabStripModel_->count());
    600 }
    601 
    602 // (Private) Returns the number of open, mini-tabs.
    603 - (NSInteger)numberOfOpenMiniTabs {
    604   // Ask the model for the number of mini tabs. Note that tabs which are in
    605   // the process of closing (i.e., whose controllers are in
    606   // |closingControllers_|) have already been removed from the model.
    607   return tabStripModel_->IndexOfFirstNonMiniTab();
    608 }
    609 
    610 // (Private) Returns the number of open, non-mini tabs.
    611 - (NSInteger)numberOfOpenNonMiniTabs {
    612   NSInteger number = [self numberOfOpenTabs] - [self numberOfOpenMiniTabs];
    613   DCHECK_GE(number, 0);
    614   return number;
    615 }
    616 
    617 // Given an index into the tab model, returns the index into the tab controller
    618 // or tab contents controller array accounting for tabs that are currently
    619 // closing. For example, if there are two tabs in the process of closing before
    620 // |index|, this returns |index| + 2. If there are no closing tabs, this will
    621 // return |index|.
    622 - (NSInteger)indexFromModelIndex:(NSInteger)index {
    623   DCHECK(index >= 0);
    624   if (index < 0)
    625     return index;
    626 
    627   NSInteger i = 0;
    628   for (TabController* controller in tabArray_.get()) {
    629     if ([closingControllers_ containsObject:controller]) {
    630       DCHECK([(TabView*)[controller view] isClosing]);
    631       ++index;
    632     }
    633     if (i == index)  // No need to check anything after, it has no effect.
    634       break;
    635     ++i;
    636   }
    637   return index;
    638 }
    639 
    640 
    641 // Returns the index of the subview |view|. Returns -1 if not present. Takes
    642 // closing tabs into account such that this index will correctly match the tab
    643 // model. If |view| is in the process of closing, returns -1, as closing tabs
    644 // are no longer in the model.
    645 - (NSInteger)modelIndexForTabView:(NSView*)view {
    646   NSInteger index = 0;
    647   for (TabController* current in tabArray_.get()) {
    648     // If |current| is closing, skip it.
    649     if ([closingControllers_ containsObject:current])
    650       continue;
    651     else if ([current view] == view)
    652       return index;
    653     ++index;
    654   }
    655   return -1;
    656 }
    657 
    658 // Returns the index of the contents subview |view|. Returns -1 if not present.
    659 // Takes closing tabs into account such that this index will correctly match the
    660 // tab model. If |view| is in the process of closing, returns -1, as closing
    661 // tabs are no longer in the model.
    662 - (NSInteger)modelIndexForContentsView:(NSView*)view {
    663   NSInteger index = 0;
    664   NSInteger i = 0;
    665   for (TabContentsController* current in tabContentsArray_.get()) {
    666     // If the TabController corresponding to |current| is closing, skip it.
    667     TabController* controller = [tabArray_ objectAtIndex:i];
    668     if ([closingControllers_ containsObject:controller]) {
    669       ++i;
    670       continue;
    671     } else if ([current view] == view) {
    672       return index;
    673     }
    674     ++index;
    675     ++i;
    676   }
    677   return -1;
    678 }
    679 
    680 
    681 // Returns the view at the given index, using the array of TabControllers to
    682 // get the associated view. Returns nil if out of range.
    683 - (NSView*)viewAtIndex:(NSUInteger)index {
    684   if (index >= [tabArray_ count])
    685     return NULL;
    686   return [[tabArray_ objectAtIndex:index] view];
    687 }
    688 
    689 - (NSUInteger)viewsCount {
    690   return [tabArray_ count];
    691 }
    692 
    693 // Called when the user clicks a tab. Tell the model the selection has changed,
    694 // which feeds back into us via a notification.
    695 - (void)selectTab:(id)sender {
    696   DCHECK([sender isKindOfClass:[NSView class]]);
    697   int index = [self modelIndexForTabView:sender];
    698   if (tabStripModel_->ContainsIndex(index))
    699     tabStripModel_->ActivateTabAt(index, true);
    700 }
    701 
    702 // Called when the user closes a tab. Asks the model to close the tab. |sender|
    703 // is the TabView that is potentially going away.
    704 - (void)closeTab:(id)sender {
    705   DCHECK([sender isKindOfClass:[TabView class]]);
    706   if ([hoveredTab_ isEqual:sender]) {
    707     hoveredTab_ = nil;
    708   }
    709 
    710   NSInteger index = [self modelIndexForTabView:sender];
    711   if (!tabStripModel_->ContainsIndex(index))
    712     return;
    713 
    714   TabContentsWrapper* contents = tabStripModel_->GetTabContentsAt(index);
    715   if (contents)
    716     UserMetrics::RecordAction(UserMetricsAction("CloseTab_Mouse"),
    717                               contents->tab_contents()->profile());
    718   const NSInteger numberOfOpenTabs = [self numberOfOpenTabs];
    719   if (numberOfOpenTabs > 1) {
    720     bool isClosingLastTab = index == numberOfOpenTabs - 1;
    721     if (!isClosingLastTab) {
    722       // Limit the width available for laying out tabs so that tabs are not
    723       // resized until a later time (when the mouse leaves the tab strip).
    724       // However, if the tab being closed is a pinned tab, break out of
    725       // rapid-closure mode since the mouse is almost guaranteed not to be over
    726       // the closebox of the adjacent tab (due to the difference in widths).
    727       // TODO(pinkerton): re-visit when handling tab overflow.
    728       // http://crbug.com/188
    729       if (tabStripModel_->IsTabPinned(index)) {
    730         availableResizeWidth_ = kUseFullAvailableWidth;
    731       } else {
    732         NSView* penultimateTab = [self viewAtIndex:numberOfOpenTabs - 2];
    733         availableResizeWidth_ = NSMaxX([penultimateTab frame]);
    734       }
    735     } else {
    736       // If the rightmost tab is closed, change the available width so that
    737       // another tab's close button lands below the cursor (assuming the tabs
    738       // are currently below their maximum width and can grow).
    739       NSView* lastTab = [self viewAtIndex:numberOfOpenTabs - 1];
    740       availableResizeWidth_ = NSMaxX([lastTab frame]);
    741     }
    742     tabStripModel_->CloseTabContentsAt(
    743         index,
    744         TabStripModel::CLOSE_USER_GESTURE |
    745         TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
    746   } else {
    747     // Use the standard window close if this is the last tab
    748     // this prevents the tab from being removed from the model until after
    749     // the window dissapears
    750     [[tabStripView_ window] performClose:nil];
    751   }
    752 }
    753 
    754 // Dispatch context menu commands for the given tab controller.
    755 - (void)commandDispatch:(TabStripModel::ContextMenuCommand)command
    756           forController:(TabController*)controller {
    757   int index = [self modelIndexForTabView:[controller view]];
    758   if (tabStripModel_->ContainsIndex(index))
    759     tabStripModel_->ExecuteContextMenuCommand(index, command);
    760 }
    761 
    762 // Returns YES if the specificed command should be enabled for the given
    763 // controller.
    764 - (BOOL)isCommandEnabled:(TabStripModel::ContextMenuCommand)command
    765            forController:(TabController*)controller {
    766   int index = [self modelIndexForTabView:[controller view]];
    767   if (!tabStripModel_->ContainsIndex(index))
    768     return NO;
    769   return tabStripModel_->IsContextMenuCommandEnabled(index, command) ? YES : NO;
    770 }
    771 
    772 - (void)insertPlaceholderForTab:(TabView*)tab
    773                           frame:(NSRect)frame
    774                   yStretchiness:(CGFloat)yStretchiness {
    775   placeholderTab_ = tab;
    776   placeholderFrame_ = frame;
    777   placeholderStretchiness_ = yStretchiness;
    778   [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:NO];
    779 }
    780 
    781 - (BOOL)isDragSessionActive {
    782   return placeholderTab_ != nil;
    783 }
    784 
    785 - (BOOL)isTabFullyVisible:(TabView*)tab {
    786   NSRect frame = [tab frame];
    787   return NSMinX(frame) >= [self indentForControls] &&
    788       NSMaxX(frame) <= NSMaxX([tabStripView_ frame]);
    789 }
    790 
    791 - (void)showNewTabButton:(BOOL)show {
    792   forceNewTabButtonHidden_ = show ? NO : YES;
    793   if (forceNewTabButtonHidden_)
    794     [newTabButton_ setHidden:YES];
    795 }
    796 
    797 // Lay out all tabs in the order of their TabContentsControllers, which matches
    798 // the ordering in the TabStripModel. This call isn't that expensive, though
    799 // it is O(n) in the number of tabs. Tabs will animate to their new position
    800 // if the window is visible and |animate| is YES.
    801 // TODO(pinkerton): Note this doesn't do too well when the number of min-sized
    802 // tabs would cause an overflow. http://crbug.com/188
    803 - (void)layoutTabsWithAnimation:(BOOL)animate
    804              regenerateSubviews:(BOOL)doUpdate {
    805   DCHECK([NSThread isMainThread]);
    806   if (![tabArray_ count])
    807     return;
    808 
    809   const CGFloat kMaxTabWidth = [TabController maxTabWidth];
    810   const CGFloat kMinTabWidth = [TabController minTabWidth];
    811   const CGFloat kMinSelectedTabWidth = [TabController minSelectedTabWidth];
    812   const CGFloat kMiniTabWidth = [TabController miniTabWidth];
    813   const CGFloat kAppTabWidth = [TabController appTabWidth];
    814 
    815   NSRect enclosingRect = NSZeroRect;
    816   ScopedNSAnimationContextGroup mainAnimationGroup(animate);
    817   mainAnimationGroup.SetCurrentContextDuration(kAnimationDuration);
    818 
    819   // Update the current subviews and their z-order if requested.
    820   if (doUpdate)
    821     [self regenerateSubviewList];
    822 
    823   // Compute the base width of tabs given how much room we're allowed. Note that
    824   // mini-tabs have a fixed width. We may not be able to use the entire width
    825   // if the user is quickly closing tabs. This may be negative, but that's okay
    826   // (taken care of by |MAX()| when calculating tab sizes).
    827   CGFloat availableSpace = 0;
    828   if (verticalLayout_) {
    829     availableSpace = NSHeight([tabStripView_ bounds]);
    830   } else {
    831     if ([self inRapidClosureMode]) {
    832       availableSpace = availableResizeWidth_;
    833     } else {
    834       availableSpace = NSWidth([tabStripView_ frame]);
    835 
    836       // Account for the widths of the new tab button, the incognito badge, and
    837       // the fullscreen button if any/all are present.
    838       availableSpace -= NSWidth([newTabButton_ frame]) + kNewTabButtonOffset;
    839       if (browser_->profile()->IsOffTheRecord())
    840         availableSpace -= kIncognitoBadgeTabStripShrink;
    841       if ([[tabStripView_ window]
    842           respondsToSelector:@selector(toggleFullScreen:)]) {
    843         NSButton* fullscreenButton = [[tabStripView_ window]
    844             standardWindowButton:NSWindowFullScreenButton];
    845         if (fullscreenButton)
    846           availableSpace -= [fullscreenButton frame].size.width;
    847       }
    848     }
    849     availableSpace -= [self indentForControls];
    850   }
    851 
    852   // This may be negative, but that's okay (taken care of by |MAX()| when
    853   // calculating tab sizes). "mini" tabs in horizontal mode just get a special
    854   // section, they don't change size.
    855   CGFloat availableSpaceForNonMini = availableSpace;
    856   if (!verticalLayout_) {
    857       availableSpaceForNonMini -=
    858           [self numberOfOpenMiniTabs] * (kMiniTabWidth - kTabOverlap);
    859   }
    860 
    861   // Initialize |nonMiniTabWidth| in case there aren't any non-mini-tabs; this
    862   // value shouldn't actually be used.
    863   CGFloat nonMiniTabWidth = kMaxTabWidth;
    864   const NSInteger numberOfOpenNonMiniTabs = [self numberOfOpenNonMiniTabs];
    865   if (!verticalLayout_ && numberOfOpenNonMiniTabs) {
    866     // Find the width of a non-mini-tab. This only applies to horizontal
    867     // mode. Add in the amount we "get back" from the tabs overlapping.
    868     availableSpaceForNonMini += (numberOfOpenNonMiniTabs - 1) * kTabOverlap;
    869 
    870     // Divide up the space between the non-mini-tabs.
    871     nonMiniTabWidth = availableSpaceForNonMini / numberOfOpenNonMiniTabs;
    872 
    873     // Clamp the width between the max and min.
    874     nonMiniTabWidth = MAX(MIN(nonMiniTabWidth, kMaxTabWidth), kMinTabWidth);
    875   }
    876 
    877   BOOL visible = [[tabStripView_ window] isVisible];
    878 
    879   CGFloat offset = [self indentForControls];
    880   bool hasPlaceholderGap = false;
    881   for (TabController* tab in tabArray_.get()) {
    882     // Ignore a tab that is going through a close animation.
    883     if ([closingControllers_ containsObject:tab])
    884       continue;
    885 
    886     BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_];
    887     NSRect tabFrame = [[tab view] frame];
    888     tabFrame.size.height = [[self class] defaultTabHeight] + 1;
    889     if (verticalLayout_) {
    890       tabFrame.origin.y = availableSpace - tabFrame.size.height - offset;
    891       tabFrame.origin.x = 0;
    892     } else {
    893       tabFrame.origin.y = 0;
    894       tabFrame.origin.x = offset;
    895     }
    896     // If the tab is hidden, we consider it a new tab. We make it visible
    897     // and animate it in.
    898     BOOL newTab = [[tab view] isHidden];
    899     if (newTab)
    900       [[tab view] setHidden:NO];
    901 
    902     if (isPlaceholder) {
    903       // Move the current tab to the correct location instantly.
    904       // We need a duration or else it doesn't cancel an inflight animation.
    905       ScopedNSAnimationContextGroup localAnimationGroup(animate);
    906       localAnimationGroup.SetCurrentContextShortestDuration();
    907       if (verticalLayout_)
    908         tabFrame.origin.y = availableSpace - tabFrame.size.height - offset;
    909       else
    910         tabFrame.origin.x = placeholderFrame_.origin.x;
    911       // TODO(alcor): reenable this
    912       //tabFrame.size.height += 10.0 * placeholderStretchiness_;
    913       id target = animate ? [[tab view] animator] : [tab view];
    914       [target setFrame:tabFrame];
    915 
    916       // Store the frame by identifier to aviod redundant calls to animator.
    917       NSValue* identifier = [NSValue valueWithPointer:[tab view]];
    918       [targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
    919                         forKey:identifier];
    920       continue;
    921     }
    922 
    923     if (placeholderTab_ && !hasPlaceholderGap) {
    924       const CGFloat placeholderMin =
    925           verticalLayout_ ? NSMinY(placeholderFrame_) :
    926                             NSMinX(placeholderFrame_);
    927       if (verticalLayout_) {
    928         if (NSMidY(tabFrame) > placeholderMin) {
    929           hasPlaceholderGap = true;
    930           offset += NSHeight(placeholderFrame_);
    931           tabFrame.origin.y = availableSpace - tabFrame.size.height - offset;
    932         }
    933       } else {
    934         // If the left edge is to the left of the placeholder's left, but the
    935         // mid is to the right of it slide over to make space for it.
    936         if (NSMidX(tabFrame) > placeholderMin) {
    937           hasPlaceholderGap = true;
    938           offset += NSWidth(placeholderFrame_);
    939           offset -= kTabOverlap;
    940           tabFrame.origin.x = offset;
    941         }
    942       }
    943     }
    944 
    945     // Set the width. Selected tabs are slightly wider when things get really
    946     // small and thus we enforce a different minimum width.
    947     tabFrame.size.width = [tab mini] ?
    948         ([tab app] ? kAppTabWidth : kMiniTabWidth) : nonMiniTabWidth;
    949     if ([tab selected])
    950       tabFrame.size.width = MAX(tabFrame.size.width, kMinSelectedTabWidth);
    951 
    952     // Animate a new tab in by putting it below the horizon unless told to put
    953     // it in a specific location (i.e., from a drop).
    954     // TODO(pinkerton): figure out vertical tab animations.
    955     if (newTab && visible && animate) {
    956       if (NSEqualRects(droppedTabFrame_, NSZeroRect)) {
    957         [[tab view] setFrame:NSOffsetRect(tabFrame, 0, -NSHeight(tabFrame))];
    958       } else {
    959         [[tab view] setFrame:droppedTabFrame_];
    960         droppedTabFrame_ = NSZeroRect;
    961       }
    962     }
    963 
    964     // Check the frame by identifier to avoid redundant calls to animator.
    965     id frameTarget = visible && animate ? [[tab view] animator] : [tab view];
    966     NSValue* identifier = [NSValue valueWithPointer:[tab view]];
    967     NSValue* oldTargetValue = [targetFrames_ objectForKey:identifier];
    968     if (!oldTargetValue ||
    969         !NSEqualRects([oldTargetValue rectValue], tabFrame)) {
    970       [frameTarget setFrame:tabFrame];
    971       [targetFrames_ setObject:[NSValue valueWithRect:tabFrame]
    972                         forKey:identifier];
    973     }
    974 
    975     enclosingRect = NSUnionRect(tabFrame, enclosingRect);
    976 
    977     if (verticalLayout_) {
    978       offset += NSHeight(tabFrame);
    979     } else {
    980       offset += NSWidth(tabFrame);
    981       offset -= kTabOverlap;
    982     }
    983   }
    984 
    985   // Hide the new tab button if we're explicitly told to. It may already
    986   // be hidden, doing it again doesn't hurt. Otherwise position it
    987   // appropriately, showing it if necessary.
    988   if (forceNewTabButtonHidden_) {
    989     [newTabButton_ setHidden:YES];
    990   } else {
    991     NSRect newTabNewFrame = [newTabButton_ frame];
    992     // We've already ensured there's enough space for the new tab button
    993     // so we don't have to check it against the available space. We do need
    994     // to make sure we put it after any placeholder.
    995     CGFloat maxTabX = MAX(offset, NSMaxX(placeholderFrame_) - kTabOverlap);
    996     newTabNewFrame.origin = NSMakePoint(maxTabX + kNewTabButtonOffset, 0);
    997     if ([tabContentsArray_ count])
    998       [newTabButton_ setHidden:NO];
    999 
   1000     if (!NSEqualRects(newTabTargetFrame_, newTabNewFrame)) {
   1001       // Set the new tab button image correctly based on where the cursor is.
   1002       NSWindow* window = [tabStripView_ window];
   1003       NSPoint currentMouse = [window mouseLocationOutsideOfEventStream];
   1004       currentMouse = [tabStripView_ convertPoint:currentMouse fromView:nil];
   1005 
   1006       BOOL shouldShowHover = [newTabButton_ pointIsOverButton:currentMouse];
   1007       [self setNewTabButtonHoverState:shouldShowHover];
   1008 
   1009       // Move the new tab button into place. We want to animate the new tab
   1010       // button if it's moving to the left (closing a tab), but not when it's
   1011       // moving to the right (inserting a new tab). If moving right, we need
   1012       // to use a very small duration to make sure we cancel any in-flight
   1013       // animation to the left.
   1014       if (visible && animate) {
   1015         ScopedNSAnimationContextGroup localAnimationGroup(true);
   1016         BOOL movingLeft = NSMinX(newTabNewFrame) < NSMinX(newTabTargetFrame_);
   1017         if (!movingLeft) {
   1018           localAnimationGroup.SetCurrentContextShortestDuration();
   1019         }
   1020         [[newTabButton_ animator] setFrame:newTabNewFrame];
   1021         newTabTargetFrame_ = newTabNewFrame;
   1022       } else {
   1023         [newTabButton_ setFrame:newTabNewFrame];
   1024         newTabTargetFrame_ = newTabNewFrame;
   1025       }
   1026     }
   1027   }
   1028 
   1029   if (profileMenuButton_ && ![profileMenuButton_ isHidden]) {
   1030     CGFloat maxX;
   1031     if ([newTabButton_ isHidden]) {
   1032       maxX = std::max(offset, NSMaxX(placeholderFrame_) - kTabOverlap);
   1033     } else {
   1034       maxX = NSMaxX(newTabTargetFrame_);
   1035     }
   1036     NSRect profileMenuButtonFrame = [profileMenuButton_ frame];
   1037     NSSize minSize = [profileMenuButton_ minControlSize];
   1038 
   1039     // Make room for the full screen button if necessary.
   1040     if (!hasUpdatedProfileMenuButtonXOffset_) {
   1041       hasUpdatedProfileMenuButtonXOffset_ = YES;
   1042       if ([[profileMenuButton_ window]
   1043           respondsToSelector:@selector(toggleFullScreen:)]) {
   1044         NSButton* fullscreenButton = [[profileMenuButton_ window]
   1045             standardWindowButton:NSWindowFullScreenButton];
   1046         if (fullscreenButton) {
   1047           profileMenuButtonFrame.origin.x = NSMinX([fullscreenButton frame]) -
   1048               NSWidth(profileMenuButtonFrame) - kProfileMenuButtonOffset;
   1049         }
   1050       }
   1051     }
   1052 
   1053     // TODO(sail): Animate this.
   1054     CGFloat availableWidth = NSMaxX(profileMenuButtonFrame) - maxX -
   1055                              kProfileMenuButtonOffset;
   1056     if (availableWidth > minSize.width) {
   1057       [profileMenuButton_ setShouldShowProfileDisplayName:YES];
   1058     } else {
   1059       [profileMenuButton_ setShouldShowProfileDisplayName:NO];
   1060     }
   1061 
   1062     NSSize desiredSize = [profileMenuButton_ desiredControlSize];
   1063     NSRect rect;
   1064     rect.size.width = std::min(desiredSize.width,
   1065                                std::max(availableWidth, minSize.width));
   1066     rect.size.height = desiredSize.height;
   1067     rect.origin.y = NSMaxY(profileMenuButtonFrame) - rect.size.height;
   1068     rect.origin.x = NSMaxX(profileMenuButtonFrame) - rect.size.width;
   1069     [profileMenuButton_ setFrame:rect];
   1070   }
   1071 
   1072   [dragBlockingView_ setFrame:enclosingRect];
   1073 
   1074   // Mark that we've successfully completed layout of at least one tab.
   1075   initialLayoutComplete_ = YES;
   1076 }
   1077 
   1078 // When we're told to layout from the public API we usually want to animate,
   1079 // except when it's the first time.
   1080 - (void)layoutTabs {
   1081   [self layoutTabsWithAnimation:initialLayoutComplete_ regenerateSubviews:YES];
   1082 }
   1083 
   1084 // Handles setting the title of the tab based on the given |contents|. Uses
   1085 // a canned string if |contents| is NULL.
   1086 - (void)setTabTitle:(NSViewController*)tab withContents:(TabContents*)contents {
   1087   NSString* titleString = nil;
   1088   if (contents)
   1089     titleString = base::SysUTF16ToNSString(contents->GetTitle());
   1090   if (![titleString length]) {
   1091     titleString = l10n_util::GetNSString(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED);
   1092   }
   1093   [tab setTitle:titleString];
   1094 }
   1095 
   1096 // Called when a notification is received from the model to insert a new tab
   1097 // at |modelIndex|.
   1098 - (void)insertTabWithContents:(TabContentsWrapper*)contents
   1099                       atIndex:(NSInteger)modelIndex
   1100                  inForeground:(bool)inForeground {
   1101   DCHECK(contents);
   1102   DCHECK(modelIndex == TabStripModel::kNoTab ||
   1103          tabStripModel_->ContainsIndex(modelIndex));
   1104 
   1105   // Take closing tabs into account.
   1106   NSInteger index = [self indexFromModelIndex:modelIndex];
   1107 
   1108   // Make a new tab. Load the contents of this tab from the nib and associate
   1109   // the new controller with |contents| so it can be looked up later.
   1110   scoped_nsobject<TabContentsController> contentsController(
   1111       [[TabContentsController alloc] initWithContents:contents->tab_contents()
   1112                                              delegate:self]);
   1113   [tabContentsArray_ insertObject:contentsController atIndex:index];
   1114 
   1115   // Make a new tab and add it to the strip. Keep track of its controller.
   1116   TabController* newController = [self newTab];
   1117   [newController setMini:tabStripModel_->IsMiniTab(modelIndex)];
   1118   [newController setPinned:tabStripModel_->IsTabPinned(modelIndex)];
   1119   [newController setApp:tabStripModel_->IsAppTab(modelIndex)];
   1120   [newController setUrl:contents->tab_contents()->GetURL()];
   1121   [tabArray_ insertObject:newController atIndex:index];
   1122   NSView* newView = [newController view];
   1123 
   1124   // Set the originating frame to just below the strip so that it animates
   1125   // upwards as it's being initially layed out. Oddly, this works while doing
   1126   // something similar in |-layoutTabs| confuses the window server.
   1127   [newView setFrame:NSOffsetRect([newView frame],
   1128                                  0, -[[self class] defaultTabHeight])];
   1129 
   1130   [self setTabTitle:newController withContents:contents->tab_contents()];
   1131 
   1132   // If a tab is being inserted, we can again use the entire tab strip width
   1133   // for layout.
   1134   availableResizeWidth_ = kUseFullAvailableWidth;
   1135 
   1136   // We don't need to call |-layoutTabs| if the tab will be in the foreground
   1137   // because it will get called when the new tab is selected by the tab model.
   1138   // Whenever |-layoutTabs| is called, it'll also add the new subview.
   1139   if (!inForeground) {
   1140     [self layoutTabs];
   1141   }
   1142 
   1143   // During normal loading, we won't yet have a favicon and we'll get
   1144   // subsequent state change notifications to show the throbber, but when we're
   1145   // dragging a tab out into a new window, we have to put the tab's favicon
   1146   // into the right state up front as we won't be told to do it from anywhere
   1147   // else.
   1148   [self updateFaviconForContents:contents->tab_contents() atIndex:modelIndex];
   1149 
   1150   // Send a broadcast that the number of tabs have changed.
   1151   [[NSNotificationCenter defaultCenter]
   1152       postNotificationName:kTabStripNumberOfTabsChanged
   1153                     object:self];
   1154 }
   1155 
   1156 // Called when a notification is received from the model to select a particular
   1157 // tab. Swaps in the toolbar and content area associated with |newContents|.
   1158 - (void)selectTabWithContents:(TabContentsWrapper*)newContents
   1159              previousContents:(TabContentsWrapper*)oldContents
   1160                       atIndex:(NSInteger)modelIndex
   1161                   userGesture:(bool)wasUserGesture {
   1162   // Take closing tabs into account.
   1163   NSInteger index = [self indexFromModelIndex:modelIndex];
   1164 
   1165   if (oldContents && oldContents != newContents) {
   1166     int oldModelIndex =
   1167         browser_->GetIndexOfController(&(oldContents->controller()));
   1168     if (oldModelIndex != -1) {  // When closing a tab, the old tab may be gone.
   1169       NSInteger oldIndex = [self indexFromModelIndex:oldModelIndex];
   1170       TabContentsController* oldController =
   1171           [tabContentsArray_ objectAtIndex:oldIndex];
   1172       [oldController willBecomeUnselectedTab];
   1173       oldContents->view()->StoreFocus();
   1174       oldContents->tab_contents()->WasHidden();
   1175     }
   1176   }
   1177 
   1178   // De-select all other tabs and select the new tab.
   1179   int i = 0;
   1180   for (TabController* current in tabArray_.get()) {
   1181     [current setSelected:(i == index) ? YES : NO];
   1182     ++i;
   1183   }
   1184 
   1185   // Tell the new tab contents it is about to become the selected tab. Here it
   1186   // can do things like make sure the toolbar is up to date.
   1187   TabContentsController* newController =
   1188       [tabContentsArray_ objectAtIndex:index];
   1189   [newController willBecomeSelectedTab];
   1190 
   1191   // Relayout for new tabs and to let the selected tab grow to be larger in
   1192   // size than surrounding tabs if the user has many. This also raises the
   1193   // selected tab to the top.
   1194   [self layoutTabs];
   1195 
   1196   // Swap in the contents for the new tab.
   1197   [self swapInTabAtIndex:modelIndex];
   1198 
   1199   if (newContents) {
   1200     newContents->tab_contents()->DidBecomeSelected();
   1201     newContents->view()->RestoreFocus();
   1202 
   1203     if (newContents->find_tab_helper()->find_ui_active())
   1204       browser_->GetFindBarController()->find_bar()->SetFocusAndSelection();
   1205   }
   1206 }
   1207 
   1208 - (void)tabReplacedWithContents:(TabContentsWrapper*)newContents
   1209                previousContents:(TabContentsWrapper*)oldContents
   1210                         atIndex:(NSInteger)modelIndex {
   1211   NSInteger index = [self indexFromModelIndex:modelIndex];
   1212   TabContentsController* oldController =
   1213       [tabContentsArray_ objectAtIndex:index];
   1214   DCHECK_EQ(oldContents->tab_contents(), [oldController tabContents]);
   1215 
   1216   // Simply create a new TabContentsController for |newContents| and place it
   1217   // into the array, replacing |oldContents|.  A TabSelectedAt notification will
   1218   // follow, at which point we will install the new view.
   1219   scoped_nsobject<TabContentsController> newController(
   1220       [[TabContentsController alloc]
   1221           initWithContents:newContents->tab_contents()
   1222                   delegate:self]);
   1223 
   1224   // Bye bye, |oldController|.
   1225   [tabContentsArray_ replaceObjectAtIndex:index withObject:newController];
   1226 
   1227   [delegate_ onReplaceTabWithContents:newContents->tab_contents()];
   1228 
   1229   // Fake a tab changed notification to force tab titles and favicons to update.
   1230   [self tabChangedWithContents:newContents
   1231                        atIndex:modelIndex
   1232                     changeType:TabStripModelObserver::ALL];
   1233 }
   1234 
   1235 // Remove all knowledge about this tab and its associated controller, and remove
   1236 // the view from the strip.
   1237 - (void)removeTab:(TabController*)controller {
   1238   NSUInteger index = [tabArray_ indexOfObject:controller];
   1239 
   1240   // Release the tab contents controller so those views get destroyed. This
   1241   // will remove all the tab content Cocoa views from the hierarchy. A
   1242   // subsequent "select tab" notification will follow from the model. To
   1243   // tell us what to swap in in its absence.
   1244   [tabContentsArray_ removeObjectAtIndex:index];
   1245 
   1246   // Remove the view from the tab strip.
   1247   NSView* tab = [controller view];
   1248   [tab removeFromSuperview];
   1249 
   1250   // Remove ourself as an observer.
   1251   [[NSNotificationCenter defaultCenter]
   1252       removeObserver:self
   1253                 name:NSViewDidUpdateTrackingAreasNotification
   1254               object:tab];
   1255 
   1256   // Clear the tab controller's target.
   1257   // TODO(viettrungluu): [crbug.com/23829] Find a better way to handle the tab
   1258   // controller's target.
   1259   [controller setTarget:nil];
   1260 
   1261   if ([hoveredTab_ isEqual:tab])
   1262     hoveredTab_ = nil;
   1263 
   1264   NSValue* identifier = [NSValue valueWithPointer:tab];
   1265   [targetFrames_ removeObjectForKey:identifier];
   1266 
   1267   // Once we're totally done with the tab, delete its controller
   1268   [tabArray_ removeObjectAtIndex:index];
   1269 }
   1270 
   1271 // Called by the CAAnimation delegate when the tab completes the closing
   1272 // animation.
   1273 - (void)animationDidStopForController:(TabController*)controller
   1274                              finished:(BOOL)finished {
   1275   [closingControllers_ removeObject:controller];
   1276   [self removeTab:controller];
   1277 }
   1278 
   1279 // Save off which TabController is closing and tell its view's animator
   1280 // where to move the tab to. Registers a delegate to call back when the
   1281 // animation is complete in order to remove the tab from the model.
   1282 - (void)startClosingTabWithAnimation:(TabController*)closingTab {
   1283   DCHECK([NSThread isMainThread]);
   1284   // Save off the controller into the set of animating tabs. This alerts
   1285   // the layout method to not do anything with it and allows us to correctly
   1286   // calculate offsets when working with indices into the model.
   1287   [closingControllers_ addObject:closingTab];
   1288 
   1289   // Mark the tab as closing. This prevents it from generating any drags or
   1290   // selections while it's animating closed.
   1291   [(TabView*)[closingTab view] setClosing:YES];
   1292 
   1293   // Register delegate (owned by the animation system).
   1294   NSView* tabView = [closingTab view];
   1295   CAAnimation* animation = [[tabView animationForKey:@"frameOrigin"] copy];
   1296   [animation autorelease];
   1297   scoped_nsobject<TabCloseAnimationDelegate> delegate(
   1298     [[TabCloseAnimationDelegate alloc] initWithTabStrip:self
   1299                                           tabController:closingTab]);
   1300   [animation setDelegate:delegate.get()];  // Retains delegate.
   1301   NSMutableDictionary* animationDictionary =
   1302       [NSMutableDictionary dictionaryWithDictionary:[tabView animations]];
   1303   [animationDictionary setObject:animation forKey:@"frameOrigin"];
   1304   [tabView setAnimations:animationDictionary];
   1305 
   1306   // Periscope down! Animate the tab.
   1307   NSRect newFrame = [tabView frame];
   1308   newFrame = NSOffsetRect(newFrame, 0, -newFrame.size.height);
   1309   ScopedNSAnimationContextGroup animationGroup(true);
   1310   animationGroup.SetCurrentContextDuration(kAnimationDuration);
   1311   [[tabView animator] setFrame:newFrame];
   1312 }
   1313 
   1314 // Called when a notification is received from the model that the given tab
   1315 // has gone away. Start an animation then force a layout to put everything
   1316 // in motion.
   1317 - (void)tabDetachedWithContents:(TabContentsWrapper*)contents
   1318                         atIndex:(NSInteger)modelIndex {
   1319   // Take closing tabs into account.
   1320   NSInteger index = [self indexFromModelIndex:modelIndex];
   1321 
   1322   TabController* tab = [tabArray_ objectAtIndex:index];
   1323   if (tabStripModel_->count() > 0) {
   1324     [self startClosingTabWithAnimation:tab];
   1325     [self layoutTabs];
   1326   } else {
   1327     [self removeTab:tab];
   1328   }
   1329 
   1330   // Send a broadcast that the number of tabs have changed.
   1331   [[NSNotificationCenter defaultCenter]
   1332       postNotificationName:kTabStripNumberOfTabsChanged
   1333                     object:self];
   1334 
   1335   [delegate_ onTabDetachedWithContents:contents->tab_contents()];
   1336 }
   1337 
   1338 // A helper routine for creating an NSImageView to hold the favicon or app icon
   1339 // for |contents|.
   1340 - (NSImageView*)iconImageViewForContents:(TabContents*)contents {
   1341   TabContentsWrapper* wrapper =
   1342       TabContentsWrapper::GetCurrentWrapperForContents(contents);
   1343   BOOL isApp = wrapper->extension_tab_helper()->is_app();
   1344   NSImage* image = nil;
   1345   // Favicons come from the renderer, and the renderer draws everything in the
   1346   // system color space.
   1347   CGColorSpaceRef colorSpace = base::mac::GetSystemColorSpace();
   1348   if (isApp) {
   1349     SkBitmap* icon = wrapper->extension_tab_helper()->GetExtensionAppIcon();
   1350     if (icon)
   1351       image = gfx::SkBitmapToNSImageWithColorSpace(*icon, colorSpace);
   1352   } else {
   1353     image = mac::FaviconForTabContents(contents);
   1354   }
   1355 
   1356   // Either we don't have a valid favicon or there was some issue converting it
   1357   // from an SkBitmap. Either way, just show the default.
   1358   if (!image)
   1359     image = defaultFavicon_.get();
   1360   NSRect frame = NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
   1361   NSImageView* view = [[[NSImageView alloc] initWithFrame:frame] autorelease];
   1362   [view setImage:image];
   1363   return view;
   1364 }
   1365 
   1366 // Updates the current loading state, replacing the icon view with a favicon,
   1367 // a throbber, the default icon, or nothing at all.
   1368 - (void)updateFaviconForContents:(TabContents*)contents
   1369                          atIndex:(NSInteger)modelIndex {
   1370   if (!contents)
   1371     return;
   1372 
   1373   static NSImage* throbberWaitingImage =
   1374       [ResourceBundle::GetSharedInstance().GetNativeImageNamed(
   1375           IDR_THROBBER_WAITING) retain];
   1376   static NSImage* throbberLoadingImage =
   1377       [ResourceBundle::GetSharedInstance().GetNativeImageNamed(IDR_THROBBER)
   1378         retain];
   1379   static NSImage* sadFaviconImage =
   1380       [ResourceBundle::GetSharedInstance().GetNativeImageNamed(IDR_SAD_FAVICON)
   1381         retain];
   1382 
   1383   // Take closing tabs into account.
   1384   NSInteger index = [self indexFromModelIndex:modelIndex];
   1385   TabController* tabController = [tabArray_ objectAtIndex:index];
   1386 
   1387   bool oldHasIcon = [tabController iconView] != nil;
   1388   bool newHasIcon = contents->ShouldDisplayFavicon() ||
   1389       tabStripModel_->IsMiniTab(modelIndex);  // Always show icon if mini.
   1390 
   1391   TabLoadingState oldState = [tabController loadingState];
   1392   TabLoadingState newState = kTabDone;
   1393   NSImage* throbberImage = nil;
   1394   if (contents->is_crashed()) {
   1395     newState = kTabCrashed;
   1396     newHasIcon = true;
   1397   } else if (contents->waiting_for_response()) {
   1398     newState = kTabWaiting;
   1399     throbberImage = throbberWaitingImage;
   1400   } else if (contents->is_loading()) {
   1401     newState = kTabLoading;
   1402     throbberImage = throbberLoadingImage;
   1403   }
   1404 
   1405   if (oldState != newState)
   1406     [tabController setLoadingState:newState];
   1407 
   1408   // While loading, this function is called repeatedly with the same state.
   1409   // To avoid expensive unnecessary view manipulation, only make changes when
   1410   // the state is actually changing.  When loading is complete (kTabDone),
   1411   // every call to this function is significant.
   1412   if (newState == kTabDone || oldState != newState ||
   1413       oldHasIcon != newHasIcon) {
   1414     NSView* iconView = nil;
   1415     if (newHasIcon) {
   1416       if (newState == kTabDone) {
   1417         iconView = [self iconImageViewForContents:contents];
   1418       } else if (newState == kTabCrashed) {
   1419         NSImage* oldImage = [[self iconImageViewForContents:contents] image];
   1420         NSRect frame =
   1421             NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
   1422         iconView = [ThrobberView toastThrobberViewWithFrame:frame
   1423                                                 beforeImage:oldImage
   1424                                                  afterImage:sadFaviconImage];
   1425       } else {
   1426         NSRect frame =
   1427             NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
   1428         iconView = [ThrobberView filmstripThrobberViewWithFrame:frame
   1429                                                           image:throbberImage];
   1430       }
   1431     }
   1432 
   1433     [tabController setIconView:iconView];
   1434   }
   1435 }
   1436 
   1437 // Called when a notification is received from the model that the given tab
   1438 // has been updated. |loading| will be YES when we only want to update the
   1439 // throbber state, not anything else about the (partially) loading tab.
   1440 - (void)tabChangedWithContents:(TabContentsWrapper*)contents
   1441                        atIndex:(NSInteger)modelIndex
   1442                     changeType:(TabStripModelObserver::TabChangeType)change {
   1443   // Take closing tabs into account.
   1444   NSInteger index = [self indexFromModelIndex:modelIndex];
   1445 
   1446   if (modelIndex == tabStripModel_->active_index())
   1447     [delegate_ onSelectedTabChange:change];
   1448 
   1449   if (change == TabStripModelObserver::TITLE_NOT_LOADING) {
   1450     // TODO(sky): make this work.
   1451     // We'll receive another notification of the change asynchronously.
   1452     return;
   1453   }
   1454 
   1455   TabController* tabController = [tabArray_ objectAtIndex:index];
   1456 
   1457   if (change != TabStripModelObserver::LOADING_ONLY)
   1458     [self setTabTitle:tabController withContents:contents->tab_contents()];
   1459 
   1460   [self updateFaviconForContents:contents->tab_contents() atIndex:modelIndex];
   1461 
   1462   TabContentsController* updatedController =
   1463       [tabContentsArray_ objectAtIndex:index];
   1464   [updatedController tabDidChange:contents->tab_contents()];
   1465 }
   1466 
   1467 // Called when a tab is moved (usually by drag&drop). Keep our parallel arrays
   1468 // in sync with the tab strip model. It can also be pinned/unpinned
   1469 // simultaneously, so we need to take care of that.
   1470 - (void)tabMovedWithContents:(TabContentsWrapper*)contents
   1471                    fromIndex:(NSInteger)modelFrom
   1472                      toIndex:(NSInteger)modelTo {
   1473   // Take closing tabs into account.
   1474   NSInteger from = [self indexFromModelIndex:modelFrom];
   1475   NSInteger to = [self indexFromModelIndex:modelTo];
   1476 
   1477   scoped_nsobject<TabContentsController> movedTabContentsController(
   1478       [[tabContentsArray_ objectAtIndex:from] retain]);
   1479   [tabContentsArray_ removeObjectAtIndex:from];
   1480   [tabContentsArray_ insertObject:movedTabContentsController.get()
   1481                           atIndex:to];
   1482   scoped_nsobject<TabController> movedTabController(
   1483       [[tabArray_ objectAtIndex:from] retain]);
   1484   DCHECK([movedTabController isKindOfClass:[TabController class]]);
   1485   [tabArray_ removeObjectAtIndex:from];
   1486   [tabArray_ insertObject:movedTabController.get() atIndex:to];
   1487 
   1488   // The tab moved, which means that the mini-tab state may have changed.
   1489   if (tabStripModel_->IsMiniTab(modelTo) != [movedTabController mini])
   1490     [self tabMiniStateChangedWithContents:contents atIndex:modelTo];
   1491 
   1492   [self layoutTabs];
   1493 }
   1494 
   1495 // Called when a tab is pinned or unpinned without moving.
   1496 - (void)tabMiniStateChangedWithContents:(TabContentsWrapper*)contents
   1497                                 atIndex:(NSInteger)modelIndex {
   1498   // Take closing tabs into account.
   1499   NSInteger index = [self indexFromModelIndex:modelIndex];
   1500 
   1501   TabController* tabController = [tabArray_ objectAtIndex:index];
   1502   DCHECK([tabController isKindOfClass:[TabController class]]);
   1503 
   1504   // Don't do anything if the change was already picked up by the move event.
   1505   if (tabStripModel_->IsMiniTab(modelIndex) == [tabController mini])
   1506     return;
   1507 
   1508   [tabController setMini:tabStripModel_->IsMiniTab(modelIndex)];
   1509   [tabController setPinned:tabStripModel_->IsTabPinned(modelIndex)];
   1510   [tabController setApp:tabStripModel_->IsAppTab(modelIndex)];
   1511   [tabController setUrl:contents->tab_contents()->GetURL()];
   1512   [self updateFaviconForContents:contents->tab_contents() atIndex:modelIndex];
   1513   // If the tab is being restored and it's pinned, the mini state is set after
   1514   // the tab has already been rendered, so re-layout the tabstrip. In all other
   1515   // cases, the state is set before the tab is rendered so this isn't needed.
   1516   [self layoutTabs];
   1517 }
   1518 
   1519 - (void)setFrameOfSelectedTab:(NSRect)frame {
   1520   NSView* view = [self selectedTabView];
   1521   NSValue* identifier = [NSValue valueWithPointer:view];
   1522   [targetFrames_ setObject:[NSValue valueWithRect:frame]
   1523                     forKey:identifier];
   1524   [view setFrame:frame];
   1525 }
   1526 
   1527 - (NSView*)selectedTabView {
   1528   int selectedIndex = tabStripModel_->active_index();
   1529   // Take closing tabs into account. They can't ever be selected.
   1530   selectedIndex = [self indexFromModelIndex:selectedIndex];
   1531   return [self viewAtIndex:selectedIndex];
   1532 }
   1533 
   1534 // Find the model index based on the x coordinate of the placeholder. If there
   1535 // is no placeholder, this returns the end of the tab strip. Closing tabs are
   1536 // not considered in computing the index.
   1537 - (int)indexOfPlaceholder {
   1538   double placeholderX = placeholderFrame_.origin.x;
   1539   int index = 0;
   1540   int location = 0;
   1541   // Use |tabArray_| here instead of the tab strip count in order to get the
   1542   // correct index when there are closing tabs to the left of the placeholder.
   1543   const int count = [tabArray_ count];
   1544   while (index < count) {
   1545     // Ignore closing tabs for simplicity. The only drawback of this is that
   1546     // if the placeholder is placed right before one or several contiguous
   1547     // currently closing tabs, the associated TabController will start at the
   1548     // end of the closing tabs.
   1549     if ([closingControllers_ containsObject:[tabArray_ objectAtIndex:index]]) {
   1550       index++;
   1551       continue;
   1552     }
   1553     NSView* curr = [self viewAtIndex:index];
   1554     // The placeholder tab works by changing the frame of the tab being dragged
   1555     // to be the bounds of the placeholder, so we need to skip it while we're
   1556     // iterating, otherwise we'll end up off by one.  Note This only effects
   1557     // dragging to the right, not to the left.
   1558     if (curr == placeholderTab_) {
   1559       index++;
   1560       continue;
   1561     }
   1562     if (placeholderX <= NSMinX([curr frame]))
   1563       break;
   1564     index++;
   1565     location++;
   1566   }
   1567   return location;
   1568 }
   1569 
   1570 // Move the given tab at index |from| in this window to the location of the
   1571 // current placeholder.
   1572 - (void)moveTabFromIndex:(NSInteger)from {
   1573   int toIndex = [self indexOfPlaceholder];
   1574   tabStripModel_->MoveTabContentsAt(from, toIndex, true);
   1575 }
   1576 
   1577 // Drop a given TabContents at the location of the current placeholder. If there
   1578 // is no placeholder, it will go at the end. Used when dragging from another
   1579 // window when we don't have access to the TabContents as part of our strip.
   1580 // |frame| is in the coordinate system of the tab strip view and represents
   1581 // where the user dropped the new tab so it can be animated into its correct
   1582 // location when the tab is added to the model. If the tab was pinned in its
   1583 // previous window, setting |pinned| to YES will propagate that state to the
   1584 // new window. Mini-tabs are either app or pinned tabs; the app state is stored
   1585 // by the |contents|, but the |pinned| state is the caller's responsibility.
   1586 - (void)dropTabContents:(TabContentsWrapper*)contents
   1587               withFrame:(NSRect)frame
   1588             asPinnedTab:(BOOL)pinned {
   1589   int modelIndex = [self indexOfPlaceholder];
   1590 
   1591   // Mark that the new tab being created should start at |frame|. It will be
   1592   // reset as soon as the tab has been positioned.
   1593   droppedTabFrame_ = frame;
   1594 
   1595   // Insert it into this tab strip. We want it in the foreground and to not
   1596   // inherit the current tab's group.
   1597   tabStripModel_->InsertTabContentsAt(
   1598       modelIndex, contents,
   1599       TabStripModel::ADD_ACTIVE | (pinned ? TabStripModel::ADD_PINNED : 0));
   1600 }
   1601 
   1602 // Called when the tab strip view changes size. As we only registered for
   1603 // changes on our view, we know it's only for our view. Layout w/out
   1604 // animations since they are blocked by the resize nested runloop. We need
   1605 // the views to adjust immediately. Neither the tabs nor their z-order are
   1606 // changed, so we don't need to update the subviews.
   1607 - (void)tabViewFrameChanged:(NSNotification*)info {
   1608   [self layoutTabsWithAnimation:NO regenerateSubviews:NO];
   1609 }
   1610 
   1611 // Called when the tracking areas for any given tab are updated. This allows
   1612 // the individual tabs to update their hover states correctly.
   1613 // Only generates the event if the cursor is in the tab strip.
   1614 - (void)tabUpdateTracking:(NSNotification*)notification {
   1615   DCHECK([[notification object] isKindOfClass:[TabView class]]);
   1616   DCHECK(mouseInside_);
   1617   NSWindow* window = [tabStripView_ window];
   1618   NSPoint location = [window mouseLocationOutsideOfEventStream];
   1619   if (NSPointInRect(location, [tabStripView_ frame])) {
   1620     NSEvent* mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved
   1621                                              location:location
   1622                                         modifierFlags:0
   1623                                             timestamp:0
   1624                                          windowNumber:[window windowNumber]
   1625                                               context:nil
   1626                                           eventNumber:0
   1627                                            clickCount:0
   1628                                              pressure:0];
   1629     [self mouseMoved:mouseEvent];
   1630   }
   1631 }
   1632 
   1633 - (BOOL)inRapidClosureMode {
   1634   return availableResizeWidth_ != kUseFullAvailableWidth;
   1635 }
   1636 
   1637 // Disable tab dragging when there are any pending animations.
   1638 - (BOOL)tabDraggingAllowed {
   1639   return [closingControllers_ count] == 0;
   1640 }
   1641 
   1642 - (void)mouseMoved:(NSEvent*)event {
   1643   // Use hit test to figure out what view we are hovering over.
   1644   NSView* targetView = [tabStripView_ hitTest:[event locationInWindow]];
   1645 
   1646   // Set the new tab button hover state iff the mouse is over the button.
   1647   BOOL shouldShowHoverImage = [targetView isKindOfClass:[NewTabButton class]];
   1648   [self setNewTabButtonHoverState:shouldShowHoverImage];
   1649 
   1650   TabView* tabView = (TabView*)targetView;
   1651   if (![tabView isKindOfClass:[TabView class]]) {
   1652     if ([[tabView superview] isKindOfClass:[TabView class]]) {
   1653       tabView = (TabView*)[targetView superview];
   1654     } else {
   1655       tabView = nil;
   1656     }
   1657   }
   1658 
   1659   if (hoveredTab_ != tabView) {
   1660     [hoveredTab_ mouseExited:nil];  // We don't pass event because moved events
   1661     [tabView mouseEntered:nil];  // don't have valid tracking areas
   1662     hoveredTab_ = tabView;
   1663   } else {
   1664     [hoveredTab_ mouseMoved:event];
   1665   }
   1666 }
   1667 
   1668 - (void)mouseEntered:(NSEvent*)event {
   1669   NSTrackingArea* area = [event trackingArea];
   1670   if ([area isEqual:trackingArea_]) {
   1671     mouseInside_ = YES;
   1672     [self setTabTrackingAreasEnabled:YES];
   1673     [self mouseMoved:event];
   1674   }
   1675 }
   1676 
   1677 // Called when the tracking area is in effect which means we're tracking to
   1678 // see if the user leaves the tab strip with their mouse. When they do,
   1679 // reset layout to use all available width.
   1680 - (void)mouseExited:(NSEvent*)event {
   1681   NSTrackingArea* area = [event trackingArea];
   1682   if ([area isEqual:trackingArea_]) {
   1683     mouseInside_ = NO;
   1684     [self setTabTrackingAreasEnabled:NO];
   1685     availableResizeWidth_ = kUseFullAvailableWidth;
   1686     [hoveredTab_ mouseExited:event];
   1687     hoveredTab_ = nil;
   1688     [self layoutTabs];
   1689   } else if ([area isEqual:newTabTrackingArea_]) {
   1690     // If the mouse is moved quickly enough, it is possible for the mouse to
   1691     // leave the tabstrip without sending any mouseMoved: messages at all.
   1692     // Since this would result in the new tab button incorrectly staying in the
   1693     // hover state, disable the hover image on every mouse exit.
   1694     [self setNewTabButtonHoverState:NO];
   1695   }
   1696 }
   1697 
   1698 // Enable/Disable the tracking areas for the tabs. They are only enabled
   1699 // when the mouse is in the tabstrip.
   1700 - (void)setTabTrackingAreasEnabled:(BOOL)enabled {
   1701   NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
   1702   for (TabController* controller in tabArray_.get()) {
   1703     TabView* tabView = [controller tabView];
   1704     if (enabled) {
   1705       // Set self up to observe tabs so hover states will be correct.
   1706       [defaultCenter addObserver:self
   1707                         selector:@selector(tabUpdateTracking:)
   1708                             name:NSViewDidUpdateTrackingAreasNotification
   1709                           object:tabView];
   1710     } else {
   1711       [defaultCenter removeObserver:self
   1712                                name:NSViewDidUpdateTrackingAreasNotification
   1713                              object:tabView];
   1714     }
   1715     [tabView setTrackingEnabled:enabled];
   1716   }
   1717 }
   1718 
   1719 // Sets the new tab button's image based on the current hover state.  Does
   1720 // nothing if the hover state is already correct.
   1721 - (void)setNewTabButtonHoverState:(BOOL)shouldShowHover {
   1722   if (shouldShowHover && !newTabButtonShowingHoverImage_) {
   1723     newTabButtonShowingHoverImage_ = YES;
   1724     [newTabButton_ setImage:
   1725         app::mac::GetCachedImageWithName(kNewTabHoverImage)];
   1726   } else if (!shouldShowHover && newTabButtonShowingHoverImage_) {
   1727     newTabButtonShowingHoverImage_ = NO;
   1728     [newTabButton_ setImage:app::mac::GetCachedImageWithName(kNewTabImage)];
   1729   }
   1730 }
   1731 
   1732 // Adds the given subview to (the end of) the list of permanent subviews
   1733 // (specified from bottom up). These subviews will always be below the
   1734 // transitory subviews (tabs). |-regenerateSubviewList| must be called to
   1735 // effectuate the addition.
   1736 - (void)addSubviewToPermanentList:(NSView*)aView {
   1737   if (aView)
   1738     [permanentSubviews_ addObject:aView];
   1739 }
   1740 
   1741 // Update the subviews, keeping the permanent ones (or, more correctly, putting
   1742 // in the ones listed in permanentSubviews_), and putting in the current tabs in
   1743 // the correct z-order. Any current subviews which is neither in the permanent
   1744 // list nor a (current) tab will be removed. So if you add such a subview, you
   1745 // should call |-addSubviewToPermanentList:| (or better yet, call that and then
   1746 // |-regenerateSubviewList| to actually add it).
   1747 - (void)regenerateSubviewList {
   1748   // Remove self as an observer from all the old tabs before a new set of
   1749   // potentially different tabs is put in place.
   1750   [self setTabTrackingAreasEnabled:NO];
   1751 
   1752   // Subviews to put in (in bottom-to-top order), beginning with the permanent
   1753   // ones.
   1754   NSMutableArray* subviews = [NSMutableArray arrayWithArray:permanentSubviews_];
   1755 
   1756   NSView* selectedTabView = nil;
   1757   // Go through tabs in reverse order, since |subviews| is bottom-to-top.
   1758   for (TabController* tab in [tabArray_ reverseObjectEnumerator]) {
   1759     NSView* tabView = [tab view];
   1760     if ([tab selected]) {
   1761       DCHECK(!selectedTabView);
   1762       selectedTabView = tabView;
   1763     } else {
   1764       [subviews addObject:tabView];
   1765     }
   1766   }
   1767   if (selectedTabView) {
   1768     [subviews addObject:selectedTabView];
   1769   }
   1770   [tabStripView_ setSubviews:subviews];
   1771   [self setTabTrackingAreasEnabled:mouseInside_];
   1772 }
   1773 
   1774 // Get the index and disposition for a potential URL(s) drop given a point (in
   1775 // the |TabStripView|'s coordinates). It considers only the x-coordinate of the
   1776 // given point. If it's in the "middle" of a tab, it drops on that tab. If it's
   1777 // to the left, it inserts to the left, and similarly for the right.
   1778 - (void)droppingURLsAt:(NSPoint)point
   1779             givesIndex:(NSInteger*)index
   1780            disposition:(WindowOpenDisposition*)disposition {
   1781   // Proportion of the tab which is considered the "middle" (and causes things
   1782   // to drop on that tab).
   1783   const double kMiddleProportion = 0.5;
   1784   const double kLRProportion = (1.0 - kMiddleProportion) / 2.0;
   1785 
   1786   DCHECK(index && disposition);
   1787   NSInteger i = 0;
   1788   for (TabController* tab in tabArray_.get()) {
   1789     NSView* view = [tab view];
   1790     DCHECK([view isKindOfClass:[TabView class]]);
   1791 
   1792     // Recall that |-[NSView frame]| is in its superview's coordinates, so a
   1793     // |TabView|'s frame is in the coordinates of the |TabStripView| (which
   1794     // matches the coordinate system of |point|).
   1795     NSRect frame = [view frame];
   1796 
   1797     // Modify the frame to make it "unoverlapped".
   1798     frame.origin.x += kTabOverlap / 2.0;
   1799     frame.size.width -= kTabOverlap;
   1800     if (frame.size.width < 1.0)
   1801       frame.size.width = 1.0;  // try to avoid complete failure
   1802 
   1803     // Drop in a new tab to the left of tab |i|?
   1804     if (point.x < (frame.origin.x + kLRProportion * frame.size.width)) {
   1805       *index = i;
   1806       *disposition = NEW_FOREGROUND_TAB;
   1807       return;
   1808     }
   1809 
   1810     // Drop on tab |i|?
   1811     if (point.x <= (frame.origin.x +
   1812                        (1.0 - kLRProportion) * frame.size.width)) {
   1813       *index = i;
   1814       *disposition = CURRENT_TAB;
   1815       return;
   1816     }
   1817 
   1818     // (Dropping in a new tab to the right of tab |i| will be taken care of in
   1819     // the next iteration.)
   1820     i++;
   1821   }
   1822 
   1823   // If we've made it here, we want to append a new tab to the end.
   1824   *index = -1;
   1825   *disposition = NEW_FOREGROUND_TAB;
   1826 }
   1827 
   1828 - (void)openURL:(GURL*)url inView:(NSView*)view at:(NSPoint)point {
   1829   // Get the index and disposition.
   1830   NSInteger index;
   1831   WindowOpenDisposition disposition;
   1832   [self droppingURLsAt:point
   1833             givesIndex:&index
   1834            disposition:&disposition];
   1835 
   1836   // Either insert a new tab or open in a current tab.
   1837   switch (disposition) {
   1838     case NEW_FOREGROUND_TAB: {
   1839       UserMetrics::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"),
   1840                                 browser_->profile());
   1841       browser::NavigateParams params(browser_, *url, PageTransition::TYPED);
   1842       params.disposition = disposition;
   1843       params.tabstrip_index = index;
   1844       params.tabstrip_add_types =
   1845           TabStripModel::ADD_ACTIVE | TabStripModel::ADD_FORCE_INDEX;
   1846       browser::Navigate(&params);
   1847       break;
   1848     }
   1849     case CURRENT_TAB:
   1850       UserMetrics::RecordAction(UserMetricsAction("Tab_DropURLOnTab"),
   1851                                 browser_->profile());
   1852       tabStripModel_->GetTabContentsAt(index)
   1853           ->tab_contents()->OpenURL(*url, GURL(), CURRENT_TAB,
   1854                                     PageTransition::TYPED);
   1855       tabStripModel_->ActivateTabAt(index, true);
   1856       break;
   1857     default:
   1858       NOTIMPLEMENTED();
   1859   }
   1860 }
   1861 
   1862 // (URLDropTargetController protocol)
   1863 - (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point {
   1864   DCHECK_EQ(view, tabStripView_.get());
   1865 
   1866   if ([urls count] < 1) {
   1867     NOTREACHED();
   1868     return;
   1869   }
   1870 
   1871   //TODO(viettrungluu): dropping multiple URLs.
   1872   if ([urls count] > 1)
   1873     NOTIMPLEMENTED();
   1874 
   1875   // Get the first URL and fix it up.
   1876   GURL url(GURL(URLFixerUpper::FixupURL(
   1877       base::SysNSStringToUTF8([urls objectAtIndex:0]), std::string())));
   1878 
   1879   [self openURL:&url inView:view at:point];
   1880 }
   1881 
   1882 // (URLDropTargetController protocol)
   1883 - (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point {
   1884   DCHECK_EQ(view, tabStripView_.get());
   1885 
   1886   // If the input is plain text, classify the input and make the URL.
   1887   AutocompleteMatch match;
   1888   browser_->profile()->GetAutocompleteClassifier()->Classify(
   1889       base::SysNSStringToUTF16(text), string16(), false, &match, NULL);
   1890   GURL url(match.destination_url);
   1891 
   1892   [self openURL:&url inView:view at:point];
   1893 }
   1894 
   1895 // (URLDropTargetController protocol)
   1896 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
   1897   DCHECK_EQ(view, tabStripView_.get());
   1898 
   1899   // The minimum y-coordinate at which one should consider place the arrow.
   1900   const CGFloat arrowBaseY = 25;
   1901 
   1902   NSInteger index;
   1903   WindowOpenDisposition disposition;
   1904   [self droppingURLsAt:point
   1905             givesIndex:&index
   1906            disposition:&disposition];
   1907 
   1908   NSPoint arrowPos = NSMakePoint(0, arrowBaseY);
   1909   if (index == -1) {
   1910     // Append a tab at the end.
   1911     DCHECK(disposition == NEW_FOREGROUND_TAB);
   1912     NSInteger lastIndex = [tabArray_ count] - 1;
   1913     NSRect overRect = [[[tabArray_ objectAtIndex:lastIndex] view] frame];
   1914     arrowPos.x = overRect.origin.x + overRect.size.width - kTabOverlap / 2.0;
   1915   } else {
   1916     NSRect overRect = [[[tabArray_ objectAtIndex:index] view] frame];
   1917     switch (disposition) {
   1918       case NEW_FOREGROUND_TAB:
   1919         // Insert tab (to the left of the given tab).
   1920         arrowPos.x = overRect.origin.x + kTabOverlap / 2.0;
   1921         break;
   1922       case CURRENT_TAB:
   1923         // Overwrite the given tab.
   1924         arrowPos.x = overRect.origin.x + overRect.size.width / 2.0;
   1925         break;
   1926       default:
   1927         NOTREACHED();
   1928     }
   1929   }
   1930 
   1931   [tabStripView_ setDropArrowPosition:arrowPos];
   1932   [tabStripView_ setDropArrowShown:YES];
   1933   [tabStripView_ setNeedsDisplay:YES];
   1934 }
   1935 
   1936 // (URLDropTargetController protocol)
   1937 - (void)hideDropURLsIndicatorInView:(NSView*)view {
   1938   DCHECK_EQ(view, tabStripView_.get());
   1939 
   1940   if ([tabStripView_ dropArrowShown]) {
   1941     [tabStripView_ setDropArrowShown:NO];
   1942     [tabStripView_ setNeedsDisplay:YES];
   1943   }
   1944 }
   1945 
   1946 - (GTMWindowSheetController*)sheetController {
   1947   if (!sheetController_.get())
   1948     sheetController_.reset([[GTMWindowSheetController alloc]
   1949         initWithWindow:[switchView_ window] delegate:self]);
   1950   return sheetController_.get();
   1951 }
   1952 
   1953 - (void)destroySheetController {
   1954   // Make sure there are no open sheets.
   1955   DCHECK_EQ(0U, [[sheetController_ viewsWithAttachedSheets] count]);
   1956   sheetController_.reset();
   1957 }
   1958 
   1959 // TabContentsControllerDelegate protocol.
   1960 - (void)tabContentsViewFrameWillChange:(TabContentsController*)source
   1961                              frameRect:(NSRect)frameRect {
   1962   id<TabContentsControllerDelegate> controller =
   1963       [[switchView_ window] windowController];
   1964   [controller tabContentsViewFrameWillChange:source frameRect:frameRect];
   1965 }
   1966 
   1967 - (TabContentsController*)activeTabContentsController {
   1968   int modelIndex = tabStripModel_->active_index();
   1969   if (modelIndex < 0)
   1970     return nil;
   1971   NSInteger index = [self indexFromModelIndex:modelIndex];
   1972   if (index < 0 ||
   1973       index >= (NSInteger)[tabContentsArray_ count])
   1974     return nil;
   1975   return [tabContentsArray_ objectAtIndex:index];
   1976 }
   1977 
   1978 - (void)gtm_systemRequestsVisibilityForView:(NSView*)view {
   1979   // This implementation is required by GTMWindowSheetController.
   1980 
   1981   // Raise window...
   1982   [[switchView_ window] makeKeyAndOrderFront:self];
   1983 
   1984   // ...and raise a tab with a sheet.
   1985   NSInteger index = [self modelIndexForContentsView:view];
   1986   DCHECK(index >= 0);
   1987   if (index >= 0)
   1988     tabStripModel_->ActivateTabAt(index, false /* not a user gesture */);
   1989 }
   1990 
   1991 - (void)attachConstrainedWindow:(ConstrainedWindowMac*)window {
   1992   // TODO(thakis, avi): Figure out how to make this work when tabs are dragged
   1993   // out or if fullscreen mode is toggled.
   1994 
   1995   // View hierarchy of the contents view:
   1996   // NSView  -- switchView, same for all tabs
   1997   // +- NSView  -- TabContentsController's view
   1998   //    +- TabContentsViewCocoa
   1999   // Changing it? Do not forget to modify removeConstrainedWindow too.
   2000   // We use the TabContentsController's view in |swapInTabAtIndex|, so we have
   2001   // to pass it to the sheet controller here.
   2002   NSView* tabContentsView = [window->owner()->GetNativeView() superview];
   2003   window->delegate()->RunSheet([self sheetController], tabContentsView);
   2004 
   2005   // TODO(avi, thakis): GTMWindowSheetController has no api to move tabsheets
   2006   // between windows. Until then, we have to prevent having to move a tabsheet
   2007   // between windows, e.g. no tearing off of tabs.
   2008   NSInteger modelIndex = [self modelIndexForContentsView:tabContentsView];
   2009   NSInteger index = [self indexFromModelIndex:modelIndex];
   2010   BrowserWindowController* controller =
   2011       (BrowserWindowController*)[[switchView_ window] windowController];
   2012   DCHECK(controller != nil);
   2013   DCHECK(index >= 0);
   2014   if (index >= 0) {
   2015     [controller setTab:[self viewAtIndex:index] isDraggable:NO];
   2016   }
   2017 }
   2018 
   2019 - (void)removeConstrainedWindow:(ConstrainedWindowMac*)window {
   2020   NSView* tabContentsView = [window->owner()->GetNativeView() superview];
   2021 
   2022   // TODO(avi, thakis): GTMWindowSheetController has no api to move tabsheets
   2023   // between windows. Until then, we have to prevent having to move a tabsheet
   2024   // between windows, e.g. no tearing off of tabs.
   2025   NSInteger modelIndex = [self modelIndexForContentsView:tabContentsView];
   2026   NSInteger index = [self indexFromModelIndex:modelIndex];
   2027   BrowserWindowController* controller =
   2028       (BrowserWindowController*)[[switchView_ window] windowController];
   2029   DCHECK(index >= 0);
   2030   if (index >= 0) {
   2031     [controller setTab:[self viewAtIndex:index] isDraggable:YES];
   2032   }
   2033 }
   2034 
   2035 - (BOOL)shouldShowProfileMenuButton {
   2036   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles))
   2037     return NO;
   2038   if (browser_->profile()->IsOffTheRecord())
   2039     return NO;
   2040   return (!browser_->profile()->GetPrefs()->GetString(
   2041         prefs::kGoogleServicesUsername).empty());
   2042 }
   2043 
   2044 - (void)updateProfileMenuButton {
   2045   if (![self shouldShowProfileMenuButton]) {
   2046     [profileMenuButton_ setHidden:YES];
   2047     return;
   2048   }
   2049 
   2050   std::string profileName = browser_->profile()->GetPrefs()->GetString(
   2051       prefs::kGoogleServicesUsername);
   2052   [profileMenuButton_ setProfileDisplayName:
   2053       [NSString stringWithUTF8String:profileName.c_str()]];
   2054   [profileMenuButton_ setHidden:NO];
   2055 
   2056   NSMenu* menu = [profileMenuButton_ menu];
   2057   while ([menu numberOfItems] > 0) {
   2058     [menu removeItemAtIndex:0];
   2059   }
   2060 
   2061   NSString* menuTitle =
   2062       l10n_util::GetNSStringWithFixup(IDS_PROFILES_CREATE_NEW_PROFILE_OPTION);
   2063   [menu addItemWithTitle:menuTitle
   2064                   action:NULL
   2065            keyEquivalent:@""];
   2066 }
   2067 
   2068 @end
   2069