1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/app_shim_remote_cocoa/web_contents_ns_view_bridge.h"
#include "base/apple/foundation_util.h"
#import "base/task/sequenced_task_runner.h"
#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
#include "components/remote_cocoa/app_shim/ns_view_ids.h"
#import "content/app_shim_remote_cocoa/web_contents_view_cocoa.h"
#include "content/browser/web_contents/web_contents_view_mac.h"
#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
#include "ui/gfx/image/image_skia_util_mac.h"
namespace remote_cocoa {
WebContentsNSViewBridge::WebContentsNSViewBridge(
uint64_t view_id,
mojo::PendingAssociatedRemote<mojom::WebContentsNSViewHost> client)
: host_(std::move(client),
ui::WindowResizeHelperMac::Get()->task_runner()) {
ns_view_ = [[WebContentsViewCocoa alloc] initWithViewsHostableView:nullptr];
[ns_view_ setHost:host_.get()];
[ns_view_ enableDroppedScreenShotCopier];
view_id_ =
std::make_unique<remote_cocoa::ScopedNSViewIdMapping>(view_id, ns_view_);
}
WebContentsNSViewBridge::WebContentsNSViewBridge(
uint64_t view_id,
content::WebContentsViewMac* web_contents_view) {
ns_view_ = [[WebContentsViewCocoa alloc]
initWithViewsHostableView:web_contents_view];
[ns_view_ setHost:web_contents_view];
view_id_ =
std::make_unique<remote_cocoa::ScopedNSViewIdMapping>(view_id, ns_view_);
}
WebContentsNSViewBridge::~WebContentsNSViewBridge() {
// This handles the case where a renderer close call was deferred
// while the user was operating a UI control which resulted in a
// close. In that case, the Cocoa view outlives the
// WebContentsViewMac instance due to Cocoa retain count.
[ns_view_ setHost:nullptr];
[ns_view_ clearViewsHostableView];
[ns_view_ removeFromSuperview];
}
void WebContentsNSViewBridge::Bind(
mojo::PendingAssociatedReceiver<mojom::WebContentsNSView> receiver,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
receiver_.Bind(std::move(receiver), std::move(task_runner));
receiver_.set_disconnect_handler(base::BindOnce(
&WebContentsNSViewBridge::Destroy, base::Unretained(this)));
}
void WebContentsNSViewBridge::Destroy() {
delete this;
}
void WebContentsNSViewBridge::SetParentNSView(uint64_t parent_ns_view_id) {
NSView* parent_ns_view = remote_cocoa::GetNSViewFromId(parent_ns_view_id);
// If the browser passed an invalid handle, then there is no recovery.
CHECK(parent_ns_view);
[parent_ns_view addSubview:ns_view_];
}
void WebContentsNSViewBridge::ResetParentNSView() {
[ns_view_ removeFromSuperview];
}
void WebContentsNSViewBridge::SetBounds(const gfx::Rect& bounds_in_window) {
NSWindow* window = [ns_view_ window];
NSRect window_content_rect = [window contentRectForFrameRect:[window frame]];
NSRect ns_bounds_in_window =
NSMakeRect(bounds_in_window.x(),
window_content_rect.size.height - bounds_in_window.y() -
bounds_in_window.height(),
bounds_in_window.width(), bounds_in_window.height());
NSRect ns_bounds_in_superview =
[[ns_view_ superview] convertRect:ns_bounds_in_window fromView:nil];
[ns_view_ setFrame:ns_bounds_in_superview];
}
void WebContentsNSViewBridge::SetVisible(bool visible) {
// If the first responder is a child of the current view, AppKit will search
// for a new first responder during `-setHidden:`. The key view loop is
// searched for a view that can become key. Typically this search yields no
// results and the window becomes the default first responder. However if this
// occurs after an immersive fullscreen restore an infinite loop can occur
// leading to an OOM. This occurs because of the existence of an NSToolbar,
// which causes the key loop traversal to jump back and forth between the
// view's window and the AppKit owned NSToolbarFullscreenWindow which hosts
// the toolbar in immersive fullscreen. To prevent this set the window's first
// responder to nil which will make the window the first responder before the
// hide.
// TODO(http://crbug.com/40261565): Remove when FB12010731 is fixed in
// AppKit.
NativeWidgetMacNSWindow* widget_window =
base::apple::ObjCCast<NativeWidgetMacNSWindow>(ns_view_.window);
if (!visible && [widget_window immersiveFullscreen]) {
NSView* first_responder =
base::apple::ObjCCast<NSView>(ns_view_.window.firstResponder);
if ([first_responder isDescendantOf:ns_view_]) {
[ns_view_.window makeFirstResponder:nil];
}
}
[ns_view_ setHidden:!visible];
}
void WebContentsNSViewBridge::MakeFirstResponder() {
if ([ns_view_ acceptsFirstResponder])
[[ns_view_ window] makeFirstResponder:ns_view_];
}
void WebContentsNSViewBridge::TakeFocus(bool reverse) {
if (reverse)
[[ns_view_ window] selectPreviousKeyView:ns_view_];
else
[[ns_view_ window] selectNextKeyView:ns_view_];
}
void WebContentsNSViewBridge::StartDrag(const content::DropData& drop_data,
const url::Origin& source_origin,
uint32_t operation_mask,
const gfx::ImageSkia& image,
const gfx::Vector2d& image_offset,
bool is_privileged) {
NSPoint offset = NSPointFromCGPoint(
gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint());
[ns_view_ startDragWithDropData:drop_data
sourceOrigin:source_origin
dragOperationMask:operation_mask
image:gfx::NSImageFromImageSkia(image)
offset:offset
isPrivileged:is_privileged];
}
} // namespace remote_cocoa
|