| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ui/views/cocoa/bridged_native_widget.h" | 5 #import "ui/views/cocoa/bridged_native_widget.h" |
| 6 | 6 |
| 7 #import <objc/runtime.h> | 7 #import <objc/runtime.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/mac/mac_util.h" | 10 #include "base/mac/mac_util.h" |
| 11 #import "base/mac/sdk_forward_declarations.h" | 11 #import "base/mac/sdk_forward_declarations.h" |
| 12 #include "base/thread_task_runner_handle.h" | 12 #include "base/thread_task_runner_handle.h" |
| 13 #include "ui/base/ime/input_method.h" | 13 #include "ui/base/ime/input_method.h" |
| 14 #include "ui/base/ime/input_method_factory.h" | 14 #include "ui/base/ime/input_method_factory.h" |
| 15 #include "ui/base/ui_base_switches_util.h" | 15 #include "ui/base/ui_base_switches_util.h" |
| 16 #include "ui/gfx/display.h" | 16 #include "ui/gfx/display.h" |
| 17 #include "ui/gfx/geometry/dip_util.h" | 17 #include "ui/gfx/geometry/dip_util.h" |
| 18 #import "ui/gfx/mac/coordinate_conversion.h" | 18 #import "ui/gfx/mac/coordinate_conversion.h" |
| 19 #import "ui/gfx/mac/nswindow_frame_controls.h" | 19 #import "ui/gfx/mac/nswindow_frame_controls.h" |
| 20 #include "ui/gfx/screen.h" | 20 #include "ui/gfx/screen.h" |
| 21 #import "ui/views/cocoa/cocoa_mouse_capture.h" | 21 #import "ui/views/cocoa/cocoa_mouse_capture.h" |
| 22 #import "ui/views/cocoa/bridged_content_view.h" | 22 #import "ui/views/cocoa/bridged_content_view.h" |
| 23 #import "ui/views/cocoa/views_nswindow_delegate.h" | 23 #import "ui/views/cocoa/views_nswindow_delegate.h" |
| 24 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" |
| 24 #include "ui/views/widget/native_widget_mac.h" | 25 #include "ui/views/widget/native_widget_mac.h" |
| 25 #include "ui/views/ime/input_method_bridge.h" | 26 #include "ui/views/ime/input_method_bridge.h" |
| 26 #include "ui/views/ime/null_input_method.h" | 27 #include "ui/views/ime/null_input_method.h" |
| 27 #include "ui/views/view.h" | 28 #include "ui/views/view.h" |
| 28 #include "ui/views/views_delegate.h" | 29 #include "ui/views/views_delegate.h" |
| 29 #include "ui/views/widget/widget.h" | 30 #include "ui/views/widget/widget.h" |
| 30 #include "ui/views/widget/widget_aura_utils.h" | 31 #include "ui/views/widget/widget_aura_utils.h" |
| 31 #include "ui/views/widget/widget_delegate.h" | 32 #include "ui/views/widget/widget_delegate.h" |
| 32 | 33 |
| 33 // The NSView that hosts the composited CALayer drawing the UI. It fills the | 34 // The NSView that hosts the composited CALayer drawing the UI. It fills the |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 object:nil]; | 149 object:nil]; |
| 149 | 150 |
| 150 // Validate the window's initial state, otherwise the bridge's initial | 151 // Validate the window's initial state, otherwise the bridge's initial |
| 151 // tracking state will be incorrect. | 152 // tracking state will be incorrect. |
| 152 DCHECK(![window_ isVisible]); | 153 DCHECK(![window_ isVisible]); |
| 153 DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask); | 154 DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask); |
| 154 | 155 |
| 155 if (params.parent) { | 156 if (params.parent) { |
| 156 // Disallow creating child windows of views not currently in an NSWindow. | 157 // Disallow creating child windows of views not currently in an NSWindow. |
| 157 CHECK([params.parent window]); | 158 CHECK([params.parent window]); |
| 158 BridgedNativeWidget* parent = | 159 BridgedNativeWidget* bridged_native_widget_parent = |
| 159 NativeWidgetMac::GetBridgeForNativeWindow([params.parent window]); | 160 NativeWidgetMac::GetBridgeForNativeWindow([params.parent window]); |
| 160 // The parent could be an NSWindow without an associated Widget. That could | 161 // If the parent is another BridgedNativeWidget, just add to the collection |
| 161 // work by observing NSWindowWillCloseNotification, but for now it's not | 162 // of child windows it owns and manages. Otherwise, create an adapter to |
| 162 // supported, and there might not be a use-case for that. | 163 // anchor the child widget and observe when the parent NSWindow is closed. |
| 163 CHECK(parent); | 164 if (bridged_native_widget_parent) { |
| 164 parent_ = parent; | 165 parent_ = bridged_native_widget_parent; |
| 165 parent->child_windows_.push_back(this); | 166 bridged_native_widget_parent->child_windows_.push_back(this); |
| 167 } else { |
| 168 parent_ = new WidgetOwnerNSWindowAdapter(this, params.parent); |
| 169 } |
| 166 } | 170 } |
| 167 | 171 |
| 168 // Set a meaningful initial bounds. Note that except for frameless widgets | 172 // Set a meaningful initial bounds. Note that except for frameless widgets |
| 169 // with no WidgetDelegate, the bounds will be set again by Widget after | 173 // 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 | 174 // 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() | 175 // set at all, the creator of the Widget is expected to call SetBounds() |
| 172 // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized | 176 // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized |
| 173 // (i.e. 1x1) window appearing. | 177 // (i.e. 1x1) window appearing. |
| 174 if (!params.bounds.IsEmpty()) { | 178 if (!params.bounds.IsEmpty()) { |
| 175 SetBounds(params.bounds); | 179 SetBounds(params.bounds); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 // the first time. They also have no frame, so just update the content size. | 230 // the first time. They also have no frame, so just update the content size. |
| 227 [window_ setContentSize:NSMakeSize(clamped_content_size.width(), | 231 [window_ setContentSize:NSMakeSize(clamped_content_size.width(), |
| 228 clamped_content_size.height())]; | 232 clamped_content_size.height())]; |
| 229 return; | 233 return; |
| 230 } | 234 } |
| 231 gfx::Rect actual_new_bounds( | 235 gfx::Rect actual_new_bounds( |
| 232 new_bounds.origin(), | 236 new_bounds.origin(), |
| 233 GetWindowSizeForClientSize(window_, clamped_content_size)); | 237 GetWindowSizeForClientSize(window_, clamped_content_size)); |
| 234 | 238 |
| 235 if (parent_ && !PositionWindowInScreenCoordinates(widget, widget_type_)) | 239 if (parent_ && !PositionWindowInScreenCoordinates(widget, widget_type_)) |
| 236 actual_new_bounds.Offset(parent_->GetRestoredBounds().OffsetFromOrigin()); | 240 actual_new_bounds.Offset(parent_->GetChildWindowOffset()); |
| 237 | 241 |
| 238 [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds) | 242 [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds) |
| 239 display:YES | 243 display:YES |
| 240 animate:NO]; | 244 animate:NO]; |
| 241 } | 245 } |
| 242 | 246 |
| 243 void BridgedNativeWidget::SetRootView(views::View* view) { | 247 void BridgedNativeWidget::SetRootView(views::View* view) { |
| 244 if (view == [bridged_view_ hostedView]) | 248 if (view == [bridged_view_ hostedView]) |
| 245 return; | 249 return; |
| 246 | 250 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 271 // NSWindow API, or changes propagating out from here. | 275 // NSWindow API, or changes propagating out from here. |
| 272 wants_to_be_visible_ = new_state != HIDE_WINDOW; | 276 wants_to_be_visible_ = new_state != HIDE_WINDOW; |
| 273 | 277 |
| 274 if (new_state == HIDE_WINDOW) { | 278 if (new_state == HIDE_WINDOW) { |
| 275 [window_ orderOut:nil]; | 279 [window_ orderOut:nil]; |
| 276 DCHECK(!window_visible_); | 280 DCHECK(!window_visible_); |
| 277 return; | 281 return; |
| 278 } | 282 } |
| 279 | 283 |
| 280 DCHECK(wants_to_be_visible_); | 284 DCHECK(wants_to_be_visible_); |
| 281 | 285 // If the parent (or an ancestor) is hidden, return and wait for it to become |
| 282 // If there's a hidden ancestor, return and wait for it to become visible. | 286 // visible. |
| 283 for (BridgedNativeWidget* ancestor = parent(); | 287 if (parent() && !parent()->IsVisibleParent()) |
| 284 ancestor; | 288 return; |
| 285 ancestor = ancestor->parent()) { | |
| 286 if (!ancestor->window_visible_) | |
| 287 return; | |
| 288 } | |
| 289 | 289 |
| 290 if (native_widget_mac_->GetWidget()->IsModal()) { | 290 if (native_widget_mac_->GetWidget()->IsModal()) { |
| 291 NSWindow* parent_window = parent_->ns_window(); | 291 NSWindow* parent_window = parent_->GetNSWindow(); |
| 292 DCHECK(parent_window); | 292 DCHECK(parent_window); |
| 293 | 293 |
| 294 [NSApp beginSheet:window_ | 294 [NSApp beginSheet:window_ |
| 295 modalForWindow:parent_window | 295 modalForWindow:parent_window |
| 296 modalDelegate:[window_ delegate] | 296 modalDelegate:[window_ delegate] |
| 297 didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) | 297 didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) |
| 298 contextInfo:nullptr]; | 298 contextInfo:nullptr]; |
| 299 return; | 299 return; |
| 300 } | 300 } |
| 301 | 301 |
| 302 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { | 302 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { |
| 303 [window_ makeKeyAndOrderFront:nil]; | 303 [window_ makeKeyAndOrderFront:nil]; |
| 304 [NSApp activateIgnoringOtherApps:YES]; | 304 [NSApp activateIgnoringOtherApps:YES]; |
| 305 } else { | 305 } else { |
| 306 // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a | 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 | 307 // parent window. So, if there's a parent, order above that. Otherwise, this |
| 308 // will order above all windows at the same level. | 308 // will order above all windows at the same level. |
| 309 NSInteger parent_window_number = 0; | 309 NSInteger parent_window_number = 0; |
| 310 if (parent()) | 310 if (parent_) |
| 311 parent_window_number = [parent()->ns_window() windowNumber]; | 311 parent_window_number = [parent_->GetNSWindow() windowNumber]; |
| 312 | 312 |
| 313 [window_ orderWindow:NSWindowAbove | 313 [window_ orderWindow:NSWindowAbove |
| 314 relativeTo:parent_window_number]; | 314 relativeTo:parent_window_number]; |
| 315 } | 315 } |
| 316 DCHECK(window_visible_); | 316 DCHECK(window_visible_); |
| 317 } | 317 } |
| 318 | 318 |
| 319 void BridgedNativeWidget::AcquireCapture() { | 319 void BridgedNativeWidget::AcquireCapture() { |
| 320 DCHECK(!HasCapture()); | 320 DCHECK(!HasCapture()); |
| 321 if (!window_visible_) | 321 if (!window_visible_) |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 354 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { | 354 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { |
| 355 NSString* key = [NSString stringWithUTF8String:name]; | 355 NSString* key = [NSString stringWithUTF8String:name]; |
| 356 return [[GetWindowProperties() objectForKey:key] pointerValue]; | 356 return [[GetWindowProperties() objectForKey:key] pointerValue]; |
| 357 } | 357 } |
| 358 | 358 |
| 359 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { | 359 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { |
| 360 [window_delegate_ setCursor:cursor]; | 360 [window_delegate_ setCursor:cursor]; |
| 361 } | 361 } |
| 362 | 362 |
| 363 void BridgedNativeWidget::OnWindowWillClose() { | 363 void BridgedNativeWidget::OnWindowWillClose() { |
| 364 if (parent_) | 364 if (parent_) { |
| 365 parent_->RemoveChildWindow(this); | 365 parent_->RemoveChildWindow(this); |
| 366 parent_ = nullptr; |
| 367 } |
| 366 [window_ setDelegate:nil]; | 368 [window_ setDelegate:nil]; |
| 367 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; | 369 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; |
| 368 native_widget_mac_->OnWindowWillClose(); | 370 native_widget_mac_->OnWindowWillClose(); |
| 369 } | 371 } |
| 370 | 372 |
| 371 void BridgedNativeWidget::OnFullscreenTransitionStart( | 373 void BridgedNativeWidget::OnFullscreenTransitionStart( |
| 372 bool target_fullscreen_state) { | 374 bool target_fullscreen_state) { |
| 373 // Note: This can fail for fullscreen changes started externally, but a user | 375 // 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. | 376 // shouldn't be able to do that if the window is invisible to begin with. |
| 375 DCHECK(window_visible_); | 377 DCHECK(window_visible_); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 457 window_visible_ = new_visibility; | 459 window_visible_ = new_visibility; |
| 458 | 460 |
| 459 // If arriving via SetVisible(), |wants_to_be_visible_| should already be set. | 461 // 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) | 462 // 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 | 463 // to distinguish being *hidden* externally from being hidden by a parent |
| 462 // window - we might not need that. | 464 // window - we might not need that. |
| 463 if (window_visible_) { | 465 if (window_visible_) { |
| 464 wants_to_be_visible_ = true; | 466 wants_to_be_visible_ = true; |
| 465 | 467 |
| 466 if (parent_) | 468 if (parent_) |
| 467 [parent_->ns_window() addChildWindow:window_ ordered:NSWindowAbove]; | 469 [parent_->GetNSWindow() addChildWindow:window_ ordered:NSWindowAbove]; |
| 468 } else { | 470 } else { |
| 469 mouse_capture_.reset(); // Capture on hidden windows is not permitted. | 471 mouse_capture_.reset(); // Capture on hidden windows is not permitted. |
| 470 | 472 |
| 471 // When becoming invisible, remove the entry in any parent's childWindow | 473 // When becoming invisible, remove the entry in any parent's childWindow |
| 472 // list. Cocoa's childWindow management breaks down when child windows are | 474 // list. Cocoa's childWindow management breaks down when child windows are |
| 473 // hidden. | 475 // hidden. |
| 474 if (parent_) | 476 if (parent_) |
| 475 [parent_->ns_window() removeChildWindow:window_]; | 477 [parent_->GetNSWindow() removeChildWindow:window_]; |
| 476 } | 478 } |
| 477 | 479 |
| 478 // TODO(tapted): Investigate whether we want this for Mac. This is what Aura | 480 // 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 | 481 // does, and it is what tests expect. However, because layer drawing is |
| 480 // asynchronous (and things like deminiaturize in AppKit are not), it can | 482 // 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 | 483 // 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, | 484 // GPU process. This is a general problem. In content, a helper class, |
| 483 // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:] | 485 // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:] |
| 484 // and RenderWidgetHostView::Show() until a frame is ready. | 486 // and RenderWidgetHostView::Show() until a frame is ready. |
| 485 if (layer()) { | 487 if (layer()) { |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 657 | 659 |
| 658 void BridgedNativeWidget::AcceleratedWidgetSwapCompleted( | 660 void BridgedNativeWidget::AcceleratedWidgetSwapCompleted( |
| 659 const std::vector<ui::LatencyInfo>& latency_info) { | 661 const std::vector<ui::LatencyInfo>& latency_info) { |
| 660 } | 662 } |
| 661 | 663 |
| 662 void BridgedNativeWidget::AcceleratedWidgetHitError() { | 664 void BridgedNativeWidget::AcceleratedWidgetHitError() { |
| 663 compositor_->ScheduleFullRedraw(); | 665 compositor_->ScheduleFullRedraw(); |
| 664 } | 666 } |
| 665 | 667 |
| 666 //////////////////////////////////////////////////////////////////////////////// | 668 //////////////////////////////////////////////////////////////////////////////// |
| 667 // BridgedNativeWidget, private: | 669 // BridgedNativeWidget, BridgedNativeWidgetOwner: |
| 668 | 670 |
| 669 void BridgedNativeWidget::RemoveOrDestroyChildren() { | 671 NSWindow* BridgedNativeWidget::GetNSWindow() { |
| 670 // TODO(tapted): Implement unowned child windows if required. | 672 return window_; |
| 671 while (!child_windows_.empty()) { | 673 } |
| 672 // The NSWindow can only be destroyed after -[NSWindow close] is complete. | 674 |
| 673 // Retain the window, otherwise the reference count can reach zero when the | 675 gfx::Vector2d BridgedNativeWidget::GetChildWindowOffset() const { |
| 674 // child calls back into RemoveChildWindow() via its OnWindowWillClose(). | 676 return gfx::ScreenRectFromNSRect([window_ frame]).OffsetFromOrigin(); |
| 675 base::scoped_nsobject<NSWindow> child( | 677 } |
| 676 [child_windows_.back()->ns_window() retain]); | 678 |
| 677 [child close]; | 679 bool BridgedNativeWidget::IsVisibleParent() const { |
| 678 } | 680 return parent_ ? window_visible_ && parent_->IsVisibleParent() |
| 681 : window_visible_; |
| 679 } | 682 } |
| 680 | 683 |
| 681 void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) { | 684 void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) { |
| 682 auto location = std::find( | 685 auto location = std::find( |
| 683 child_windows_.begin(), child_windows_.end(), child); | 686 child_windows_.begin(), child_windows_.end(), child); |
| 684 DCHECK(location != child_windows_.end()); | 687 DCHECK(location != child_windows_.end()); |
| 685 child_windows_.erase(location); | 688 child_windows_.erase(location); |
| 686 child->parent_ = nullptr; | |
| 687 | 689 |
| 688 // Note the child is sometimes removed already by AppKit. This depends on OS | 690 // Note the child is sometimes removed already by AppKit. This depends on OS |
| 689 // version, and possibly some unpredictable reference counting. Removing it | 691 // version, and possibly some unpredictable reference counting. Removing it |
| 690 // here should be safe regardless. | 692 // here should be safe regardless. |
| 691 [window_ removeChildWindow:child->window_]; | 693 [window_ removeChildWindow:child->window_]; |
| 692 } | 694 } |
| 693 | 695 |
| 696 //////////////////////////////////////////////////////////////////////////////// |
| 697 // BridgedNativeWidget, private: |
| 698 |
| 699 void BridgedNativeWidget::RemoveOrDestroyChildren() { |
| 700 // TODO(tapted): Implement unowned child windows if required. |
| 701 while (!child_windows_.empty()) { |
| 702 // The NSWindow can only be destroyed after -[NSWindow close] is complete. |
| 703 // Retain the window, otherwise the reference count can reach zero when the |
| 704 // child calls back into RemoveChildWindow() via its OnWindowWillClose(). |
| 705 base::scoped_nsobject<NSWindow> child( |
| 706 [child_windows_.back()->ns_window() retain]); |
| 707 [child close]; |
| 708 } |
| 709 } |
| 710 |
| 694 void BridgedNativeWidget::NotifyVisibilityChangeDown() { | 711 void BridgedNativeWidget::NotifyVisibilityChangeDown() { |
| 695 // Child windows sometimes like to close themselves in response to visibility | 712 // Child windows sometimes like to close themselves in response to visibility |
| 696 // changes. That's supported, but only with the asynchronous Widget::Close(). | 713 // changes. That's supported, but only with the asynchronous Widget::Close(). |
| 697 // Perform a heuristic to detect child removal that would break these loops. | 714 // Perform a heuristic to detect child removal that would break these loops. |
| 698 const size_t child_count = child_windows_.size(); | 715 const size_t child_count = child_windows_.size(); |
| 699 if (!window_visible_) { | 716 if (!window_visible_) { |
| 700 for (BridgedNativeWidget* child : child_windows_) { | 717 for (BridgedNativeWidget* child : child_windows_) { |
| 701 if (child->window_visible_) | 718 if (child->window_visible_) |
| 702 [child->ns_window() orderOut:nil]; | 719 [child->ns_window() orderOut:nil]; |
| 703 | 720 |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 832 window_, &kWindowPropertiesKey); | 849 window_, &kWindowPropertiesKey); |
| 833 if (!properties) { | 850 if (!properties) { |
| 834 properties = [NSMutableDictionary dictionary]; | 851 properties = [NSMutableDictionary dictionary]; |
| 835 objc_setAssociatedObject(window_, &kWindowPropertiesKey, | 852 objc_setAssociatedObject(window_, &kWindowPropertiesKey, |
| 836 properties, OBJC_ASSOCIATION_RETAIN); | 853 properties, OBJC_ASSOCIATION_RETAIN); |
| 837 } | 854 } |
| 838 return properties; | 855 return properties; |
| 839 } | 856 } |
| 840 | 857 |
| 841 } // namespace views | 858 } // namespace views |
| OLD | NEW |