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 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "content/public/browser/native_web_keyboard_event.h"
19 #include "content/public/browser/notification_source.h"
20 #include "content/public/browser/notification_types.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_contents_view.h"
24 #include "third_party/skia/include/core/SkRegion.h"
26 // NOTE: State Before Update.
28 // Internal state, such as |is_maximized_|, must be set before the window
29 // state is changed so that it is accurate when e.g. a resize results in a call
30 // to |OnNativeWindowChanged|.
32 // NOTE: Maximize and Zoom.
34 // Zooming is implemented manually in order to implement maximize functionality
35 // and to support non resizable windows. The window will be resized explicitly
36 // in the |WindowWillZoom| call.
38 // Attempting maximize and restore functionality with non resizable windows
39 // using the native zoom method did not work, even with
40 // windowWillUseStandardFrame, as the window would not restore back to the
44 using apps::ShellWindow;
46 @interface NSWindow (NSPrivateApis)
47 - (void)setBottomCornerRounded:(BOOL)rounded;
50 // Replicate specific 10.7 SDK declarations for building with prior SDKs.
51 #if !defined(MAC_OS_X_VERSION_10_7) || \
52 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
54 @interface NSWindow (LionSDKDeclarations)
55 - (void)toggleFullScreen:(id)sender;
59 NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7
62 #endif // MAC_OS_X_VERSION_10_7
64 @implementation NativeAppWindowController
66 @synthesize appWindow = appWindow_;
68 - (void)windowWillClose:(NSNotification*)notification {
70 appWindow_->WindowWillClose();
73 - (void)windowDidBecomeKey:(NSNotification*)notification {
75 appWindow_->WindowDidBecomeKey();
78 - (void)windowDidResignKey:(NSNotification*)notification {
80 appWindow_->WindowDidResignKey();
83 - (void)windowDidResize:(NSNotification*)notification {
85 appWindow_->WindowDidResize();
88 - (void)windowDidMove:(NSNotification*)notification {
90 appWindow_->WindowDidMove();
93 - (void)windowDidMiniaturize:(NSNotification*)notification {
95 appWindow_->WindowDidMiniaturize();
98 - (void)windowDidDeminiaturize:(NSNotification*)notification {
100 appWindow_->WindowDidDeminiaturize();
103 - (BOOL)windowShouldZoom:(NSWindow*)window
104 toFrame:(NSRect)newFrame {
106 appWindow_->WindowWillZoom();
107 return NO; // See top of file NOTE: Maximize and Zoom.
110 // Allow non resizable windows (without NSResizableWindowMask) to enter
111 // fullscreen by passing through the full size in willUseFullScreenContentSize.
112 - (NSSize)window:(NSWindow *)window
113 willUseFullScreenContentSize:(NSSize)proposedSize {
117 - (void)executeCommand:(int)command {
118 // No-op, swallow the event.
121 - (BOOL)handledByExtensionCommand:(NSEvent*)event {
123 return appWindow_->HandledByExtensionCommand(event);
129 // This is really a method on NSGrayFrame, so it should only be called on the
130 // view passed into -[NSWindow drawCustomFrameRect:forView:].
131 @interface NSView (PrivateMethods)
132 - (CGFloat)roundedCornerRadius;
135 @interface ShellNSWindow : ChromeEventProcessingWindow
137 @implementation ShellNSWindow
140 @interface ShellCustomFrameNSWindow : ShellNSWindow
142 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view;
146 @implementation ShellCustomFrameNSWindow
148 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
149 [[NSBezierPath bezierPathWithRect:rect] addClip];
150 [[NSColor clearColor] set];
154 CGFloat cornerRadius = 4.0;
155 if ([view respondsToSelector:@selector(roundedCornerRadius)])
156 cornerRadius = [view roundedCornerRadius];
157 [[NSBezierPath bezierPathWithRoundedRect:[view bounds]
159 yRadius:cornerRadius] addClip];
160 [[NSColor whiteColor] set];
166 @interface ShellFramelessNSWindow : ShellCustomFrameNSWindow
170 @implementation ShellFramelessNSWindow
172 + (NSRect)frameRectForContentRect:(NSRect)contentRect
173 styleMask:(NSUInteger)mask {
177 + (NSRect)contentRectForFrameRect:(NSRect)frameRect
178 styleMask:(NSUInteger)mask {
182 - (NSRect)frameRectForContentRect:(NSRect)contentRect {
186 - (NSRect)contentRectForFrameRect:(NSRect)frameRect {
192 @interface ControlRegionView : NSView {
194 NativeAppWindowCocoa* appWindow_; // Weak; owns self.
199 @implementation ControlRegionView
201 - (id)initWithAppWindow:(NativeAppWindowCocoa*)appWindow {
202 if ((self = [super init]))
203 appWindow_ = appWindow;
207 - (BOOL)mouseDownCanMoveWindow {
211 - (NSView*)hitTest:(NSPoint)aPoint {
212 if (appWindow_->use_system_drag() ||
213 !appWindow_->IsWithinDraggableRegion(aPoint)) {
219 - (void)mouseDown:(NSEvent*)event {
220 appWindow_->HandleMouseEvent(event);
223 - (void)mouseDragged:(NSEvent*)event {
224 appWindow_->HandleMouseEvent(event);
229 @interface NSView (WebContentsView)
230 - (void)setMouseDownCanMoveWindow:(BOOL)can_move;
233 NativeAppWindowCocoa::NativeAppWindowCocoa(
234 ShellWindow* shell_window,
235 const ShellWindow::CreateParams& params)
236 : shell_window_(shell_window),
237 has_frame_(params.frame == ShellWindow::FRAME_CHROME),
239 is_hidden_with_app_(false),
240 is_maximized_(false),
241 is_fullscreen_(false),
242 attention_request_id_(0),
243 use_system_drag_(true) {
244 // Flip coordinates based on the primary screen.
245 NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
246 NSRect cocoa_bounds = NSMakeRect(params.bounds.x(),
247 NSHeight(main_screen_rect) - params.bounds.y() - params.bounds.height(),
248 params.bounds.width(), params.bounds.height());
250 // If coordinates are < 0, center window on primary screen
251 if (params.bounds.x() == INT_MIN) {
252 cocoa_bounds.origin.x =
253 floor((NSWidth(main_screen_rect) - NSWidth(cocoa_bounds)) / 2);
255 if (params.bounds.y() == INT_MIN) {
256 cocoa_bounds.origin.y =
257 floor((NSHeight(main_screen_rect) - NSHeight(cocoa_bounds)) / 2);
260 // Initialize |restored_bounds_| after |cocoa_bounds| have been sanitized.
261 restored_bounds_ = cocoa_bounds;
263 resizable_ = params.resizable;
264 base::scoped_nsobject<NSWindow> window;
267 bool should_use_native_frame =
268 CommandLine::ForCurrentProcess()->HasSwitch(
269 switches::kAppsUseNativeFrame);
270 window_class = should_use_native_frame ?
271 [ShellNSWindow class] : [ShellCustomFrameNSWindow class];
273 window_class = [ShellFramelessNSWindow class];
275 window.reset([[window_class alloc]
276 initWithContentRect:cocoa_bounds
277 styleMask:GetWindowStyleMask()
278 backing:NSBackingStoreBuffered
280 [window setTitle:base::SysUTF8ToNSString(extension()->name())];
281 min_size_ = params.minimum_size;
282 if (min_size_.width() || min_size_.height()) {
283 [window setContentMinSize:
284 NSMakeSize(min_size_.width(), min_size_.height())];
286 max_size_ = params.maximum_size;
287 if (max_size_.width() || max_size_.height()) {
288 CGFloat max_width = max_size_.width() ? max_size_.width() : CGFLOAT_MAX;
289 CGFloat max_height = max_size_.height() ? max_size_.height() : CGFLOAT_MAX;
290 [window setContentMaxSize:NSMakeSize(max_width, max_height)];
293 if (base::mac::IsOSSnowLeopard() &&
294 [window respondsToSelector:@selector(setBottomCornerRounded:)])
295 [window setBottomCornerRounded:NO];
297 // Set the window to participate in Lion Fullscreen mode. Setting this flag
298 // has no effect on Snow Leopard or earlier. Packaged apps don't show the
299 // fullscreen button on their window decorations.
300 NSWindowCollectionBehavior behavior = [window collectionBehavior];
301 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
302 [window setCollectionBehavior:behavior];
304 window_controller_.reset(
305 [[NativeAppWindowController alloc] initWithWindow:window.release()]);
307 NSView* view = web_contents()->GetView()->GetNativeView();
308 [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
310 // By default, the whole frameless window is not draggable.
312 gfx::Rect window_bounds(
313 0, 0, NSWidth(cocoa_bounds), NSHeight(cocoa_bounds));
314 system_drag_exclude_areas_.push_back(window_bounds);
319 [[window_controller_ window] setDelegate:window_controller_];
320 [window_controller_ setAppWindow:this];
322 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryCocoa(
323 shell_window_->profile(),
325 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
329 NSUInteger NativeAppWindowCocoa::GetWindowStyleMask() const {
330 NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
331 NSMiniaturizableWindowMask;
333 style_mask |= NSResizableWindowMask;
335 !CommandLine::ForCurrentProcess()->HasSwitch(
336 switches::kAppsUseNativeFrame)) {
337 style_mask |= NSTexturedBackgroundWindowMask;
342 void NativeAppWindowCocoa::InstallView() {
343 NSView* view = web_contents()->GetView()->GetNativeView();
345 [view setFrame:[[window() contentView] bounds]];
346 [[window() contentView] addSubview:view];
347 if (!max_size_.IsEmpty() && min_size_ == max_size_) {
348 [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
349 [window() setShowsResizeIndicator:NO];
352 // TODO(jeremya): find a cleaner way to send this information to the
353 // WebContentsViewCocoa view.
355 respondsToSelector:@selector(setMouseDownCanMoveWindow:)]);
356 [view setMouseDownCanMoveWindow:YES];
358 NSView* frameView = [[window() contentView] superview];
359 [view setFrame:[frameView bounds]];
360 [frameView addSubview:view];
362 [[window() standardWindowButton:NSWindowZoomButton] setHidden:YES];
363 [[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
364 [[window() standardWindowButton:NSWindowCloseButton] setHidden:YES];
366 // Some third-party OS X utilities check the zoom button's enabled state to
367 // determine whether to show custom UI on hover, so we disable it here to
368 // prevent them from doing so in a frameless app window.
369 [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
371 InstallDraggableRegionViews();
375 void NativeAppWindowCocoa::UninstallView() {
376 NSView* view = web_contents()->GetView()->GetNativeView();
377 [view removeFromSuperview];
380 bool NativeAppWindowCocoa::IsActive() const {
381 return [window() isKeyWindow];
384 bool NativeAppWindowCocoa::IsMaximized() const {
385 return is_maximized_;
388 bool NativeAppWindowCocoa::IsMinimized() const {
389 return [window() isMiniaturized];
392 bool NativeAppWindowCocoa::IsFullscreen() const {
393 return is_fullscreen_;
396 void NativeAppWindowCocoa::SetFullscreen(bool fullscreen) {
397 if (fullscreen == is_fullscreen_)
399 is_fullscreen_ = fullscreen;
401 if (base::mac::IsOSLionOrLater()) {
402 [window() toggleFullScreen:nil];
406 DCHECK(base::mac::IsOSSnowLeopard());
409 const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
410 bool did_fade_out = false;
411 CGDisplayFadeReservationToken token;
412 if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token) ==
415 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
416 kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
419 // Since frameless windows insert the WebContentsView into the NSThemeFrame
420 // ([[window contentView] superview]), and since that NSThemeFrame is
421 // destroyed and recreated when we change the styleMask of the window, we
422 // need to remove the view from the window when we change the style, and
423 // add it back afterwards.
426 UpdateRestoredBounds();
427 [window() setStyleMask:NSBorderlessWindowMask];
428 [window() setFrame:[window()
429 frameRectForContentRect:[[window() screen] frame]]
431 base::mac::RequestFullScreen(base::mac::kFullScreenModeAutoHideAll);
433 base::mac::ReleaseFullScreen(base::mac::kFullScreenModeAutoHideAll);
434 [window() setStyleMask:GetWindowStyleMask()];
435 [window() setFrame:restored_bounds_ display:YES];
441 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
442 kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
443 CGReleaseDisplayFadeReservation(token);
447 bool NativeAppWindowCocoa::IsFullscreenOrPending() const {
448 return is_fullscreen_;
451 bool NativeAppWindowCocoa::IsDetached() const {
455 gfx::NativeWindow NativeAppWindowCocoa::GetNativeWindow() {
459 gfx::Rect NativeAppWindowCocoa::GetRestoredBounds() const {
460 // Flip coordinates based on the primary screen.
461 NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
462 NSRect frame = restored_bounds_;
463 gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
464 bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
468 ui::WindowShowState NativeAppWindowCocoa::GetRestoredState() const {
470 return ui::SHOW_STATE_MAXIMIZED;
471 return ui::SHOW_STATE_NORMAL;
474 gfx::Rect NativeAppWindowCocoa::GetBounds() const {
475 // Flip coordinates based on the primary screen.
476 NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
477 NSRect frame = [window() frame];
478 gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
479 bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
483 void NativeAppWindowCocoa::Show() {
486 if (is_hidden_with_app_) {
487 // If there is a shim to gently request attention, return here. Otherwise
488 // show the window as usual.
489 if (apps::ExtensionAppShimHandler::RequestUserAttentionForWindow(
495 [window_controller_ showWindow:nil];
496 [window() makeKeyAndOrderFront:window_controller_];
499 void NativeAppWindowCocoa::ShowInactive() {
501 [window() orderFront:window_controller_];
504 void NativeAppWindowCocoa::Hide() {
506 HideWithoutMarkingHidden();
509 void NativeAppWindowCocoa::Close() {
510 [window() performClose:nil];
513 void NativeAppWindowCocoa::Activate() {
514 [BrowserWindowUtils activateWindowForController:window_controller_];
517 void NativeAppWindowCocoa::Deactivate() {
518 // TODO(jcivelli): http://crbug.com/51364 Implement me.
522 void NativeAppWindowCocoa::Maximize() {
523 UpdateRestoredBounds();
524 is_maximized_ = true; // See top of file NOTE: State Before Update.
525 [window() setFrame:[[window() screen] visibleFrame] display:YES animate:YES];
528 void NativeAppWindowCocoa::Minimize() {
529 [window() miniaturize:window_controller_];
532 void NativeAppWindowCocoa::Restore() {
533 DCHECK(!IsFullscreenOrPending()); // SetFullscreen, not Restore, expected.
536 is_maximized_ = false; // See top of file NOTE: State Before Update.
537 [window() setFrame:restored_bounds() display:YES animate:YES];
538 } else if (IsMinimized()) {
539 is_maximized_ = false; // See top of file NOTE: State Before Update.
540 [window() deminiaturize:window_controller_];
544 void NativeAppWindowCocoa::SetBounds(const gfx::Rect& bounds) {
545 // Enforce minimum/maximum bounds.
546 gfx::Rect checked_bounds = bounds;
548 NSSize min_size = [window() minSize];
549 if (bounds.width() < min_size.width)
550 checked_bounds.set_width(min_size.width);
551 if (bounds.height() < min_size.height)
552 checked_bounds.set_height(min_size.height);
553 NSSize max_size = [window() maxSize];
554 if (checked_bounds.width() > max_size.width)
555 checked_bounds.set_width(max_size.width);
556 if (checked_bounds.height() > max_size.height)
557 checked_bounds.set_height(max_size.height);
559 NSRect cocoa_bounds = NSMakeRect(checked_bounds.x(), 0,
560 checked_bounds.width(),
561 checked_bounds.height());
562 // Flip coordinates based on the primary screen.
563 NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
564 cocoa_bounds.origin.y = NSHeight([screen frame]) - checked_bounds.bottom();
566 [window() setFrame:cocoa_bounds display:YES];
569 void NativeAppWindowCocoa::UpdateWindowIcon() {
570 // TODO(junmin): implement.
573 void NativeAppWindowCocoa::UpdateWindowTitle() {
574 string16 title = shell_window_->GetTitle();
575 [window() setTitle:base::SysUTF16ToNSString(title)];
578 void NativeAppWindowCocoa::UpdateInputRegion(scoped_ptr<SkRegion> region) {
582 void NativeAppWindowCocoa::UpdateDraggableRegions(
583 const std::vector<extensions::DraggableRegion>& regions) {
584 // Draggable region is not supported for non-frameless window.
588 // To use system drag, the window has to be marked as draggable with
589 // non-draggable areas being excluded via overlapping views.
590 // 1) If no draggable area is provided, the window is not draggable at all.
591 // 2) If only one draggable area is given, as this is the most common
592 // case, use the system drag. The non-draggable areas that are opposite of
593 // the draggable area are computed.
594 // 3) Otherwise, use the custom drag. As such, we lose the capability to
595 // support some features like snapping into other space.
597 // Determine how to perform the drag by counting the number of draggable
599 const extensions::DraggableRegion* draggable_area = NULL;
600 use_system_drag_ = true;
601 for (std::vector<extensions::DraggableRegion>::const_iterator iter =
603 iter != regions.end();
605 if (iter->draggable) {
606 // If more than one draggable area is found, use custom drag.
607 if (draggable_area) {
608 use_system_drag_ = false;
611 draggable_area = &(*iter);
615 if (use_system_drag_)
616 UpdateDraggableRegionsForSystemDrag(regions, draggable_area);
618 UpdateDraggableRegionsForCustomDrag(regions);
620 InstallDraggableRegionViews();
623 void NativeAppWindowCocoa::UpdateDraggableRegionsForSystemDrag(
624 const std::vector<extensions::DraggableRegion>& regions,
625 const extensions::DraggableRegion* draggable_area) {
626 NSView* web_view = web_contents()->GetView()->GetNativeView();
627 NSInteger web_view_width = NSWidth([web_view bounds]);
628 NSInteger web_view_height = NSHeight([web_view bounds]);
630 system_drag_exclude_areas_.clear();
632 // The whole window is not draggable if no draggable area is given.
633 if (!draggable_area) {
634 gfx::Rect window_bounds(0, 0, web_view_width, web_view_height);
635 system_drag_exclude_areas_.push_back(window_bounds);
639 // Otherwise, there is only one draggable area. Compute non-draggable areas
640 // that are the opposite of the given draggable area, combined with the
641 // remaining provided non-draggable areas.
643 // Copy all given non-draggable areas.
644 for (std::vector<extensions::DraggableRegion>::const_iterator iter =
646 iter != regions.end();
648 if (!iter->draggable)
649 system_drag_exclude_areas_.push_back(iter->bounds);
652 gfx::Rect draggable_bounds = draggable_area->bounds;
653 gfx::Rect non_draggable_bounds;
655 // Add the non-draggable area above the given draggable area.
656 if (draggable_bounds.y() > 0) {
657 non_draggable_bounds.SetRect(0,
660 draggable_bounds.y() - 1);
661 system_drag_exclude_areas_.push_back(non_draggable_bounds);
664 // Add the non-draggable area below the given draggable area.
665 if (draggable_bounds.bottom() < web_view_height) {
666 non_draggable_bounds.SetRect(0,
667 draggable_bounds.bottom() + 1,
669 web_view_height - draggable_bounds.bottom());
670 system_drag_exclude_areas_.push_back(non_draggable_bounds);
673 // Add the non-draggable area to the left of the given draggable area.
674 if (draggable_bounds.x() > 0) {
675 non_draggable_bounds.SetRect(0,
676 draggable_bounds.y(),
677 draggable_bounds.x() - 1,
678 draggable_bounds.height());
679 system_drag_exclude_areas_.push_back(non_draggable_bounds);
682 // Add the non-draggable area to the right of the given draggable area.
683 if (draggable_bounds.right() < web_view_width) {
684 non_draggable_bounds.SetRect(draggable_bounds.right() + 1,
685 draggable_bounds.y(),
686 web_view_width - draggable_bounds.right(),
687 draggable_bounds.height());
688 system_drag_exclude_areas_.push_back(non_draggable_bounds);
692 void NativeAppWindowCocoa::UpdateDraggableRegionsForCustomDrag(
693 const std::vector<extensions::DraggableRegion>& regions) {
694 // We still need one ControlRegionView to cover the whole window such that
695 // mouse events could be captured.
696 NSView* web_view = web_contents()->GetView()->GetNativeView();
697 gfx::Rect window_bounds(
698 0, 0, NSWidth([web_view bounds]), NSHeight([web_view bounds]));
699 system_drag_exclude_areas_.clear();
700 system_drag_exclude_areas_.push_back(window_bounds);
702 // Aggregate the draggable areas and non-draggable areas such that hit test
703 // could be performed easily.
704 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions));
707 void NativeAppWindowCocoa::HandleKeyboardEvent(
708 const content::NativeWebKeyboardEvent& event) {
709 if (event.skip_in_browser ||
710 event.type == content::NativeWebKeyboardEvent::Char) {
713 [window() redispatchKeyEvent:event.os_event];
716 void NativeAppWindowCocoa::InstallDraggableRegionViews() {
719 // All ControlRegionViews should be added as children of the WebContentsView,
720 // because WebContentsView will be removed and re-added when entering and
721 // leaving fullscreen mode.
722 NSView* webView = web_contents()->GetView()->GetNativeView();
723 NSInteger webViewHeight = NSHeight([webView bounds]);
725 // Remove all ControlRegionViews that are added last time.
726 // Note that [webView subviews] returns the view's mutable internal array and
727 // it should be copied to avoid mutating the original array while enumerating
729 base::scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
730 for (NSView* subview in subviews.get())
731 if ([subview isKindOfClass:[ControlRegionView class]])
732 [subview removeFromSuperview];
734 // Create and add ControlRegionView for each region that needs to be excluded
735 // from the dragging.
736 for (std::vector<gfx::Rect>::const_iterator iter =
737 system_drag_exclude_areas_.begin();
738 iter != system_drag_exclude_areas_.end();
740 base::scoped_nsobject<NSView> controlRegion(
741 [[ControlRegionView alloc] initWithAppWindow:this]);
742 [controlRegion setFrame:NSMakeRect(iter->x(),
743 webViewHeight - iter->bottom(),
746 [controlRegion setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
747 [webView addSubview:controlRegion];
751 void NativeAppWindowCocoa::FlashFrame(bool flash) {
753 attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
755 [NSApp cancelUserAttentionRequest:attention_request_id_];
756 attention_request_id_ = 0;
760 bool NativeAppWindowCocoa::IsAlwaysOnTop() const {
764 void NativeAppWindowCocoa::RenderViewHostChanged() {
765 web_contents()->GetView()->Focus();
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 gfx::NativeView NativeAppWindowCocoa::GetHostView() const {
792 gfx::Point NativeAppWindowCocoa::GetDialogPosition(const gfx::Size& size) {
797 void NativeAppWindowCocoa::AddObserver(
798 web_modal::WebContentsModalDialogHostObserver* observer) {
802 void NativeAppWindowCocoa::RemoveObserver(
803 web_modal::WebContentsModalDialogHostObserver* observer) {
807 void NativeAppWindowCocoa::WindowWillClose() {
808 [window_controller_ setAppWindow:NULL];
809 shell_window_->OnNativeWindowChanged();
810 shell_window_->OnNativeClose();
813 void NativeAppWindowCocoa::WindowDidBecomeKey() {
814 content::RenderWidgetHostView* rwhv =
815 web_contents()->GetRenderWidgetHostView();
817 rwhv->SetActive(true);
818 shell_window_->OnNativeWindowActivated();
821 void NativeAppWindowCocoa::WindowDidResignKey() {
822 // If our app is still active and we're still the key window, ignore this
823 // message, since it just means that a menu extra (on the "system status bar")
824 // was activated; we'll get another |-windowDidResignKey| if we ever really
825 // lose key window status.
826 if ([NSApp isActive] && ([NSApp keyWindow] == window()))
829 content::RenderWidgetHostView* rwhv =
830 web_contents()->GetRenderWidgetHostView();
832 rwhv->SetActive(false);
835 void NativeAppWindowCocoa::WindowDidResize() {
836 // Update |is_maximized_| if needed:
837 // - Exit maximized state if resized.
838 // - Consider us maximized if resize places us back to maximized location.
839 // This happens when returning from fullscreen.
840 NSRect frame = [window() frame];
841 NSRect screen = [[window() screen] visibleFrame];
842 if (!NSEqualSizes(frame.size, screen.size))
843 is_maximized_ = false;
844 else if (NSEqualPoints(frame.origin, screen.origin))
845 is_maximized_ = true;
847 shell_window_->OnNativeWindowChanged();
850 void NativeAppWindowCocoa::WindowDidMove() {
851 shell_window_->OnNativeWindowChanged();
854 void NativeAppWindowCocoa::WindowDidMiniaturize() {
855 shell_window_->OnNativeWindowChanged();
858 void NativeAppWindowCocoa::WindowDidDeminiaturize() {
859 shell_window_->OnNativeWindowChanged();
862 void NativeAppWindowCocoa::WindowWillZoom() {
863 // See top of file NOTE: Maximize and Zoom.
870 bool NativeAppWindowCocoa::HandledByExtensionCommand(NSEvent* event) {
871 return extension_keybinding_registry_->ProcessKeyEvent(
872 content::NativeWebKeyboardEvent(event));
875 void NativeAppWindowCocoa::HandleMouseEvent(NSEvent* event) {
876 if ([event type] == NSLeftMouseDown) {
877 last_mouse_location_ =
878 [window() convertBaseToScreen:[event locationInWindow]];
879 } else if ([event type] == NSLeftMouseDragged) {
880 NSPoint current_mouse_location =
881 [window() convertBaseToScreen:[event locationInWindow]];
882 NSPoint frame_origin = [window() frame].origin;
883 frame_origin.x += current_mouse_location.x - last_mouse_location_.x;
884 frame_origin.y += current_mouse_location.y - last_mouse_location_.y;
885 [window() setFrameOrigin:frame_origin];
886 last_mouse_location_ = current_mouse_location;
890 bool NativeAppWindowCocoa::IsWithinDraggableRegion(NSPoint point) const {
891 if (!draggable_region_)
893 NSView* webView = web_contents()->GetView()->GetNativeView();
894 NSInteger webViewHeight = NSHeight([webView bounds]);
895 // |draggable_region_| is stored in local platform-indepdent coordiate system
896 // while |point| is in local Cocoa coordinate system. Do the conversion
897 // to match these two.
898 return draggable_region_->contains(point.x, webViewHeight - point.y);
901 void NativeAppWindowCocoa::HideWithApp() {
902 is_hidden_with_app_ = true;
903 HideWithoutMarkingHidden();
906 void NativeAppWindowCocoa::ShowWithApp() {
907 is_hidden_with_app_ = false;
912 void NativeAppWindowCocoa::HideWithoutMarkingHidden() {
913 [window() orderOut:window_controller_];
916 NativeAppWindowCocoa::~NativeAppWindowCocoa() {
919 ShellNSWindow* NativeAppWindowCocoa::window() const {
920 NSWindow* window = [window_controller_ window];
921 CHECK(!window || [window isKindOfClass:[ShellNSWindow class]]);
922 return static_cast<ShellNSWindow*>(window);
925 void NativeAppWindowCocoa::UpdateRestoredBounds() {
926 if (IsRestored(*this))
927 restored_bounds_ = [window() frame];