Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / native_widget_mac.mm
1 // Copyright 2014 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 "ui/views/widget/native_widget_mac.h"
6
7 #import <Cocoa/Cocoa.h>
8
9 #include "base/mac/foundation_util.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "ui/gfx/font_list.h"
13 #import "ui/gfx/mac/coordinate_conversion.h"
14 #include "ui/native_theme/native_theme.h"
15 #import "ui/views/cocoa/bridged_content_view.h"
16 #import "ui/views/cocoa/bridged_native_widget.h"
17 #import "ui/views/cocoa/views_nswindow_delegate.h"
18 #include "ui/views/window/native_frame_view.h"
19
20 @interface NativeWidgetMacNSWindow : NSWindow
21 - (ViewsNSWindowDelegate*)viewsNSWindowDelegate;
22 @end
23
24 @implementation NativeWidgetMacNSWindow
25
26 - (ViewsNSWindowDelegate*)viewsNSWindowDelegate {
27   return base::mac::ObjCCastStrict<ViewsNSWindowDelegate>([self delegate]);
28 }
29
30 // Override canBecome{Key,Main}Window to always return YES, otherwise Windows
31 // with a styleMask of NSBorderlessWindowMask default to NO.
32 - (BOOL)canBecomeKeyWindow {
33   return YES;
34 }
35
36 - (BOOL)canBecomeMainWindow {
37   return YES;
38 }
39
40 // Override orderWindow to intercept visibility changes, since there is no way
41 // to observe these changes via NSWindowDelegate.
42 - (void)orderWindow:(NSWindowOrderingMode)orderingMode
43          relativeTo:(NSInteger)otherWindowNumber {
44   [[self viewsNSWindowDelegate] onWindowOrderWillChange:orderingMode];
45   [super orderWindow:orderingMode relativeTo:otherWindowNumber];
46   [[self viewsNSWindowDelegate] onWindowOrderChanged];
47 }
48
49 @end
50
51 namespace views {
52 namespace {
53
54 NSInteger StyleMaskForParams(const Widget::InitParams& params) {
55   // TODO(tapted): Determine better masks when there are use cases for it.
56   if (params.remove_standard_frame)
57     return NSBorderlessWindowMask;
58
59   if (params.type == Widget::InitParams::TYPE_WINDOW) {
60     return NSTitledWindowMask | NSClosableWindowMask |
61            NSMiniaturizableWindowMask | NSResizableWindowMask;
62   }
63   return NSBorderlessWindowMask;
64 }
65
66 NSRect ValidateContentRect(NSRect content_rect) {
67   // A contentRect with zero width or height is a banned practice in Chrome, due
68   // to unpredictable OSX treatment. For now, silently give a minimum dimension.
69   // TODO(tapted): Add a DCHECK, or add emulation logic (e.g. to auto-hide).
70   if (NSWidth(content_rect) == 0)
71     content_rect.size.width = 1;
72
73   if (NSHeight(content_rect) == 0)
74     content_rect.size.height = 1;
75
76   return content_rect;
77 }
78
79 gfx::Size WindowSizeForClientAreaSize(NSWindow* window, const gfx::Size& size) {
80   NSRect content_rect = NSMakeRect(0, 0, size.width(), size.height());
81   NSRect frame_rect = [window frameRectForContentRect:content_rect];
82   return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect));
83 }
84
85 }  // namespace
86
87 ////////////////////////////////////////////////////////////////////////////////
88 // NativeWidgetMac, public:
89
90 NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate)
91     : delegate_(delegate),
92       bridge_(new BridgedNativeWidget(this)),
93       ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
94 }
95
96 NativeWidgetMac::~NativeWidgetMac() {
97   if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
98     delete delegate_;
99   else
100     CloseNow();
101 }
102
103 void NativeWidgetMac::OnWindowWillClose() {
104   delegate_->OnNativeWidgetDestroying();
105   // Note: If closed via CloseNow(), |bridge_| will already be reset. If closed
106   // by the user, or via Close() and a RunLoop, this will reset it.
107   bridge_.reset();
108   delegate_->OnNativeWidgetDestroyed();
109   if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
110     delete this;
111 }
112
113 ////////////////////////////////////////////////////////////////////////////////
114 // NativeWidgetMac, internal::NativeWidgetPrivate implementation:
115
116 void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) {
117   ownership_ = params.ownership;
118
119   NSInteger style_mask = StyleMaskForParams(params);
120   NSRect content_rect = ValidateContentRect(
121       [NSWindow contentRectForFrameRect:gfx::ScreenRectToNSRect(params.bounds)
122                               styleMask:style_mask]);
123
124   base::scoped_nsobject<NSWindow> window([[NativeWidgetMacNSWindow alloc]
125       initWithContentRect:content_rect
126                 styleMask:style_mask
127                   backing:NSBackingStoreBuffered
128                     defer:YES]);
129   [window setReleasedWhenClosed:NO];  // Owned by scoped_nsobject.
130   bridge_->Init(window, params);
131
132   delegate_->OnNativeWidgetCreated(true);
133
134   bridge_->SetFocusManager(GetWidget()->GetFocusManager());
135
136   DCHECK(GetWidget()->GetRootView());
137   bridge_->SetRootView(GetWidget()->GetRootView());
138 }
139
140 NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() {
141   return new NativeFrameView(GetWidget());
142 }
143
144 bool NativeWidgetMac::ShouldUseNativeFrame() const {
145   return true;
146 }
147
148 bool NativeWidgetMac::ShouldWindowContentsBeTransparent() const {
149   NOTIMPLEMENTED();
150   return false;
151 }
152
153 void NativeWidgetMac::FrameTypeChanged() {
154   NOTIMPLEMENTED();
155 }
156
157 Widget* NativeWidgetMac::GetWidget() {
158   return delegate_->AsWidget();
159 }
160
161 const Widget* NativeWidgetMac::GetWidget() const {
162   return delegate_->AsWidget();
163 }
164
165 gfx::NativeView NativeWidgetMac::GetNativeView() const {
166   // Returns a BridgedContentView, unless there is no views::RootView set.
167   return [GetNativeWindow() contentView];
168 }
169
170 gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const {
171   return bridge_ ? bridge_->ns_window() : nil;
172 }
173
174 Widget* NativeWidgetMac::GetTopLevelWidget() {
175   NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
176   return native_widget ? native_widget->GetWidget() : NULL;
177 }
178
179 const ui::Compositor* NativeWidgetMac::GetCompositor() const {
180   NOTIMPLEMENTED();
181   return NULL;
182 }
183
184 const ui::Layer* NativeWidgetMac::GetLayer() const {
185   NOTIMPLEMENTED();
186   return NULL;
187 }
188
189 void NativeWidgetMac::ReorderNativeViews() {
190   if (bridge_)
191     bridge_->SetRootView(GetWidget()->GetRootView());
192 }
193
194 void NativeWidgetMac::ViewRemoved(View* view) {
195   NOTIMPLEMENTED();
196 }
197
198 void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) {
199   NOTIMPLEMENTED();
200 }
201
202 void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const {
203   NOTIMPLEMENTED();
204   return NULL;
205 }
206
207 TooltipManager* NativeWidgetMac::GetTooltipManager() const {
208   NOTIMPLEMENTED();
209   return NULL;
210 }
211
212 void NativeWidgetMac::SetCapture() {
213   NOTIMPLEMENTED();
214 }
215
216 void NativeWidgetMac::ReleaseCapture() {
217   NOTIMPLEMENTED();
218 }
219
220 bool NativeWidgetMac::HasCapture() const {
221   NOTIMPLEMENTED();
222   return false;
223 }
224
225 InputMethod* NativeWidgetMac::CreateInputMethod() {
226   return bridge_ ? bridge_->CreateInputMethod() : NULL;
227 }
228
229 internal::InputMethodDelegate* NativeWidgetMac::GetInputMethodDelegate() {
230   return bridge_.get();
231 }
232
233 ui::InputMethod* NativeWidgetMac::GetHostInputMethod() {
234   return bridge_ ? bridge_->GetHostInputMethod() : NULL;
235 }
236
237 void NativeWidgetMac::CenterWindow(const gfx::Size& size) {
238   SetSize(WindowSizeForClientAreaSize(GetNativeWindow(), size));
239   // Note that this is not the precise center of screen, but it is the standard
240   // location for windows like dialogs to appear on screen for Mac.
241   // TODO(tapted): If there is a parent window, center in that instead.
242   [GetNativeWindow() center];
243 }
244
245 void NativeWidgetMac::GetWindowPlacement(gfx::Rect* bounds,
246                                          ui::WindowShowState* maximized) const {
247   NOTIMPLEMENTED();
248 }
249
250 bool NativeWidgetMac::SetWindowTitle(const base::string16& title) {
251   NSWindow* window = GetNativeWindow();
252   NSString* current_title = [window title];
253   NSString* new_title = SysUTF16ToNSString(title);
254   if ([current_title isEqualToString:new_title])
255     return false;
256
257   [window setTitle:new_title];
258   return true;
259 }
260
261 void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon,
262                                      const gfx::ImageSkia& app_icon) {
263   NOTIMPLEMENTED();
264 }
265
266 void NativeWidgetMac::InitModalType(ui::ModalType modal_type) {
267   NOTIMPLEMENTED();
268 }
269
270 gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const {
271   return gfx::ScreenRectFromNSRect([GetNativeWindow() frame]);
272 }
273
274 gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const {
275   NSWindow* window = GetNativeWindow();
276   return gfx::ScreenRectFromNSRect(
277       [window contentRectForFrameRect:[window frame]]);
278 }
279
280 gfx::Rect NativeWidgetMac::GetRestoredBounds() const {
281   return bridge_ ? bridge_->GetRestoredBounds() : gfx::Rect();
282 }
283
284 void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
285   [GetNativeWindow() setFrame:gfx::ScreenRectToNSRect(bounds)
286                       display:YES
287                       animate:NO];
288 }
289
290 void NativeWidgetMac::SetSize(const gfx::Size& size) {
291   // Ensure the top-left corner stays in-place (rather than the bottom-left,
292   // which -[NSWindow setContentSize:] would do).
293   SetBounds(gfx::Rect(GetWindowBoundsInScreen().origin(), size));
294 }
295
296 void NativeWidgetMac::StackAbove(gfx::NativeView native_view) {
297   NOTIMPLEMENTED();
298 }
299
300 void NativeWidgetMac::StackAtTop() {
301   NOTIMPLEMENTED();
302 }
303
304 void NativeWidgetMac::StackBelow(gfx::NativeView native_view) {
305   NOTIMPLEMENTED();
306 }
307
308 void NativeWidgetMac::SetShape(gfx::NativeRegion shape) {
309   NOTIMPLEMENTED();
310 }
311
312 void NativeWidgetMac::Close() {
313   if (!bridge_)
314     return;
315
316   // Clear the view early to suppress repaints.
317   bridge_->SetRootView(NULL);
318
319   NSWindow* window = GetNativeWindow();
320   // Calling performClose: will momentarily highlight the close button, but
321   // AppKit will reject it if there is no close button.
322   SEL close_selector = ([window styleMask] & NSClosableWindowMask)
323                            ? @selector(performClose:)
324                            : @selector(close);
325   [window performSelector:close_selector withObject:nil afterDelay:0];
326 }
327
328 void NativeWidgetMac::CloseNow() {
329   // Reset |bridge_| to NULL before destroying it.
330   scoped_ptr<BridgedNativeWidget> bridge(bridge_.Pass());
331 }
332
333 void NativeWidgetMac::Show() {
334   ShowWithWindowState(ui::SHOW_STATE_NORMAL);
335 }
336
337 void NativeWidgetMac::Hide() {
338   NOTIMPLEMENTED();
339 }
340
341 void NativeWidgetMac::ShowMaximizedWithBounds(
342     const gfx::Rect& restored_bounds) {
343   NOTIMPLEMENTED();
344 }
345
346 void NativeWidgetMac::ShowWithWindowState(ui::WindowShowState state) {
347   switch (state) {
348     case ui::SHOW_STATE_DEFAULT:
349     case ui::SHOW_STATE_NORMAL:
350     case ui::SHOW_STATE_INACTIVE:
351       break;
352     case ui::SHOW_STATE_MINIMIZED:
353     case ui::SHOW_STATE_MAXIMIZED:
354     case ui::SHOW_STATE_FULLSCREEN:
355       NOTIMPLEMENTED();
356       break;
357     case ui::SHOW_STATE_END:
358       NOTREACHED();
359       break;
360   }
361   if (state == ui::SHOW_STATE_INACTIVE) {
362     if (!IsVisible())
363       [GetNativeWindow() orderBack:nil];
364   } else {
365     Activate();
366   }
367 }
368
369 bool NativeWidgetMac::IsVisible() const {
370   return [GetNativeWindow() isVisible];
371 }
372
373 void NativeWidgetMac::Activate() {
374   [GetNativeWindow() makeKeyAndOrderFront:nil];
375   [NSApp activateIgnoringOtherApps:YES];
376 }
377
378 void NativeWidgetMac::Deactivate() {
379   NOTIMPLEMENTED();
380 }
381
382 bool NativeWidgetMac::IsActive() const {
383   // To behave like ::GetActiveWindow on Windows, IsActive() must return the
384   // "active" window attached to the calling application. NSWindow provides
385   // -isKeyWindow and -isMainWindow, but these are system-wide and update
386   // asynchronously. A window can not be main or key on Mac without the
387   // application being active.
388   // Here, define the active window as the frontmost visible window in the
389   // application.
390   // Note that this might not be the keyWindow, even when Chrome is active.
391   // Also note that -[NSApplication orderedWindows] excludes panels and other
392   // "unscriptable" windows, but includes invisible windows.
393   if (!IsVisible())
394     return false;
395
396   NSWindow* window = GetNativeWindow();
397   for (NSWindow* other_window in [NSApp orderedWindows]) {
398     if ([window isEqual:other_window])
399       return true;
400
401     if ([other_window isVisible])
402       return false;
403   }
404
405   return false;
406 }
407
408 void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) {
409   NOTIMPLEMENTED();
410 }
411
412 bool NativeWidgetMac::IsAlwaysOnTop() const {
413   NOTIMPLEMENTED();
414   return false;
415 }
416
417 void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) {
418   NOTIMPLEMENTED();
419 }
420
421 void NativeWidgetMac::Maximize() {
422   NOTIMPLEMENTED();  // See IsMaximized().
423 }
424
425 void NativeWidgetMac::Minimize() {
426   NOTIMPLEMENTED();
427 }
428
429 bool NativeWidgetMac::IsMaximized() const {
430   // The window frame isn't altered on Mac unless going fullscreen. The green
431   // "+" button just makes the window bigger. So, always false.
432   return false;
433 }
434
435 bool NativeWidgetMac::IsMinimized() const {
436   NOTIMPLEMENTED();
437   return false;
438 }
439
440 void NativeWidgetMac::Restore() {
441   NOTIMPLEMENTED();
442 }
443
444 void NativeWidgetMac::SetFullscreen(bool fullscreen) {
445   if (!bridge_ || fullscreen == IsFullscreen())
446     return;
447
448   bridge_->ToggleDesiredFullscreenState();
449 }
450
451 bool NativeWidgetMac::IsFullscreen() const {
452   return bridge_ && bridge_->target_fullscreen_state();
453 }
454
455 void NativeWidgetMac::SetOpacity(unsigned char opacity) {
456   NOTIMPLEMENTED();
457 }
458
459 void NativeWidgetMac::SetUseDragFrame(bool use_drag_frame) {
460   NOTIMPLEMENTED();
461 }
462
463 void NativeWidgetMac::FlashFrame(bool flash_frame) {
464   NOTIMPLEMENTED();
465 }
466
467 void NativeWidgetMac::RunShellDrag(View* view,
468                                    const ui::OSExchangeData& data,
469                                    const gfx::Point& location,
470                                    int operation,
471                                    ui::DragDropTypes::DragEventSource source) {
472   NOTIMPLEMENTED();
473 }
474
475 void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) {
476   // TODO(tapted): This should use setNeedsDisplayInRect:, once the coordinate
477   // system of |rect| has been converted.
478   [GetNativeView() setNeedsDisplay:YES];
479 }
480
481 void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) {
482   NOTIMPLEMENTED();
483 }
484
485 bool NativeWidgetMac::IsMouseEventsEnabled() const {
486   NOTIMPLEMENTED();
487   return true;
488 }
489
490 void NativeWidgetMac::ClearNativeFocus() {
491   NOTIMPLEMENTED();
492 }
493
494 gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const {
495   NOTIMPLEMENTED();
496   return gfx::Rect();
497 }
498
499 Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop(
500     const gfx::Vector2d& drag_offset,
501     Widget::MoveLoopSource source,
502     Widget::MoveLoopEscapeBehavior escape_behavior) {
503   NOTIMPLEMENTED();
504   return Widget::MOVE_LOOP_CANCELED;
505 }
506
507 void NativeWidgetMac::EndMoveLoop() {
508   NOTIMPLEMENTED();
509 }
510
511 void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) {
512   NOTIMPLEMENTED();
513 }
514
515 void NativeWidgetMac::SetVisibilityAnimationDuration(
516     const base::TimeDelta& duration) {
517   NOTIMPLEMENTED();
518 }
519
520 void NativeWidgetMac::SetVisibilityAnimationTransition(
521     Widget::VisibilityTransition transition) {
522   NOTIMPLEMENTED();
523 }
524
525 ui::NativeTheme* NativeWidgetMac::GetNativeTheme() const {
526   return ui::NativeTheme::instance();
527 }
528
529 void NativeWidgetMac::OnRootViewLayout() {
530   NOTIMPLEMENTED();
531 }
532
533 bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const {
534   return false;
535 }
536
537 void NativeWidgetMac::OnSizeConstraintsChanged() {
538   NOTIMPLEMENTED();
539 }
540
541 void NativeWidgetMac::RepostNativeEvent(gfx::NativeEvent native_event) {
542   NOTIMPLEMENTED();
543 }
544
545 ////////////////////////////////////////////////////////////////////////////////
546 // Widget, public:
547
548 bool Widget::ConvertRect(const Widget* source,
549                          const Widget* target,
550                          gfx::Rect* rect) {
551   return false;
552 }
553
554 namespace internal {
555
556 ////////////////////////////////////////////////////////////////////////////////
557 // internal::NativeWidgetPrivate, public:
558
559 // static
560 NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
561     internal::NativeWidgetDelegate* delegate) {
562   return new NativeWidgetMac(delegate);
563 }
564
565 // static
566 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
567     gfx::NativeView native_view) {
568   return GetNativeWidgetForNativeWindow([native_view window]);
569 }
570
571 // static
572 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
573     gfx::NativeWindow native_window) {
574   id<NSWindowDelegate> window_delegate = [native_window delegate];
575   if ([window_delegate respondsToSelector:@selector(nativeWidgetMac)]) {
576     ViewsNSWindowDelegate* delegate =
577         base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate);
578     return [delegate nativeWidgetMac];
579   }
580   return NULL;  // Not created by NativeWidgetMac.
581 }
582
583 // static
584 NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
585     gfx::NativeView native_view) {
586   NativeWidgetPrivate* native_widget =
587       GetNativeWidgetForNativeView(native_view);
588   if (!native_widget)
589     return NULL;
590
591   for (NativeWidgetPrivate* parent;
592        (parent = GetNativeWidgetForNativeWindow(
593             [native_widget->GetNativeWindow() parentWindow]));
594        native_widget = parent) {
595   }
596   return native_widget;
597 }
598
599 // static
600 void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
601                                              Widget::Widgets* children) {
602   NativeWidgetPrivate* native_widget =
603       GetNativeWidgetForNativeView(native_view);
604   if (!native_widget)
605     return;
606
607   // Code expects widget for |native_view| to be added to |children|.
608   if (native_widget->GetWidget())
609     children->insert(native_widget->GetWidget());
610
611   for (NSWindow* child_window : [native_widget->GetNativeWindow() childWindows])
612     GetAllChildWidgets([child_window contentView], children);
613 }
614
615 // static
616 void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view,
617                                              Widget::Widgets* owned) {
618   NOTIMPLEMENTED();
619 }
620
621 // static
622 void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
623                                              gfx::NativeView new_parent) {
624   NOTIMPLEMENTED();
625 }
626
627 // static
628 bool NativeWidgetPrivate::IsMouseButtonDown() {
629   return [NSEvent pressedMouseButtons] != 0;
630 }
631
632 // static
633 gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() {
634   NOTIMPLEMENTED();
635   return gfx::FontList();
636 }
637
638 }  // namespace internal
639 }  // namespace views