Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / views / cocoa / bridged_native_widget.mm
blob24275787c04e43ac1ab87b032e13ff5f4665461c
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 #include "ui/base/ime/input_method.h"
14 #include "ui/base/ime/input_method_factory.h"
15 #include "ui/base/ui_base_switches_util.h"
16 #include "ui/gfx/display.h"
17 #include "ui/gfx/geometry/dip_util.h"
18 #import "ui/gfx/mac/coordinate_conversion.h"
19 #import "ui/gfx/mac/nswindow_frame_controls.h"
20 #include "ui/gfx/screen.h"
21 #import "ui/views/cocoa/cocoa_mouse_capture.h"
22 #import "ui/views/cocoa/bridged_content_view.h"
23 #import "ui/views/cocoa/views_nswindow_delegate.h"
24 #include "ui/views/widget/native_widget_mac.h"
25 #include "ui/views/ime/input_method_bridge.h"
26 #include "ui/views/ime/null_input_method.h"
27 #include "ui/views/view.h"
28 #include "ui/views/views_delegate.h"
29 #include "ui/views/widget/widget.h"
30 #include "ui/views/widget/widget_aura_utils.h"
31 #include "ui/views/widget/widget_delegate.h"
33 // The NSView that hosts the composited CALayer drawing the UI. It fills the
34 // window but is not hittable so that accessibility hit tests always go to the
35 // BridgedContentView.
36 @interface ViewsCompositorSuperview : NSView
37 @end
39 @implementation ViewsCompositorSuperview
40 - (NSView*)hitTest:(NSPoint)aPoint {
41   return nil;
43 @end
45 namespace {
47 int kWindowPropertiesKey;
49 float GetDeviceScaleFactorFromView(NSView* view) {
50   gfx::Display display =
51       gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
52   DCHECK(display.is_valid());
53   return display.device_scale_factor();
56 // Returns true if bounds passed to window in SetBounds should be treated as
57 // though they are in screen coordinates.
58 bool PositionWindowInScreenCoordinates(views::Widget* widget,
59                                        views::Widget::InitParams::Type type) {
60   // Replicate the logic in desktop_aura/desktop_screen_position_client.cc.
61   if (views::GetAuraWindowTypeForWidgetType(type) == ui::wm::WINDOW_TYPE_POPUP)
62     return true;
64   return widget && widget->is_top_level();
67 // Return the content size for a minimum or maximum widget size.
68 gfx::Size GetClientSizeForWindowSize(NSWindow* window,
69                                      const gfx::Size& window_size) {
70   NSRect frame_rect =
71       NSMakeRect(0, 0, window_size.width(), window_size.height());
72   // Note gfx::Size will prevent dimensions going negative. They are allowed to
73   // be zero at this point, because Widget::GetMinimumSize() may later increase
74   // the size.
75   return gfx::Size([window contentRectForFrameRect:frame_rect].size);
78 }  // namespace
80 namespace views {
82 // static
83 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize(
84     NSWindow* window,
85     const gfx::Size& content_size) {
86   NSRect content_rect =
87       NSMakeRect(0, 0, content_size.width(), content_size.height());
88   NSRect frame_rect = [window frameRectForContentRect:content_rect];
89   return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect));
92 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
93     : native_widget_mac_(parent),
94       focus_manager_(nullptr),
95       widget_type_(Widget::InitParams::TYPE_WINDOW),  // Updated in Init().
96       parent_(nullptr),
97       target_fullscreen_state_(false),
98       in_fullscreen_transition_(false),
99       window_visible_(false),
100       wants_to_be_visible_(false) {
101   DCHECK(parent);
102   window_delegate_.reset(
103       [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
106 BridgedNativeWidget::~BridgedNativeWidget() {
107   RemoveOrDestroyChildren();
108   DCHECK(child_windows_.empty());
109   SetFocusManager(NULL);
110   SetRootView(NULL);
111   DestroyCompositor();
112   if ([window_ delegate]) {
113     // If the delegate is still set on a modal dialog, it means it was not
114     // closed via [NSApplication endSheet:]. This is probably OK if the widget
115     // was never shown. But Cocoa ignores close() calls on open sheets. Calling
116     // endSheet: here would work, but it messes up assumptions elsewhere. E.g.
117     // DialogClientView assumes its delegate is alive when closing, which isn't
118     // true after endSheet: synchronously calls OnNativeWidgetDestroyed().
119     // So ban it. Modal dialogs should be closed via Widget::Close().
120     DCHECK(!native_widget_mac_->GetWidget()->IsModal());
122     // If the delegate is still set, it means OnWindowWillClose has not been
123     // called and the window is still open. Calling -[NSWindow close] will
124     // synchronously call OnWindowWillClose and notify NativeWidgetMac.
125     [window_ close];
126   }
127   DCHECK(![window_ delegate]);
130 void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window,
131                                const Widget::InitParams& params) {
132   widget_type_ = params.type;
134   DCHECK(!window_);
135   window_.swap(window);
136   [window_ setDelegate:window_delegate_];
138   // Register for application hide notifications so that visibility can be
139   // properly tracked. This is not done in the delegate so that the lifetime is
140   // tied to the C++ object, rather than the delegate (which may be reference
141   // counted). This is required since the application hides do not send an
142   // orderOut: to individual windows. Unhide, however, does send an order
143   // message.
144   [[NSNotificationCenter defaultCenter]
145       addObserver:window_delegate_
146          selector:@selector(onWindowOrderChanged:)
147              name:NSApplicationDidHideNotification
148            object:nil];
150   // Validate the window's initial state, otherwise the bridge's initial
151   // tracking state will be incorrect.
152   DCHECK(![window_ isVisible]);
153   DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask);
155   if (params.parent) {
156     // Disallow creating child windows of views not currently in an NSWindow.
157     CHECK([params.parent window]);
158     BridgedNativeWidget* parent =
159         NativeWidgetMac::GetBridgeForNativeWindow([params.parent window]);
160     // The parent could be an NSWindow without an associated Widget. That could
161     // work by observing NSWindowWillCloseNotification, but for now it's not
162     // supported, and there might not be a use-case for that.
163     CHECK(parent);
164     parent_ = parent;
165     parent->child_windows_.push_back(this);
166   }
168   // Set a meaningful initial bounds. Note that except for frameless widgets
169   // with no WidgetDelegate, the bounds will be set again by Widget after
170   // initializing the non-client view. In the former case, if bounds were not
171   // set at all, the creator of the Widget is expected to call SetBounds()
172   // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized
173   // (i.e. 1x1) window appearing.
174   if (!params.bounds.IsEmpty()) {
175     SetBounds(params.bounds);
176   } else {
177     // If a position is set, but no size, complain. Otherwise, a 1x1 window
178     // would appear there, which might be unexpected.
179     DCHECK(params.bounds.origin().IsOrigin())
180         << "Zero-sized windows not supported on Mac.";
182     // Otherwise, bounds is all zeroes. Cocoa will currently have the window at
183     // the bottom left of the screen. To support a client calling SetSize() only
184     // (and for consistency across platforms) put it at the top-left instead.
185     // Read back the current frame: it will be a 1x1 context rect but the frame
186     // size also depends on the window style.
187     NSRect frame_rect = [window_ frame];
188     SetBounds(gfx::Rect(gfx::Point(),
189                         gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect))));
190   }
192   // Widgets for UI controls (usually layered above web contents) start visible.
193   if (params.type == Widget::InitParams::TYPE_CONTROL)
194     SetVisibilityState(SHOW_INACTIVE);
197 void BridgedNativeWidget::SetFocusManager(FocusManager* focus_manager) {
198   if (focus_manager_ == focus_manager)
199     return;
201   if (focus_manager_)
202     focus_manager_->RemoveFocusChangeListener(this);
204   if (focus_manager)
205     focus_manager->AddFocusChangeListener(this);
207   focus_manager_ = focus_manager;
210 void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) {
211   Widget* widget = native_widget_mac_->GetWidget();
212   // -[NSWindow contentMinSize] is only checked by Cocoa for user-initiated
213   // resizes. This is not what toolkit-views expects, so clamp. Note there is
214   // no check for maximum size (consistent with aura::Window::SetBounds()).
215   gfx::Size clamped_content_size =
216       GetClientSizeForWindowSize(window_, new_bounds.size());
217   clamped_content_size.SetToMax(widget->GetMinimumSize());
219   // A contentRect with zero width or height is a banned practice in ChromeMac,
220   // due to unpredictable OSX treatment.
221   DCHECK(!clamped_content_size.IsEmpty())
222       << "Zero-sized windows not supported on Mac";
224   if (!window_visible_ && widget->IsModal()) {
225     // Window-Modal dialogs (i.e. sheets) are positioned by Cocoa when shown for
226     // the first time. They also have no frame, so just update the content size.
227     [window_ setContentSize:NSMakeSize(clamped_content_size.width(),
228                                        clamped_content_size.height())];
229     return;
230   }
231   gfx::Rect actual_new_bounds(
232       new_bounds.origin(),
233       GetWindowSizeForClientSize(window_, clamped_content_size));
235   if (parent_ && !PositionWindowInScreenCoordinates(widget, widget_type_))
236     actual_new_bounds.Offset(parent_->GetRestoredBounds().OffsetFromOrigin());
238   [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds)
239             display:YES
240             animate:NO];
243 void BridgedNativeWidget::SetRootView(views::View* view) {
244   if (view == [bridged_view_ hostedView])
245     return;
247   // If this is ever false, the compositor will need to be properly torn down
248   // and replaced, pointing at the new view.
249   DCHECK(!view || !compositor_widget_);
251   [bridged_view_ clearView];
252   bridged_view_.reset();
253   // Note that there can still be references to the old |bridged_view_|
254   // floating around in Cocoa libraries at this point. However, references to
255   // the old views::View will be gone, so any method calls will become no-ops.
257   if (view) {
258     bridged_view_.reset([[BridgedContentView alloc] initWithView:view]);
259     // Objective C initializers can return nil. However, if |view| is non-NULL
260     // this should be treated as an error and caught early.
261     CHECK(bridged_view_);
262   }
263   [window_ setContentView:bridged_view_];
266 void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) {
267   // Ensure that:
268   //  - A window with an invisible parent is not made visible.
269   //  - A parent changing visibility updates child window visibility.
270   //    * But only when changed via this function - ignore changes via the
271   //      NSWindow API, or changes propagating out from here.
272   wants_to_be_visible_ = new_state != HIDE_WINDOW;
274   if (new_state == HIDE_WINDOW) {
275     [window_ orderOut:nil];
276     DCHECK(!window_visible_);
277     return;
278   }
280   DCHECK(wants_to_be_visible_);
282   // If there's a hidden ancestor, return and wait for it to become visible.
283   for (BridgedNativeWidget* ancestor = parent();
284        ancestor;
285        ancestor = ancestor->parent()) {
286     if (!ancestor->window_visible_)
287       return;
288   }
290   if (native_widget_mac_->GetWidget()->IsModal()) {
291     NSWindow* parent_window = parent_->ns_window();
292     DCHECK(parent_window);
294     [NSApp beginSheet:window_
295         modalForWindow:parent_window
296          modalDelegate:[window_ delegate]
297         didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
298            contextInfo:nullptr];
299     return;
300   }
302   if (new_state == SHOW_AND_ACTIVATE_WINDOW) {
303     [window_ makeKeyAndOrderFront:nil];
304     [NSApp activateIgnoringOtherApps:YES];
305   } else {
306     // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a
307     // parent window. So, if there's a parent, order above that. Otherwise, this
308     // will order above all windows at the same level.
309     NSInteger parent_window_number = 0;
310     if (parent())
311       parent_window_number = [parent()->ns_window() windowNumber];
313     [window_ orderWindow:NSWindowAbove
314               relativeTo:parent_window_number];
315   }
316   DCHECK(window_visible_);
319 void BridgedNativeWidget::AcquireCapture() {
320   DCHECK(!HasCapture());
321   if (!window_visible_)
322     return;  // Capture on hidden windows is disallowed.
324   mouse_capture_.reset(new CocoaMouseCapture(this));
326   // Initiating global event capture with addGlobalMonitorForEventsMatchingMask:
327   // will reset the mouse cursor to an arrow. Asking the window for an update
328   // here will restore what we want. However, it can sometimes cause the cursor
329   // to flicker, once, on the initial mouseDown.
330   // TOOD(tapted): Make this unnecessary by only asking for global mouse capture
331   // for the cases that need it (e.g. menus, but not drag and drop).
332   [window_ cursorUpdate:[NSApp currentEvent]];
335 void BridgedNativeWidget::ReleaseCapture() {
336   mouse_capture_.reset();
339 bool BridgedNativeWidget::HasCapture() {
340   return mouse_capture_ && mouse_capture_->IsActive();
343 void BridgedNativeWidget::SetNativeWindowProperty(const char* name,
344                                                   void* value) {
345   NSString* key = [NSString stringWithUTF8String:name];
346   if (value) {
347     [GetWindowProperties() setObject:[NSValue valueWithPointer:value]
348                               forKey:key];
349   } else {
350     [GetWindowProperties() removeObjectForKey:key];
351   }
354 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const {
355   NSString* key = [NSString stringWithUTF8String:name];
356   return [[GetWindowProperties() objectForKey:key] pointerValue];
359 void BridgedNativeWidget::SetCursor(NSCursor* cursor) {
360   [window_delegate_ setCursor:cursor];
363 void BridgedNativeWidget::OnWindowWillClose() {
364   if (parent_)
365     parent_->RemoveChildWindow(this);
366   [window_ setDelegate:nil];
367   [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_];
368   native_widget_mac_->OnWindowWillClose();
371 void BridgedNativeWidget::OnFullscreenTransitionStart(
372     bool target_fullscreen_state) {
373   // Note: This can fail for fullscreen changes started externally, but a user
374   // shouldn't be able to do that if the window is invisible to begin with.
375   DCHECK(window_visible_);
377   DCHECK_NE(target_fullscreen_state, target_fullscreen_state_);
378   target_fullscreen_state_ = target_fullscreen_state;
379   in_fullscreen_transition_ = true;
381   // If going into fullscreen, store an answer for GetRestoredBounds().
382   if (target_fullscreen_state)
383     bounds_before_fullscreen_ = gfx::ScreenRectFromNSRect([window_ frame]);
386 void BridgedNativeWidget::OnFullscreenTransitionComplete(
387     bool actual_fullscreen_state) {
388   in_fullscreen_transition_ = false;
389   if (target_fullscreen_state_ == actual_fullscreen_state)
390     return;
392   // First update to reflect reality so that OnTargetFullscreenStateChanged()
393   // expects the change.
394   target_fullscreen_state_ = actual_fullscreen_state;
395   ToggleDesiredFullscreenState();
397   // Usually ToggleDesiredFullscreenState() sets |in_fullscreen_transition_| via
398   // OnFullscreenTransitionStart(). When it does not, it means Cocoa ignored the
399   // toggleFullScreen: request. This can occur when the fullscreen transition
400   // fails and Cocoa is *about* to send windowDidFailToEnterFullScreen:.
401   // Annoyingly, for this case, Cocoa first sends windowDidExitFullScreen:.
402   if (in_fullscreen_transition_)
403     DCHECK_NE(target_fullscreen_state_, actual_fullscreen_state);
406 void BridgedNativeWidget::ToggleDesiredFullscreenState() {
407   // If there is currently an animation into or out of fullscreen, then AppKit
408   // emits the string "not in fullscreen state" to stdio and does nothing. For
409   // this case, schedule a transition back into the desired state when the
410   // animation completes.
411   if (in_fullscreen_transition_) {
412     target_fullscreen_state_ = !target_fullscreen_state_;
413     return;
414   }
416   // Going fullscreen implicitly makes the window visible. AppKit does this.
417   // That is, -[NSWindow isVisible] is always true after a call to -[NSWindow
418   // toggleFullScreen:]. Unfortunately, this change happens after AppKit calls
419   // -[NSWindowDelegate windowWillEnterFullScreen:], and AppKit doesn't send an
420   // orderWindow message. So intercepting the implicit change is hard.
421   // Luckily, to trigger externally, the window typically needs to be visible in
422   // the first place. So we can just ensure the window is visible here instead
423   // of relying on AppKit to do it, and not worry that OnVisibilityChanged()
424   // won't be called for externally triggered fullscreen requests.
425   if (!window_visible_)
426     SetVisibilityState(SHOW_INACTIVE);
428   if (base::mac::IsOSSnowLeopard()) {
429     NOTIMPLEMENTED();
430     return;  // TODO(tapted): Implement this for Snow Leopard.
431   }
433   // Since fullscreen requests are ignored if the collection behavior does not
434   // allow it, save the collection behavior and restore it after.
435   NSWindowCollectionBehavior behavior = [window_ collectionBehavior];
436   [window_ setCollectionBehavior:behavior |
437                                  NSWindowCollectionBehaviorFullScreenPrimary];
438   [window_ toggleFullScreen:nil];
439   [window_ setCollectionBehavior:behavior];
442 void BridgedNativeWidget::OnSizeChanged() {
443   gfx::Size new_size = GetClientAreaSize();
444   native_widget_mac_->GetWidget()->OnNativeWidgetSizeChanged(new_size);
445   if (layer())
446     UpdateLayerProperties();
449 void BridgedNativeWidget::OnVisibilityChanged() {
450   OnVisibilityChangedTo([window_ isVisible]);
453 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) {
454   if (window_visible_ == new_visibility)
455     return;
457   window_visible_ = new_visibility;
459   // If arriving via SetVisible(), |wants_to_be_visible_| should already be set.
460   // If made visible externally (e.g. Cmd+H), just roll with it. Don't try (yet)
461   // to distinguish being *hidden* externally from being hidden by a parent
462   // window - we might not need that.
463   if (window_visible_) {
464     wants_to_be_visible_ = true;
466     if (parent_)
467       [parent_->ns_window() addChildWindow:window_ ordered:NSWindowAbove];
468   } else {
469     mouse_capture_.reset();  // Capture on hidden windows is not permitted.
471     // When becoming invisible, remove the entry in any parent's childWindow
472     // list. Cocoa's childWindow management breaks down when child windows are
473     // hidden.
474     if (parent_)
475       [parent_->ns_window() removeChildWindow:window_];
476   }
478   // TODO(tapted): Investigate whether we want this for Mac. This is what Aura
479   // does, and it is what tests expect. However, because layer drawing is
480   // asynchronous (and things like deminiaturize in AppKit are not), it can
481   // result in a CALayer appearing on screen before it has been redrawn in the
482   // GPU process. This is a general problem. In content, a helper class,
483   // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:]
484   // and RenderWidgetHostView::Show() until a frame is ready.
485   if (layer()) {
486     layer()->SetVisible(window_visible_);
487     layer()->SchedulePaint(gfx::Rect(GetClientAreaSize()));
488   }
490   NotifyVisibilityChangeDown();
492   native_widget_mac_->GetWidget()->OnNativeWidgetVisibilityChanged(
493       window_visible_);
495   // Toolkit-views suppresses redraws while not visible. To prevent Cocoa asking
496   // for an "empty" draw, disable auto-display while hidden. For example, this
497   // prevents Cocoa drawing just *after* a minimize, resulting in a blank window
498   // represented in the deminiaturize animation.
499   [window_ setAutodisplay:window_visible_];
502 void BridgedNativeWidget::OnBackingPropertiesChanged() {
503   if (layer())
504     UpdateLayerProperties();
507 void BridgedNativeWidget::OnWindowKeyStatusChangedTo(bool is_key) {
508   Widget* widget = native_widget_mac()->GetWidget();
509   widget->OnNativeWidgetActivationChanged(is_key);
510   // The contentView is the BridgedContentView hosting the views::RootView. The
511   // focus manager will already know if a native subview has focus.
512   if ([window_ contentView] == [window_ firstResponder]) {
513     if (is_key) {
514       widget->OnNativeFocus();
515       widget->GetFocusManager()->RestoreFocusedView();
516     } else {
517       widget->OnNativeBlur();
518       widget->GetFocusManager()->StoreFocusedView(true);
519     }
520   }
523 void BridgedNativeWidget::OnSizeConstraintsChanged() {
524   NSWindow* window = ns_window();
525   Widget* widget = native_widget_mac()->GetWidget();
526   gfx::Size min_size = widget->GetMinimumSize();
527   gfx::Size max_size = widget->GetMaximumSize();
528   bool is_resizable = widget->widget_delegate()->CanResize();
529   bool shows_resize_controls =
530       is_resizable && (min_size.IsEmpty() || min_size != max_size);
531   bool shows_fullscreen_controls =
532       is_resizable && widget->widget_delegate()->CanMaximize();
534   gfx::ApplyNSWindowSizeConstraints(window, min_size, max_size,
535                                     shows_resize_controls,
536                                     shows_fullscreen_controls);
539 InputMethod* BridgedNativeWidget::CreateInputMethod() {
540   if (switches::IsTextInputFocusManagerEnabled())
541     return new NullInputMethod();
543   return new InputMethodBridge(this, GetHostInputMethod(), true);
546 ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() {
547   if (!input_method_) {
548     // Delegate is NULL because Mac IME does not need DispatchKeyEventPostIME
549     // callbacks.
550     input_method_ = ui::CreateInputMethod(NULL, nil);
551   }
552   return input_method_.get();
555 gfx::Rect BridgedNativeWidget::GetRestoredBounds() const {
556   if (target_fullscreen_state_ || in_fullscreen_transition_)
557     return bounds_before_fullscreen_;
559   return gfx::ScreenRectFromNSRect([window_ frame]);
562 void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type,
563                                       bool translucent) {
564   DCHECK(bridged_view_);
565   DCHECK(!layer());
567   CreateCompositor();
568   DCHECK(compositor_);
570   SetLayer(new ui::Layer(layer_type));
571   // Note, except for controls, this will set the layer to be hidden, since it
572   // is only called during Init().
573   layer()->SetVisible(window_visible_);
574   layer()->set_delegate(this);
576   InitCompositor();
578   // Transparent window support.
579   layer()->GetCompositor()->SetHostHasTransparentBackground(translucent);
580   layer()->SetFillsBoundsOpaquely(!translucent);
581   if (translucent) {
582     [window_ setOpaque:NO];
583     [window_ setBackgroundColor:[NSColor clearColor]];
584   }
586   UpdateLayerProperties();
589 ////////////////////////////////////////////////////////////////////////////////
590 // BridgedNativeWidget, internal::InputMethodDelegate:
592 void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) {
593   DCHECK(focus_manager_);
594   native_widget_mac_->GetWidget()->OnKeyEvent(const_cast<ui::KeyEvent*>(&key));
595   if (!key.handled())
596     focus_manager_->OnKeyEvent(key);
599 ////////////////////////////////////////////////////////////////////////////////
600 // BridgedNativeWidget, CocoaMouseCaptureDelegate:
602 void BridgedNativeWidget::PostCapturedEvent(NSEvent* event) {
603   [bridged_view_ processCapturedMouseEvent:event];
606 void BridgedNativeWidget::OnMouseCaptureLost() {
607   native_widget_mac_->GetWidget()->OnMouseCaptureLost();
610 ////////////////////////////////////////////////////////////////////////////////
611 // BridgedNativeWidget, FocusChangeListener:
613 void BridgedNativeWidget::OnWillChangeFocus(View* focused_before,
614                                             View* focused_now) {
617 void BridgedNativeWidget::OnDidChangeFocus(View* focused_before,
618                                            View* focused_now) {
619   ui::TextInputClient* input_client =
620       focused_now ? focused_now->GetTextInputClient() : NULL;
621   [bridged_view_ setTextInputClient:input_client];
624 ////////////////////////////////////////////////////////////////////////////////
625 // BridgedNativeWidget, LayerDelegate:
627 void BridgedNativeWidget::OnPaintLayer(const ui::PaintContext& context) {
628   DCHECK(window_visible_);
629   native_widget_mac_->GetWidget()->OnNativeWidgetPaint(context);
632 void BridgedNativeWidget::OnDelegatedFrameDamage(
633     const gfx::Rect& damage_rect_in_dip) {
634   NOTIMPLEMENTED();
637 void BridgedNativeWidget::OnDeviceScaleFactorChanged(
638     float device_scale_factor) {
639   NOTIMPLEMENTED();
642 base::Closure BridgedNativeWidget::PrepareForLayerBoundsChange() {
643   NOTIMPLEMENTED();
644   return base::Closure();
647 ////////////////////////////////////////////////////////////////////////////////
648 // BridgedNativeWidget, AcceleratedWidgetMac:
650 NSView* BridgedNativeWidget::AcceleratedWidgetGetNSView() const {
651   return compositor_superview_;
654 bool BridgedNativeWidget::AcceleratedWidgetShouldIgnoreBackpressure() const {
655   return true;
658 void BridgedNativeWidget::AcceleratedWidgetSwapCompleted(
659     const std::vector<ui::LatencyInfo>& latency_info) {
662 void BridgedNativeWidget::AcceleratedWidgetHitError() {
663   compositor_->ScheduleFullRedraw();
666 ////////////////////////////////////////////////////////////////////////////////
667 // BridgedNativeWidget, private:
669 void BridgedNativeWidget::RemoveOrDestroyChildren() {
670   // TODO(tapted): Implement unowned child windows if required.
671   while (!child_windows_.empty()) {
672     // The NSWindow can only be destroyed after -[NSWindow close] is complete.
673     // Retain the window, otherwise the reference count can reach zero when the
674     // child calls back into RemoveChildWindow() via its OnWindowWillClose().
675     base::scoped_nsobject<NSWindow> child(
676         [child_windows_.back()->ns_window() retain]);
677     [child close];
678   }
681 void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) {
682   auto location = std::find(
683       child_windows_.begin(), child_windows_.end(), child);
684   DCHECK(location != child_windows_.end());
685   child_windows_.erase(location);
686   child->parent_ = nullptr;
688   // Note the child is sometimes removed already by AppKit. This depends on OS
689   // version, and possibly some unpredictable reference counting. Removing it
690   // here should be safe regardless.
691   [window_ removeChildWindow:child->window_];
694 void BridgedNativeWidget::NotifyVisibilityChangeDown() {
695   // Child windows sometimes like to close themselves in response to visibility
696   // changes. That's supported, but only with the asynchronous Widget::Close().
697   // Perform a heuristic to detect child removal that would break these loops.
698   const size_t child_count = child_windows_.size();
699   if (!window_visible_) {
700     for (BridgedNativeWidget* child : child_windows_) {
701       if (child->window_visible_)
702         [child->ns_window() orderOut:nil];
704       DCHECK(!child->window_visible_);
705       CHECK_EQ(child_count, child_windows_.size());
706     }
707     // The orderOut calls above should result in a call to OnVisibilityChanged()
708     // in each child. There, children will remove themselves from the NSWindow
709     // childWindow list as well as propagate NotifyVisibilityChangeDown() calls
710     // to any children of their own.
711     DCHECK_EQ(0u, [[window_ childWindows] count]);
712     return;
713   }
715   NSUInteger visible_children = 0;  // For a DCHECK below.
716   NSInteger parent_window_number = [window_ windowNumber];
717   for (BridgedNativeWidget* child: child_windows_) {
718     // Note: order the child windows on top, regardless of whether or not they
719     // are currently visible. They probably aren't, since the parent was hidden
720     // prior to this, but they could have been made visible in other ways.
721     if (child->wants_to_be_visible_) {
722       ++visible_children;
723       // Here -[NSWindow orderWindow:relativeTo:] is used to put the window on
724       // screen. However, that by itself is insufficient to guarantee a correct
725       // z-order relationship. If this function is being called from a z-order
726       // change in the parent, orderWindow turns out to be unreliable (i.e. the
727       // ordering doesn't always take effect). What this actually relies on is
728       // the resulting call to OnVisibilityChanged() in the child, which will
729       // then insert itself into -[NSWindow childWindows] to let Cocoa do its
730       // internal layering magic.
731       [child->ns_window() orderWindow:NSWindowAbove
732                            relativeTo:parent_window_number];
733       DCHECK(child->window_visible_);
734     }
735     CHECK_EQ(child_count, child_windows_.size());
736   }
737   DCHECK_EQ(visible_children, [[window_ childWindows] count]);
740 gfx::Size BridgedNativeWidget::GetClientAreaSize() const {
741   NSRect content_rect = [window_ contentRectForFrameRect:[window_ frame]];
742   return gfx::Size(NSWidth(content_rect), NSHeight(content_rect));
745 void BridgedNativeWidget::CreateCompositor() {
746   DCHECK(!compositor_);
747   DCHECK(!compositor_widget_);
748   DCHECK(ViewsDelegate::views_delegate);
750   ui::ContextFactory* context_factory =
751       ViewsDelegate::views_delegate->GetContextFactory();
752   DCHECK(context_factory);
754   AddCompositorSuperview();
756   // TODO(tapted): Get this value from GpuDataManagerImpl via ViewsDelegate.
757   bool needs_gl_finish_workaround = false;
759   compositor_widget_.reset(
760       new ui::AcceleratedWidgetMac(needs_gl_finish_workaround));
761   compositor_.reset(new ui::Compositor(compositor_widget_->accelerated_widget(),
762                                        context_factory,
763                                        base::ThreadTaskRunnerHandle::Get()));
764   compositor_widget_->SetNSView(this);
767 void BridgedNativeWidget::InitCompositor() {
768   DCHECK(layer());
769   float scale_factor = GetDeviceScaleFactorFromView(compositor_superview_);
770   gfx::Size size_in_dip = GetClientAreaSize();
771   compositor_->SetScaleAndSize(scale_factor,
772                                ConvertSizeToPixel(scale_factor, size_in_dip));
773   compositor_->SetRootLayer(layer());
776 void BridgedNativeWidget::DestroyCompositor() {
777   if (layer())
778     layer()->set_delegate(nullptr);
779   DestroyLayer();
781   if (!compositor_widget_) {
782     DCHECK(!compositor_);
783     return;
784   }
785   compositor_widget_->ResetNSView();
786   compositor_.reset();
787   compositor_widget_.reset();
790 void BridgedNativeWidget::AddCompositorSuperview() {
791   DCHECK(!compositor_superview_);
792   compositor_superview_.reset(
793       [[ViewsCompositorSuperview alloc] initWithFrame:[bridged_view_ bounds]]);
795   // Size and resize automatically with |bridged_view_|.
796   [compositor_superview_
797       setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
799   // Enable HiDPI backing when supported (only on 10.7+).
800   if ([compositor_superview_ respondsToSelector:
801       @selector(setWantsBestResolutionOpenGLSurface:)]) {
802     [compositor_superview_ setWantsBestResolutionOpenGLSurface:YES];
803   }
805   base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
806   [background_layer
807       setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
809   // Set the layer first to create a layer-hosting view (not layer-backed).
810   [compositor_superview_ setLayer:background_layer];
811   [compositor_superview_ setWantsLayer:YES];
813   // The UI compositor should always be the first subview, to ensure webviews
814   // are drawn on top of it.
815   DCHECK_EQ(0u, [[bridged_view_ subviews] count]);
816   [bridged_view_ addSubview:compositor_superview_];
819 void BridgedNativeWidget::UpdateLayerProperties() {
820   DCHECK(layer());
821   DCHECK(compositor_superview_);
822   gfx::Size size_in_dip = GetClientAreaSize();
823   layer()->SetBounds(gfx::Rect(size_in_dip));
825   float scale_factor = GetDeviceScaleFactorFromView(compositor_superview_);
826   compositor_->SetScaleAndSize(scale_factor,
827                                ConvertSizeToPixel(scale_factor, size_in_dip));
830 NSMutableDictionary* BridgedNativeWidget::GetWindowProperties() const {
831   NSMutableDictionary* properties = objc_getAssociatedObject(
832       window_, &kWindowPropertiesKey);
833   if (!properties) {
834     properties = [NSMutableDictionary dictionary];
835     objc_setAssociatedObject(window_, &kWindowPropertiesKey,
836                              properties, OBJC_ASSOCIATION_RETAIN);
837   }
838   return properties;
841 }  // namespace views