Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / framed_browser_window.mm
blob5d098be8d3c00a47cd7a24ab4c2703c7ba498748
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
7 #include "base/logging.h"
8 #include "base/mac/sdk_forward_declarations.h"
9 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
10 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
11 #include "chrome/browser/themes/theme_properties.h"
12 #include "chrome/browser/themes/theme_service.h"
13 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
14 #import "chrome/browser/ui/cocoa/browser_window_utils.h"
15 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
16 #import "chrome/browser/ui/cocoa/themed_window.h"
17 #include "grit/theme_resources.h"
18 #include "ui/base/cocoa/nsgraphics_context_additions.h"
19 #import "ui/base/cocoa/nsview_additions.h"
21 // Implementer's note: Moving the window controls is tricky. When altering the
22 // code, ensure that:
23 // - accessibility hit testing works
24 // - the accessibility hierarchy is correct
25 // - close/min in the background don't bring the window forward
26 // - rollover effects work correctly
28 namespace {
30 // Size of the gradient. Empirically determined so that the gradient looks
31 // like what the heuristic does when there are just a few tabs.
32 const CGFloat kWindowGradientHeight = 24.0;
36 @interface FramedBrowserWindow (Private)
38 - (void)adjustCloseButton:(NSNotification*)notification;
39 - (void)adjustMiniaturizeButton:(NSNotification*)notification;
40 - (void)adjustZoomButton:(NSNotification*)notification;
41 - (void)adjustButton:(NSButton*)button
42               ofKind:(NSWindowButton)kind;
44 @end
46 @implementation FramedBrowserWindow
48 - (id)initWithContentRect:(NSRect)contentRect
49               hasTabStrip:(BOOL)hasTabStrip{
50   NSUInteger styleMask = NSTitledWindowMask |
51                          NSClosableWindowMask |
52                          NSMiniaturizableWindowMask |
53                          NSResizableWindowMask |
54                          NSTexturedBackgroundWindowMask;
55   if ((self = [super initWithContentRect:contentRect
56                                styleMask:styleMask
57                                  backing:NSBackingStoreBuffered
58                                    defer:YES
59                   wantsViewsOverTitlebar:hasTabStrip])) {
60     // The 10.6 fullscreen code copies the title to a different window, which
61     // will assert if it's nil.
62     [self setTitle:@""];
64     // The following two calls fix http://crbug.com/25684 by preventing the
65     // window from recalculating the border thickness as the window is
66     // resized.
67     // This was causing the window tint to change for the default system theme
68     // when the window was being resized.
69     [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
70     [self setContentBorderThickness:kWindowGradientHeight forEdge:NSMaxYEdge];
72     hasTabStrip_ = hasTabStrip;
73     closeButton_ = [self standardWindowButton:NSWindowCloseButton];
74     [closeButton_ setPostsFrameChangedNotifications:YES];
75     miniaturizeButton_ = [self standardWindowButton:NSWindowMiniaturizeButton];
76     [miniaturizeButton_ setPostsFrameChangedNotifications:YES];
77     zoomButton_ = [self standardWindowButton:NSWindowZoomButton];
78     [zoomButton_ setPostsFrameChangedNotifications:YES];
80     windowButtonsInterButtonSpacing_ =
81         NSMinX([miniaturizeButton_ frame]) - NSMaxX([closeButton_ frame]);
83     [self adjustButton:closeButton_ ofKind:NSWindowCloseButton];
84     [self adjustButton:miniaturizeButton_ ofKind:NSWindowMiniaturizeButton];
85     [self adjustButton:zoomButton_ ofKind:NSWindowZoomButton];
87     NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
88     [center addObserver:self
89                selector:@selector(adjustCloseButton:)
90                    name:NSViewFrameDidChangeNotification
91                  object:closeButton_];
92     [center addObserver:self
93                selector:@selector(adjustMiniaturizeButton:)
94                    name:NSViewFrameDidChangeNotification
95                  object:miniaturizeButton_];
96     [center addObserver:self
97                selector:@selector(adjustZoomButton:)
98                    name:NSViewFrameDidChangeNotification
99                  object:zoomButton_];
100   }
102   return self;
105 - (void)dealloc {
106   [[NSNotificationCenter defaultCenter] removeObserver:self];
107   [super dealloc];
110 - (void)adjustCloseButton:(NSNotification*)notification {
111   [self adjustButton:[notification object]
112               ofKind:NSWindowCloseButton];
115 - (void)adjustMiniaturizeButton:(NSNotification*)notification {
116   [self adjustButton:[notification object]
117               ofKind:NSWindowMiniaturizeButton];
120 - (void)adjustZoomButton:(NSNotification*)notification {
121   [self adjustButton:[notification object]
122               ofKind:NSWindowZoomButton];
125 - (void)adjustButton:(NSButton*)button
126               ofKind:(NSWindowButton)kind {
127   NSRect buttonFrame = [button frame];
129   CGFloat xOffset = hasTabStrip_
130       ? kFramedWindowButtonsWithTabStripOffsetFromLeft
131       : kFramedWindowButtonsWithoutTabStripOffsetFromLeft;
132   CGFloat yOffset = hasTabStrip_
133       ? kFramedWindowButtonsWithTabStripOffsetFromTop
134       : kFramedWindowButtonsWithoutTabStripOffsetFromTop;
135   buttonFrame.origin =
136       NSMakePoint(xOffset, (NSHeight([self frame]) -
137                             NSHeight(buttonFrame) - yOffset));
139   switch (kind) {
140     case NSWindowZoomButton:
141       buttonFrame.origin.x += NSWidth([miniaturizeButton_ frame]);
142       buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
143       // fallthrough
144     case NSWindowMiniaturizeButton:
145       buttonFrame.origin.x += NSWidth([closeButton_ frame]);
146       buttonFrame.origin.x += windowButtonsInterButtonSpacing_;
147       // fallthrough
148     default:
149       break;
150   }
152   BOOL didPost = [button postsBoundsChangedNotifications];
153   [button setPostsFrameChangedNotifications:NO];
154   [button setFrame:buttonFrame];
155   [button setPostsFrameChangedNotifications:didPost];
158 // The tab strip view covers our window buttons. So we add hit testing here
159 // to find them properly and return them to the accessibility system.
160 - (id)accessibilityHitTest:(NSPoint)point {
161   NSPoint windowPoint = [self convertScreenToBase:point];
162   NSControl* controls[] = { closeButton_, zoomButton_, miniaturizeButton_ };
163   id value = nil;
164   for (size_t i = 0; i < sizeof(controls) / sizeof(controls[0]); ++i) {
165     if (NSPointInRect(windowPoint, [controls[i] frame])) {
166       value = [controls[i] accessibilityHitTest:point];
167       break;
168     }
169   }
170   if (!value) {
171     value = [super accessibilityHitTest:point];
172   }
173   return value;
176 - (void)setShouldHideTitle:(BOOL)flag {
177   shouldHideTitle_ = flag;
180 - (BOOL)_isTitleHidden {
181   return shouldHideTitle_;
184 - (CGFloat)windowButtonsInterButtonSpacing {
185   return windowButtonsInterButtonSpacing_;
188 // This method is called whenever a window is moved in order to ensure it fits
189 // on the screen.  We cannot always handle resizes without breaking, so we
190 // prevent frame constraining in those cases.
191 - (NSRect)constrainFrameRect:(NSRect)frame toScreen:(NSScreen*)screen {
192   // Do not constrain the frame rect if our delegate says no.  In this case,
193   // return the original (unconstrained) frame.
194   id delegate = [self delegate];
195   if ([delegate respondsToSelector:@selector(shouldConstrainFrameRect)] &&
196       ![delegate shouldConstrainFrameRect])
197     return frame;
199   return [super constrainFrameRect:frame toScreen:screen];
202 // This method is overridden in order to send the toggle fullscreen message
203 // through the cross-platform browser framework before going fullscreen.  The
204 // message will eventually come back as a call to |-toggleSystemFullScreen|,
205 // which in turn calls AppKit's |NSWindow -toggleFullScreen:|.
206 - (void)toggleFullScreen:(id)sender {
207   id delegate = [self delegate];
208   if ([delegate respondsToSelector:@selector(handleLionToggleFullscreen)])
209     [delegate handleLionToggleFullscreen];
212 - (void)toggleSystemFullScreen {
213   if ([super respondsToSelector:@selector(toggleFullScreen:)])
214     [super toggleFullScreen:nil];
217 - (NSPoint)fullScreenButtonOriginAdjustment {
218   if (!hasTabStrip_)
219     return NSZeroPoint;
221   // Vertically center the button.
222   NSPoint origin = NSMakePoint(0, -6);
224   // If there is a profile avatar icon present, shift the button over by its
225   // width and some padding. The new avatar button is displayed to the right
226   // of the fullscreen icon, so it doesn't need to be shifted.
227   BrowserWindowController* bwc =
228       static_cast<BrowserWindowController*>([self windowController]);
229   if ([bwc shouldShowAvatar] && ![bwc shouldUseNewAvatarButton]) {
230     NSView* avatarButton = [[bwc avatarButtonController] view];
231     origin.x = -(NSWidth([avatarButton frame]) + 3);
232   } else {
233     origin.x -= 6;
234   }
236   return origin;
239 + (BOOL)drawWindowThemeInDirtyRect:(NSRect)dirtyRect
240                            forView:(NSView*)view
241                             bounds:(NSRect)bounds
242               forceBlackBackground:(BOOL)forceBlackBackground {
243   ui::ThemeProvider* themeProvider = [[view window] themeProvider];
244   if (!themeProvider)
245     return NO;
247   ThemedWindowStyle windowStyle = [[view window] themedWindowStyle];
249   // Devtools windows don't get themed.
250   if (windowStyle & THEMED_DEVTOOLS)
251     return NO;
253   BOOL active = [[view window] isMainWindow];
254   BOOL incognito = windowStyle & THEMED_INCOGNITO;
255   BOOL popup = windowStyle & THEMED_POPUP;
257   // Find a theme image.
258   NSColor* themeImageColor = nil;
259   if (!popup) {
260     int themeImageID;
261     if (active && incognito)
262       themeImageID = IDR_THEME_FRAME_INCOGNITO;
263     else if (active && !incognito)
264       themeImageID = IDR_THEME_FRAME;
265     else if (!active && incognito)
266       themeImageID = IDR_THEME_FRAME_INCOGNITO_INACTIVE;
267     else
268       themeImageID = IDR_THEME_FRAME_INACTIVE;
269     if (themeProvider->HasCustomImage(IDR_THEME_FRAME))
270       themeImageColor = themeProvider->GetNSImageColorNamed(themeImageID);
271   }
273   // If no theme image, use a gradient if incognito.
274   NSGradient* gradient = nil;
275   if (!themeImageColor && incognito)
276     gradient = themeProvider->GetNSGradient(
277         active ? ThemeProperties::GRADIENT_FRAME_INCOGNITO :
278                  ThemeProperties::GRADIENT_FRAME_INCOGNITO_INACTIVE);
280   BOOL themed = NO;
281   if (themeImageColor) {
282     // Default to replacing any existing pixels with the theme image, but if
283     // asked paint black first and blend the theme with black.
284     NSCompositingOperation operation = NSCompositeCopy;
285     if (forceBlackBackground) {
286       [[NSColor blackColor] set];
287       NSRectFill(dirtyRect);
288       operation = NSCompositeSourceOver;
289     }
291     NSPoint position = [[view window] themeImagePositionForAlignment:
292         THEME_IMAGE_ALIGN_WITH_FRAME];
293     [[NSGraphicsContext currentContext] cr_setPatternPhase:position
294                                                    forView:view];
296     [themeImageColor set];
297     NSRectFillUsingOperation(dirtyRect, operation);
298     themed = YES;
299   } else if (gradient) {
300     NSPoint startPoint = NSMakePoint(NSMinX(bounds), NSMaxY(bounds));
301     NSPoint endPoint = startPoint;
302     endPoint.y -= kBrowserFrameViewPaintHeight;
303     [gradient drawFromPoint:startPoint toPoint:endPoint options:0];
304     themed = YES;
305   }
307   // Check to see if we have an overlay image.
308   NSImage* overlayImage = nil;
309   if (themeProvider->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && !incognito &&
310       !popup) {
311     overlayImage = themeProvider->
312         GetNSImageNamed(active ? IDR_THEME_FRAME_OVERLAY :
313                                  IDR_THEME_FRAME_OVERLAY_INACTIVE);
314   }
316   if (overlayImage) {
317     // Anchor to top-left and don't scale.
318     NSPoint position = [[view window] themeImagePositionForAlignment:
319         THEME_IMAGE_ALIGN_WITH_FRAME];
320     position = [view convertPoint:position fromView:nil];
321     NSSize overlaySize = [overlayImage size];
322     NSRect imageFrame = NSMakeRect(0, 0, overlaySize.width, overlaySize.height);
323     [overlayImage drawAtPoint:NSMakePoint(position.x,
324                                           position.y - overlaySize.height)
325                      fromRect:imageFrame
326                     operation:NSCompositeSourceOver
327                      fraction:1.0];
328   }
330   return themed;
333 - (NSColor*)titleColor {
334   ui::ThemeProvider* themeProvider = [self themeProvider];
335   if (!themeProvider)
336     return [NSColor windowFrameTextColor];
338   ThemedWindowStyle windowStyle = [self themedWindowStyle];
339   BOOL incognito = windowStyle & THEMED_INCOGNITO;
341   if (incognito)
342     return [NSColor whiteColor];
343   else
344     return [NSColor windowFrameTextColor];
347 @end