[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / apps / native_app_window_cocoa.mm
blobecb10c0b1b6af5a339dfe71b3c331b4a6363862f
1 // Copyright 2013 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 #include "chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h"
7 #include "apps/app_shim/extension_app_shim_handler_mac.h"
8 #include "base/command_line.h"
9 #include "base/mac/mac_util.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/cocoa/browser_window_utils.h"
13 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
14 #include "chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h"
15 #include "chrome/browser/ui/cocoa/extensions/extension_view_mac.h"
16 #import "chrome/browser/ui/cocoa/nsview_additions.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "content/public/browser/native_web_keyboard_event.h"
19 #include "content/public/browser/render_widget_host_view.h"
20 #include "content/public/browser/web_contents.h"
21 #include "extensions/common/extension.h"
22 #include "third_party/skia/include/core/SkRegion.h"
23 #include "ui/gfx/skia_util.h"
25 // NOTE: State Before Update.
27 // Internal state, such as |is_maximized_|, must be set before the window
28 // state is changed so that it is accurate when e.g. a resize results in a call
29 // to |OnNativeWindowChanged|.
31 // NOTE: Maximize and Zoom.
33 // Zooming is implemented manually in order to implement maximize functionality
34 // and to support non resizable windows. The window will be resized explicitly
35 // in the |WindowWillZoom| call.
37 // Attempting maximize and restore functionality with non resizable windows
38 // using the native zoom method did not work, even with
39 // windowWillUseStandardFrame, as the window would not restore back to the
40 // desired size.
42 using apps::AppWindow;
44 @interface NSWindow (NSPrivateApis)
45 - (void)setBottomCornerRounded:(BOOL)rounded;
46 @end
48 // Replicate specific 10.7 SDK declarations for building with prior SDKs.
49 #if !defined(MAC_OS_X_VERSION_10_7) || \
50     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
52 @interface NSWindow (LionSDKDeclarations)
53 - (void)toggleFullScreen:(id)sender;
54 @end
56 enum {
57   NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
58   NSFullScreenWindowMask = 1 << 14
61 #endif  // MAC_OS_X_VERSION_10_7
63 namespace {
65 void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) {
66   NSWindowCollectionBehavior behavior = [window collectionBehavior];
67   if (allow_fullscreen)
68     behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
69   else
70     behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
71   [window setCollectionBehavior:behavior];
74 void InitCollectionBehavior(NSWindow* window) {
75   // Since always-on-top windows have a higher window level
76   // than NSNormalWindowLevel, they will default to
77   // NSWindowCollectionBehaviorTransient. Set the value
78   // explicitly here to match normal windows.
79   NSWindowCollectionBehavior behavior = [window collectionBehavior];
80   behavior |= NSWindowCollectionBehaviorManaged;
81   [window setCollectionBehavior:behavior];
84 // Returns the level for windows that are configured to be always on top.
85 // This is not a constant because NSFloatingWindowLevel is a macro defined
86 // as a function call.
87 NSInteger AlwaysOnTopWindowLevel() {
88   return NSFloatingWindowLevel;
91 NSRect GfxToCocoaBounds(gfx::Rect bounds) {
92   typedef apps::AppWindow::BoundsSpecification BoundsSpecification;
94   NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
96   // If coordinates are unspecified, center window on primary screen.
97   if (bounds.x() == BoundsSpecification::kUnspecifiedPosition)
98     bounds.set_x(floor((NSWidth(main_screen_rect) - bounds.width()) / 2));
99   if (bounds.y() == BoundsSpecification::kUnspecifiedPosition)
100     bounds.set_y(floor((NSHeight(main_screen_rect) - bounds.height()) / 2));
102   // Convert to Mac coordinates.
103   NSRect cocoa_bounds = NSRectFromCGRect(bounds.ToCGRect());
104   cocoa_bounds.origin.y = NSHeight(main_screen_rect) - NSMaxY(cocoa_bounds);
105   return cocoa_bounds;
108 // Return a vector of non-draggable regions that fill a window of size
109 // |width| by |height|, but leave gaps where the window should be draggable.
110 std::vector<gfx::Rect> CalculateNonDraggableRegions(
111     const std::vector<extensions::DraggableRegion>& regions,
112     int width,
113     int height) {
114   std::vector<gfx::Rect> result;
115   if (regions.empty()) {
116     result.push_back(gfx::Rect(0, 0, width, height));
117   } else {
118     scoped_ptr<SkRegion> draggable(
119         AppWindow::RawDraggableRegionsToSkRegion(regions));
120     scoped_ptr<SkRegion> non_draggable(new SkRegion);
121     non_draggable->op(0, 0, width, height, SkRegion::kUnion_Op);
122     non_draggable->op(*draggable, SkRegion::kDifference_Op);
123     for (SkRegion::Iterator it(*non_draggable); !it.done(); it.next()) {
124       result.push_back(gfx::SkIRectToRect(it.rect()));
125     }
126   }
127   return result;
130 }  // namespace
132 @implementation NativeAppWindowController
134 @synthesize appWindow = appWindow_;
136 - (void)windowWillClose:(NSNotification*)notification {
137   if (appWindow_)
138     appWindow_->WindowWillClose();
141 - (void)windowDidBecomeKey:(NSNotification*)notification {
142   if (appWindow_)
143     appWindow_->WindowDidBecomeKey();
146 - (void)windowDidResignKey:(NSNotification*)notification {
147   if (appWindow_)
148     appWindow_->WindowDidResignKey();
151 - (void)windowDidResize:(NSNotification*)notification {
152   if (appWindow_)
153     appWindow_->WindowDidResize();
156 - (void)windowDidEndLiveResize:(NSNotification*)notification {
157   if (appWindow_)
158     appWindow_->WindowDidFinishResize();
161 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
162   if (appWindow_)
163     appWindow_->WindowDidEnterFullscreen();
166 - (void)windowDidExitFullScreen:(NSNotification*)notification {
167   if (appWindow_)
168     appWindow_->WindowDidExitFullscreen();
171 - (void)windowDidMove:(NSNotification*)notification {
172   if (appWindow_)
173     appWindow_->WindowDidMove();
176 - (void)windowDidMiniaturize:(NSNotification*)notification {
177   if (appWindow_)
178     appWindow_->WindowDidMiniaturize();
181 - (void)windowDidDeminiaturize:(NSNotification*)notification {
182   if (appWindow_)
183     appWindow_->WindowDidDeminiaturize();
186 - (BOOL)windowShouldZoom:(NSWindow*)window
187                  toFrame:(NSRect)newFrame {
188   if (appWindow_)
189     appWindow_->WindowWillZoom();
190   return NO;  // See top of file NOTE: Maximize and Zoom.
193 // Allow non resizable windows (without NSResizableWindowMask) to enter
194 // fullscreen by passing through the full size in willUseFullScreenContentSize.
195 - (NSSize)window:(NSWindow *)window
196     willUseFullScreenContentSize:(NSSize)proposedSize {
197   return proposedSize;
200 - (void)executeCommand:(int)command {
201   // No-op, swallow the event.
204 - (BOOL)handledByExtensionCommand:(NSEvent*)event {
205   if (appWindow_)
206     return appWindow_->HandledByExtensionCommand(event);
207   return NO;
210 @end
212 // This is really a method on NSGrayFrame, so it should only be called on the
213 // view passed into -[NSWindow drawCustomFrameRect:forView:].
214 @interface NSView (PrivateMethods)
215 - (CGFloat)roundedCornerRadius;
216 @end
218 // TODO(jamescook): Should these be AppNSWindow to match apps::AppWindow?
219 // http://crbug.com/344082
220 @interface ShellNSWindow : ChromeEventProcessingWindow
221 @end
222 @implementation ShellNSWindow
223 @end
225 @interface ShellCustomFrameNSWindow : ShellNSWindow
227 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view;
229 @end
231 @implementation ShellCustomFrameNSWindow
233 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
234   [[NSBezierPath bezierPathWithRect:rect] addClip];
235   [[NSColor clearColor] set];
236   NSRectFill(rect);
238   // Set up our clip.
239   CGFloat cornerRadius = 4.0;
240   if ([view respondsToSelector:@selector(roundedCornerRadius)])
241     cornerRadius = [view roundedCornerRadius];
242   [[NSBezierPath bezierPathWithRoundedRect:[view bounds]
243                                    xRadius:cornerRadius
244                                    yRadius:cornerRadius] addClip];
245   [[NSColor whiteColor] set];
246   NSRectFill(rect);
249 @end
251 @interface ShellFramelessNSWindow : ShellCustomFrameNSWindow
253 @end
255 @implementation ShellFramelessNSWindow
257 + (NSRect)frameRectForContentRect:(NSRect)contentRect
258                         styleMask:(NSUInteger)mask {
259   return contentRect;
262 + (NSRect)contentRectForFrameRect:(NSRect)frameRect
263                         styleMask:(NSUInteger)mask {
264   return frameRect;
267 - (NSRect)frameRectForContentRect:(NSRect)contentRect {
268   return contentRect;
271 - (NSRect)contentRectForFrameRect:(NSRect)frameRect {
272   return frameRect;
275 @end
277 @interface ControlRegionView : NSView
278 @end
280 @implementation ControlRegionView
282 - (BOOL)mouseDownCanMoveWindow {
283   return NO;
286 - (NSView*)hitTest:(NSPoint)aPoint {
287   return nil;
290 @end
292 @interface NSView (WebContentsView)
293 - (void)setMouseDownCanMoveWindow:(BOOL)can_move;
294 @end
296 NativeAppWindowCocoa::NativeAppWindowCocoa(
297     AppWindow* app_window,
298     const AppWindow::CreateParams& params)
299     : app_window_(app_window),
300       has_frame_(params.frame == AppWindow::FRAME_CHROME),
301       is_hidden_with_app_(false),
302       is_maximized_(false),
303       is_fullscreen_(false),
304       is_resizable_(params.resizable),
305       shows_resize_controls_(true),
306       shows_fullscreen_controls_(true),
307       attention_request_id_(0) {
308   Observe(WebContents());
310   base::scoped_nsobject<NSWindow> window;
311   Class window_class;
312   if (has_frame_) {
313     bool should_use_native_frame =
314         CommandLine::ForCurrentProcess()->HasSwitch(
315             switches::kAppsUseNativeFrame);
316     window_class = should_use_native_frame ?
317         [ShellNSWindow class] : [ShellCustomFrameNSWindow class];
318   } else {
319     window_class = [ShellFramelessNSWindow class];
320   }
322   // Estimate the initial bounds of the window. Once the frame insets are known,
323   // the window bounds and constraints can be set precisely.
324   NSRect cocoa_bounds = GfxToCocoaBounds(
325       params.GetInitialWindowBounds(gfx::Insets()));
326   window.reset([[window_class alloc]
327       initWithContentRect:cocoa_bounds
328                 styleMask:GetWindowStyleMask()
329                   backing:NSBackingStoreBuffered
330                     defer:NO]);
332   std::string name;
333   const extensions::Extension* extension = app_window_->GetExtension();
334   if (extension)
335     name = extension->name();
336   [window setTitle:base::SysUTF8ToNSString(name)];
337   [[window contentView] cr_setWantsLayer:YES];
339   if (base::mac::IsOSSnowLeopard() &&
340       [window respondsToSelector:@selector(setBottomCornerRounded:)])
341     [window setBottomCornerRounded:NO];
343   if (params.always_on_top)
344     [window setLevel:AlwaysOnTopWindowLevel()];
345   InitCollectionBehavior(window);
347   window_controller_.reset(
348       [[NativeAppWindowController alloc] initWithWindow:window.release()]);
350   NSView* view = WebContents()->GetNativeView();
351   [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
353   InstallView();
355   [[window_controller_ window] setDelegate:window_controller_];
356   [window_controller_ setAppWindow:this];
358   // We can now compute the precise window bounds and constraints.
359   gfx::Insets insets = GetFrameInsets();
360   SetBounds(params.GetInitialWindowBounds(insets));
361   SetContentSizeConstraints(params.GetContentMinimumSize(insets),
362                             params.GetContentMaximumSize(insets));
364   // Initialize |restored_bounds_|.
365   restored_bounds_ = [this->window() frame];
367   extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryCocoa(
368       Profile::FromBrowserContext(app_window_->browser_context()),
369       window,
370       extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
371       NULL));
374 NSUInteger NativeAppWindowCocoa::GetWindowStyleMask() const {
375   NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
376                           NSMiniaturizableWindowMask;
377   if (shows_resize_controls_)
378     style_mask |= NSResizableWindowMask;
379   if (!has_frame_ ||
380       !CommandLine::ForCurrentProcess()->HasSwitch(
381           switches::kAppsUseNativeFrame)) {
382     style_mask |= NSTexturedBackgroundWindowMask;
383   }
384   return style_mask;
387 void NativeAppWindowCocoa::InstallView() {
388   NSView* view = WebContents()->GetNativeView();
389   if (has_frame_) {
390     [view setFrame:[[window() contentView] bounds]];
391     [[window() contentView] addSubview:view];
392     if (!shows_fullscreen_controls_)
393       [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
394     if (!shows_resize_controls_)
395       [window() setShowsResizeIndicator:NO];
396   } else {
397     // TODO(jeremya): find a cleaner way to send this information to the
398     // WebContentsViewCocoa view.
399     DCHECK([view
400         respondsToSelector:@selector(setMouseDownCanMoveWindow:)]);
401     [view setMouseDownCanMoveWindow:YES];
403     NSView* frameView = [[window() contentView] superview];
404     [view setFrame:[frameView bounds]];
405     [frameView addSubview:view];
407     [[window() standardWindowButton:NSWindowZoomButton] setHidden:YES];
408     [[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
409     [[window() standardWindowButton:NSWindowCloseButton] setHidden:YES];
411     // Some third-party OS X utilities check the zoom button's enabled state to
412     // determine whether to show custom UI on hover, so we disable it here to
413     // prevent them from doing so in a frameless app window.
414     [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
416     UpdateDraggableRegionViews();
417   }
420 void NativeAppWindowCocoa::UninstallView() {
421   NSView* view = WebContents()->GetNativeView();
422   [view removeFromSuperview];
425 bool NativeAppWindowCocoa::IsActive() const {
426   return [window() isKeyWindow];
429 bool NativeAppWindowCocoa::IsMaximized() const {
430   return is_maximized_;
433 bool NativeAppWindowCocoa::IsMinimized() const {
434   return [window() isMiniaturized];
437 bool NativeAppWindowCocoa::IsFullscreen() const {
438   return is_fullscreen_;
441 void NativeAppWindowCocoa::SetFullscreen(int fullscreen_types) {
442   bool fullscreen = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE);
443   if (fullscreen == is_fullscreen_)
444     return;
445   is_fullscreen_ = fullscreen;
447   if (base::mac::IsOSLionOrLater()) {
448     // If going fullscreen, but the window is constrained (fullscreen UI control
449     // is disabled), temporarily enable it. It will be disabled again on leaving
450     // fullscreen.
451     if (fullscreen && !shows_fullscreen_controls_)
452       SetFullScreenCollectionBehavior(window(), true);
453     [window() toggleFullScreen:nil];
454     return;
455   }
457   DCHECK(base::mac::IsOSSnowLeopard());
459   // Fade to black.
460   const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
461   bool did_fade_out = false;
462   CGDisplayFadeReservationToken token;
463   if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token) ==
464       kCGErrorSuccess) {
465     did_fade_out = true;
466     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
467         kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
468   }
470   // Since frameless windows insert the WebContentsView into the NSThemeFrame
471   // ([[window contentView] superview]), and since that NSThemeFrame is
472   // destroyed and recreated when we change the styleMask of the window, we
473   // need to remove the view from the window when we change the style, and
474   // add it back afterwards.
475   UninstallView();
476   if (fullscreen) {
477     UpdateRestoredBounds();
478     [window() setStyleMask:NSBorderlessWindowMask];
479     [window() setFrame:[window()
480         frameRectForContentRect:[[window() screen] frame]]
481                display:YES];
482     base::mac::RequestFullScreen(base::mac::kFullScreenModeAutoHideAll);
483   } else {
484     base::mac::ReleaseFullScreen(base::mac::kFullScreenModeAutoHideAll);
485     [window() setStyleMask:GetWindowStyleMask()];
486     [window() setFrame:restored_bounds_ display:YES];
487   }
488   InstallView();
490   // Fade back in.
491   if (did_fade_out) {
492     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
493         kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
494     CGReleaseDisplayFadeReservation(token);
495   }
498 bool NativeAppWindowCocoa::IsFullscreenOrPending() const {
499   return is_fullscreen_;
502 bool NativeAppWindowCocoa::IsDetached() const {
503   return false;
506 gfx::NativeWindow NativeAppWindowCocoa::GetNativeWindow() {
507   return window();
510 gfx::Rect NativeAppWindowCocoa::GetRestoredBounds() const {
511   // Flip coordinates based on the primary screen.
512   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
513   NSRect frame = restored_bounds_;
514   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
515   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
516   return bounds;
519 ui::WindowShowState NativeAppWindowCocoa::GetRestoredState() const {
520   if (IsMaximized())
521     return ui::SHOW_STATE_MAXIMIZED;
522   if (IsFullscreen())
523     return ui::SHOW_STATE_FULLSCREEN;
524   return ui::SHOW_STATE_NORMAL;
527 gfx::Rect NativeAppWindowCocoa::GetBounds() const {
528   // Flip coordinates based on the primary screen.
529   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
530   NSRect frame = [window() frame];
531   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
532   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
533   return bounds;
536 void NativeAppWindowCocoa::Show() {
537   if (is_hidden_with_app_) {
538     // If there is a shim to gently request attention, return here. Otherwise
539     // show the window as usual.
540     if (apps::ExtensionAppShimHandler::RequestUserAttentionForWindow(
541             app_window_)) {
542       return;
543     }
544   }
546   [window_controller_ showWindow:nil];
547   Activate();
550 void NativeAppWindowCocoa::ShowInactive() {
551   [window() orderFront:window_controller_];
554 void NativeAppWindowCocoa::Hide() {
555   HideWithoutMarkingHidden();
558 void NativeAppWindowCocoa::Close() {
559   [window() performClose:nil];
562 void NativeAppWindowCocoa::Activate() {
563   [BrowserWindowUtils activateWindowForController:window_controller_];
566 void NativeAppWindowCocoa::Deactivate() {
567   // TODO(jcivelli): http://crbug.com/51364 Implement me.
568   NOTIMPLEMENTED();
571 void NativeAppWindowCocoa::Maximize() {
572   UpdateRestoredBounds();
573   is_maximized_ = true;  // See top of file NOTE: State Before Update.
574   [window() setFrame:[[window() screen] visibleFrame] display:YES animate:YES];
577 void NativeAppWindowCocoa::Minimize() {
578   [window() miniaturize:window_controller_];
581 void NativeAppWindowCocoa::Restore() {
582   DCHECK(!IsFullscreenOrPending());   // SetFullscreen, not Restore, expected.
584   if (IsMaximized()) {
585     is_maximized_ = false;  // See top of file NOTE: State Before Update.
586     [window() setFrame:restored_bounds() display:YES animate:YES];
587   } else if (IsMinimized()) {
588     is_maximized_ = false;  // See top of file NOTE: State Before Update.
589     [window() deminiaturize:window_controller_];
590   }
593 void NativeAppWindowCocoa::SetBounds(const gfx::Rect& bounds) {
594   // Enforce minimum/maximum bounds.
595   gfx::Rect checked_bounds = bounds;
597   NSSize min_size = [window() minSize];
598   if (bounds.width() < min_size.width)
599     checked_bounds.set_width(min_size.width);
600   if (bounds.height() < min_size.height)
601     checked_bounds.set_height(min_size.height);
602   NSSize max_size = [window() maxSize];
603   if (checked_bounds.width() > max_size.width)
604     checked_bounds.set_width(max_size.width);
605   if (checked_bounds.height() > max_size.height)
606     checked_bounds.set_height(max_size.height);
608   NSRect cocoa_bounds = GfxToCocoaBounds(checked_bounds);
609   [window() setFrame:cocoa_bounds display:YES];
610   // setFrame: without animate: does not trigger a windowDidEndLiveResize: so
611   // call it here.
612   WindowDidFinishResize();
615 void NativeAppWindowCocoa::UpdateWindowIcon() {
616   // TODO(junmin): implement.
619 void NativeAppWindowCocoa::UpdateWindowTitle() {
620   base::string16 title = app_window_->GetTitle();
621   [window() setTitle:base::SysUTF16ToNSString(title)];
624 void NativeAppWindowCocoa::UpdateBadgeIcon() {
625   // TODO(benwells): implement.
626   NOTIMPLEMENTED();
629 void NativeAppWindowCocoa::UpdateShape(scoped_ptr<SkRegion> region) {
630   NOTIMPLEMENTED();
633 void NativeAppWindowCocoa::UpdateDraggableRegions(
634     const std::vector<extensions::DraggableRegion>& regions) {
635   // Draggable region is not supported for non-frameless window.
636   if (has_frame_)
637     return;
639   draggable_regions_ = regions;
640   UpdateDraggableRegionViews();
643 SkRegion* NativeAppWindowCocoa::GetDraggableRegion() {
644   return NULL;
647 void NativeAppWindowCocoa::HandleKeyboardEvent(
648     const content::NativeWebKeyboardEvent& event) {
649   if (event.skip_in_browser ||
650       event.type == content::NativeWebKeyboardEvent::Char) {
651     return;
652   }
653   [window() redispatchKeyEvent:event.os_event];
656 void NativeAppWindowCocoa::UpdateDraggableRegionViews() {
657   if (has_frame_)
658     return;
660   // All ControlRegionViews should be added as children of the WebContentsView,
661   // because WebContentsView will be removed and re-added when entering and
662   // leaving fullscreen mode.
663   NSView* webView = WebContents()->GetNativeView();
664   NSInteger webViewWidth = NSWidth([webView bounds]);
665   NSInteger webViewHeight = NSHeight([webView bounds]);
667   // Remove all ControlRegionViews that are added last time.
668   // Note that [webView subviews] returns the view's mutable internal array and
669   // it should be copied to avoid mutating the original array while enumerating
670   // it.
671   base::scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
672   for (NSView* subview in subviews.get())
673     if ([subview isKindOfClass:[ControlRegionView class]])
674       [subview removeFromSuperview];
676   // Draggable regions is implemented by having the whole web view draggable
677   // (mouseDownCanMoveWindow) and overlaying regions that are not draggable.
678   std::vector<gfx::Rect> system_drag_exclude_areas =
679       CalculateNonDraggableRegions(
680           draggable_regions_, webViewWidth, webViewHeight);
682   // Create and add a ControlRegionView for each region that needs to be
683   // excluded from the dragging.
684   for (std::vector<gfx::Rect>::const_iterator iter =
685            system_drag_exclude_areas.begin();
686        iter != system_drag_exclude_areas.end();
687        ++iter) {
688     base::scoped_nsobject<NSView> controlRegion(
689         [[ControlRegionView alloc] initWithFrame:NSZeroRect]);
690     [controlRegion setFrame:NSMakeRect(iter->x(),
691                                        webViewHeight - iter->bottom(),
692                                        iter->width(),
693                                        iter->height())];
694     [webView addSubview:controlRegion];
695   }
698 void NativeAppWindowCocoa::FlashFrame(bool flash) {
699   if (flash) {
700     attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
701   } else {
702     [NSApp cancelUserAttentionRequest:attention_request_id_];
703     attention_request_id_ = 0;
704   }
707 bool NativeAppWindowCocoa::IsAlwaysOnTop() const {
708   return [window() level] == AlwaysOnTopWindowLevel();
711 void NativeAppWindowCocoa::RenderViewCreated(content::RenderViewHost* rvh) {
712   if (IsActive())
713     WebContents()->RestoreFocus();
716 bool NativeAppWindowCocoa::IsFrameless() const {
717   return !has_frame_;
720 bool NativeAppWindowCocoa::HasFrameColor() const {
721   // TODO(benwells): Implement this.
722   return false;
725 SkColor NativeAppWindowCocoa::ActiveFrameColor() const {
726   // TODO(benwells): Implement this.
727   return SkColor();
730 SkColor NativeAppWindowCocoa::InactiveFrameColor() const {
731   // TODO(benwells): Implement this.
732   return SkColor();
735 gfx::Insets NativeAppWindowCocoa::GetFrameInsets() const {
736   if (!has_frame_)
737     return gfx::Insets();
739   // Flip the coordinates based on the main screen.
740   NSInteger screen_height =
741       NSHeight([[[NSScreen screens] objectAtIndex:0] frame]);
743   NSRect frame_nsrect = [window() frame];
744   gfx::Rect frame_rect(NSRectToCGRect(frame_nsrect));
745   frame_rect.set_y(screen_height - NSMaxY(frame_nsrect));
747   NSRect content_nsrect = [window() contentRectForFrameRect:frame_nsrect];
748   gfx::Rect content_rect(NSRectToCGRect(content_nsrect));
749   content_rect.set_y(screen_height - NSMaxY(content_nsrect));
751   return frame_rect.InsetsFrom(content_rect);
754 gfx::NativeView NativeAppWindowCocoa::GetHostView() const {
755   NOTIMPLEMENTED();
756   return NULL;
759 gfx::Point NativeAppWindowCocoa::GetDialogPosition(const gfx::Size& size) {
760   NOTIMPLEMENTED();
761   return gfx::Point();
764 gfx::Size NativeAppWindowCocoa::GetMaximumDialogSize() {
765   NOTIMPLEMENTED();
766   return gfx::Size();
769 void NativeAppWindowCocoa::AddObserver(
770     web_modal::ModalDialogHostObserver* observer) {
771   NOTIMPLEMENTED();
774 void NativeAppWindowCocoa::RemoveObserver(
775     web_modal::ModalDialogHostObserver* observer) {
776   NOTIMPLEMENTED();
779 void NativeAppWindowCocoa::WindowWillClose() {
780   [window_controller_ setAppWindow:NULL];
781   app_window_->OnNativeWindowChanged();
782   app_window_->OnNativeClose();
785 void NativeAppWindowCocoa::WindowDidBecomeKey() {
786   content::RenderWidgetHostView* rwhv =
787       WebContents()->GetRenderWidgetHostView();
788   if (rwhv)
789     rwhv->SetActive(true);
790   app_window_->OnNativeWindowActivated();
792   WebContents()->RestoreFocus();
795 void NativeAppWindowCocoa::WindowDidResignKey() {
796   // If our app is still active and we're still the key window, ignore this
797   // message, since it just means that a menu extra (on the "system status bar")
798   // was activated; we'll get another |-windowDidResignKey| if we ever really
799   // lose key window status.
800   if ([NSApp isActive] && ([NSApp keyWindow] == window()))
801     return;
803   WebContents()->StoreFocus();
805   content::RenderWidgetHostView* rwhv =
806       WebContents()->GetRenderWidgetHostView();
807   if (rwhv)
808     rwhv->SetActive(false);
811 void NativeAppWindowCocoa::WindowDidFinishResize() {
812   // Update |is_maximized_| if needed:
813   // - Exit maximized state if resized.
814   // - Consider us maximized if resize places us back to maximized location.
815   //   This happens when returning from fullscreen.
816   NSRect frame = [window() frame];
817   NSRect screen = [[window() screen] visibleFrame];
818   if (!NSEqualSizes(frame.size, screen.size))
819     is_maximized_ = false;
820   else if (NSEqualPoints(frame.origin, screen.origin))
821     is_maximized_ = true;
823   UpdateRestoredBounds();
826 void NativeAppWindowCocoa::WindowDidResize() {
827   app_window_->OnNativeWindowChanged();
828   UpdateDraggableRegionViews();
831 void NativeAppWindowCocoa::WindowDidMove() {
832   UpdateRestoredBounds();
833   app_window_->OnNativeWindowChanged();
836 void NativeAppWindowCocoa::WindowDidMiniaturize() {
837   app_window_->OnNativeWindowChanged();
840 void NativeAppWindowCocoa::WindowDidDeminiaturize() {
841   app_window_->OnNativeWindowChanged();
844 void NativeAppWindowCocoa::WindowDidEnterFullscreen() {
845   is_fullscreen_ = true;
846   app_window_->OSFullscreen();
847   app_window_->OnNativeWindowChanged();
850 void NativeAppWindowCocoa::WindowDidExitFullscreen() {
851   is_fullscreen_ = false;
852   if (!shows_fullscreen_controls_)
853     SetFullScreenCollectionBehavior(window(), false);
855   app_window_->Restore();
856   app_window_->OnNativeWindowChanged();
859 void NativeAppWindowCocoa::WindowWillZoom() {
860   // See top of file NOTE: Maximize and Zoom.
861   if (IsMaximized())
862     Restore();
863   else
864     Maximize();
867 bool NativeAppWindowCocoa::HandledByExtensionCommand(NSEvent* event) {
868   return extension_keybinding_registry_->ProcessKeyEvent(
869       content::NativeWebKeyboardEvent(event));
872 void NativeAppWindowCocoa::ShowWithApp() {
873   is_hidden_with_app_ = false;
874   if (!app_window_->is_hidden())
875     ShowInactive();
878 void NativeAppWindowCocoa::HideWithApp() {
879   is_hidden_with_app_ = true;
880   HideWithoutMarkingHidden();
883 void NativeAppWindowCocoa::UpdateShelfMenu() {
884   // TODO(tmdiep): To be implemented for Mac.
885   NOTIMPLEMENTED();
888 gfx::Size NativeAppWindowCocoa::GetContentMinimumSize() const {
889   return size_constraints_.GetMinimumSize();
892 gfx::Size NativeAppWindowCocoa::GetContentMaximumSize() const {
893   return size_constraints_.GetMaximumSize();
896 void NativeAppWindowCocoa::SetContentSizeConstraints(
897     const gfx::Size& min_size, const gfx::Size& max_size) {
898   // Update the size constraints.
899   size_constraints_.set_minimum_size(min_size);
900   size_constraints_.set_maximum_size(max_size);
902   gfx::Size minimum_size = size_constraints_.GetMinimumSize();
903   [window() setContentMinSize:NSMakeSize(minimum_size.width(),
904                                          minimum_size.height())];
906   gfx::Size maximum_size = size_constraints_.GetMaximumSize();
907   const int kUnboundedSize = apps::SizeConstraints::kUnboundedSize;
908   CGFloat max_width = maximum_size.width() == kUnboundedSize ?
909       CGFLOAT_MAX : maximum_size.width();
910   CGFloat max_height = maximum_size.height() == kUnboundedSize ?
911       CGFLOAT_MAX : maximum_size.height();
912   [window() setContentMaxSize:NSMakeSize(max_width, max_height)];
914   // Update the window controls.
915   shows_resize_controls_ =
916       is_resizable_ && !size_constraints_.HasFixedSize();
917   shows_fullscreen_controls_ =
918       is_resizable_ && !size_constraints_.HasMaximumSize() && has_frame_;
920   if (!is_fullscreen_) {
921     [window() setStyleMask:GetWindowStyleMask()];
923     // Set the window to participate in Lion Fullscreen mode. Setting this flag
924     // has no effect on Snow Leopard or earlier. UI controls for fullscreen are
925     // only shown for apps that have unbounded size.
926     SetFullScreenCollectionBehavior(window(), shows_fullscreen_controls_);
927   }
929   if (has_frame_) {
930     [window() setShowsResizeIndicator:shows_resize_controls_];
931     [[window() standardWindowButton:NSWindowZoomButton]
932         setEnabled:shows_fullscreen_controls_];
933   }
936 void NativeAppWindowCocoa::SetAlwaysOnTop(bool always_on_top) {
937   [window() setLevel:(always_on_top ? AlwaysOnTopWindowLevel() :
938                                       NSNormalWindowLevel)];
941 NativeAppWindowCocoa::~NativeAppWindowCocoa() {
944 ShellNSWindow* NativeAppWindowCocoa::window() const {
945   NSWindow* window = [window_controller_ window];
946   CHECK(!window || [window isKindOfClass:[ShellNSWindow class]]);
947   return static_cast<ShellNSWindow*>(window);
950 content::WebContents* NativeAppWindowCocoa::WebContents() const {
951   return app_window_->web_contents();
954 void NativeAppWindowCocoa::UpdateRestoredBounds() {
955   if (IsRestored(*this))
956     restored_bounds_ = [window() frame];
959 void NativeAppWindowCocoa::HideWithoutMarkingHidden() {
960   [window() orderOut:window_controller_];