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