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 "base/command_line.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/mac/mac_util.h"
10 #include "base/mac/sdk_forward_declarations.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/cocoa/browser_window_utils.h"
15 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
16 #import "chrome/browser/ui/cocoa/custom_frame_view.h"
17 #include "chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h"
18 #include "chrome/browser/ui/cocoa/extensions/extension_view_mac.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "content/public/browser/native_web_keyboard_event.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/web_contents.h"
23 #include "extensions/common/extension.h"
24 #include "skia/ext/skia_utils_mac.h"
25 #include "third_party/skia/include/core/SkRegion.h"
26 #include "ui/gfx/skia_util.h"
28 // NOTE: State Before Update.
30 // Internal state, such as |is_maximized_|, must be set before the window
31 // state is changed so that it is accurate when e.g. a resize results in a call
32 // to |OnNativeWindowChanged|.
34 // NOTE: Maximize and Zoom.
36 // Zooming is implemented manually in order to implement maximize functionality
37 // and to support non resizable windows. The window will be resized explicitly
38 // in the |WindowWillZoom| call.
40 // Attempting maximize and restore functionality with non resizable windows
41 // using the native zoom method did not work, even with
42 // windowWillUseStandardFrame, as the window would not restore back to the
45 using extensions::AppWindow;
47 @interface NSWindow (NSPrivateApis)
48 - (void)setBottomCornerRounded:(BOOL)rounded;
49 - (BOOL)_isTitleHidden;
54 void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) {
55 NSWindowCollectionBehavior behavior = [window collectionBehavior];
57 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
59 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
60 [window setCollectionBehavior:behavior];
63 void SetWorkspacesCollectionBehavior(NSWindow* window, bool always_visible) {
64 NSWindowCollectionBehavior behavior = [window collectionBehavior];
66 behavior |= NSWindowCollectionBehaviorCanJoinAllSpaces;
68 behavior &= ~NSWindowCollectionBehaviorCanJoinAllSpaces;
69 [window setCollectionBehavior:behavior];
72 void InitCollectionBehavior(NSWindow* window) {
73 // Since always-on-top windows have a higher window level
74 // than NSNormalWindowLevel, they will default to
75 // NSWindowCollectionBehaviorTransient. Set the value
76 // explicitly here to match normal windows.
77 NSWindowCollectionBehavior behavior = [window collectionBehavior];
78 behavior |= NSWindowCollectionBehaviorManaged;
79 [window setCollectionBehavior:behavior];
82 // Returns the level for windows that are configured to be always on top.
83 // This is not a constant because NSFloatingWindowLevel is a macro defined
84 // as a function call.
85 NSInteger AlwaysOnTopWindowLevel() {
86 return NSFloatingWindowLevel;
89 NSRect GfxToCocoaBounds(gfx::Rect bounds) {
90 typedef AppWindow::BoundsSpecification BoundsSpecification;
92 NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
94 // If coordinates are unspecified, center window on primary screen.
95 if (bounds.x() == BoundsSpecification::kUnspecifiedPosition)
96 bounds.set_x(floor((NSWidth(main_screen_rect) - bounds.width()) / 2));
97 if (bounds.y() == BoundsSpecification::kUnspecifiedPosition)
98 bounds.set_y(floor((NSHeight(main_screen_rect) - bounds.height()) / 2));
100 // Convert to Mac coordinates.
101 NSRect cocoa_bounds = NSRectFromCGRect(bounds.ToCGRect());
102 cocoa_bounds.origin.y = NSHeight(main_screen_rect) - NSMaxY(cocoa_bounds);
106 // Return a vector of non-draggable regions that fill a window of size
107 // |width| by |height|, but leave gaps where the window should be draggable.
108 std::vector<gfx::Rect> CalculateNonDraggableRegions(
109 const std::vector<extensions::DraggableRegion>& regions,
112 std::vector<gfx::Rect> result;
113 if (regions.empty()) {
114 result.push_back(gfx::Rect(0, 0, width, height));
116 scoped_ptr<SkRegion> draggable(
117 AppWindow::RawDraggableRegionsToSkRegion(regions));
118 scoped_ptr<SkRegion> non_draggable(new SkRegion);
119 non_draggable->op(0, 0, width, height, SkRegion::kUnion_Op);
120 non_draggable->op(*draggable, SkRegion::kDifference_Op);
121 for (SkRegion::Iterator it(*non_draggable); !it.done(); it.next()) {
122 result.push_back(gfx::SkIRectToRect(it.rect()));
130 @implementation NativeAppWindowController
132 @synthesize appWindow = appWindow_;
134 - (void)windowWillClose:(NSNotification*)notification {
136 appWindow_->WindowWillClose();
139 - (void)windowDidBecomeKey:(NSNotification*)notification {
141 appWindow_->WindowDidBecomeKey();
144 - (void)windowDidResignKey:(NSNotification*)notification {
146 appWindow_->WindowDidResignKey();
149 - (void)windowDidResize:(NSNotification*)notification {
151 appWindow_->WindowDidResize();
154 - (void)windowDidEndLiveResize:(NSNotification*)notification {
156 appWindow_->WindowDidFinishResize();
159 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
161 appWindow_->WindowDidEnterFullscreen();
164 - (void)windowDidExitFullScreen:(NSNotification*)notification {
166 appWindow_->WindowDidExitFullscreen();
169 - (void)windowDidMove:(NSNotification*)notification {
171 appWindow_->WindowDidMove();
174 - (void)windowDidMiniaturize:(NSNotification*)notification {
176 appWindow_->WindowDidMiniaturize();
179 - (void)windowDidDeminiaturize:(NSNotification*)notification {
181 appWindow_->WindowDidDeminiaturize();
184 - (BOOL)windowShouldZoom:(NSWindow*)window
185 toFrame:(NSRect)newFrame {
187 appWindow_->WindowWillZoom();
188 return NO; // See top of file NOTE: Maximize and Zoom.
191 // Allow non resizable windows (without NSResizableWindowMask) to enter
192 // fullscreen by passing through the full size in willUseFullScreenContentSize.
193 - (NSSize)window:(NSWindow *)window
194 willUseFullScreenContentSize:(NSSize)proposedSize {
198 - (void)executeCommand:(int)command {
199 // No-op, swallow the event.
202 - (BOOL)handledByExtensionCommand:(NSEvent*)event
203 priority:(ui::AcceleratorManager::HandlerPriority)priority {
205 return appWindow_->HandledByExtensionCommand(event, priority);
211 // This is really a method on NSGrayFrame, so it should only be called on the
212 // view passed into -[NSWindow drawCustomFrameRect:forView:].
213 @interface NSView (PrivateMethods)
214 - (CGFloat)roundedCornerRadius;
217 // TODO(jamescook): Should these be AppNSWindow to match AppWindow?
218 // http://crbug.com/344082
219 @interface ShellNSWindow : ChromeEventProcessingWindow
221 @implementation ShellNSWindow
223 // Similar to ChromeBrowserWindow, don't draw the title, but allow it to be seen
224 // in menus, Expose, etc.
225 - (BOOL)_isTitleHidden {
229 - (void)drawCustomFrameRect:(NSRect)frameRect forView:(NSView*)view {
230 // Make the background color of the content area white. We can't just call
231 // -setBackgroundColor as that causes the title bar to be drawn in a solid
233 NSRect rect = [self contentRectForFrameRect:frameRect];
234 [[NSColor whiteColor] set];
237 // Draw the native title bar. We remove the content area since the native
238 // implementation draws a gray background.
239 rect.origin.y = NSMaxY(rect);
240 rect.size.height = CGFLOAT_MAX;
241 rect = NSIntersectionRect(rect, frameRect);
243 [NSBezierPath clipRect:rect];
244 [super drawCustomFrameRect:frameRect
250 @interface ShellCustomFrameNSWindow : ShellNSWindow {
252 base::scoped_nsobject<NSColor> color_;
253 base::scoped_nsobject<NSColor> inactiveColor_;
256 - (void)setColor:(NSColor*)color
257 inactiveColor:(NSColor*)inactiveColor;
261 @implementation ShellCustomFrameNSWindow
263 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
264 [[NSBezierPath bezierPathWithRect:rect] addClip];
265 [[NSColor clearColor] set];
269 CGFloat cornerRadius = 4.0;
270 if ([view respondsToSelector:@selector(roundedCornerRadius)])
271 cornerRadius = [view roundedCornerRadius];
272 [[NSBezierPath bezierPathWithRoundedRect:[view bounds]
274 yRadius:cornerRadius] addClip];
275 if ([self isMainWindow] || [self isKeyWindow])
278 [inactiveColor_ set];
282 - (void)setColor:(NSColor*)color
283 inactiveColor:(NSColor*)inactiveColor {
284 color_.reset([color retain]);
285 inactiveColor_.reset([inactiveColor retain]);
290 @interface ShellFramelessNSWindow : ShellNSWindow
293 @implementation ShellFramelessNSWindow
295 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {}
297 + (NSRect)frameRectForContentRect:(NSRect)contentRect
298 styleMask:(NSUInteger)mask {
302 + (NSRect)contentRectForFrameRect:(NSRect)frameRect
303 styleMask:(NSUInteger)mask {
307 - (NSRect)frameRectForContentRect:(NSRect)contentRect {
311 - (NSRect)contentRectForFrameRect:(NSRect)frameRect {
317 @interface ControlRegionView : NSView
320 @implementation ControlRegionView
322 - (BOOL)mouseDownCanMoveWindow {
326 - (NSView*)hitTest:(NSPoint)aPoint {
332 @interface NSView (WebContentsView)
333 - (void)setMouseDownCanMoveWindow:(BOOL)can_move;
336 NativeAppWindowCocoa::NativeAppWindowCocoa(
337 AppWindow* app_window,
338 const AppWindow::CreateParams& params)
339 : app_window_(app_window),
340 has_frame_(params.frame == AppWindow::FRAME_CHROME),
341 is_hidden_with_app_(false),
342 is_maximized_(false),
343 is_fullscreen_(false),
344 is_resizable_(params.resizable),
345 shows_resize_controls_(true),
346 shows_fullscreen_controls_(true),
347 has_frame_color_(params.has_frame_color),
348 active_frame_color_(params.active_frame_color),
349 inactive_frame_color_(params.inactive_frame_color) {
350 Observe(WebContents());
352 base::scoped_nsobject<NSWindow> window;
355 window_class = has_frame_color_ ?
356 [ShellCustomFrameNSWindow class] : [ShellNSWindow class];
358 window_class = [ShellFramelessNSWindow class];
361 // Estimate the initial bounds of the window. Once the frame insets are known,
362 // the window bounds and constraints can be set precisely.
363 NSRect cocoa_bounds = GfxToCocoaBounds(
364 params.GetInitialWindowBounds(gfx::Insets()));
365 window.reset([[window_class alloc]
366 initWithContentRect:cocoa_bounds
367 styleMask:GetWindowStyleMask()
368 backing:NSBackingStoreBuffered
372 const extensions::Extension* extension = app_window_->GetExtension();
374 name = extension->name();
375 [window setTitle:base::SysUTF8ToNSString(name)];
376 [[window contentView] setWantsLayer:YES];
377 if (has_frame_ && has_frame_color_) {
378 [base::mac::ObjCCastStrict<ShellCustomFrameNSWindow>(window)
379 setColor:gfx::SkColorToSRGBNSColor(active_frame_color_)
380 inactiveColor:gfx::SkColorToSRGBNSColor(inactive_frame_color_)];
383 if (base::mac::IsOSSnowLeopard() &&
384 [window respondsToSelector:@selector(setBottomCornerRounded:)])
385 [window setBottomCornerRounded:NO];
387 if (params.always_on_top)
388 [window setLevel:AlwaysOnTopWindowLevel()];
389 InitCollectionBehavior(window);
391 SetWorkspacesCollectionBehavior(window, params.visible_on_all_workspaces);
393 window_controller_.reset(
394 [[NativeAppWindowController alloc] initWithWindow:window.release()]);
396 NSView* view = WebContents()->GetNativeView();
397 [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
401 [[window_controller_ window] setDelegate:window_controller_];
402 [window_controller_ setAppWindow:this];
404 // We can now compute the precise window bounds and constraints.
405 gfx::Insets insets = GetFrameInsets();
406 SetBounds(params.GetInitialWindowBounds(insets));
407 SetContentSizeConstraints(params.GetContentMinimumSize(insets),
408 params.GetContentMaximumSize(insets));
410 // Initialize |restored_bounds_|.
411 restored_bounds_ = [this->window() frame];
413 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryCocoa(
414 Profile::FromBrowserContext(app_window_->browser_context()),
416 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
420 NSUInteger NativeAppWindowCocoa::GetWindowStyleMask() const {
421 NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
422 NSMiniaturizableWindowMask |
423 NSTexturedBackgroundWindowMask;
424 if (shows_resize_controls_)
425 style_mask |= NSResizableWindowMask;
429 void NativeAppWindowCocoa::InstallView() {
430 NSView* view = WebContents()->GetNativeView();
432 [view setFrame:[[window() contentView] bounds]];
433 [[window() contentView] addSubview:view];
434 if (!shows_fullscreen_controls_)
435 [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
436 if (!shows_resize_controls_)
437 [window() setShowsResizeIndicator:NO];
439 // TODO(jeremya): find a cleaner way to send this information to the
440 // WebContentsViewCocoa view.
442 respondsToSelector:@selector(setMouseDownCanMoveWindow:)]);
443 [view setMouseDownCanMoveWindow:YES];
445 NSView* frameView = [[window() contentView] superview];
446 [view setFrame:[frameView bounds]];
447 [frameView addSubview:view];
449 [[window() standardWindowButton:NSWindowZoomButton] setHidden:YES];
450 [[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
451 [[window() standardWindowButton:NSWindowCloseButton] setHidden:YES];
453 // Some third-party OS X utilities check the zoom button's enabled state to
454 // determine whether to show custom UI on hover, so we disable it here to
455 // prevent them from doing so in a frameless app window.
456 [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
458 UpdateDraggableRegionViews();
462 void NativeAppWindowCocoa::UninstallView() {
463 NSView* view = WebContents()->GetNativeView();
464 [view removeFromSuperview];
467 bool NativeAppWindowCocoa::IsActive() const {
468 return [window() isKeyWindow];
471 bool NativeAppWindowCocoa::IsMaximized() const {
472 return is_maximized_;
475 bool NativeAppWindowCocoa::IsMinimized() const {
476 return [window() isMiniaturized];
479 bool NativeAppWindowCocoa::IsFullscreen() const {
480 return is_fullscreen_;
483 void NativeAppWindowCocoa::SetFullscreen(int fullscreen_types) {
484 bool fullscreen = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE);
485 if (fullscreen == is_fullscreen_)
487 is_fullscreen_ = fullscreen;
489 if (base::mac::IsOSLionOrLater()) {
490 // If going fullscreen, but the window is constrained (fullscreen UI control
491 // is disabled), temporarily enable it. It will be disabled again on leaving
493 if (fullscreen && !shows_fullscreen_controls_)
494 SetFullScreenCollectionBehavior(window(), true);
495 [window() toggleFullScreen:nil];
499 DCHECK(base::mac::IsOSSnowLeopard());
502 const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
503 bool did_fade_out = false;
504 CGDisplayFadeReservationToken token;
505 if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token) ==
508 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
509 kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
512 // Since frameless windows insert the WebContentsView into the NSThemeFrame
513 // ([[window contentView] superview]), and since that NSThemeFrame is
514 // destroyed and recreated when we change the styleMask of the window, we
515 // need to remove the view from the window when we change the style, and
516 // add it back afterwards.
519 UpdateRestoredBounds();
520 [window() setStyleMask:NSBorderlessWindowMask];
521 [window() setFrame:[window()
522 frameRectForContentRect:[[window() screen] frame]]
524 base::mac::RequestFullScreen(base::mac::kFullScreenModeAutoHideAll);
526 base::mac::ReleaseFullScreen(base::mac::kFullScreenModeAutoHideAll);
527 [window() setStyleMask:GetWindowStyleMask()];
528 [window() setFrame:restored_bounds_ display:YES];
534 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
535 kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
536 CGReleaseDisplayFadeReservation(token);
540 bool NativeAppWindowCocoa::IsFullscreenOrPending() const {
541 return is_fullscreen_;
544 gfx::NativeWindow NativeAppWindowCocoa::GetNativeWindow() const {
548 gfx::Rect NativeAppWindowCocoa::GetRestoredBounds() const {
549 // Flip coordinates based on the primary screen.
550 NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
551 NSRect frame = restored_bounds_;
552 gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
553 bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
557 ui::WindowShowState NativeAppWindowCocoa::GetRestoredState() const {
559 return ui::SHOW_STATE_MAXIMIZED;
561 return ui::SHOW_STATE_FULLSCREEN;
562 return ui::SHOW_STATE_NORMAL;
565 gfx::Rect NativeAppWindowCocoa::GetBounds() const {
566 // Flip coordinates based on the primary screen.
567 NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
568 NSRect frame = [window() frame];
569 gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
570 bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
574 void NativeAppWindowCocoa::Show() {
575 if (is_hidden_with_app_) {
576 // If there is a shim to gently request attention, return here. Otherwise
577 // show the window as usual.
578 if (apps::ExtensionAppShimHandler::ActivateAndRequestUserAttentionForWindow(
584 [window_controller_ showWindow:nil];
588 void NativeAppWindowCocoa::ShowInactive() {
589 [window() orderFront:window_controller_];
592 void NativeAppWindowCocoa::Hide() {
593 HideWithoutMarkingHidden();
596 void NativeAppWindowCocoa::Close() {
600 void NativeAppWindowCocoa::Activate() {
601 [BrowserWindowUtils activateWindowForController:window_controller_];
604 void NativeAppWindowCocoa::Deactivate() {
605 // TODO(jcivelli): http://crbug.com/51364 Implement me.
609 void NativeAppWindowCocoa::Maximize() {
610 UpdateRestoredBounds();
611 is_maximized_ = true; // See top of file NOTE: State Before Update.
612 [window() setFrame:[[window() screen] visibleFrame] display:YES animate:YES];
615 void NativeAppWindowCocoa::Minimize() {
616 [window() miniaturize:window_controller_];
619 void NativeAppWindowCocoa::Restore() {
620 DCHECK(!IsFullscreenOrPending()); // SetFullscreen, not Restore, expected.
623 is_maximized_ = false; // See top of file NOTE: State Before Update.
624 [window() setFrame:restored_bounds() display:YES animate:YES];
625 } else if (IsMinimized()) {
626 is_maximized_ = false; // See top of file NOTE: State Before Update.
627 [window() deminiaturize:window_controller_];
631 void NativeAppWindowCocoa::SetBounds(const gfx::Rect& bounds) {
632 // Enforce minimum/maximum bounds.
633 gfx::Rect checked_bounds = bounds;
635 NSSize min_size = [window() minSize];
636 if (bounds.width() < min_size.width)
637 checked_bounds.set_width(min_size.width);
638 if (bounds.height() < min_size.height)
639 checked_bounds.set_height(min_size.height);
640 NSSize max_size = [window() maxSize];
641 if (checked_bounds.width() > max_size.width)
642 checked_bounds.set_width(max_size.width);
643 if (checked_bounds.height() > max_size.height)
644 checked_bounds.set_height(max_size.height);
646 NSRect cocoa_bounds = GfxToCocoaBounds(checked_bounds);
647 [window() setFrame:cocoa_bounds display:YES];
648 // setFrame: without animate: does not trigger a windowDidEndLiveResize: so
650 WindowDidFinishResize();
653 void NativeAppWindowCocoa::UpdateWindowIcon() {
654 // TODO(junmin): implement.
657 void NativeAppWindowCocoa::UpdateWindowTitle() {
658 base::string16 title = app_window_->GetTitle();
659 [window() setTitle:base::SysUTF16ToNSString(title)];
662 void NativeAppWindowCocoa::UpdateBadgeIcon() {
663 // TODO(benwells): implement.
667 void NativeAppWindowCocoa::UpdateShape(scoped_ptr<SkRegion> region) {
671 void NativeAppWindowCocoa::UpdateDraggableRegions(
672 const std::vector<extensions::DraggableRegion>& regions) {
673 // Draggable region is not supported for non-frameless window.
677 draggable_regions_ = regions;
678 UpdateDraggableRegionViews();
681 SkRegion* NativeAppWindowCocoa::GetDraggableRegion() {
685 void NativeAppWindowCocoa::HandleKeyboardEvent(
686 const content::NativeWebKeyboardEvent& event) {
687 if (event.skip_in_browser ||
688 event.type == content::NativeWebKeyboardEvent::Char) {
691 [window() redispatchKeyEvent:event.os_event];
694 void NativeAppWindowCocoa::UpdateDraggableRegionViews() {
698 // All ControlRegionViews should be added as children of the WebContentsView,
699 // because WebContentsView will be removed and re-added when entering and
700 // leaving fullscreen mode.
701 NSView* webView = WebContents()->GetNativeView();
702 NSInteger webViewWidth = NSWidth([webView bounds]);
703 NSInteger webViewHeight = NSHeight([webView bounds]);
705 // Remove all ControlRegionViews that are added last time.
706 // Note that [webView subviews] returns the view's mutable internal array and
707 // it should be copied to avoid mutating the original array while enumerating
709 base::scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
710 for (NSView* subview in subviews.get())
711 if ([subview isKindOfClass:[ControlRegionView class]])
712 [subview removeFromSuperview];
714 // Draggable regions is implemented by having the whole web view draggable
715 // (mouseDownCanMoveWindow) and overlaying regions that are not draggable.
716 std::vector<gfx::Rect> system_drag_exclude_areas =
717 CalculateNonDraggableRegions(
718 draggable_regions_, webViewWidth, webViewHeight);
720 // Create and add a ControlRegionView for each region that needs to be
721 // excluded from the dragging.
722 for (std::vector<gfx::Rect>::const_iterator iter =
723 system_drag_exclude_areas.begin();
724 iter != system_drag_exclude_areas.end();
726 base::scoped_nsobject<NSView> controlRegion(
727 [[ControlRegionView alloc] initWithFrame:NSZeroRect]);
728 [controlRegion setFrame:NSMakeRect(iter->x(),
729 webViewHeight - iter->bottom(),
732 [webView addSubview:controlRegion];
736 void NativeAppWindowCocoa::FlashFrame(bool flash) {
737 apps::ExtensionAppShimHandler::RequestUserAttentionForWindow(
739 flash ? apps::APP_SHIM_ATTENTION_CRITICAL
740 : apps::APP_SHIM_ATTENTION_CANCEL);
743 bool NativeAppWindowCocoa::IsAlwaysOnTop() const {
744 return [window() level] == AlwaysOnTopWindowLevel();
747 void NativeAppWindowCocoa::RenderViewCreated(content::RenderViewHost* rvh) {
749 WebContents()->RestoreFocus();
752 bool NativeAppWindowCocoa::IsFrameless() const {
756 bool NativeAppWindowCocoa::HasFrameColor() const {
757 return has_frame_color_;
760 SkColor NativeAppWindowCocoa::ActiveFrameColor() const {
761 return active_frame_color_;
764 SkColor NativeAppWindowCocoa::InactiveFrameColor() const {
765 return inactive_frame_color_;
768 gfx::Insets NativeAppWindowCocoa::GetFrameInsets() const {
770 return gfx::Insets();
772 // Flip the coordinates based on the main screen.
773 NSInteger screen_height =
774 NSHeight([[[NSScreen screens] objectAtIndex:0] frame]);
776 NSRect frame_nsrect = [window() frame];
777 gfx::Rect frame_rect(NSRectToCGRect(frame_nsrect));
778 frame_rect.set_y(screen_height - NSMaxY(frame_nsrect));
780 NSRect content_nsrect = [window() contentRectForFrameRect:frame_nsrect];
781 gfx::Rect content_rect(NSRectToCGRect(content_nsrect));
782 content_rect.set_y(screen_height - NSMaxY(content_nsrect));
784 return frame_rect.InsetsFrom(content_rect);
787 bool NativeAppWindowCocoa::CanHaveAlphaEnabled() const {
791 gfx::NativeView NativeAppWindowCocoa::GetHostView() const {
796 gfx::Point NativeAppWindowCocoa::GetDialogPosition(const gfx::Size& size) {
801 gfx::Size NativeAppWindowCocoa::GetMaximumDialogSize() {
806 void NativeAppWindowCocoa::AddObserver(
807 web_modal::ModalDialogHostObserver* observer) {
811 void NativeAppWindowCocoa::RemoveObserver(
812 web_modal::ModalDialogHostObserver* observer) {
816 void NativeAppWindowCocoa::WindowWillClose() {
817 [window_controller_ setAppWindow:NULL];
818 app_window_->OnNativeWindowChanged();
819 app_window_->OnNativeClose();
822 void NativeAppWindowCocoa::WindowDidBecomeKey() {
823 content::RenderWidgetHostView* rwhv =
824 WebContents()->GetRenderWidgetHostView();
826 rwhv->SetActive(true);
827 app_window_->OnNativeWindowActivated();
829 WebContents()->RestoreFocus();
832 void NativeAppWindowCocoa::WindowDidResignKey() {
833 // If our app is still active and we're still the key window, ignore this
834 // message, since it just means that a menu extra (on the "system status bar")
835 // was activated; we'll get another |-windowDidResignKey| if we ever really
836 // lose key window status.
837 if ([NSApp isActive] && ([NSApp keyWindow] == window()))
840 WebContents()->StoreFocus();
842 content::RenderWidgetHostView* rwhv =
843 WebContents()->GetRenderWidgetHostView();
845 rwhv->SetActive(false);
848 void NativeAppWindowCocoa::WindowDidFinishResize() {
849 // Update |is_maximized_| if needed:
850 // - Exit maximized state if resized.
851 // - Consider us maximized if resize places us back to maximized location.
852 // This happens when returning from fullscreen.
853 NSRect frame = [window() frame];
854 NSRect screen = [[window() screen] visibleFrame];
855 if (!NSEqualSizes(frame.size, screen.size))
856 is_maximized_ = false;
857 else if (NSEqualPoints(frame.origin, screen.origin))
858 is_maximized_ = true;
860 UpdateRestoredBounds();
863 void NativeAppWindowCocoa::WindowDidResize() {
864 app_window_->OnNativeWindowChanged();
865 UpdateDraggableRegionViews();
868 void NativeAppWindowCocoa::WindowDidMove() {
869 UpdateRestoredBounds();
870 app_window_->OnNativeWindowChanged();
873 void NativeAppWindowCocoa::WindowDidMiniaturize() {
874 app_window_->OnNativeWindowChanged();
877 void NativeAppWindowCocoa::WindowDidDeminiaturize() {
878 app_window_->OnNativeWindowChanged();
881 void NativeAppWindowCocoa::WindowDidEnterFullscreen() {
882 is_fullscreen_ = true;
883 app_window_->OSFullscreen();
884 app_window_->OnNativeWindowChanged();
887 void NativeAppWindowCocoa::WindowDidExitFullscreen() {
888 is_fullscreen_ = false;
889 if (!shows_fullscreen_controls_)
890 SetFullScreenCollectionBehavior(window(), false);
892 app_window_->Restore();
893 app_window_->OnNativeWindowChanged();
896 void NativeAppWindowCocoa::WindowWillZoom() {
897 // See top of file NOTE: Maximize and Zoom.
904 bool NativeAppWindowCocoa::HandledByExtensionCommand(
906 ui::AcceleratorManager::HandlerPriority priority) {
907 return extension_keybinding_registry_->ProcessKeyEvent(
908 content::NativeWebKeyboardEvent(event), priority);
911 void NativeAppWindowCocoa::ShowWithApp() {
912 is_hidden_with_app_ = false;
913 if (!app_window_->is_hidden())
917 void NativeAppWindowCocoa::HideWithApp() {
918 is_hidden_with_app_ = true;
919 HideWithoutMarkingHidden();
922 void NativeAppWindowCocoa::UpdateShelfMenu() {
923 // TODO(tmdiep): To be implemented for Mac.
927 gfx::Size NativeAppWindowCocoa::GetContentMinimumSize() const {
928 return size_constraints_.GetMinimumSize();
931 gfx::Size NativeAppWindowCocoa::GetContentMaximumSize() const {
932 return size_constraints_.GetMaximumSize();
935 void NativeAppWindowCocoa::SetContentSizeConstraints(
936 const gfx::Size& min_size, const gfx::Size& max_size) {
937 // Update the size constraints.
938 size_constraints_.set_minimum_size(min_size);
939 size_constraints_.set_maximum_size(max_size);
941 gfx::Size minimum_size = size_constraints_.GetMinimumSize();
942 [window() setContentMinSize:NSMakeSize(minimum_size.width(),
943 minimum_size.height())];
945 gfx::Size maximum_size = size_constraints_.GetMaximumSize();
946 const int kUnboundedSize = extensions::SizeConstraints::kUnboundedSize;
947 CGFloat max_width = maximum_size.width() == kUnboundedSize ?
948 CGFLOAT_MAX : maximum_size.width();
949 CGFloat max_height = maximum_size.height() == kUnboundedSize ?
950 CGFLOAT_MAX : maximum_size.height();
951 [window() setContentMaxSize:NSMakeSize(max_width, max_height)];
953 // Update the window controls.
954 shows_resize_controls_ =
955 is_resizable_ && !size_constraints_.HasFixedSize();
956 shows_fullscreen_controls_ =
957 is_resizable_ && !size_constraints_.HasMaximumSize() && has_frame_;
959 if (!is_fullscreen_) {
960 [window() setStyleMask:GetWindowStyleMask()];
962 // Set the window to participate in Lion Fullscreen mode. Setting this flag
963 // has no effect on Snow Leopard or earlier. UI controls for fullscreen are
964 // only shown for apps that have unbounded size.
965 if (base::mac::IsOSLionOrLater())
966 SetFullScreenCollectionBehavior(window(), shows_fullscreen_controls_);
970 [window() setShowsResizeIndicator:shows_resize_controls_];
971 [[window() standardWindowButton:NSWindowZoomButton]
972 setEnabled:shows_fullscreen_controls_];
976 void NativeAppWindowCocoa::SetAlwaysOnTop(bool always_on_top) {
977 [window() setLevel:(always_on_top ? AlwaysOnTopWindowLevel() :
978 NSNormalWindowLevel)];
981 void NativeAppWindowCocoa::SetVisibleOnAllWorkspaces(bool always_visible) {
982 SetWorkspacesCollectionBehavior(window(), always_visible);
985 NativeAppWindowCocoa::~NativeAppWindowCocoa() {
988 ShellNSWindow* NativeAppWindowCocoa::window() const {
989 NSWindow* window = [window_controller_ window];
990 CHECK(!window || [window isKindOfClass:[ShellNSWindow class]]);
991 return static_cast<ShellNSWindow*>(window);
994 content::WebContents* NativeAppWindowCocoa::WebContents() const {
995 return app_window_->web_contents();
998 void NativeAppWindowCocoa::UpdateRestoredBounds() {
999 if (IsRestored(*this))
1000 restored_bounds_ = [window() frame];
1003 void NativeAppWindowCocoa::HideWithoutMarkingHidden() {
1004 [window() orderOut:window_controller_];