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