#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/autocomplete/autocomplete_classifier.h"
#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
-#include "chrome/browser/autocomplete/autocomplete_input.h"
-#include "chrome/browser/autocomplete/autocomplete_match.h"
-#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_model_observer_bridge.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
#import "chrome/browser/ui/cocoa/tabs/tab_view.h"
-#import "chrome/browser/ui/cocoa/tabs/throbber_view.h"
#include "chrome/browser/ui/find_bar/find_bar.h"
#include "chrome/browser/ui/find_bar/find_bar_controller.h"
#include "chrome/browser/ui/find_bar/find_tab_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/net/url_fixer_upper.h"
#include "chrome/common/pref_names.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/autocomplete_match.h"
+#include "components/url_fixer/url_fixer.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_view.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "grit/ui_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/image/image.h"
+#include "ui/gfx/mac/scoped_ns_disable_screen_updates.h"
using base::UserMetricsAction;
using content::OpenURLParams;
// The amount by which mini tabs are separated from normal tabs.
const CGFloat kLastMiniTabSpacing = 2.0;
-// The width and height for a tab's icon.
-const CGFloat kIconWidthAndHeight = 16.0;
-
// The amount by which the new tab button is offset (from the tabs).
const CGFloat kNewTabButtonOffset = 8.0;
base::scoped_nsobject<NSImage> result([[NSImage alloc] initWithSize:size]);
[NSGraphicsContext saveGraphicsState];
for (ui::ScaleFactor scale_factor : ui::GetSupportedScaleFactors()) {
- float scale = GetImageScale(scale_factor);
+ float scale = GetScaleForScaleFactor(scale_factor);
NSBitmapImageRep *bmpImageRep = [[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:size.width * scale
- (void)addSubviewToPermanentList:(NSView*)aView;
- (void)regenerateSubviewList;
- (NSInteger)indexForContentsView:(NSView*)view;
-- (NSImageView*)iconImageViewForContents:(content::WebContents*)contents;
+- (NSImage*)iconImageForContents:(content::WebContents*)contents;
- (void)updateIconsForContents:(content::WebContents*)contents
atIndex:(NSInteger)modelIndex;
- (void)layoutTabsWithAnimation:(BOOL)animate
regenerateSubviews:(BOOL)doUpdate;
-- (void)animationDidStopForController:(TabController*)controller
- finished:(BOOL)finished;
+- (void)animationDidStop:(CAAnimation*)animation
+ forController:(TabController*)controller
+ finished:(BOOL)finished;
- (NSInteger)indexFromModelIndex:(NSInteger)index;
- (void)clickNewTabButton:(id)sender;
- (NSInteger)numberOfOpenTabs;
}
- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished {
- [strip_ animationDidStopForController:controller_ finished:finished];
+ [strip_ animationDidStop:animation
+ forController:controller_
+ finished:finished];
}
@end
[self addSubviewToPermanentList:dragBlockingView_];
newTabButton_ = [view getNewTabButton];
+ [newTabButton_ setWantsLayer:YES];
[self addSubviewToPermanentList:newTabButton_];
[newTabButton_ setTarget:self];
[newTabButton_ setAction:@selector(clickNewTabButton:)];
NSInteger index = [self indexFromModelIndex:modelIndex];
TabContentsController* controller = [tabContentsArray_ objectAtIndex:index];
+ // Make sure we do not draw any transient arrangements of views.
+ gfx::ScopedNSDisableScreenUpdates ns_disabler;
// Make sure that any layers that move are not animated to their new
// positions.
- ScopedCAActionDisabler disabler;
+ ScopedCAActionDisabler ca_disabler;
// Resize the new view to fit the window. Calling |view| may lazily
// instantiate the TabContentsController from the nib. Until we call
return -1;
}
+- (NSArray*)selectedViews {
+ NSMutableArray* views = [NSMutableArray arrayWithCapacity:[tabArray_ count]];
+ for (TabController* tab in tabArray_.get()) {
+ if ([tab selected])
+ [views addObject:[tab tabView]];
+ }
+ return views;
+}
// Returns the view at the given index, using the array of TabControllers to
// get the associated view. Returns nil if out of range.
// Make a new tab. Load the contents of this tab from the nib and associate
// the new controller with |contents| so it can be looked up later.
- const BOOL autoEmbedFullscreen =
- implicit_cast<content::WebContentsDelegate*>(browser_)->
- EmbedsFullscreenWidget();
base::scoped_nsobject<TabContentsController> contentsController(
- [[TabContentsController alloc] initWithContents:contents
- andAutoEmbedFullscreen:autoEmbedFullscreen]);
+ [[TabContentsController alloc] initWithContents:contents]);
[tabContentsArray_ insertObject:contentsController atIndex:index];
// Make a new tab and add it to the strip. Keep track of its controller.
// Called before |contents| is deactivated.
- (void)tabDeactivatedWithContents:(content::WebContents*)contents {
- contents->GetView()->StoreFocus();
+ contents->StoreFocus();
}
// Called when a notification is received from the model to select a particular
if (newContents) {
newContents->WasShown();
- newContents->GetView()->RestoreFocus();
+ newContents->RestoreFocus();
}
}
// Simply create a new TabContentsController for |newContents| and place it
// into the array, replacing |oldContents|. An ActiveTabChanged notification
// will follow, at which point we will install the new view.
- const BOOL autoEmbedFullscreen =
- implicit_cast<content::WebContentsDelegate*>(browser_)->
- EmbedsFullscreenWidget();
base::scoped_nsobject<TabContentsController> newController(
- [[TabContentsController alloc] initWithContents:newContents
- andAutoEmbedFullscreen:autoEmbedFullscreen]);
+ [[TabContentsController alloc] initWithContents:newContents]);
// Bye bye, |oldController|.
[tabContentsArray_ replaceObjectAtIndex:index withObject:newController];
// Called by the CAAnimation delegate when the tab completes the closing
// animation.
-- (void)animationDidStopForController:(TabController*)controller
- finished:(BOOL)finished {
+- (void)animationDidStop:(CAAnimation*)animation
+ forController:(TabController*)controller
+ finished:(BOOL)finished{
+ [[animation delegate] invalidate];
[closingControllers_ removeObject:controller];
[self removeTab:controller];
}
// A helper routine for creating an NSImageView to hold the favicon or app icon
// for |contents|.
-- (NSImageView*)iconImageViewForContents:(content::WebContents*)contents {
+- (NSImage*)iconImageForContents:(content::WebContents*)contents {
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(contents);
BOOL isApp = extensions_tab_helper->is_app();
// from an SkBitmap. Either way, just show the default.
if (!image)
image = defaultFavicon_.get();
- NSRect frame = NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
- NSImageView* view = [[[NSImageView alloc] initWithFrame:frame] autorelease];
- [view setImage:image];
- return view;
+
+ return image;
}
// Updates the current loading state, replacing the icon view with a favicon,
} else if (contents->IsWaitingForResponse()) {
newState = kTabWaiting;
throbberImage = throbberWaitingImage;
- } else if (contents->IsLoading()) {
+ } else if (contents->IsLoadingToDifferentDocument()) {
newState = kTabLoading;
throbberImage = throbberLoadingImage;
}
// every call to this function is significant.
if (newState == kTabDone || oldState != newState ||
oldHasIcon != newHasIcon) {
- NSView* iconView = nil;
if (newHasIcon) {
if (newState == kTabDone) {
- iconView = [self iconImageViewForContents:contents];
+ [tabController setIconImage:[self iconImageForContents:contents]];
const TabMediaState mediaState =
chrome::GetTabMediaStateForContents(contents);
// Create MediaIndicatorView upon first use.
}
[[tabController mediaIndicatorView] updateIndicator:mediaState];
} else if (newState == kTabCrashed) {
- NSImage* oldImage = [[self iconImageViewForContents:contents] image];
- NSRect frame =
- NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
- iconView = [ThrobberView toastThrobberViewWithFrame:frame
- beforeImage:oldImage
- afterImage:sadFaviconImage];
+ [tabController setIconImage:sadFaviconImage withToastAnimation:YES];
[[tabController mediaIndicatorView]
updateIndicator:TAB_MEDIA_STATE_NONE];
} else {
- NSRect frame =
- NSMakeRect(0, 0, kIconWidthAndHeight, kIconWidthAndHeight);
- iconView = [ThrobberView filmstripThrobberViewWithFrame:frame
- image:throbberImage];
+ [tabController setIconImage:throbberImage];
}
- }
-
- [tabController setIconView:iconView];
- if (iconView) {
- // See the comment above kTabOverlap for why these DCHECKs exist.
- DCHECK_GE(NSMinX([iconView frame]), kTabOverlap);
- // TODO(thakis): Ideally, this would be true too, but it's not true in
- // some tests.
- //DCHECK_LE(NSMaxX([iconView frame]),
- // NSWidth([[tabController view] frame]) - kTabOverlap);
+ } else {
+ [tabController setIconImage:nil];
}
}
}
[self layoutTabs];
}
-- (void)setFrameOfActiveTab:(NSRect)frame {
- NSView* view = [self activeTabView];
+- (void)setFrame:(NSRect)frame ofTabView:(NSView*)view {
NSValue* identifier = [NSValue valueWithPointer:view];
[targetFrames_ setObject:[NSValue valueWithRect:frame]
forKey:identifier];
return tabStripModel_;
}
+- (NSArray*)tabViews {
+ NSMutableArray* views = [NSMutableArray arrayWithCapacity:[tabArray_ count]];
+ for (TabController* tab in tabArray_.get()) {
+ [views addObject:[tab tabView]];
+ }
+ return views;
+}
+
- (NSView*)activeTabView {
int activeIndex = tabStripModel_->active_index();
// Take closing tabs into account. They can't ever be selected.
return [self viewAtIndex:activeIndex];
}
-// Find the model index based on the x coordinate of the placeholder. If there
-// is no placeholder, this returns the end of the tab strip. Closing tabs are
-// not considered in computing the index.
- (int)indexOfPlaceholder {
- double placeholderX = placeholderFrame_.origin.x;
- int index = 0;
- int location = 0;
// Use |tabArray_| here instead of the tab strip count in order to get the
// correct index when there are closing tabs to the left of the placeholder.
const int count = [tabArray_ count];
+
+ // No placeholder, return the end of the strip.
+ if (placeholderTab_ == nil)
+ return count;
+
+ double placeholderX = placeholderFrame_.origin.x;
+ int index = 0;
+ int location = 0;
while (index < count) {
// Ignore closing tabs for simplicity. The only drawback of this is that
// if the placeholder is placed right before one or several contiguous
// new window. Mini-tabs are either app or pinned tabs; the app state is stored
// by the |contents|, but the |pinned| state is the caller's responsibility.
- (void)dropWebContents:(WebContents*)contents
+ atIndex:(int)modelIndex
withFrame:(NSRect)frame
- asPinnedTab:(BOOL)pinned {
- int modelIndex = [self indexOfPlaceholder];
-
+ asPinnedTab:(BOOL)pinned
+ activate:(BOOL)activate {
// Mark that the new tab being created should start at |frame|. It will be
// reset as soon as the tab has been positioned.
droppedTabFrame_ = frame;
// Insert it into this tab strip. We want it in the foreground and to not
// inherit the current tab's group.
tabStripModel_->InsertWebContentsAt(
- modelIndex, contents,
- TabStripModel::ADD_ACTIVE | (pinned ? TabStripModel::ADD_PINNED : 0));
+ modelIndex,
+ contents,
+ (activate ? TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE) |
+ (pinned ? TabStripModel::ADD_PINNED : TabStripModel::ADD_NONE));
}
// Called when the tab strip view changes size. As we only registered for
NOTIMPLEMENTED();
// Get the first URL and fix it up.
- GURL url(GURL(URLFixerUpper::FixupURL(
+ GURL url(GURL(url_fixer::FixupURL(
base::SysNSStringToUTF8([urls objectAtIndex:0]), std::string())));
[self openURL:&url inView:view at:point];
// If the input is plain text, classify the input and make the URL.
AutocompleteMatch match;
AutocompleteClassifierFactory::GetForProfile(browser_->profile())->Classify(
- base::SysNSStringToUTF16(text), false, false, AutocompleteInput::BLANK,
- &match, NULL);
+ base::SysNSStringToUTF16(text), false, false,
+ metrics::OmniboxEventProto::BLANK, &match, NULL);
GURL url(match.destination_url);
[self openURL:&url inView:view at:point];
return [tabContentsArray_ objectAtIndex:index];
}
+- (void)addWindowControls {
+ if (!fullscreenWindowControls_) {
+ // Make the container view.
+ CGFloat height = NSHeight([tabStripView_ frame]);
+ NSRect frame = NSMakeRect(0, 0, [self leftIndentForControls], height);
+ fullscreenWindowControls_.reset([[NSView alloc] initWithFrame:frame]);
+ [fullscreenWindowControls_
+ setAutoresizingMask:NSViewMaxXMargin | NSViewHeightSizable];
+
+ // Add the traffic light buttons. The horizontal layout was determined by
+ // manual inspection on Yosemite.
+ CGFloat closeButtonX = 11;
+ CGFloat miniButtonX = 31;
+ CGFloat zoomButtonX = 51;
+
+ NSUInteger styleMask = [[tabStripView_ window] styleMask];
+ NSButton* closeButton = [NSWindow standardWindowButton:NSWindowCloseButton
+ forStyleMask:styleMask];
+
+ // Vertically center the buttons in the tab strip.
+ CGFloat buttonY = floor((height - NSHeight([closeButton bounds])) / 2);
+ [closeButton setFrameOrigin:NSMakePoint(closeButtonX, buttonY)];
+ [fullscreenWindowControls_ addSubview:closeButton];
+
+ NSButton* miniaturizeButton =
+ [NSWindow standardWindowButton:NSWindowMiniaturizeButton
+ forStyleMask:styleMask];
+ [miniaturizeButton setFrameOrigin:NSMakePoint(miniButtonX, buttonY)];
+ [miniaturizeButton setEnabled:NO];
+ [fullscreenWindowControls_ addSubview:miniaturizeButton];
+
+ NSButton* zoomButton =
+ [NSWindow standardWindowButton:NSWindowZoomButton
+ forStyleMask:styleMask];
+ [fullscreenWindowControls_ addSubview:zoomButton];
+ [zoomButton setFrameOrigin:NSMakePoint(zoomButtonX, buttonY)];
+ }
+
+ if (![permanentSubviews_ containsObject:fullscreenWindowControls_]) {
+ [self addSubviewToPermanentList:fullscreenWindowControls_];
+ [self regenerateSubviewList];
+ }
+}
+
+- (void)removeWindowControls {
+ if (fullscreenWindowControls_)
+ [permanentSubviews_ removeObject:fullscreenWindowControls_];
+ [self regenerateSubviewList];
+}
+
- (void)themeDidChangeNotification:(NSNotification*)notification {
[self setNewTabImages];
}
// View hierarchy of the contents view:
// NSView -- switchView, same for all tabs
// +- NSView -- TabContentsController's view
- // +- TabContentsViewCocoa
+ // +- WebContentsViewCocoa
//
// Changing it? Do not forget to modify
// -[TabStripController swapInTabAtIndex:] too.
- return [web_contents->GetView()->GetNativeView() superview];
+ return [web_contents->GetNativeView() superview];
+}
+
+NSRect GetSheetParentBoundsForParentView(NSView* view) {
+ // If the devtools view is open, it shrinks the size of the WebContents, so go
+ // up the hierarchy to the devtools container view to avoid that. Note that
+ // the devtools view is always in the hierarchy even if it is not open or it
+ // is detached.
+ NSView* devtools_view = [[[view superview] superview] superview];
+ return [devtools_view convertRect:[devtools_view bounds] toView:nil];
}