2d949c0b4314cfc407d09fda331593be40d1a7a7
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / apps / native_app_window_cocoa.mm
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.
4
5 #include "chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h"
6
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 "content/public/browser/web_contents_view.h"
22 #include "extensions/common/extension.h"
23 #include "third_party/skia/include/core/SkRegion.h"
24 #include "ui/gfx/skia_util.h"
25
26 // NOTE: State Before Update.
27 //
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|.
31
32 // NOTE: Maximize and Zoom.
33 //
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.
37 //
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
41 // desired size.
42
43 using apps::AppWindow;
44
45 @interface NSWindow (NSPrivateApis)
46 - (void)setBottomCornerRounded:(BOOL)rounded;
47 @end
48
49 // Replicate specific 10.7 SDK declarations for building with prior SDKs.
50 #if !defined(MAC_OS_X_VERSION_10_7) || \
51     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
52
53 @interface NSWindow (LionSDKDeclarations)
54 - (void)toggleFullScreen:(id)sender;
55 @end
56
57 enum {
58   NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
59   NSFullScreenWindowMask = 1 << 14
60 };
61
62 #endif  // MAC_OS_X_VERSION_10_7
63
64 namespace {
65
66 void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) {
67   NSWindowCollectionBehavior behavior = [window collectionBehavior];
68   if (allow_fullscreen)
69     behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
70   else
71     behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
72   [window setCollectionBehavior:behavior];
73 }
74
75 void InitCollectionBehavior(NSWindow* window) {
76   // Since always-on-top windows have a higher window level
77   // than NSNormalWindowLevel, they will default to
78   // NSWindowCollectionBehaviorTransient. Set the value
79   // explicitly here to match normal windows.
80   NSWindowCollectionBehavior behavior = [window collectionBehavior];
81   behavior |= NSWindowCollectionBehaviorManaged;
82   [window setCollectionBehavior:behavior];
83 }
84
85 // Returns the level for windows that are configured to be always on top.
86 // This is not a constant because NSFloatingWindowLevel is a macro defined
87 // as a function call.
88 NSInteger AlwaysOnTopWindowLevel() {
89   return NSFloatingWindowLevel;
90 }
91
92 NSRect GfxToCocoaBounds(gfx::Rect bounds) {
93   typedef apps::AppWindow::BoundsSpecification BoundsSpecification;
94
95   NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
96
97   // If coordinates are unspecified, center window on primary screen.
98   if (bounds.x() == BoundsSpecification::kUnspecifiedPosition)
99     bounds.set_x(floor((NSWidth(main_screen_rect) - bounds.width()) / 2));
100   if (bounds.y() == BoundsSpecification::kUnspecifiedPosition)
101     bounds.set_y(floor((NSHeight(main_screen_rect) - bounds.height()) / 2));
102
103   // Convert to Mac coordinates.
104   NSRect cocoa_bounds = NSRectFromCGRect(bounds.ToCGRect());
105   cocoa_bounds.origin.y = NSHeight(main_screen_rect) - NSMaxY(cocoa_bounds);
106   return cocoa_bounds;
107 }
108
109 // Return a vector of non-draggable regions that fill a window of size
110 // |width| by |height|, but leave gaps where the window should be draggable.
111 std::vector<gfx::Rect> CalculateNonDraggableRegions(
112     const std::vector<extensions::DraggableRegion>& regions,
113     int width,
114     int height) {
115   scoped_ptr<SkRegion> draggable(
116       AppWindow::RawDraggableRegionsToSkRegion(regions));
117   scoped_ptr<SkRegion> non_draggable(new SkRegion);
118   non_draggable->op(0, 0, width, height, SkRegion::kUnion_Op);
119   non_draggable->op(*draggable, SkRegion::kDifference_Op);
120
121   std::vector<gfx::Rect> result;
122   for (SkRegion::Iterator it(*non_draggable); !it.done(); it.next()) {
123     result.push_back(gfx::SkIRectToRect(it.rect()));
124   }
125   return result;
126 }
127
128 }  // namespace
129
130 @implementation NativeAppWindowController
131
132 @synthesize appWindow = appWindow_;
133
134 - (void)windowWillClose:(NSNotification*)notification {
135   if (appWindow_)
136     appWindow_->WindowWillClose();
137 }
138
139 - (void)windowDidBecomeKey:(NSNotification*)notification {
140   if (appWindow_)
141     appWindow_->WindowDidBecomeKey();
142 }
143
144 - (void)windowDidResignKey:(NSNotification*)notification {
145   if (appWindow_)
146     appWindow_->WindowDidResignKey();
147 }
148
149 - (void)windowDidResize:(NSNotification*)notification {
150   if (appWindow_)
151     appWindow_->WindowDidResize();
152 }
153
154 - (void)windowDidEndLiveResize:(NSNotification*)notification {
155   if (appWindow_)
156     appWindow_->WindowDidFinishResize();
157 }
158
159 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
160   if (appWindow_)
161     appWindow_->WindowDidFinishResize();
162 }
163
164 - (void)windowDidExitFullScreen:(NSNotification*)notification {
165   if (appWindow_)
166     appWindow_->WindowDidFinishResize();
167 }
168
169 - (void)windowDidMove:(NSNotification*)notification {
170   if (appWindow_)
171     appWindow_->WindowDidMove();
172 }
173
174 - (void)windowDidMiniaturize:(NSNotification*)notification {
175   if (appWindow_)
176     appWindow_->WindowDidMiniaturize();
177 }
178
179 - (void)windowDidDeminiaturize:(NSNotification*)notification {
180   if (appWindow_)
181     appWindow_->WindowDidDeminiaturize();
182 }
183
184 - (BOOL)windowShouldZoom:(NSWindow*)window
185                  toFrame:(NSRect)newFrame {
186   if (appWindow_)
187     appWindow_->WindowWillZoom();
188   return NO;  // See top of file NOTE: Maximize and Zoom.
189 }
190
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 {
195   return proposedSize;
196 }
197
198 - (void)executeCommand:(int)command {
199   // No-op, swallow the event.
200 }
201
202 - (BOOL)handledByExtensionCommand:(NSEvent*)event {
203   if (appWindow_)
204     return appWindow_->HandledByExtensionCommand(event);
205   return NO;
206 }
207
208 @end
209
210 // This is really a method on NSGrayFrame, so it should only be called on the
211 // view passed into -[NSWindow drawCustomFrameRect:forView:].
212 @interface NSView (PrivateMethods)
213 - (CGFloat)roundedCornerRadius;
214 @end
215
216 // TODO(jamescook): Should these be AppNSWindow to match apps::AppWindow?
217 // http://crbug.com/344082
218 @interface ShellNSWindow : ChromeEventProcessingWindow
219 @end
220 @implementation ShellNSWindow
221 @end
222
223 @interface ShellCustomFrameNSWindow : ShellNSWindow
224
225 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view;
226
227 @end
228
229 @implementation ShellCustomFrameNSWindow
230
231 - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
232   [[NSBezierPath bezierPathWithRect:rect] addClip];
233   [[NSColor clearColor] set];
234   NSRectFill(rect);
235
236   // Set up our clip.
237   CGFloat cornerRadius = 4.0;
238   if ([view respondsToSelector:@selector(roundedCornerRadius)])
239     cornerRadius = [view roundedCornerRadius];
240   [[NSBezierPath bezierPathWithRoundedRect:[view bounds]
241                                    xRadius:cornerRadius
242                                    yRadius:cornerRadius] addClip];
243   [[NSColor whiteColor] set];
244   NSRectFill(rect);
245 }
246
247 @end
248
249 @interface ShellFramelessNSWindow : ShellCustomFrameNSWindow
250
251 @end
252
253 @implementation ShellFramelessNSWindow
254
255 + (NSRect)frameRectForContentRect:(NSRect)contentRect
256                         styleMask:(NSUInteger)mask {
257   return contentRect;
258 }
259
260 + (NSRect)contentRectForFrameRect:(NSRect)frameRect
261                         styleMask:(NSUInteger)mask {
262   return frameRect;
263 }
264
265 - (NSRect)frameRectForContentRect:(NSRect)contentRect {
266   return contentRect;
267 }
268
269 - (NSRect)contentRectForFrameRect:(NSRect)frameRect {
270   return frameRect;
271 }
272
273 @end
274
275 @interface ControlRegionView : NSView
276 @end
277
278 @implementation ControlRegionView
279
280 - (BOOL)mouseDownCanMoveWindow {
281   return NO;
282 }
283
284 - (NSView*)hitTest:(NSPoint)aPoint {
285   return nil;
286 }
287
288 @end
289
290 @interface NSView (WebContentsView)
291 - (void)setMouseDownCanMoveWindow:(BOOL)can_move;
292 @end
293
294 NativeAppWindowCocoa::NativeAppWindowCocoa(
295     AppWindow* app_window,
296     const AppWindow::CreateParams& params)
297     : app_window_(app_window),
298       has_frame_(params.frame == AppWindow::FRAME_CHROME),
299       is_hidden_(false),
300       is_hidden_with_app_(false),
301       is_maximized_(false),
302       is_fullscreen_(false),
303       is_resizable_(params.resizable),
304       shows_resize_controls_(true),
305       shows_fullscreen_controls_(true),
306       attention_request_id_(0) {
307   Observe(web_contents());
308
309   base::scoped_nsobject<NSWindow> window;
310   Class window_class;
311   if (has_frame_) {
312     bool should_use_native_frame =
313         CommandLine::ForCurrentProcess()->HasSwitch(
314             switches::kAppsUseNativeFrame);
315     window_class = should_use_native_frame ?
316         [ShellNSWindow class] : [ShellCustomFrameNSWindow class];
317   } else {
318     window_class = [ShellFramelessNSWindow class];
319   }
320
321   // Estimate the initial bounds of the window. Once the frame insets are known,
322   // the window bounds and constraints can be set precisely.
323   NSRect cocoa_bounds = GfxToCocoaBounds(
324       params.GetInitialWindowBounds(gfx::Insets()));
325   window.reset([[window_class alloc]
326       initWithContentRect:cocoa_bounds
327                 styleMask:GetWindowStyleMask()
328                   backing:NSBackingStoreBuffered
329                     defer:NO]);
330   [window setTitle:base::SysUTF8ToNSString(extension()->name())];
331   [[window contentView] cr_setWantsLayer:YES];
332
333   if (base::mac::IsOSSnowLeopard() &&
334       [window respondsToSelector:@selector(setBottomCornerRounded:)])
335     [window setBottomCornerRounded:NO];
336
337   if (params.always_on_top)
338     [window setLevel:AlwaysOnTopWindowLevel()];
339   InitCollectionBehavior(window);
340
341   window_controller_.reset(
342       [[NativeAppWindowController alloc] initWithWindow:window.release()]);
343
344   NSView* view = web_contents()->GetView()->GetNativeView();
345   [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
346
347   // By default, the whole frameless window is not draggable.
348   if (!has_frame_) {
349     gfx::Rect window_bounds(
350         0, 0, NSWidth(cocoa_bounds), NSHeight(cocoa_bounds));
351     system_drag_exclude_areas_.push_back(window_bounds);
352   }
353
354   InstallView();
355
356   [[window_controller_ window] setDelegate:window_controller_];
357   [window_controller_ setAppWindow:this];
358
359   // We can now compute the precise window bounds and constraints.
360   gfx::Insets insets = GetFrameInsets();
361   SetBounds(params.GetInitialWindowBounds(insets));
362   SetContentSizeConstraints(params.GetContentMinimumSize(insets),
363                             params.GetContentMaximumSize(insets));
364
365   // Initialize |restored_bounds_|.
366   restored_bounds_ = [this->window() frame];
367
368   extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryCocoa(
369       Profile::FromBrowserContext(app_window_->browser_context()),
370       window,
371       extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
372       NULL));
373 }
374
375 NSUInteger NativeAppWindowCocoa::GetWindowStyleMask() const {
376   NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
377                           NSMiniaturizableWindowMask;
378   if (shows_resize_controls_)
379     style_mask |= NSResizableWindowMask;
380   if (!has_frame_ ||
381       !CommandLine::ForCurrentProcess()->HasSwitch(
382           switches::kAppsUseNativeFrame)) {
383     style_mask |= NSTexturedBackgroundWindowMask;
384   }
385   return style_mask;
386 }
387
388 void NativeAppWindowCocoa::InstallView() {
389   NSView* view = web_contents()->GetView()->GetNativeView();
390   if (has_frame_) {
391     [view setFrame:[[window() contentView] bounds]];
392     [[window() contentView] addSubview:view];
393     if (!shows_fullscreen_controls_)
394       [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
395     if (!shows_resize_controls_)
396       [window() setShowsResizeIndicator:NO];
397   } else {
398     // TODO(jeremya): find a cleaner way to send this information to the
399     // WebContentsViewCocoa view.
400     DCHECK([view
401         respondsToSelector:@selector(setMouseDownCanMoveWindow:)]);
402     [view setMouseDownCanMoveWindow:YES];
403
404     NSView* frameView = [[window() contentView] superview];
405     [view setFrame:[frameView bounds]];
406     [frameView addSubview:view];
407
408     [[window() standardWindowButton:NSWindowZoomButton] setHidden:YES];
409     [[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
410     [[window() standardWindowButton:NSWindowCloseButton] setHidden:YES];
411
412     // Some third-party OS X utilities check the zoom button's enabled state to
413     // determine whether to show custom UI on hover, so we disable it here to
414     // prevent them from doing so in a frameless app window.
415     [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO];
416
417     InstallDraggableRegionViews();
418   }
419 }
420
421 void NativeAppWindowCocoa::UninstallView() {
422   NSView* view = web_contents()->GetView()->GetNativeView();
423   [view removeFromSuperview];
424 }
425
426 bool NativeAppWindowCocoa::IsActive() const {
427   return [window() isKeyWindow];
428 }
429
430 bool NativeAppWindowCocoa::IsMaximized() const {
431   return is_maximized_;
432 }
433
434 bool NativeAppWindowCocoa::IsMinimized() const {
435   return [window() isMiniaturized];
436 }
437
438 bool NativeAppWindowCocoa::IsFullscreen() const {
439   return is_fullscreen_;
440 }
441
442 void NativeAppWindowCocoa::SetFullscreen(int fullscreen_types) {
443   bool fullscreen = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE);
444   if (fullscreen == is_fullscreen_)
445     return;
446   is_fullscreen_ = fullscreen;
447
448   if (base::mac::IsOSLionOrLater()) {
449     // If going fullscreen, but the window is constrained (fullscreen UI control
450     // is disabled), temporarily enable it. It will be disabled again on leaving
451     // fullscreen.
452     if (fullscreen && !shows_fullscreen_controls_)
453       SetFullScreenCollectionBehavior(window(), true);
454     [window() toggleFullScreen:nil];
455     return;
456   }
457
458   DCHECK(base::mac::IsOSSnowLeopard());
459
460   // Fade to black.
461   const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
462   bool did_fade_out = false;
463   CGDisplayFadeReservationToken token;
464   if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token) ==
465       kCGErrorSuccess) {
466     did_fade_out = true;
467     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
468         kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
469   }
470
471   // Since frameless windows insert the WebContentsView into the NSThemeFrame
472   // ([[window contentView] superview]), and since that NSThemeFrame is
473   // destroyed and recreated when we change the styleMask of the window, we
474   // need to remove the view from the window when we change the style, and
475   // add it back afterwards.
476   UninstallView();
477   if (fullscreen) {
478     UpdateRestoredBounds();
479     [window() setStyleMask:NSBorderlessWindowMask];
480     [window() setFrame:[window()
481         frameRectForContentRect:[[window() screen] frame]]
482                display:YES];
483     base::mac::RequestFullScreen(base::mac::kFullScreenModeAutoHideAll);
484   } else {
485     base::mac::ReleaseFullScreen(base::mac::kFullScreenModeAutoHideAll);
486     [window() setStyleMask:GetWindowStyleMask()];
487     [window() setFrame:restored_bounds_ display:YES];
488   }
489   InstallView();
490
491   // Fade back in.
492   if (did_fade_out) {
493     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
494         kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
495     CGReleaseDisplayFadeReservation(token);
496   }
497 }
498
499 bool NativeAppWindowCocoa::IsFullscreenOrPending() const {
500   return is_fullscreen_;
501 }
502
503 bool NativeAppWindowCocoa::IsDetached() const {
504   return false;
505 }
506
507 gfx::NativeWindow NativeAppWindowCocoa::GetNativeWindow() {
508   return window();
509 }
510
511 gfx::Rect NativeAppWindowCocoa::GetRestoredBounds() const {
512   // Flip coordinates based on the primary screen.
513   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
514   NSRect frame = restored_bounds_;
515   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
516   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
517   return bounds;
518 }
519
520 ui::WindowShowState NativeAppWindowCocoa::GetRestoredState() const {
521   if (IsMaximized())
522     return ui::SHOW_STATE_MAXIMIZED;
523   if (IsFullscreen())
524     return ui::SHOW_STATE_FULLSCREEN;
525   return ui::SHOW_STATE_NORMAL;
526 }
527
528 gfx::Rect NativeAppWindowCocoa::GetBounds() const {
529   // Flip coordinates based on the primary screen.
530   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
531   NSRect frame = [window() frame];
532   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
533   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
534   return bounds;
535 }
536
537 void NativeAppWindowCocoa::Show() {
538   is_hidden_ = false;
539
540   if (is_hidden_with_app_) {
541     // If there is a shim to gently request attention, return here. Otherwise
542     // show the window as usual.
543     if (apps::ExtensionAppShimHandler::RequestUserAttentionForWindow(
544             app_window_)) {
545       return;
546     }
547   }
548
549   [window_controller_ showWindow:nil];
550   Activate();
551 }
552
553 void NativeAppWindowCocoa::ShowInactive() {
554   is_hidden_ = false;
555   [window() orderFront:window_controller_];
556 }
557
558 void NativeAppWindowCocoa::Hide() {
559   is_hidden_ = true;
560   HideWithoutMarkingHidden();
561 }
562
563 void NativeAppWindowCocoa::Close() {
564   [window() performClose:nil];
565 }
566
567 void NativeAppWindowCocoa::Activate() {
568   [BrowserWindowUtils activateWindowForController:window_controller_];
569 }
570
571 void NativeAppWindowCocoa::Deactivate() {
572   // TODO(jcivelli): http://crbug.com/51364 Implement me.
573   NOTIMPLEMENTED();
574 }
575
576 void NativeAppWindowCocoa::Maximize() {
577   UpdateRestoredBounds();
578   is_maximized_ = true;  // See top of file NOTE: State Before Update.
579   [window() setFrame:[[window() screen] visibleFrame] display:YES animate:YES];
580 }
581
582 void NativeAppWindowCocoa::Minimize() {
583   [window() miniaturize:window_controller_];
584 }
585
586 void NativeAppWindowCocoa::Restore() {
587   DCHECK(!IsFullscreenOrPending());   // SetFullscreen, not Restore, expected.
588
589   if (IsMaximized()) {
590     is_maximized_ = false;  // See top of file NOTE: State Before Update.
591     [window() setFrame:restored_bounds() display:YES animate:YES];
592   } else if (IsMinimized()) {
593     is_maximized_ = false;  // See top of file NOTE: State Before Update.
594     [window() deminiaturize:window_controller_];
595   }
596 }
597
598 void NativeAppWindowCocoa::SetBounds(const gfx::Rect& bounds) {
599   // Enforce minimum/maximum bounds.
600   gfx::Rect checked_bounds = bounds;
601
602   NSSize min_size = [window() minSize];
603   if (bounds.width() < min_size.width)
604     checked_bounds.set_width(min_size.width);
605   if (bounds.height() < min_size.height)
606     checked_bounds.set_height(min_size.height);
607   NSSize max_size = [window() maxSize];
608   if (checked_bounds.width() > max_size.width)
609     checked_bounds.set_width(max_size.width);
610   if (checked_bounds.height() > max_size.height)
611     checked_bounds.set_height(max_size.height);
612
613   NSRect cocoa_bounds = GfxToCocoaBounds(checked_bounds);
614   [window() setFrame:cocoa_bounds display:YES];
615   // setFrame: without animate: does not trigger a windowDidEndLiveResize: so
616   // call it here.
617   WindowDidFinishResize();
618 }
619
620 void NativeAppWindowCocoa::UpdateWindowIcon() {
621   // TODO(junmin): implement.
622 }
623
624 void NativeAppWindowCocoa::UpdateWindowTitle() {
625   base::string16 title = app_window_->GetTitle();
626   [window() setTitle:base::SysUTF16ToNSString(title)];
627 }
628
629 void NativeAppWindowCocoa::UpdateBadgeIcon() {
630   // TODO(benwells): implement.
631   NOTIMPLEMENTED();
632 }
633
634 void NativeAppWindowCocoa::UpdateShape(scoped_ptr<SkRegion> region) {
635   NOTIMPLEMENTED();
636 }
637
638 void NativeAppWindowCocoa::UpdateDraggableRegions(
639     const std::vector<extensions::DraggableRegion>& regions) {
640   // Draggable region is not supported for non-frameless window.
641   if (has_frame_)
642     return;
643
644   // Draggable regions is implemented by having the whole web view draggable
645   // (mouseDownCanMoveWindow) and overlaying regions that are not draggable.
646   NSView* web_view = web_contents()->GetView()->GetNativeView();
647   system_drag_exclude_areas_ = CalculateNonDraggableRegions(
648       regions, NSWidth([web_view bounds]), NSHeight([web_view bounds]));
649
650   InstallDraggableRegionViews();
651 }
652
653 SkRegion* NativeAppWindowCocoa::GetDraggableRegion() {
654   return NULL;
655 }
656
657 void NativeAppWindowCocoa::HandleKeyboardEvent(
658     const content::NativeWebKeyboardEvent& event) {
659   if (event.skip_in_browser ||
660       event.type == content::NativeWebKeyboardEvent::Char) {
661     return;
662   }
663   [window() redispatchKeyEvent:event.os_event];
664 }
665
666 void NativeAppWindowCocoa::InstallDraggableRegionViews() {
667   DCHECK(!has_frame_);
668
669   // All ControlRegionViews should be added as children of the WebContentsView,
670   // because WebContentsView will be removed and re-added when entering and
671   // leaving fullscreen mode.
672   NSView* webView = web_contents()->GetView()->GetNativeView();
673   NSInteger webViewHeight = NSHeight([webView bounds]);
674
675   // Remove all ControlRegionViews that are added last time.
676   // Note that [webView subviews] returns the view's mutable internal array and
677   // it should be copied to avoid mutating the original array while enumerating
678   // it.
679   base::scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
680   for (NSView* subview in subviews.get())
681     if ([subview isKindOfClass:[ControlRegionView class]])
682       [subview removeFromSuperview];
683
684   // Create and add ControlRegionView for each region that needs to be excluded
685   // from the dragging.
686   for (std::vector<gfx::Rect>::const_iterator iter =
687            system_drag_exclude_areas_.begin();
688        iter != system_drag_exclude_areas_.end();
689        ++iter) {
690     base::scoped_nsobject<NSView> controlRegion(
691         [[ControlRegionView alloc] initWithFrame:NSZeroRect]);
692     [controlRegion setFrame:NSMakeRect(iter->x(),
693                                        webViewHeight - iter->bottom(),
694                                        iter->width(),
695                                        iter->height())];
696     [controlRegion setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
697     [webView addSubview:controlRegion];
698   }
699 }
700
701 void NativeAppWindowCocoa::FlashFrame(bool flash) {
702   if (flash) {
703     attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
704   } else {
705     [NSApp cancelUserAttentionRequest:attention_request_id_];
706     attention_request_id_ = 0;
707   }
708 }
709
710 bool NativeAppWindowCocoa::IsAlwaysOnTop() const {
711   return [window() level] == AlwaysOnTopWindowLevel();
712 }
713
714 void NativeAppWindowCocoa::RenderViewCreated(content::RenderViewHost* rvh) {
715   if (IsActive())
716     web_contents()->GetView()->RestoreFocus();
717 }
718
719 bool NativeAppWindowCocoa::IsFrameless() const {
720   return !has_frame_;
721 }
722
723 bool NativeAppWindowCocoa::HasFrameColor() const {
724   // TODO(benwells): Implement this.
725   return false;
726 }
727
728 SkColor NativeAppWindowCocoa::FrameColor() const {
729   // TODO(benwells): Implement this.
730   return SkColor();
731 }
732
733 gfx::Insets NativeAppWindowCocoa::GetFrameInsets() const {
734   if (!has_frame_)
735     return gfx::Insets();
736
737   // Flip the coordinates based on the main screen.
738   NSInteger screen_height =
739       NSHeight([[[NSScreen screens] objectAtIndex:0] frame]);
740
741   NSRect frame_nsrect = [window() frame];
742   gfx::Rect frame_rect(NSRectToCGRect(frame_nsrect));
743   frame_rect.set_y(screen_height - NSMaxY(frame_nsrect));
744
745   NSRect content_nsrect = [window() contentRectForFrameRect:frame_nsrect];
746   gfx::Rect content_rect(NSRectToCGRect(content_nsrect));
747   content_rect.set_y(screen_height - NSMaxY(content_nsrect));
748
749   return frame_rect.InsetsFrom(content_rect);
750 }
751
752 gfx::NativeView NativeAppWindowCocoa::GetHostView() const {
753   NOTIMPLEMENTED();
754   return NULL;
755 }
756
757 gfx::Point NativeAppWindowCocoa::GetDialogPosition(const gfx::Size& size) {
758   NOTIMPLEMENTED();
759   return gfx::Point();
760 }
761
762 gfx::Size NativeAppWindowCocoa::GetMaximumDialogSize() {
763   NOTIMPLEMENTED();
764   return gfx::Size();
765 }
766
767 void NativeAppWindowCocoa::AddObserver(
768     web_modal::ModalDialogHostObserver* observer) {
769   NOTIMPLEMENTED();
770 }
771
772 void NativeAppWindowCocoa::RemoveObserver(
773     web_modal::ModalDialogHostObserver* observer) {
774   NOTIMPLEMENTED();
775 }
776
777 void NativeAppWindowCocoa::WindowWillClose() {
778   [window_controller_ setAppWindow:NULL];
779   app_window_->OnNativeWindowChanged();
780   app_window_->OnNativeClose();
781 }
782
783 void NativeAppWindowCocoa::WindowDidBecomeKey() {
784   content::RenderWidgetHostView* rwhv =
785       web_contents()->GetRenderWidgetHostView();
786   if (rwhv)
787     rwhv->SetActive(true);
788   app_window_->OnNativeWindowActivated();
789
790   web_contents()->GetView()->RestoreFocus();
791 }
792
793 void NativeAppWindowCocoa::WindowDidResignKey() {
794   // If our app is still active and we're still the key window, ignore this
795   // message, since it just means that a menu extra (on the "system status bar")
796   // was activated; we'll get another |-windowDidResignKey| if we ever really
797   // lose key window status.
798   if ([NSApp isActive] && ([NSApp keyWindow] == window()))
799     return;
800
801   web_contents()->GetView()->StoreFocus();
802
803   content::RenderWidgetHostView* rwhv =
804       web_contents()->GetRenderWidgetHostView();
805   if (rwhv)
806     rwhv->SetActive(false);
807 }
808
809 void NativeAppWindowCocoa::WindowDidFinishResize() {
810   // Update |is_maximized_| if needed:
811   // - Exit maximized state if resized.
812   // - Consider us maximized if resize places us back to maximized location.
813   //   This happens when returning from fullscreen.
814   NSRect frame = [window() frame];
815   NSRect screen = [[window() screen] visibleFrame];
816   if (!NSEqualSizes(frame.size, screen.size))
817     is_maximized_ = false;
818   else if (NSEqualPoints(frame.origin, screen.origin))
819     is_maximized_ = true;
820
821   // Update |is_fullscreen_| if needed.
822   is_fullscreen_ = ([window() styleMask] & NSFullScreenWindowMask) != 0;
823   // If not fullscreen but the window is constrained, disable the fullscreen UI
824   // control.
825   if (!is_fullscreen_ && !shows_fullscreen_controls_)
826     SetFullScreenCollectionBehavior(window(), false);
827
828   UpdateRestoredBounds();
829 }
830
831 void NativeAppWindowCocoa::WindowDidResize() {
832   app_window_->OnNativeWindowChanged();
833 }
834
835 void NativeAppWindowCocoa::WindowDidMove() {
836   UpdateRestoredBounds();
837   app_window_->OnNativeWindowChanged();
838 }
839
840 void NativeAppWindowCocoa::WindowDidMiniaturize() {
841   app_window_->OnNativeWindowChanged();
842 }
843
844 void NativeAppWindowCocoa::WindowDidDeminiaturize() {
845   app_window_->OnNativeWindowChanged();
846 }
847
848 void NativeAppWindowCocoa::WindowWillZoom() {
849   // See top of file NOTE: Maximize and Zoom.
850   if (IsMaximized())
851     Restore();
852   else
853     Maximize();
854 }
855
856 bool NativeAppWindowCocoa::HandledByExtensionCommand(NSEvent* event) {
857   return extension_keybinding_registry_->ProcessKeyEvent(
858       content::NativeWebKeyboardEvent(event));
859 }
860
861 void NativeAppWindowCocoa::ShowWithApp() {
862   is_hidden_with_app_ = false;
863   if (!is_hidden_)
864     ShowInactive();
865 }
866
867 void NativeAppWindowCocoa::HideWithApp() {
868   is_hidden_with_app_ = true;
869   HideWithoutMarkingHidden();
870 }
871
872 void NativeAppWindowCocoa::UpdateShelfMenu() {
873   // TODO(tmdiep): To be implemented for Mac.
874   NOTIMPLEMENTED();
875 }
876
877 gfx::Size NativeAppWindowCocoa::GetContentMinimumSize() const {
878   return size_constraints_.GetMinimumSize();
879 }
880
881 gfx::Size NativeAppWindowCocoa::GetContentMaximumSize() const {
882   return size_constraints_.GetMaximumSize();
883 }
884
885 void NativeAppWindowCocoa::SetContentSizeConstraints(
886     const gfx::Size& min_size, const gfx::Size& max_size) {
887   // Update the size constraints.
888   size_constraints_.set_minimum_size(min_size);
889   size_constraints_.set_maximum_size(max_size);
890
891   gfx::Size minimum_size = size_constraints_.GetMinimumSize();
892   [window() setContentMinSize:NSMakeSize(minimum_size.width(),
893                                          minimum_size.height())];
894
895   gfx::Size maximum_size = size_constraints_.GetMaximumSize();
896   const int kUnboundedSize = apps::SizeConstraints::kUnboundedSize;
897   CGFloat max_width = maximum_size.width() == kUnboundedSize ?
898       CGFLOAT_MAX : maximum_size.width();
899   CGFloat max_height = maximum_size.height() == kUnboundedSize ?
900       CGFLOAT_MAX : maximum_size.height();
901   [window() setContentMaxSize:NSMakeSize(max_width, max_height)];
902
903   // Update the window controls.
904   shows_resize_controls_ =
905       is_resizable_ && !size_constraints_.HasFixedSize();
906   shows_fullscreen_controls_ =
907       is_resizable_ && !size_constraints_.HasMaximumSize();
908
909   if (!is_fullscreen_) {
910     [window() setStyleMask:GetWindowStyleMask()];
911
912     // Set the window to participate in Lion Fullscreen mode. Setting this flag
913     // has no effect on Snow Leopard or earlier. UI controls for fullscreen are
914     // only shown for apps that have unbounded size.
915     SetFullScreenCollectionBehavior(window(), shows_fullscreen_controls_);
916   }
917
918   if (has_frame_) {
919     [window() setShowsResizeIndicator:shows_resize_controls_];
920     [[window() standardWindowButton:NSWindowZoomButton]
921         setEnabled:shows_fullscreen_controls_];
922   }
923 }
924
925 void NativeAppWindowCocoa::SetAlwaysOnTop(bool always_on_top) {
926   [window() setLevel:(always_on_top ? AlwaysOnTopWindowLevel() :
927                                       NSNormalWindowLevel)];
928 }
929
930 NativeAppWindowCocoa::~NativeAppWindowCocoa() {
931 }
932
933 ShellNSWindow* NativeAppWindowCocoa::window() const {
934   NSWindow* window = [window_controller_ window];
935   CHECK(!window || [window isKindOfClass:[ShellNSWindow class]]);
936   return static_cast<ShellNSWindow*>(window);
937 }
938
939 void NativeAppWindowCocoa::UpdateRestoredBounds() {
940   if (IsRestored(*this))
941     restored_bounds_ = [window() frame];
942 }
943
944 void NativeAppWindowCocoa::HideWithoutMarkingHidden() {
945   [window() orderOut:window_controller_];
946 }