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
  142
  143
  144
  145
  146

content / app_shim_remote_cocoa / web_contents_ns_view_bridge.mm [blame]

// 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];
}

void WebContentsNSViewBridge::UpdateWindowControlsOverlay(
    const gfx::Rect& bounding_rect) {
  [ns_view_ updateWindowControlsOverlay:bounding_rect];
}

}  // namespace remote_cocoa