Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / constrained_window / constrained_window_sheet_controller.mm
blob9375daa5b8af5f1c90ff7df3fc44c2b18a884877
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/constrained_window/constrained_window_sheet_controller.h"
7 #include <map>
9 #include "base/logging.h"
10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h"
11 #include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h"
13 namespace {
15 // Maps parent windows to sheet controllers.
16 NSMutableDictionary* g_sheetControllers;
18 // Get a value for the given window that can be used as a key in a dictionary.
19 NSValue* GetKeyForParentWindow(NSWindow* parent_window) {
20   return [NSValue valueWithNonretainedObject:parent_window];
23 // Returns the bounds to use when showing a sheet for a given parent view. This
24 // returns a rect in window coordinates.
25 NSRect GetSheetParentBoundsForParentView(NSView* view) {
26   // If the devtools view is open, it shrinks the size of the WebContents, so go
27   // up the hierarchy to the devtools container view to avoid that. Note that
28   // the devtools view is always in the hierarchy even if it is not open or it
29   // is detached.
30   NSView* devtools_view = [[[view superview] superview] superview];
31   if (devtools_view)
32     view = devtools_view;
33   return [view convertRect:[view bounds] toView:nil];
36 }  // namespace
38 // An invisible overlay window placed on top of the sheet's parent view.
39 // This window blocks interaction with the underlying view.
40 @interface CWSheetOverlayWindow : NSWindow {
41   base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
43 @end
45 @interface ConstrainedWindowSheetController ()
46 - (id)initWithParentWindow:(NSWindow*)parentWindow;
47 - (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView;
48 - (ConstrainedWindowSheetInfo*)
49     findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet;
50 - (void)onParentWindowWillClose:(NSNotification*)note;
51 - (void)onParentWindowSizeDidChange:(NSNotification*)note;
52 - (void)updateSheetPosition:(NSView*)parentView;
53 - (NSRect)overlayWindowFrameForParentView:(NSView*)parentView;
54 - (NSPoint)originForSheetSize:(NSSize)sheetSize
55               inContainerRect:(NSRect)containerRect;
56 - (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow;
57 - (void)closeSheet:(ConstrainedWindowSheetInfo*)info
58      withAnimation:(BOOL)withAnimation;
59 @end
61 @implementation CWSheetOverlayWindow
63 - (id)initWithContentRect:(NSRect)rect
64                controller:(ConstrainedWindowSheetController*)controller {
65   if ((self = [super initWithContentRect:rect
66                                styleMask:NSBorderlessWindowMask
67                                  backing:NSBackingStoreBuffered
68                                    defer:NO])) {
69     [self setOpaque:NO];
70     [self setBackgroundColor:[NSColor clearColor]];
71     [self setIgnoresMouseEvents:NO];
72     [self setReleasedWhenClosed:NO];
73     controller_.reset([controller retain]);
74   }
75   return self;
78 - (void)mouseDown:(NSEvent*)event {
79   [controller_ onOverlayWindowMouseDown:self];
82 @end
84 @implementation ConstrainedWindowSheetController
86 + (ConstrainedWindowSheetController*)
87     controllerForParentWindow:(NSWindow*)parentWindow {
88   DCHECK(parentWindow);
89   ConstrainedWindowSheetController* controller =
90       [g_sheetControllers objectForKey:GetKeyForParentWindow(parentWindow)];
91   if (controller)
92     return controller;
94   base::scoped_nsobject<ConstrainedWindowSheetController> new_controller(
95       [[ConstrainedWindowSheetController alloc]
96           initWithParentWindow:parentWindow]);
97   if (!g_sheetControllers)
98     g_sheetControllers = [[NSMutableDictionary alloc] init];
99   [g_sheetControllers setObject:new_controller
100                          forKey:GetKeyForParentWindow(parentWindow)];
101   return new_controller;
104 + (ConstrainedWindowSheetController*)
105     controllerForSheet:(id<ConstrainedWindowSheet>)sheet {
106   for (ConstrainedWindowSheetController* controller in
107        [g_sheetControllers objectEnumerator]) {
108     if ([controller findSheetInfoForSheet:sheet])
109       return controller;
110   }
111   return nil;
114 + (id<ConstrainedWindowSheet>)sheetForOverlayWindow:(NSWindow*)overlayWindow {
115   for (ConstrainedWindowSheetController* controller in
116           [g_sheetControllers objectEnumerator]) {
117     for (ConstrainedWindowSheetInfo* info in controller->sheets_.get()) {
118       if ([overlayWindow isEqual:[info overlayWindow]])
119         return [info sheet];
120     }
121   }
122   return nil;
125 - (id)initWithParentWindow:(NSWindow*)parentWindow {
126   if ((self = [super init])) {
127     parentWindow_.reset([parentWindow retain]);
128     sheets_.reset([[NSMutableArray alloc] init]);
130     [[NSNotificationCenter defaultCenter]
131         addObserver:self
132            selector:@selector(onParentWindowWillClose:)
133                name:NSWindowWillCloseNotification
134              object:parentWindow_];
135   }
136   return self;
139 - (void)showSheet:(id<ConstrainedWindowSheet>)sheet
140     forParentView:(NSView*)parentView {
141   DCHECK(sheet);
142   DCHECK(parentView);
143   if (!activeView_.get())
144     activeView_.reset([parentView retain]);
146   // Observe the parent window's size.
147   [[NSNotificationCenter defaultCenter]
148       addObserver:self
149          selector:@selector(onParentWindowSizeDidChange:)
150              name:NSWindowDidResizeNotification
151            object:parentWindow_];
153   // Create an invisible overlay window.
154   NSRect rect = [self overlayWindowFrameForParentView:parentView];
155   base::scoped_nsobject<NSWindow> overlayWindow(
156       [[CWSheetOverlayWindow alloc] initWithContentRect:rect controller:self]);
157   [parentWindow_ addChildWindow:overlayWindow
158                         ordered:NSWindowAbove];
160   // Add an entry for the sheet.
161   base::scoped_nsobject<ConstrainedWindowSheetInfo> info(
162       [[ConstrainedWindowSheetInfo alloc] initWithSheet:sheet
163                                              parentView:parentView
164                                           overlayWindow:overlayWindow]);
165   [sheets_ addObject:info];
167   // Show or hide the sheet.
168   if ([activeView_ isEqual:parentView])
169     [info showSheet];
170   else
171     [info hideSheet];
174 - (NSPoint)originForSheet:(id<ConstrainedWindowSheet>)sheet
175            withWindowSize:(NSSize)size {
176   ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
177   DCHECK(info);
178   NSRect containerRect =
179       [self overlayWindowFrameForParentView:[info parentView]];
180   return [self originForSheetSize:size inContainerRect:containerRect];
183 - (void)closeSheet:(id<ConstrainedWindowSheet>)sheet {
184   ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
185   DCHECK(info);
186   [self closeSheet:info withAnimation:YES];
189 - (void)parentViewDidBecomeActive:(NSView*)parentView {
190   [[self findSheetInfoForParentView:activeView_] hideSheet];
191   activeView_.reset([parentView retain]);
192   [self updateSheetPosition:parentView];
193   [[self findSheetInfoForParentView:activeView_] showSheet];
196 - (void)pulseSheet:(id<ConstrainedWindowSheet>)sheet {
197   ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
198   DCHECK(info);
199   if ([activeView_ isEqual:[info parentView]])
200     [[info sheet] pulseSheet];
203 - (int)sheetCount {
204   return [sheets_ count];
207 - (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView {
208   for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
209     if ([parentView isEqual:[info parentView]])
210       return info;
211   }
212   return NULL;
215 - (ConstrainedWindowSheetInfo*)
216     findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet {
217   for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
218     if ([sheet isEqual:[info sheet]])
219       return info;
220   }
221   return NULL;
224 - (void)onParentWindowWillClose:(NSNotification*)note {
225   [[NSNotificationCenter defaultCenter]
226       removeObserver:self
227                 name:NSWindowWillCloseNotification
228               object:parentWindow_];
230   // Close all sheets.
231   NSArray* sheets = [NSArray arrayWithArray:sheets_];
232   for (ConstrainedWindowSheetInfo* info in sheets)
233     [self closeSheet:info withAnimation:NO];
235   // Delete this instance.
236   [g_sheetControllers removeObjectForKey:GetKeyForParentWindow(parentWindow_)];
237   if (![g_sheetControllers count]) {
238     [g_sheetControllers release];
239     g_sheetControllers = nil;
240   }
243 - (void)onParentWindowSizeDidChange:(NSNotification*)note {
244   [self updateSheetPosition:activeView_];
247 - (void)updateSheetPosition:(NSView*)parentView {
248   ConstrainedWindowSheetInfo* info =
249       [self findSheetInfoForParentView:parentView];
250   if (!info)
251     return;
253   NSRect rect = [self overlayWindowFrameForParentView:parentView];
254   [[info overlayWindow] setFrame:rect display:YES];
255   [[info sheet] updateSheetPosition];
258 - (NSRect)overlayWindowFrameForParentView:(NSView*)parentView {
259   NSRect viewFrame = GetSheetParentBoundsForParentView(parentView);
261   id<NSWindowDelegate> delegate = [[parentView window] delegate];
262   if ([delegate respondsToSelector:@selector(window:
263                                   willPositionSheet:
264                                           usingRect:)]) {
265     NSRect sheetFrame = NSZeroRect;
266     // This API needs Y to be the distance from the bottom of the overlay to
267     // the top of the sheet. X, width, and height are ignored.
268     sheetFrame.origin.y = NSMaxY(viewFrame);
269     NSRect customSheetFrame = [delegate window:[parentView window]
270                              willPositionSheet:nil
271                                      usingRect:sheetFrame];
272     viewFrame.size.height += NSMinY(customSheetFrame) - NSMinY(sheetFrame);
273   }
275   viewFrame.origin = [[parentView window] convertBaseToScreen:viewFrame.origin];
276   return viewFrame;
279 - (NSPoint)originForSheetSize:(NSSize)sheetSize
280               inContainerRect:(NSRect)containerRect {
281   NSPoint origin;
282   origin.x = roundf(NSMinX(containerRect) +
283                     (NSWidth(containerRect) - sheetSize.width) / 2.0);
284   origin.y = NSMaxY(containerRect) + 5 - sheetSize.height;
285   return origin;
288 - (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow {
289   for (ConstrainedWindowSheetInfo* curInfo in sheets_.get()) {
290     if ([overlayWindow isEqual:[curInfo overlayWindow]]) {
291       [self pulseSheet:[curInfo sheet]];
292       [[curInfo sheet] makeSheetKeyAndOrderFront];
293       break;
294     }
295   }
298 - (void)closeSheet:(ConstrainedWindowSheetInfo*)info
299      withAnimation:(BOOL)withAnimation {
300   if (![sheets_ containsObject:info])
301     return;
303   [[NSNotificationCenter defaultCenter]
304       removeObserver:self
305                 name:NSWindowDidResizeNotification
306               object:parentWindow_];
308   [parentWindow_ removeChildWindow:[info overlayWindow]];
309   [[info sheet] closeSheetWithAnimation:withAnimation];
310   [[info overlayWindow] close];
311   [sheets_ removeObject:info];
314 @end