MacViews: Use the ContrainedWindow animations for MODAL_TYPE_CHILD
[chromium-blink-merge.git] / ui / views / cocoa / bridged_native_widget.mm
blob3e281b595559f1fc079268f30458cbfa4e971d9a
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.
5 #import "ui/views/cocoa/bridged_native_widget.h"
7 #import <objc/runtime.h>
9 #include "base/logging.h"
10 #include "base/mac/mac_util.h"
11 #import "base/mac/sdk_forward_declarations.h"
12 #include "base/thread_task_runner_handle.h"
13 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
14 #include "ui/base/ime/input_method.h"
15 #include "ui/base/ime/input_method_factory.h"
16 #include "ui/base/ui_base_switches_util.h"
17 #include "ui/gfx/display.h"
18 #include "ui/gfx/geometry/dip_util.h"
19 #import "ui/gfx/mac/coordinate_conversion.h"
20 #import "ui/gfx/mac/nswindow_frame_controls.h"
21 #include "ui/gfx/screen.h"
22 #import "ui/views/cocoa/bridged_content_view.h"
23 #import "ui/views/cocoa/cocoa_mouse_capture.h"
24 #include "ui/views/cocoa/tooltip_manager_mac.h"
25 #import "ui/views/cocoa/views_nswindow_delegate.h"
26 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h"
27 #include "ui/views/ime/input_method_bridge.h"
28 #include "ui/views/ime/null_input_method.h"
29 #include "ui/views/view.h"
30 #include "ui/views/views_delegate.h"
31 #include "ui/views/widget/native_widget_mac.h"
32 #include "ui/views/widget/widget.h"
33 #include "ui/views/widget/widget_aura_utils.h"
34 #include "ui/views/widget/widget_delegate.h"
36 // The NSView that hosts the composited CALayer drawing the UI. It fills the
37 // window but is not hittable so that accessibility hit tests always go to the
38 // BridgedContentView.
39 @interface ViewsCompositorSuperview : NSView
40 @end
42 @implementation ViewsCompositorSuperview
43 - (NSView*)hitTest:(NSPoint)aPoint {
44   return nil;
46 @end
48 namespace {
50 int kWindowPropertiesKey;
52 float GetDeviceScaleFactorFromView(NSView* view) {
53   gfx::Display display =
54       gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
55   DCHECK(display.is_valid());
56   return display.device_scale_factor();
59 // Returns true if bounds passed to window in SetBounds should be treated as
60 // though they are in screen coordinates.
61 bool PositionWindowInScreenCoordinates(views::Widget* widget,
62                                        views::Widget::InitParams::Type type) {
63   // Replicate the logic in desktop_aura/desktop_screen_position_client.cc.
64   if (views::GetAuraWindowTypeForWidgetType(type) == ui::wm::WINDOW_TYPE_POPUP)
65     return true;
67   return widget && widget->is_top_level();
70 // Return the content size for a minimum or maximum widget size.
71 gfx::Size GetClientSizeForWindowSize(NSWindow* window,
72                                      const gfx::Size& window_size) {
73   NSRect frame_rect =
74       NSMakeRect(0, 0, window_size.width(), window_size.height());
75   // Note gfx::Size will prevent dimensions going negative. They are allowed to
76   // be zero at this point, because Widget::GetMinimumSize() may later increase
77   // the size.
78   return gfx::Size([window contentRectForFrameRect:frame_rect].size);
81 }  // namespace
83 namespace views {
85 // static
86 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize(
87     NSWindow* window,
88     const gfx::Size& content_size) {
89   NSRect content_rect =
90       NSMakeRect(0, 0, content_size.width(), content_size.height());
91   NSRect frame_rect = [window frameRectForContentRect:content_rect];
92   return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect));
95 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
96     : native_widget_mac_(parent),
97       focus_manager_(nullptr),
98       widget_type_(Widget::InitParams::TYPE_WINDOW),  // Updated in Init().
99       parent_(nullptr),
100       target_fullscreen_state_(false),
101       in_fullscreen_transition_(false),
102       window_visible_(false),
103       wants_to_be_visible_(false) {
104   DCHECK(parent);
105   window_delegate_.reset(
106       [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
109 BridgedNativeWidget::~BridgedNativeWidget() {
110   RemoveOrDestroyChildren();
111   DCHECK(child_windows_.empty());
112   SetFocusManager(NULL);
113   SetRootView(NULL);
114   DestroyCompositor();
115   if ([window_ delegate]) {
116     // If the delegate is still set on a modal dialog, it means it was not
117     // closed via [NSApplication endSheet:]. This is probably OK if the widget
118     // was never shown. But Cocoa ignores close() calls on open sheets. Calling
119     // endSheet: here would work, but it messes up assumptions elsewhere. E.g.
120     // DialogClientView assumes its delegate is alive when closing, which isn't
121     // true after endSheet: synchronously calls OnNativeWidgetDestroyed().
122     // So ban it. Modal dialogs should be closed via Widget::Close().
123     DCHECK(!native_widget_mac_->IsWindowModalSheet());
125     // If the delegate is still set, it means OnWindowWillClose has not been
126     // called and the window is still open. Calling -[NSWindow close] will
127     // synchronously call OnWindowWillClose and notify NativeWidgetMac.
128     [window_ close];
129   }
130   DCHECK(![window_ delegate]);
133 void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window,
134                                const Widget::InitParams& params) {
135   widget_type_ = params.type;
137   DCHECK(!window_);
138   window_.swap(window);
139   [window_ setDelegate:window_delegate_];
141   // Register for application hide notifications so that visibility can be
142   // properly tracked. This is not done in the delegate so that the lifetime is
143   // tied to the C++ object, rather than the delegate (which may be reference
144   // counted). This is required since the application hides do not send an
145   // orderOut: to individual windows. Unhide, however, does send an order
146   // message.
147   [[NSNotificationCenter defaultCenter]
148       addObserver:window_delegate_
149          selector:@selector(onWindowOrderChanged:)
150              name:NSApplicationDidHideNotification
151            object:nil];
153   // Validate the window's initial state, otherwise the bridge's initial
154   // tracking state will be incorrect.
155   DCHECK(![window_ isVisible]);
156   DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask);
158   if (params.parent) {
159     // Disallow creating child windows of views not currently in an NSWindow.
160     CHECK([params.parent window]);
161     BridgedNativeWidget* bridged_native_widget_parent =
162         NativeWidgetMac::GetBridgeForNativeWindow([params.parent window]);
163     // If the parent is another BridgedNativeWidget, just add to the collection
164     // of child windows it owns and manages. Otherwise, create an adapter to
165     // anchor the child widget and observe when the parent NSWindow is closed.
166     if (bridged_native_widget_parent) {
167       parent_ = bridged_native_widget_parent;
168       bridged_native_widget_parent->child_windows_.push_back(this);
169     } else {
170       parent_ = new WidgetOwnerNSWindowAdapter(this, params.parent);
171     }
172   }
174   // Set a meaningful initial bounds. Note that except for frameless widgets
175   // with no WidgetDelegate, the bounds will be set again by Widget after
176   // initializing the non-client view. In the former case, if bounds were not
177   // set at all, the creator of the Widget is expected to call SetBounds()
178   // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized
179   // (i.e. 1x1) window appearing.
180   if (!params.bounds.IsEmpty()) {
181     SetBounds(params.bounds);
182   } else {
183     // If a position is set, but no size, complain. Otherwise, a 1x1 window
184     // would appear there, which might be unexpected.
185     DCHECK(params.bounds.origin().IsOrigin())
186         << "Zero-sized windows not supported on Mac.";
188     // Otherwise, bounds is all zeroes. Cocoa will currently have the window at
189     // the bottom left of the screen. To support a client calling SetSize() only
190     // (and for consistency across platforms) put it at the top-left instead.
191     // Read back the current frame: it will be a 1x1 context rect but the frame
192     // size also depends on the window style.
193     NSRect frame_rect = [window_ frame];
194     SetBounds(gfx::Rect(gfx::Point(),
195                         gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect))));
196   }
198   // Widgets for UI controls (usually layered above web contents) start visible.
199   if (params.type == Widget::InitParams::TYPE_CONTROL)
200     SetVisibilityState(SHOW_INACTIVE);
202   // Tooltip Widgets shouldn't have their own tooltip manager, but tooltips are
203   // native on Mac, so nothing should ever want one in Widget form.
204   DCHECK_NE(params.type, Widget::InitParams::TYPE_TOOLTIP);
205   tooltip_manager_.reset(new TooltipManagerMac(this));
208 void BridgedNativeWidget::SetFocusManager(FocusManager* focus_manager) {
209   if (focus_manager_ == focus_manager)
210     return;
212   if (focus_manager_)
213     focus_manager_->RemoveFocusChangeListener(this);
215   if (focus_manager)
216     focus_manager->AddFocusChangeListener(this);
218   focus_manager_ = focus_manager;
221 void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) {
222   Widget* widget = native_widget_mac_->GetWidget();
223   // -[NSWindow contentMinSize] is only checked by Cocoa for user-initiated
224   // resizes. This is not what toolkit-views expects, so clamp. Note there is
225   // no check for maximum size (consistent with aura::Window::SetBounds()).
226   gfx::Size clamped_content_size =
227       GetClientSizeForWindowSize(window_, new_bounds.size());
228   clamped_content_size.SetToMax(widget->GetMinimumSize());
230   // A contentRect with zero width or height is a banned practice in ChromeMac,
231   // due to unpredictable OSX treatment.
232   DCHECK(!clamped_content_size.IsEmpty())
233       << "Zero-sized windows not supported on Mac";
235   if (!window_visible_ && native_widget_mac_->IsWindowModalSheet()) {
236     // Window-Modal dialogs (i.e. sheets) are positioned by Cocoa when shown for
237     // the first time. They also have no frame, so just update the content size.
238     [window_ setContentSize:NSMakeSize(clamped_content_size.width(),
239                                        clamped_content_size.height())];
240     return;
241   }
242   gfx::Rect actual_new_bounds(
243       new_bounds.origin(),
244       GetWindowSizeForClientSize(window_, clamped_content_size));
246   if (parent_ && !PositionWindowInScreenCoordinates(widget, widget_type_))
247     actual_new_bounds.Offset(parent_->GetChildWindowOffset());
249   [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds)
250             display:YES
251             animate:NO];
254 void BridgedNativeWidget::SetRootView(views::View* view) {
255   if (view == [bridged_view_ hostedView])
256     return;
258   // If this is ever false, the compositor will need to be properly torn down
259   // and replaced, pointing at the new view.
260   DCHECK(!view || !compositor_widget_);
262   [bridged_view_ clearView];
263   bridged_view_.reset();
264   // Note that there can still be references to the old |bridged_view_|
265   // floating around in Cocoa libraries at this point. However, references to
266   // the old views::View will be gone, so any method calls will become no-ops.
268   if (view) {
269     bridged_view_.reset([[BridgedContentView alloc] initWithView:view]);
270     // Objective C initializers can return nil. However, if |view| is non-NULL
271     // this should be treated as an error and caught early.
272     CHECK(bridged_view_);
273   }
274   [window_ setContentView:bridged_view_];
277 void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
278   // Ensure that:
279   //  - A window with an invisible parent is not made visible.
280   //  - A parent changing visibility updates child window visibility.
281   //    * But only when changed via this function - ignore changes via the
282   //      NSWindow API, or changes propagating out from here.
283   wants_to_be_visible_ = new_state != HIDE_WINDOW;
285   if (new_state == HIDE_WINDOW) {
286     [window_ orderOut:nil];
287     DCHECK(!window_visible_);
288     return;
289   }
291   DCHECK(wants_to_be_visible_);
292   // If the parent (or an ancestor) is hidden, return and wait for it to become
293   // visible.
294   if (parent() && !parent()->IsVisibleParent())
295     return;
297   if (native_widget_mac_->IsWindowModalSheet()) {
298     NSWindow* parent_window = parent_->GetNSWindow();
299     DCHECK(parent_window);
301     [NSApp beginSheet:window_
302         modalForWindow:parent_window
303          modalDelegate:[window_ delegate]
304         didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
305            contextInfo:nullptr];
306     return;
307   }
309   if (new_state == SHOW_AND_ACTIVATE_WINDOW) {
310     [window_ makeKeyAndOrderFront:nil];
311     [NSApp activateIgnoringOtherApps:YES];
312   } else {
313     // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a
314     // parent window. So, if there's a parent, order above that. Otherwise, this
315     // will order above all windows at the same level.
316     NSInteger parent_window_number = 0;
317     if (parent_)
318       parent_window_number = [parent_->GetNSWindow() windowNumber];
320     [window_ orderWindow:NSWindowAbove
321               relativeTo:parent_window_number];
322   }
323   DCHECK(window_visible_);
325   // For non-sheet modal types, use the constrained window animations to make
326   // the window appear.
327   if (native_widget_mac_->GetWidget()->IsModal()) {
328     base::scoped_nsobject<NSAnimation> show_animation(
329         [[ConstrainedWindowAnimationShow alloc] initWithWindow:window_]);
330     // The default mode is blocking, which would block the UI thread for the
331     // duration of the animation, but would keep it smooth. The window also
332     // hasn't yet received a frame from the compositor at this stage, so it is
333     // fully transparent until the GPU sends a frame swap IPC. For the blocking
334     // option, the animation needs to wait until AcceleratedWidgetSwapCompleted
335     // has been called at least once, otherwise it will animate nothing.
336     [show_animation setAnimationBlockingMode:NSAnimationNonblocking];
337     [show_animation startAnimation];
338   }
341 void BridgedNativeWidget::AcquireCapture() {
342   DCHECK(!HasCapture());
343   if (!window_visible_)
344     return;  // Capture on hidden windows is disallowed.
346   mouse_capture_.reset(new CocoaMouseCapture(this));
348   // Initiating global event capture with addGlobalMonitorForEventsMatchingMask:
349   // will reset the mouse cursor to an arrow. Asking the window for an update
350   // here will restore what we want. However, it can sometimes cause the cursor
351   // to flicker, once, on the initial mouseDown.
352   // TOOD(tapted): Make this unnecessary by only asking for global mouse capture
353   // for the cases that need it (e.g. menus, but not drag and drop).
354   [window_ cursorUpdate:[NSApp currentEvent]];
357 void BridgedNativeWidget::ReleaseCapture() {
358   mouse_capture_.reset();
361 bool BridgedNativeWidget::HasCapture() {
362   return mouse_capture_ && mouse_capture_->IsActive();
365 void BridgedNativeWidget::SetNativeWindowProperty(const char* name,
366                                                   void* value) {
367   NSString* key = [NSString stringWithUTF8String:name];
368   if (value) {
369     [GetWindowProperties() setObject:[NSValue valueWithPointer:value]
370                               forKey:key];
371   } else {
372     [GetWindowProperties() removeObjectForKey:key];
373   }
376 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const {
377   NSString* key = [NSString stringWithUTF8String:name];
378   return [[GetWindowProperties() objectForKey:key] pointerValue];
381 void BridgedNativeWidget::SetCursor(NSCursor* cursor) {
382   [window_delegate_ setCursor:cursor];
385 void BridgedNativeWidget::OnWindowWillClose() {
386   if (parent_) {
387     parent_->RemoveChildWindow(this);
388     parent_ = nullptr;
389   }
390   [window_ setDelegate:nil];
391   [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_];
392   native_widget_mac_->OnWindowWillClose();
395 void BridgedNativeWidget::OnFullscreenTransitionStart(
396     bool target_fullscreen_state) {
397   // Note: This can fail for fullscreen changes started externally, but a user
398   // shouldn't be able to do that if the window is invisible to begin with.
399   DCHECK(window_visible_);
401   DCHECK_NE(target_fullscreen_state, target_fullscreen_state_);
402   target_fullscreen_state_ = target_fullscreen_state;
403   in_fullscreen_transition_ = true;
405   // If going into fullscreen, store an answer for GetRestoredBounds().
406   if (target_fullscreen_state)
407     bounds_before_fullscreen_ = gfx::ScreenRectFromNSRect([window_ frame]);
410 void BridgedNativeWidget::OnFullscreenTransitionComplete(
411     bool actual_fullscreen_state) {
412   in_fullscreen_transition_ = false;
414   if (target_fullscreen_state_ == actual_fullscreen_state) {
415     // Ensure constraints are re-applied when completing a transition.
416     OnSizeConstraintsChanged();
417     return;
418   }
420   // First update to reflect reality so that OnTargetFullscreenStateChanged()
421   // expects the change.
422   target_fullscreen_state_ = actual_fullscreen_state;
423   ToggleDesiredFullscreenState();
425   // Usually ToggleDesiredFullscreenState() sets |in_fullscreen_transition_| via
426   // OnFullscreenTransitionStart(). When it does not, it means Cocoa ignored the
427   // toggleFullScreen: request. This can occur when the fullscreen transition
428   // fails and Cocoa is *about* to send windowDidFailToEnterFullScreen:.
429   // Annoyingly, for this case, Cocoa first sends windowDidExitFullScreen:.
430   if (in_fullscreen_transition_)
431     DCHECK_NE(target_fullscreen_state_, actual_fullscreen_state);
434 void BridgedNativeWidget::ToggleDesiredFullscreenState() {
435   // If there is currently an animation into or out of fullscreen, then AppKit
436   // emits the string "not in fullscreen state" to stdio and does nothing. For
437   // this case, schedule a transition back into the desired state when the
438   // animation completes.
439   if (in_fullscreen_transition_) {
440     target_fullscreen_state_ = !target_fullscreen_state_;
441     return;
442   }
444   // Going fullscreen implicitly makes the window visible. AppKit does this.
445   // That is, -[NSWindow isVisible] is always true after a call to -[NSWindow
446   // toggleFullScreen:]. Unfortunately, this change happens after AppKit calls
447   // -[NSWindowDelegate windowWillEnterFullScreen:], and AppKit doesn't send an
448   // orderWindow message. So intercepting the implicit change is hard.
449   // Luckily, to trigger externally, the window typically needs to be visible in
450   // the first place. So we can just ensure the window is visible here instead
451   // of relying on AppKit to do it, and not worry that OnVisibilityChanged()
452   // won't be called for externally triggered fullscreen requests.
453   if (!window_visible_)
454     SetVisibilityState(SHOW_INACTIVE);
456   if (base::mac::IsOSSnowLeopard()) {
457     NOTIMPLEMENTED();
458     return;  // TODO(tapted): Implement this for Snow Leopard.
459   }
461   // Enable fullscreen collection behavior because:
462   // 1: -[NSWindow toggleFullscreen:] would otherwise be ignored,
463   // 2: the fullscreen button must be enabled so the user can leave fullscreen.
464   // This will be reset when a transition out of fullscreen completes.
465   gfx::SetNSWindowCanFullscreen(window_, true);
467   [window_ toggleFullScreen:nil];
470 void BridgedNativeWidget::OnSizeChanged() {
471   gfx::Size new_size = GetClientAreaSize();
472   native_widget_mac_->GetWidget()->OnNativeWidgetSizeChanged(new_size);
473   if (layer())
474     UpdateLayerProperties();
477 void BridgedNativeWidget::OnVisibilityChanged() {
478   OnVisibilityChangedTo([window_ isVisible]);
481 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) {
482   if (window_visible_ == new_visibility)
483     return;
485   window_visible_ = new_visibility;
487   // If arriving via SetVisible(), |wants_to_be_visible_| should already be set.
488   // If made visible externally (e.g. Cmd+H), just roll with it. Don't try (yet)
489   // to distinguish being *hidden* externally from being hidden by a parent
490   // window - we might not need that.
491   if (window_visible_) {
492     wants_to_be_visible_ = true;
494     if (parent_)
495       [parent_->GetNSWindow() addChildWindow:window_ ordered:NSWindowAbove];
496   } else {
497     mouse_capture_.reset();  // Capture on hidden windows is not permitted.
499     // When becoming invisible, remove the entry in any parent's childWindow
500     // list. Cocoa's childWindow management breaks down when child windows are
501     // hidden.
502     if (parent_)
503       [parent_->GetNSWindow() removeChildWindow:window_];
504   }
506   // TODO(tapted): Investigate whether we want this for Mac. This is what Aura
507   // does, and it is what tests expect. However, because layer drawing is
508   // asynchronous (and things like deminiaturize in AppKit are not), it can
509   // result in a CALayer appearing on screen before it has been redrawn in the
510   // GPU process. This is a general problem. In content, a helper class,
511   // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:]
512   // and RenderWidgetHostView::Show() until a frame is ready.
513   if (layer()) {
514     layer()->SetVisible(window_visible_);
515     layer()->SchedulePaint(gfx::Rect(GetClientAreaSize()));
516   }
518   NotifyVisibilityChangeDown();
520   native_widget_mac_->GetWidget()->OnNativeWidgetVisibilityChanged(
521       window_visible_);
523   // Toolkit-views suppresses redraws while not visible. To prevent Cocoa asking
524   // for an "empty" draw, disable auto-display while hidden. For example, this
525   // prevents Cocoa drawing just *after* a minimize, resulting in a blank window
526   // represented in the deminiaturize animation.
527   [window_ setAutodisplay:window_visible_];
530 void BridgedNativeWidget::OnBackingPropertiesChanged() {
531   if (layer())
532     UpdateLayerProperties();
535 void BridgedNativeWidget::OnWindowKeyStatusChangedTo(bool is_key) {
536   Widget* widget = native_widget_mac()->GetWidget();
537   widget->OnNativeWidgetActivationChanged(is_key);
538   // The contentView is the BridgedContentView hosting the views::RootView. The
539   // focus manager will already know if a native subview has focus.
540   if ([window_ contentView] == [window_ firstResponder]) {
541     if (is_key) {
542       widget->OnNativeFocus();
543       widget->GetFocusManager()->RestoreFocusedView();
544     } else {
545       widget->OnNativeBlur();
546       widget->GetFocusManager()->StoreFocusedView(true);
547     }
548   }
551 void BridgedNativeWidget::OnSizeConstraintsChanged() {
552   // Don't modify the size constraints or fullscreen collection behavior while
553   // in fullscreen or during a transition. OnFullscreenTransitionComplete will
554   // reset these after leaving fullscreen.
555   if (target_fullscreen_state_ || in_fullscreen_transition_)
556     return;
558   Widget* widget = native_widget_mac()->GetWidget();
559   gfx::Size min_size = widget->GetMinimumSize();
560   gfx::Size max_size = widget->GetMaximumSize();
561   bool is_resizable = widget->widget_delegate()->CanResize();
562   bool shows_resize_controls =
563       is_resizable && (min_size.IsEmpty() || min_size != max_size);
564   bool shows_fullscreen_controls =
565       is_resizable && widget->widget_delegate()->CanMaximize();
567   gfx::ApplyNSWindowSizeConstraints(window_, min_size, max_size,
568                                     shows_resize_controls,
569                                     shows_fullscreen_controls);
572 InputMethod* BridgedNativeWidget::CreateInputMethod() {
573   if (switches::IsTextInputFocusManagerEnabled())
574     return new NullInputMethod();
576   return new InputMethodBridge(this, GetHostInputMethod(), true);
579 ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() {
580   if (!input_method_) {
581     // Delegate is NULL because Mac IME does not need DispatchKeyEventPostIME
582     // callbacks.
583     input_method_ = ui::CreateInputMethod(NULL, nil);
584   }
585   return input_method_.get();
588 gfx::Rect BridgedNativeWidget::GetRestoredBounds() const {
589   if (target_fullscreen_state_ || in_fullscreen_transition_)
590     return bounds_before_fullscreen_;
592   return gfx::ScreenRectFromNSRect([window_ frame]);
595 void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type,
596                                       bool translucent) {
597   DCHECK(bridged_view_);
598   DCHECK(!layer());
600   CreateCompositor();
601   DCHECK(compositor_);
603   SetLayer(new ui::Layer(layer_type));
604   // Note, except for controls, this will set the layer to be hidden, since it
605   // is only called during Init().
606   layer()->SetVisible(window_visible_);
607   layer()->set_delegate(this);
609   InitCompositor();
611   // Transparent window support.
612   layer()->GetCompositor()->SetHostHasTransparentBackground(translucent);
613   layer()->SetFillsBoundsOpaquely(!translucent);
614   if (translucent) {
615     [window_ setOpaque:NO];
616     [window_ setBackgroundColor:[NSColor clearColor]];
617   }
619   UpdateLayerProperties();
622 ////////////////////////////////////////////////////////////////////////////////
623 // BridgedNativeWidget, internal::InputMethodDelegate:
625 void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) {
626   DCHECK(focus_manager_);
627   native_widget_mac_->GetWidget()->OnKeyEvent(const_cast<ui::KeyEvent*>(&key));
628   if (!key.handled())
629     focus_manager_->OnKeyEvent(key);
632 ////////////////////////////////////////////////////////////////////////////////
633 // BridgedNativeWidget, CocoaMouseCaptureDelegate:
635 void BridgedNativeWidget::PostCapturedEvent(NSEvent* event) {
636   [bridged_view_ processCapturedMouseEvent:event];
639 void BridgedNativeWidget::OnMouseCaptureLost() {
640   native_widget_mac_->GetWidget()->OnMouseCaptureLost();
643 ////////////////////////////////////////////////////////////////////////////////
644 // BridgedNativeWidget, FocusChangeListener:
646 void BridgedNativeWidget::OnWillChangeFocus(View* focused_before,
647                                             View* focused_now) {
650 void BridgedNativeWidget::OnDidChangeFocus(View* focused_before,
651                                            View* focused_now) {
652   ui::TextInputClient* input_client =
653       focused_now ? focused_now->GetTextInputClient() : NULL;
654   [bridged_view_ setTextInputClient:input_client];
657 ////////////////////////////////////////////////////////////////////////////////
658 // BridgedNativeWidget, LayerDelegate:
660 void BridgedNativeWidget::OnPaintLayer(const ui::PaintContext& context) {
661   DCHECK(window_visible_);
662   native_widget_mac_->GetWidget()->OnNativeWidgetPaint(context);
665 void BridgedNativeWidget::OnDelegatedFrameDamage(
666     const gfx::Rect& damage_rect_in_dip) {
667   NOTIMPLEMENTED();
670 void BridgedNativeWidget::OnDeviceScaleFactorChanged(
671     float device_scale_factor) {
672   NOTIMPLEMENTED();
675 base::Closure BridgedNativeWidget::PrepareForLayerBoundsChange() {
676   NOTIMPLEMENTED();
677   return base::Closure();
680 ////////////////////////////////////////////////////////////////////////////////
681 // BridgedNativeWidget, AcceleratedWidgetMac:
683 NSView* BridgedNativeWidget::AcceleratedWidgetGetNSView() const {
684   return compositor_superview_;
687 bool BridgedNativeWidget::AcceleratedWidgetShouldIgnoreBackpressure() const {
688   return true;
691 void BridgedNativeWidget::AcceleratedWidgetSwapCompleted(
692     const std::vector<ui::LatencyInfo>& latency_info) {
695 void BridgedNativeWidget::AcceleratedWidgetHitError() {
696   compositor_->ScheduleFullRedraw();
699 ////////////////////////////////////////////////////////////////////////////////
700 // BridgedNativeWidget, BridgedNativeWidgetOwner:
702 NSWindow* BridgedNativeWidget::GetNSWindow() {
703   return window_;
706 gfx::Vector2d BridgedNativeWidget::GetChildWindowOffset() const {
707   return gfx::ScreenRectFromNSRect([window_ frame]).OffsetFromOrigin();
710 bool BridgedNativeWidget::IsVisibleParent() const {
711   return parent_ ? window_visible_ && parent_->IsVisibleParent()
712                  : window_visible_;
715 void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) {
716   auto location = std::find(
717       child_windows_.begin(), child_windows_.end(), child);
718   DCHECK(location != child_windows_.end());
719   child_windows_.erase(location);
721   // Note the child is sometimes removed already by AppKit. This depends on OS
722   // version, and possibly some unpredictable reference counting. Removing it
723   // here should be safe regardless.
724   [window_ removeChildWindow:child->window_];
727 ////////////////////////////////////////////////////////////////////////////////
728 // BridgedNativeWidget, private:
730 void BridgedNativeWidget::RemoveOrDestroyChildren() {
731   // TODO(tapted): Implement unowned child windows if required.
732   while (!child_windows_.empty()) {
733     // The NSWindow can only be destroyed after -[NSWindow close] is complete.
734     // Retain the window, otherwise the reference count can reach zero when the
735     // child calls back into RemoveChildWindow() via its OnWindowWillClose().
736     base::scoped_nsobject<NSWindow> child(
737         [child_windows_.back()->ns_window() retain]);
738     [child close];
739   }
742 void BridgedNativeWidget::NotifyVisibilityChangeDown() {
743   // Child windows sometimes like to close themselves in response to visibility
744   // changes. That's supported, but only with the asynchronous Widget::Close().
745   // Perform a heuristic to detect child removal that would break these loops.
746   const size_t child_count = child_windows_.size();
747   if (!window_visible_) {
748     for (BridgedNativeWidget* child : child_windows_) {
749       if (child->window_visible_)
750         [child->ns_window() orderOut:nil];
752       DCHECK(!child->window_visible_);
753       CHECK_EQ(child_count, child_windows_.size());
754     }
755     // The orderOut calls above should result in a call to OnVisibilityChanged()
756     // in each child. There, children will remove themselves from the NSWindow
757     // childWindow list as well as propagate NotifyVisibilityChangeDown() calls
758     // to any children of their own.
759     DCHECK_EQ(0u, [[window_ childWindows] count]);
760     return;
761   }
763   NSUInteger visible_children = 0;  // For a DCHECK below.
764   NSInteger parent_window_number = [window_ windowNumber];
765   for (BridgedNativeWidget* child: child_windows_) {
766     // Note: order the child windows on top, regardless of whether or not they
767     // are currently visible. They probably aren't, since the parent was hidden
768     // prior to this, but they could have been made visible in other ways.
769     if (child->wants_to_be_visible_) {
770       ++visible_children;
771       // Here -[NSWindow orderWindow:relativeTo:] is used to put the window on
772       // screen. However, that by itself is insufficient to guarantee a correct
773       // z-order relationship. If this function is being called from a z-order
774       // change in the parent, orderWindow turns out to be unreliable (i.e. the
775       // ordering doesn't always take effect). What this actually relies on is
776       // the resulting call to OnVisibilityChanged() in the child, which will
777       // then insert itself into -[NSWindow childWindows] to let Cocoa do its
778       // internal layering magic.
779       [child->ns_window() orderWindow:NSWindowAbove
780                            relativeTo:parent_window_number];
781       DCHECK(child->window_visible_);
782     }
783     CHECK_EQ(child_count, child_windows_.size());
784   }
785   DCHECK_EQ(visible_children, [[window_ childWindows] count]);
788 gfx::Size BridgedNativeWidget::GetClientAreaSize() const {
789   NSRect content_rect = [window_ contentRectForFrameRect:[window_ frame]];
790   return gfx::Size(NSWidth(content_rect), NSHeight(content_rect));
793 void BridgedNativeWidget::CreateCompositor() {
794   DCHECK(!compositor_);
795   DCHECK(!compositor_widget_);
796   DCHECK(ViewsDelegate::views_delegate);
798   ui::ContextFactory* context_factory =
799       ViewsDelegate::views_delegate->GetContextFactory();
800   DCHECK(context_factory);
802   AddCompositorSuperview();
804   // TODO(tapted): Get this value from GpuDataManagerImpl via ViewsDelegate.
805   bool needs_gl_finish_workaround = false;
807   compositor_widget_.reset(
808       new ui::AcceleratedWidgetMac(needs_gl_finish_workaround));
809   compositor_.reset(new ui::Compositor(compositor_widget_->accelerated_widget(),
810                                        context_factory,
811                                        base::ThreadTaskRunnerHandle::Get()));
812   compositor_widget_->SetNSView(this);
815 void BridgedNativeWidget::InitCompositor() {
816   DCHECK(layer());
817   float scale_factor = GetDeviceScaleFactorFromView(compositor_superview_);
818   gfx::Size size_in_dip = GetClientAreaSize();
819   compositor_->SetScaleAndSize(scale_factor,
820                                ConvertSizeToPixel(scale_factor, size_in_dip));
821   compositor_->SetRootLayer(layer());
824 void BridgedNativeWidget::DestroyCompositor() {
825   if (layer())
826     layer()->set_delegate(nullptr);
827   DestroyLayer();
829   if (!compositor_widget_) {
830     DCHECK(!compositor_);
831     return;
832   }
833   compositor_widget_->ResetNSView();
834   compositor_.reset();
835   compositor_widget_.reset();
838 void BridgedNativeWidget::AddCompositorSuperview() {
839   DCHECK(!compositor_superview_);
840   compositor_superview_.reset(
841       [[ViewsCompositorSuperview alloc] initWithFrame:[bridged_view_ bounds]]);
843   // Size and resize automatically with |bridged_view_|.
844   [compositor_superview_
845       setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
847   // Enable HiDPI backing when supported (only on 10.7+).
848   if ([compositor_superview_ respondsToSelector:
849       @selector(setWantsBestResolutionOpenGLSurface:)]) {
850     [compositor_superview_ setWantsBestResolutionOpenGLSurface:YES];
851   }
853   base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
854   [background_layer
855       setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
857   // Set the layer first to create a layer-hosting view (not layer-backed).
858   [compositor_superview_ setLayer:background_layer];
859   [compositor_superview_ setWantsLayer:YES];
861   // The UI compositor should always be the first subview, to ensure webviews
862   // are drawn on top of it.
863   DCHECK_EQ(0u, [[bridged_view_ subviews] count]);
864   [bridged_view_ addSubview:compositor_superview_];
867 void BridgedNativeWidget::UpdateLayerProperties() {
868   DCHECK(layer());
869   DCHECK(compositor_superview_);
870   gfx::Size size_in_dip = GetClientAreaSize();
871   layer()->SetBounds(gfx::Rect(size_in_dip));
873   float scale_factor = GetDeviceScaleFactorFromView(compositor_superview_);
874   compositor_->SetScaleAndSize(scale_factor,
875                                ConvertSizeToPixel(scale_factor, size_in_dip));
878 NSMutableDictionary* BridgedNativeWidget::GetWindowProperties() const {
879   NSMutableDictionary* properties = objc_getAssociatedObject(
880       window_, &kWindowPropertiesKey);
881   if (!properties) {
882     properties = [NSMutableDictionary dictionary];
883     objc_setAssociatedObject(window_, &kWindowPropertiesKey,
884                              properties, OBJC_ASSOCIATION_RETAIN);
885   }
886   return properties;
889 }  // namespace views