1// Copyright (c) 2012 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.
4
5#include "content/browser/renderer_host/render_widget_host_view_mac.h"
6
7#import <Carbon/Carbon.h>
8
9#include <limits>
10#include <utility>
11
12#include "base/bind.h"
13#include "base/command_line.h"
14#include "base/feature_list.h"
15#include "base/logging.h"
16#include "base/mac/mac_util.h"
17#include "base/mac/scoped_cftyperef.h"
18#include "base/macros.h"
19#include "base/optional.h"
20#include "base/strings/sys_string_conversions.h"
21#include "base/strings/utf_string_conversions.h"
22#include "base/time/time.h"
23#include "components/remote_cocoa/browser/ns_view_ids.h"
24#include "components/remote_cocoa/common/application.mojom.h"
25#include "components/viz/common/features.h"
26#include "components/viz/common/switches.h"
27#import "content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.h"
28#import "content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h"
29#import "content/browser/accessibility/browser_accessibility_cocoa.h"
30#import "content/browser/accessibility/browser_accessibility_mac.h"
31#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
32#include "content/browser/renderer_host/cursor_manager.h"
33#include "content/browser/renderer_host/input/motion_event_web.h"
34#import "content/browser/renderer_host/input/synthetic_gesture_target_mac.h"
35#include "content/browser/renderer_host/input/web_input_event_builders_mac.h"
36#include "content/browser/renderer_host/render_view_host_delegate.h"
37#include "content/browser/renderer_host/render_view_host_impl.h"
38#include "content/browser/renderer_host/render_widget_helper.h"
39#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
40#import "content/browser/renderer_host/text_input_client_mac.h"
41#import "content/browser/renderer_host/ui_events_helper.h"
42#include "content/public/browser/browser_context.h"
43#include "content/public/browser/browser_plugin_guest_manager.h"
44#include "content/public/browser/native_web_keyboard_event.h"
45#include "content/public/browser/render_widget_host.h"
46#include "content/public/browser/web_contents.h"
47#include "skia/ext/platform_canvas.h"
48#include "skia/ext/skia_utils_mac.h"
49#include "third_party/blink/public/common/input/web_input_event.h"
50#import "ui/base/clipboard/clipboard_util_mac.h"
51#include "ui/base/cocoa/animation_utils.h"
52#include "ui/base/cocoa/cocoa_base_utils.h"
53#include "ui/base/cocoa/remote_accessibility_api.h"
54#import "ui/base/cocoa/secure_password_input.h"
55#include "ui/base/cocoa/text_services_context_menu.h"
56#include "ui/base/ime/mojom/text_input_state.mojom.h"
57#include "ui/base/mojom/attributed_string.mojom.h"
58#include "ui/base/ui_base_features.h"
59#include "ui/display/display.h"
60#include "ui/display/screen.h"
61#include "ui/events/cocoa/cocoa_event_utils.h"
62#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
63#include "ui/events/keycodes/dom/dom_code.h"
64#include "ui/events/keycodes/dom/dom_keyboard_layout_map.h"
65#include "ui/gfx/geometry/dip_util.h"
66#include "ui/gfx/mac/coordinate_conversion.h"
67
68using blink::WebInputEvent;
69using blink::WebMouseEvent;
70using blink::WebGestureEvent;
71using blink::WebTouchEvent;
72
73namespace content {
74
75////////////////////////////////////////////////////////////////////////////////
76// BrowserCompositorMacClient, public:
77
78SkColor RenderWidgetHostViewMac::BrowserCompositorMacGetGutterColor() const {
79  // When making an element on the page fullscreen the element's background
80  // may not match the page's, so use black as the gutter color to avoid
81  // flashes of brighter colors during the transition.
82  if (host()->delegate() && host()->delegate()->IsFullscreen()) {
83    return SK_ColorBLACK;
84  }
85  return last_frame_root_background_color_;
86}
87
88void RenderWidgetHostViewMac::OnFrameTokenChanged(uint32_t frame_token) {
89  OnFrameTokenChangedForView(frame_token);
90}
91
92void RenderWidgetHostViewMac::DestroyCompositorForShutdown() {
93  // When RenderWidgetHostViewMac was owned by an NSView, this function was
94  // necessary to ensure that the ui::Compositor did not outlive the
95  // infrastructure that was needed to support it.
96  // https://crbug.com/805726
97  Destroy();
98}
99
100bool RenderWidgetHostViewMac::OnBrowserCompositorSurfaceIdChanged() {
101  return host()->SynchronizeVisualProperties();
102}
103
104std::vector<viz::SurfaceId>
105RenderWidgetHostViewMac::CollectSurfaceIdsForEviction() {
106  return host()->CollectSurfaceIdsForEviction();
107}
108
109////////////////////////////////////////////////////////////////////////////////
110// AcceleratedWidgetMacNSView, public:
111
112void RenderWidgetHostViewMac::AcceleratedWidgetCALayerParamsUpdated() {
113  // Set the background color for the root layer from the frame that just
114  // swapped. See RenderWidgetHostViewAura for more details. Note that this is
115  // done only after the swap has completed, so that the background is not set
116  // before the frame is up.
117  SetBackgroundLayerColor(last_frame_root_background_color_);
118
119  // Update the contents that the NSView is displaying.
120  const gfx::CALayerParams* ca_layer_params =
121      browser_compositor_->GetLastCALayerParams();
122  if (ca_layer_params)
123    ns_view_->SetCALayerParams(*ca_layer_params);
124
125  // Take this opportunity to update the VSync parameters, if needed.
126  if (display_link_) {
127    base::TimeTicks timebase;
128    base::TimeDelta interval;
129    if (display_link_->GetVSyncParameters(&timebase, &interval))
130      browser_compositor_->UpdateVSyncParameters(timebase, interval);
131  }
132}
133
134////////////////////////////////////////////////////////////////////////////////
135// views::AccessibilityFocusOverrider::Client:
136id RenderWidgetHostViewMac::GetAccessibilityFocusedUIElement() {
137  // If content is overlayed with a focused popup from native UI code, this
138  // getter must return the current menu item as the focused element, rather
139  // than the focus within the content. An example of this occurs with the
140  // Autofill feature, where focus is actually still in the textbox although
141  // the UX acts as if focus is in the popup.
142  gfx::NativeViewAccessible popup_focus_override =
143      ui::AXPlatformNode::GetPopupFocusOverride();
144  if (popup_focus_override)
145    return popup_focus_override;
146
147  BrowserAccessibilityManager* manager =
148      host()->GetRootBrowserAccessibilityManager();
149  if (manager) {
150    BrowserAccessibility* focused_item = manager->GetFocus();
151    DCHECK(focused_item);
152    if (focused_item) {
153      BrowserAccessibilityCocoa* focused_item_cocoa =
154          ToBrowserAccessibilityCocoa(focused_item);
155      DCHECK(focused_item_cocoa);
156      if (focused_item_cocoa)
157        return focused_item_cocoa;
158    }
159  }
160  return nil;
161}
162
163///////////////////////////////////////////////////////////////////////////////
164// RenderWidgetHostViewMac, public:
165
166RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
167    : RenderWidgetHostViewBase(widget),
168      page_at_minimum_scale_(true),
169      mouse_wheel_phase_handler_(this),
170      is_loading_(false),
171      popup_parent_host_view_(nullptr),
172      popup_child_host_view_(nullptr),
173      gesture_provider_(ui::GetGestureProviderConfig(
174                            ui::GestureProviderConfigType::CURRENT_PLATFORM),
175                        this),
176      accessibility_focus_overrider_(this),
177      weak_factory_(this) {
178  // The NSView is on the other side of |ns_view_|.
179  in_process_ns_view_bridge_ =
180      std::make_unique<remote_cocoa::RenderWidgetHostNSViewBridge>(this, this);
181  ns_view_ = in_process_ns_view_bridge_.get();
182
183  // Guess that the initial screen we will be on is the screen of the current
184  // window (since that's the best guess that we have, and is usually right).
185  // https://crbug.com/357443
186  display_ =
187      display::Screen::GetScreen()->GetDisplayNearestWindow([NSApp keyWindow]);
188
189  viz::FrameSinkId frame_sink_id = host()->GetFrameSinkId();
190
191  browser_compositor_.reset(new BrowserCompositorMac(
192      this, this, host()->is_hidden(), display_, frame_sink_id));
193  DCHECK(![GetInProcessNSView() window]);
194
195  host()->SetView(this);
196
197  // Let the page-level input event router know about our surface ID
198  // namespace for surface-based hit testing.
199  if (host()->delegate() && host()->delegate()->GetInputEventRouter()) {
200    host()->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner(
201        GetFrameSinkId(), this);
202  }
203
204  RenderWidgetHostOwnerDelegate* owner_delegate = host()->owner_delegate();
205  if (owner_delegate) {
206    // TODO(mostynb): actually use prefs.  Landing this as a separate CL
207    // first to rebaseline some unreliable web tests.
208    // NOTE: This will not be run for child frame widgets, which do not have
209    // an owner delegate and won't get a RenderViewHost here.
210    ignore_result(owner_delegate->GetWebkitPreferencesForWidget());
211  }
212
213  cursor_manager_.reset(new CursorManager(this));
214
215  if (GetTextInputManager())
216    GetTextInputManager()->AddObserver(this);
217
218  host()->render_frame_metadata_provider()->AddObserver(this);
219}
220
221RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
222  if (popup_parent_host_view_) {
223    DCHECK(!popup_parent_host_view_->popup_child_host_view_ ||
224           popup_parent_host_view_->popup_child_host_view_ == this);
225    popup_parent_host_view_->popup_child_host_view_ = nullptr;
226  }
227  if (popup_child_host_view_) {
228    DCHECK(!popup_child_host_view_->popup_parent_host_view_ ||
229           popup_child_host_view_->popup_parent_host_view_ == this);
230    popup_child_host_view_->popup_parent_host_view_ = nullptr;
231  }
232}
233
234void RenderWidgetHostViewMac::MigrateNSViewBridge(
235    remote_cocoa::mojom::Application* remote_cocoa_application,
236    uint64_t parent_ns_view_id) {
237  // Destroy the previous remote accessibility element.
238  remote_window_accessible_.reset();
239
240  // Disconnect from the previous bridge (this will have the effect of
241  // destroying the associated bridge), and close the receiver (to allow it
242  // to be re-bound). Note that |in_process_ns_view_bridge_| remains valid.
243  remote_ns_view_client_receiver_.reset();
244  remote_ns_view_.reset();
245
246  // Enable accessibility focus overriding for remote NSViews.
247  accessibility_focus_overrider_.SetAppIsRemote(remote_cocoa_application !=
248                                                nullptr);
249
250  // If no host is specified, then use the locally hosted NSView.
251  if (!remote_cocoa_application) {
252    ns_view_ = in_process_ns_view_bridge_.get();
253    return;
254  }
255
256  mojo::PendingAssociatedRemote<remote_cocoa::mojom::RenderWidgetHostNSViewHost>
257      client = remote_ns_view_client_receiver_.BindNewEndpointAndPassRemote();
258  mojo::PendingAssociatedReceiver<remote_cocoa::mojom::RenderWidgetHostNSView>
259      view_receiver = remote_ns_view_.BindNewEndpointAndPassReceiver();
260
261  // Cast from PendingAssociatedRemote<mojom::RenderWidgetHostNSViewHost> and
262  // mojo::PendingAssociatedReceiver<mojom::RenderWidgetHostNSView> to the
263  // public interfaces accepted by the application.
264  // TODO(ccameron): Remove the need for this cast.
265  // https://crbug.com/888290
266  mojo::PendingAssociatedRemote<remote_cocoa::mojom::StubInterface> stub_client(
267      client.PassHandle(), 0);
268  mojo::PendingAssociatedReceiver<remote_cocoa::mojom::StubInterface>
269      stub_bridge_receiver(view_receiver.PassHandle());
270  remote_cocoa_application->CreateRenderWidgetHostNSView(
271      std::move(stub_client), std::move(stub_bridge_receiver));
272
273  ns_view_ = remote_ns_view_.get();
274
275  // Popup windows will specify an invalid |parent_ns_view_id|, because popups
276  // have their own NSWindows (of which they are the content NSView).
277  if (parent_ns_view_id != remote_cocoa::kInvalidNSViewId)
278    remote_ns_view_->SetParentWebContentsNSView(parent_ns_view_id);
279}
280
281void RenderWidgetHostViewMac::SetParentUiLayer(ui::Layer* parent_ui_layer) {
282  if (parent_ui_layer && !display_only_using_parent_ui_layer_) {
283    // The first time that we display using a parent ui::Layer, permanently
284    // switch from drawing using Cocoa to only drawing using ui::Views. Erase
285    // the existing content being drawn by Cocoa (which may have been set due
286    // to races, e.g, in https://crbug.com/845807). Note that this transition
287    // must be done lazily because not all code has been updated to use
288    // ui::Views (e.g, content_shell).
289    display_only_using_parent_ui_layer_ = true;
290    ns_view_->DisableDisplay();
291  }
292  if (browser_compositor_)
293    browser_compositor_->SetParentUiLayer(parent_ui_layer);
294}
295
296void RenderWidgetHostViewMac::SetParentAccessibilityElement(
297    id parent_accessibility_element) {
298  [GetInProcessNSView()
299      setAccessibilityParentElement:parent_accessibility_element];
300}
301
302RenderWidgetHostViewCocoa* RenderWidgetHostViewMac::GetInProcessNSView() const {
303  if (in_process_ns_view_bridge_)
304    return in_process_ns_view_bridge_->GetNSView();
305  return nullptr;
306}
307
308void RenderWidgetHostViewMac::SetDelegate(
309    NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
310  [GetInProcessNSView() setResponderDelegate:delegate];
311}
312
313ui::TextInputType RenderWidgetHostViewMac::GetTextInputType() {
314  if (!GetActiveWidget())
315    return ui::TEXT_INPUT_TYPE_NONE;
316  return text_input_manager_->GetTextInputState()->type;
317}
318
319RenderWidgetHostImpl* RenderWidgetHostViewMac::GetActiveWidget() {
320  return text_input_manager_ ? text_input_manager_->GetActiveWidget() : nullptr;
321}
322
323const TextInputManager::CompositionRangeInfo*
324RenderWidgetHostViewMac::GetCompositionRangeInfo() {
325  return text_input_manager_ ? text_input_manager_->GetCompositionRangeInfo()
326                             : nullptr;
327}
328
329const TextInputManager::TextSelection*
330RenderWidgetHostViewMac::GetTextSelection() {
331  return text_input_manager_ ? text_input_manager_->GetTextSelection(
332                                   GetFocusedViewForTextSelection())
333                             : nullptr;
334}
335
336///////////////////////////////////////////////////////////////////////////////
337// RenderWidgetHostViewMac, RenderWidgetHostView implementation:
338
339void RenderWidgetHostViewMac::InitAsChild(gfx::NativeView parent_view) {
340  DCHECK_EQ(widget_type_, WidgetType::kFrame);
341}
342
343void RenderWidgetHostViewMac::InitAsPopup(
344    RenderWidgetHostView* parent_host_view,
345    const gfx::Rect& pos) {
346  DCHECK_EQ(widget_type_, WidgetType::kPopup);
347
348  popup_parent_host_view_ =
349      static_cast<RenderWidgetHostViewMac*>(parent_host_view);
350
351  RenderWidgetHostViewMac* old_child =
352      popup_parent_host_view_->popup_child_host_view_;
353  if (old_child) {
354    DCHECK(old_child->popup_parent_host_view_ == popup_parent_host_view_);
355    old_child->popup_parent_host_view_ = nullptr;
356  }
357  popup_parent_host_view_->popup_child_host_view_ = this;
358
359  // Use transparent background color for the popup in order to avoid flashing
360  // the white background on popup open when dark color-scheme is used.
361  SetContentBackgroundColor(SK_ColorTRANSPARENT);
362
363  // This path is used by the time/date picker.
364  // When FormControlsRefresh is enabled the popup window should use
365  // the native shadow.
366  bool has_shadow = features::IsFormControlsRefreshEnabled();
367  ns_view_->InitAsPopup(pos, has_shadow);
368}
369
370void RenderWidgetHostViewMac::InitAsFullscreen(
371    RenderWidgetHostView* reference_host_view) {
372  // This path appears never to be reached.
373  NOTREACHED();
374}
375
376RenderWidgetHostViewBase*
377RenderWidgetHostViewMac::GetFocusedViewForTextSelection() {
378  // We obtain the TextSelection from focused RWH which is obtained from the
379  // frame tree.
380  return GetFocusedWidget() ? GetFocusedWidget()->GetView() : nullptr;
381}
382
383RenderWidgetHostDelegate*
384RenderWidgetHostViewMac::GetFocusedRenderWidgetHostDelegate() {
385  if (auto* focused_widget = GetFocusedWidget())
386    return focused_widget->delegate();
387  return host()->delegate();
388}
389
390RenderWidgetHostImpl* RenderWidgetHostViewMac::GetWidgetForKeyboardEvent() {
391  DCHECK(in_keyboard_event_);
392  return RenderWidgetHostImpl::FromID(keyboard_event_widget_process_id_,
393                                      keyboard_event_widget_routing_id_);
394}
395
396RenderWidgetHostImpl* RenderWidgetHostViewMac::GetWidgetForIme() {
397  if (in_keyboard_event_)
398    return GetWidgetForKeyboardEvent();
399  return GetActiveWidget();
400}
401
402void RenderWidgetHostViewMac::UpdateNSViewAndDisplayProperties() {
403  display_link_ = ui::DisplayLinkMac::GetForDisplay(display_.id());
404  if (!display_link_) {
405    // Note that on some headless systems, the display link will fail to be
406    // created, so this should not be a fatal error.
407    LOG(ERROR) << "Failed to create display link.";
408  }
409
410  // During auto-resize it is the responsibility of the caller to ensure that
411  // the NSView and RenderWidgetHostImpl are kept in sync.
412  if (host()->auto_resize_enabled())
413    return;
414
415  if (host()->delegate())
416    host()->delegate()->SendScreenRects();
417  else
418    host()->SendScreenRects();
419
420  // RenderWidgetHostImpl will query BrowserCompositorMac for the dimensions
421  // to send to the renderer, so it is required that BrowserCompositorMac be
422  // updated first. Only notify RenderWidgetHostImpl of the update if any
423  // properties it will query have changed.
424  if (browser_compositor_->UpdateSurfaceFromNSView(
425          view_bounds_in_window_dip_.size(), display_)) {
426    host()->NotifyScreenInfoChanged();
427  }
428}
429
430void RenderWidgetHostViewMac::GetScreenInfo(blink::ScreenInfo* screen_info) {
431  browser_compositor_->GetRendererScreenInfo(screen_info);
432}
433
434void RenderWidgetHostViewMac::Show() {
435  is_visible_ = true;
436  ns_view_->SetVisible(is_visible_);
437  browser_compositor_->SetViewVisible(is_visible_);
438  WasUnOccluded();
439}
440
441void RenderWidgetHostViewMac::Hide() {
442  is_visible_ = false;
443  ns_view_->SetVisible(is_visible_);
444  browser_compositor_->SetViewVisible(is_visible_);
445  WasOccluded();
446}
447
448void RenderWidgetHostViewMac::WasUnOccluded() {
449  if (!host()->is_hidden())
450    return;
451
452  browser_compositor_->SetRenderWidgetHostIsHidden(false);
453
454  DelegatedFrameHost* delegated_frame_host =
455      browser_compositor_->GetDelegatedFrameHost();
456
457  bool has_saved_frame =
458      browser_compositor_->has_saved_frame_before_state_transition();
459
460  auto tab_switch_start_state = TakeRecordContentToVisibleTimeRequest();
461
462  const bool renderer_should_record_presentation_time = !has_saved_frame;
463  host()->WasShown(renderer_should_record_presentation_time
464                       ? tab_switch_start_state.Clone()
465                       : blink::mojom::RecordContentToVisibleTimeRequestPtr());
466
467  if (delegated_frame_host) {
468    // If the frame for the renderer is already available, then the
469    // tab-switching time is the presentation time for the browser-compositor.
470    const bool record_presentation_time = has_saved_frame;
471    delegated_frame_host->WasShown(
472        browser_compositor_->GetRendererLocalSurfaceId(),
473        browser_compositor_->GetRendererSize(),
474        record_presentation_time
475            ? std::move(tab_switch_start_state)
476            : blink::mojom::RecordContentToVisibleTimeRequestPtr());
477  }
478}
479
480void RenderWidgetHostViewMac::WasOccluded() {
481  if (host()->is_hidden())
482    return;
483
484  host()->WasHidden();
485  browser_compositor_->SetRenderWidgetHostIsHidden(true);
486}
487
488void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
489  gfx::Rect rect = GetViewBounds();
490  rect.set_size(size);
491  SetBounds(rect);
492}
493
494void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
495  ns_view_->SetBounds(rect);
496}
497
498gfx::NativeView RenderWidgetHostViewMac::GetNativeView() {
499  return GetInProcessNSView();
500}
501
502gfx::NativeViewAccessible RenderWidgetHostViewMac::GetNativeViewAccessible() {
503  return GetInProcessNSView();
504}
505
506void RenderWidgetHostViewMac::Focus() {
507  // Ignore redundant calls, as they can cause unending loops of focus-setting.
508  // https://crbug.com/998123
509  if (is_first_responder_)
510    return;
511  ns_view_->MakeFirstResponder();
512}
513
514bool RenderWidgetHostViewMac::HasFocus() {
515  return is_first_responder_;
516}
517
518bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() {
519  return browser_compositor_->GetDelegatedFrameHost()
520      ->CanCopyFromCompositingSurface();
521}
522
523bool RenderWidgetHostViewMac::IsShowing() {
524  return is_visible_;
525}
526
527gfx::Rect RenderWidgetHostViewMac::GetViewBounds() {
528  return view_bounds_in_window_dip_ +
529         window_frame_in_screen_dip_.OffsetFromOrigin();
530}
531
532bool RenderWidgetHostViewMac::IsMouseLocked() {
533  return mouse_locked_;
534}
535
536void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
537  GetCursorManager()->UpdateCursor(this, cursor);
538}
539
540void RenderWidgetHostViewMac::DisplayCursor(const WebCursor& cursor) {
541  ns_view_->DisplayCursor(cursor.cursor());
542}
543
544CursorManager* RenderWidgetHostViewMac::GetCursorManager() {
545  return cursor_manager_.get();
546}
547
548void RenderWidgetHostViewMac::OnDidNavigateMainFrameToNewPage() {
549  gesture_provider_.ResetDetection();
550}
551
552void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
553  is_loading_ = is_loading;
554  // If we ever decide to show the waiting cursor while the page is loading
555  // like Chrome does on Windows, call |UpdateCursor()| here.
556}
557
558void RenderWidgetHostViewMac::OnUpdateTextInputStateCalled(
559    TextInputManager* text_input_manager,
560    RenderWidgetHostViewBase* updated_view,
561    bool did_update_state) {
562  if (!did_update_state)
563    return;
564
565  const ui::mojom::TextInputState* state =
566      text_input_manager->GetTextInputState();
567  if (state)
568    ns_view_->SetTextInputState(state->type, state->flags);
569  else
570    ns_view_->SetTextInputState(ui::TEXT_INPUT_TYPE_NONE, 0);
571
572  // |updated_view| is the last view to change its TextInputState which can be
573  // used to start/stop monitoring composition info when it has a focused
574  // editable text input field.
575  RenderWidgetHostImpl* widget_host =
576      RenderWidgetHostImpl::From(updated_view->GetRenderWidgetHost());
577
578  // We might end up here when |updated_view| has had active TextInputState and
579  // then got destroyed. In that case, |updated_view->GetRenderWidgetHost()|
580  // returns nullptr.
581  if (!widget_host)
582    return;
583
584  // Set the monitor state based on the text input focus state.
585  const bool has_focus = HasFocus();
586  bool need_monitor_composition =
587      has_focus && state && state->type != ui::TEXT_INPUT_TYPE_NONE;
588
589  widget_host->RequestCompositionUpdates(false /* immediate_request */,
590                                         need_monitor_composition);
591
592  if (has_focus) {
593    SetTextInputActive(true);
594
595    // Let AppKit cache the new input context to make IMEs happy.
596    // See http://crbug.com/73039.
597    [NSApp updateWindows];
598  }
599}
600
601void RenderWidgetHostViewMac::OnImeCancelComposition(
602    TextInputManager* text_input_manager,
603    RenderWidgetHostViewBase* updated_view) {
604  ns_view_->CancelComposition();
605}
606
607void RenderWidgetHostViewMac::OnImeCompositionRangeChanged(
608    TextInputManager* text_input_manager,
609    RenderWidgetHostViewBase* updated_view) {
610  const TextInputManager::CompositionRangeInfo* info =
611      GetCompositionRangeInfo();
612  if (!info)
613    return;
614  // The RangeChanged message is only sent with valid values. The current
615  // caret position (start == end) will be sent if there is no IME range.
616  ns_view_->SetCompositionRangeInfo(info->range);
617}
618
619void RenderWidgetHostViewMac::OnSelectionBoundsChanged(
620    TextInputManager* text_input_manager,
621    RenderWidgetHostViewBase* updated_view) {
622  DCHECK_EQ(GetTextInputManager(), text_input_manager);
623
624  // The rest of the code is to support the Mac Zoom feature tracking the
625  // text caret; we can skip it if that feature is not currently enabled.
626  if (!UAZoomEnabled())
627    return;
628
629  RenderWidgetHostViewBase* focused_view = GetFocusedViewForTextSelection();
630  if (!focused_view)
631    return;
632
633  const TextInputManager::SelectionRegion* region =
634      GetTextInputManager()->GetSelectionRegion(focused_view);
635  if (!region)
636    return;
637
638  // Create a rectangle for the edge of the selection focus, which will be
639  // the same as the caret position if the selection is collapsed. That's
640  // what we want to try to keep centered on-screen if possible.
641  gfx::Rect gfx_caret_rect(region->focus.edge_start_rounded().x(),
642                           region->focus.edge_start_rounded().y(), 1,
643                           region->focus.GetHeight());
644  gfx_caret_rect += view_bounds_in_window_dip_.OffsetFromOrigin();
645  gfx_caret_rect += window_frame_in_screen_dip_.OffsetFromOrigin();
646
647  // Note that UAZoomChangeFocus wants unflipped screen coordinates.
648  NSRect caret_rect = NSRectFromCGRect(gfx_caret_rect.ToCGRect());
649  UAZoomChangeFocus(&caret_rect, &caret_rect, kUAZoomFocusTypeInsertionPoint);
650}
651
652void RenderWidgetHostViewMac::OnTextSelectionChanged(
653    TextInputManager* text_input_manager,
654    RenderWidgetHostViewBase* updated_view) {
655  DCHECK_EQ(GetTextInputManager(), text_input_manager);
656
657  const TextInputManager::TextSelection* selection = GetTextSelection();
658  if (!selection)
659    return;
660
661  ns_view_->SetTextSelection(selection->text(), selection->offset(),
662                             selection->range());
663}
664
665void RenderWidgetHostViewMac::OnGestureEvent(
666    const ui::GestureEventData& gesture) {
667  blink::WebGestureEvent web_gesture =
668      ui::CreateWebGestureEventFromGestureEventData(gesture);
669
670  ui::LatencyInfo latency_info(ui::SourceEventType::TOUCH);
671
672  if (ShouldRouteEvents()) {
673    blink::WebGestureEvent gesture_event(web_gesture);
674    host()->delegate()->GetInputEventRouter()->RouteGestureEvent(
675        this, &gesture_event, latency_info);
676  } else {
677    host()->ForwardGestureEventWithLatencyInfo(web_gesture, latency_info);
678  }
679}
680
681void RenderWidgetHostViewMac::OnRenderFrameMetadataChangedAfterActivation() {
682  last_frame_root_background_color_ = host()
683                                          ->render_frame_metadata_provider()
684                                          ->LastRenderFrameMetadata()
685                                          .root_background_color;
686}
687
688void RenderWidgetHostViewMac::RenderProcessGone() {
689  Destroy();
690}
691
692void RenderWidgetHostViewMac::Destroy() {
693  host()->render_frame_metadata_provider()->RemoveObserver(this);
694
695  // Unlock the mouse in the NSView's process before destroying our bridge to
696  // it.
697  if (mouse_locked_) {
698    mouse_locked_ = false;
699    ns_view_->SetCursorLocked(false);
700  }
701
702  // Destroy the local and remote briges to the NSView. Note that the NSView on
703  // the other side of |ns_view_| may outlive us due to other retains.
704  ns_view_ = nullptr;
705  in_process_ns_view_bridge_.reset();
706  remote_ns_view_client_receiver_.reset();
707  remote_ns_view_.reset();
708
709  // Delete the delegated frame state, which will reach back into
710  // host().
711  browser_compositor_.reset();
712
713  // Make sure none of our observers send events for us to process after
714  // we release host().
715  NotifyObserversAboutShutdown();
716
717  if (text_input_manager_)
718    text_input_manager_->RemoveObserver(this);
719
720  mouse_wheel_phase_handler_.IgnorePendingWheelEndEvent();
721
722  // The call to the base class will set host() to nullptr.
723  RenderWidgetHostViewBase::Destroy();
724
725  delete this;
726}
727
728void RenderWidgetHostViewMac::SetTooltipText(
729    const base::string16& tooltip_text) {
730  GetCursorManager()->SetTooltipTextForView(this, tooltip_text);
731}
732
733void RenderWidgetHostViewMac::DisplayTooltipText(
734    const base::string16& tooltip_text) {
735  ns_view_->SetTooltipText(tooltip_text);
736}
737
738viz::ScopedSurfaceIdAllocator
739RenderWidgetHostViewMac::DidUpdateVisualProperties(
740    const cc::RenderFrameMetadata& metadata) {
741  base::OnceCallback<void()> allocation_task = base::BindOnce(
742      base::IgnoreResult(
743          &RenderWidgetHostViewMac::OnDidUpdateVisualPropertiesComplete),
744      weak_factory_.GetWeakPtr(), metadata);
745  return browser_compositor_->GetScopedRendererSurfaceIdAllocator(
746      std::move(allocation_task));
747}
748
749void RenderWidgetHostViewMac::DidNavigate() {
750  browser_compositor_->DidNavigate();
751}
752
753gfx::Size RenderWidgetHostViewMac::GetRequestedRendererSize() {
754  return browser_compositor_->GetRendererSize();
755}
756
757namespace {
758
759// A helper function for CombineTextNodesAndMakeCallback() below. It would
760// ordinarily be a helper lambda in that class method, but it processes a tree
761// and needs to be recursive, and that's crazy difficult to do with a lambda.
762// TODO(avi): Move this to be a lambda when P0839R0 lands in C++.
763void AddTextNodesToVector(const ui::AXNode* node,
764                          std::vector<base::string16>* strings) {
765  const ui::AXNodeData& node_data = node->data();
766
767  if (node_data.role == ax::mojom::Role::kStaticText) {
768    if (node_data.HasStringAttribute(ax::mojom::StringAttribute::kName)) {
769      strings->emplace_back(
770          node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
771    }
772    return;
773  }
774
775  for (const auto* child : node->children())
776    AddTextNodesToVector(child, strings);
777}
778
779using SpeechCallback = base::OnceCallback<void(const base::string16&)>;
780void CombineTextNodesAndMakeCallback(SpeechCallback callback,
781                                     const ui::AXTreeUpdate& update) {
782  std::vector<base::string16> text_node_contents;
783  text_node_contents.reserve(update.nodes.size());
784
785  ui::AXTree tree(update);
786
787  AddTextNodesToVector(tree.root(), &text_node_contents);
788
789  std::move(callback).Run(
790      base::JoinString(text_node_contents, base::ASCIIToUTF16("\n")));
791}
792
793}  // namespace
794
795void RenderWidgetHostViewMac::GetPageTextForSpeech(SpeechCallback callback) {
796  // Note that the WebContents::RequestAXTreeSnapshot() call has a limit on the
797  // number of nodes returned. For large pages, this call might hit that limit.
798  // This is a reasonable thing. The "Start Speaking" call dates back to the
799  // earliest days of the Mac, before accessibility. It was designed to show off
800  // the speech capabilities of the Mac, which is fine, but is mostly
801  // inapplicable nowadays. Is it useful to have the Mac read megabytes of text
802  // with zero control over positioning, with no fast-forward or rewind? What
803  // does it even mean to read a Web 2.0 dynamic, AJAXy page aloud from
804  // beginning to end?
805  //
806  // If this is an issue, please file a bug explaining the situation and how the
807  // limits of this feature affect you in the real world.
808
809  GetWebContents()->RequestAXTreeSnapshot(
810      base::BindOnce(CombineTextNodesAndMakeCallback, std::move(callback)),
811      ui::AXMode::kWebContents);
812}
813
814void RenderWidgetHostViewMac::SpeakSelection() {
815  const TextInputManager::TextSelection* selection = GetTextSelection();
816  if (selection && !selection->selected_text().empty()) {
817    ui::TextServicesContextMenu::SpeakText(selection->selected_text());
818    return;
819  }
820
821  // With no selection, speak an approximation of the entire contents of the
822  // page.
823  GetPageTextForSpeech(base::BindOnce(ui::TextServicesContextMenu::SpeakText));
824}
825
826void RenderWidgetHostViewMac::SetWindowFrameInScreen(const gfx::Rect& rect) {
827  DCHECK(GetInProcessNSView() && ![GetInProcessNSView() window])
828      << "This method should only be called in headless browser!";
829  OnWindowFrameInScreenChanged(rect);
830}
831
832//
833// RenderWidgetHostViewCocoa uses the stored selection text,
834// which implements NSServicesRequests protocol.
835//
836
837void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) {
838  ns_view_->SetShowingContextMenu(showing);
839}
840
841uint32_t RenderWidgetHostViewMac::GetCaptureSequenceNumber() const {
842  return latest_capture_sequence_number_;
843}
844
845void RenderWidgetHostViewMac::CopyFromSurface(
846    const gfx::Rect& src_subrect,
847    const gfx::Size& dst_size,
848    base::OnceCallback<void(const SkBitmap&)> callback) {
849  base::WeakPtr<RenderWidgetHostImpl> popup_host;
850  base::WeakPtr<DelegatedFrameHost> popup_frame_host;
851  if (popup_child_host_view_) {
852    popup_host = popup_child_host_view_->host()->GetWeakPtr();
853    popup_frame_host = popup_child_host_view_->BrowserCompositor()
854                           ->GetDelegatedFrameHost()
855                           ->GetWeakPtr();
856  }
857  RenderWidgetHostViewBase::CopyMainAndPopupFromSurface(
858      host()->GetWeakPtr(),
859      browser_compositor_->GetDelegatedFrameHost()->GetWeakPtr(), popup_host,
860      popup_frame_host, src_subrect, dst_size, display_.device_scale_factor(),
861      std::move(callback));
862}
863
864void RenderWidgetHostViewMac::EnsureSurfaceSynchronizedForWebTest() {
865  ++latest_capture_sequence_number_;
866  browser_compositor_->ForceNewSurfaceId();
867}
868
869void RenderWidgetHostViewMac::OnDidUpdateVisualPropertiesComplete(
870    const cc::RenderFrameMetadata& metadata) {
871  browser_compositor_->UpdateSurfaceFromChild(
872      host()->auto_resize_enabled(), metadata.device_scale_factor,
873      metadata.viewport_size_in_pixels,
874      metadata.local_surface_id.value_or(viz::LocalSurfaceId()));
875}
876
877void RenderWidgetHostViewMac::TakeFallbackContentFrom(
878    RenderWidgetHostView* view) {
879  DCHECK(!static_cast<RenderWidgetHostViewBase*>(view)
880              ->IsRenderWidgetHostViewChildFrame());
881  RenderWidgetHostViewMac* view_mac =
882      static_cast<RenderWidgetHostViewMac*>(view);
883  ScopedCAActionDisabler disabler;
884  base::Optional<SkColor> color = view_mac->GetBackgroundColor();
885  if (color)
886    SetBackgroundColor(*color);
887
888  // Make the NSView for |this| display the same content as is being displayed
889  // in the NSView for |view_mac|.
890  const gfx::CALayerParams* ca_layer_params =
891      view_mac->browser_compositor_->GetLastCALayerParams();
892  if (ca_layer_params)
893    ns_view_->SetCALayerParams(*ca_layer_params);
894  browser_compositor_->TakeFallbackContentFrom(
895      view_mac->browser_compositor_.get());
896}
897
898bool RenderWidgetHostViewMac::GetLineBreakIndex(
899    const std::vector<gfx::Rect>& bounds,
900    const gfx::Range& range,
901    size_t* line_break_point) {
902  DCHECK(line_break_point);
903  if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
904    return false;
905
906  // We can't check line breaking completely from only rectangle array. Thus we
907  // assume the line breaking as the next character's y offset is larger than
908  // a threshold. Currently the threshold is determined as minimum y offset plus
909  // 75% of maximum height.
910  // TODO(nona): Check the threshold is reliable or not.
911  // TODO(nona): Bidi support.
912  const size_t loop_end_idx =
913      std::min(bounds.size(), static_cast<size_t>(range.end()));
914  int max_height = 0;
915  int min_y_offset = std::numeric_limits<int32_t>::max();
916  for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
917    max_height = std::max(max_height, bounds[idx].height());
918    min_y_offset = std::min(min_y_offset, bounds[idx].y());
919  }
920  int line_break_threshold = min_y_offset + (max_height * 3 / 4);
921  for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
922    if (bounds[idx].y() > line_break_threshold) {
923      *line_break_point = idx;
924      return true;
925    }
926  }
927  return false;
928}
929
930gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
931    const gfx::Range& range,
932    gfx::Range* actual_range) {
933  const TextInputManager::CompositionRangeInfo* composition_info =
934      GetCompositionRangeInfo();
935  if (!composition_info)
936    return gfx::Rect();
937
938  DCHECK(actual_range);
939  DCHECK(!composition_info->character_bounds.empty());
940  DCHECK(range.start() <= composition_info->character_bounds.size());
941  DCHECK(range.end() <= composition_info->character_bounds.size());
942
943  if (range.is_empty()) {
944    *actual_range = range;
945    if (range.start() == composition_info->character_bounds.size()) {
946      return gfx::Rect(
947          composition_info->character_bounds[range.start() - 1].right(),
948          composition_info->character_bounds[range.start() - 1].y(), 0,
949          composition_info->character_bounds[range.start() - 1].height());
950    } else {
951      return gfx::Rect(
952          composition_info->character_bounds[range.start()].x(),
953          composition_info->character_bounds[range.start()].y(), 0,
954          composition_info->character_bounds[range.start()].height());
955    }
956  }
957
958  size_t end_idx;
959  if (!GetLineBreakIndex(composition_info->character_bounds, range, &end_idx)) {
960    end_idx = range.end();
961  }
962  *actual_range = gfx::Range(range.start(), end_idx);
963  gfx::Rect rect = composition_info->character_bounds[range.start()];
964  for (size_t i = range.start() + 1; i < end_idx; ++i) {
965    rect.Union(composition_info->character_bounds[i]);
966  }
967  return rect;
968}
969
970gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange(
971    const gfx::Range& request_range) {
972  const TextInputManager::CompositionRangeInfo* composition_info =
973      GetCompositionRangeInfo();
974  if (!composition_info)
975    return gfx::Range::InvalidRange();
976
977  if (composition_info->range.is_empty())
978    return gfx::Range::InvalidRange();
979
980  if (composition_info->range.is_reversed())
981    return gfx::Range::InvalidRange();
982
983  if (request_range.start() < composition_info->range.start() ||
984      request_range.start() > composition_info->range.end() ||
985      request_range.end() > composition_info->range.end()) {
986    return gfx::Range::InvalidRange();
987  }
988
989  return gfx::Range(request_range.start() - composition_info->range.start(),
990                    request_range.end() - composition_info->range.start());
991}
992
993WebContents* RenderWidgetHostViewMac::GetWebContents() {
994  return WebContents::FromRenderViewHost(RenderViewHost::From(host()));
995}
996
997bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
998    const gfx::Range& requested_range,
999    gfx::Rect* rect,
1000    gfx::Range* actual_range) {
1001  if (!GetTextInputManager())
1002    return false;
1003
1004  DCHECK(rect);
1005  // This exists to make IMEs more responsive, see http://crbug.com/115920
1006  TRACE_EVENT0("browser",
1007               "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
1008
1009  const TextInputManager::TextSelection* selection = GetTextSelection();
1010  if (!selection)
1011    return false;
1012
1013  // If requested range is same as caret location, we can just return it.
1014  if (selection->range().is_empty() && requested_range == selection->range()) {
1015    DCHECK(GetFocusedWidget());
1016    if (actual_range)
1017      *actual_range = requested_range;
1018    *rect = GetTextInputManager()
1019                ->GetSelectionRegion(GetFocusedWidget()->GetView())
1020                ->caret_rect;
1021    return true;
1022  }
1023
1024  const TextInputManager::CompositionRangeInfo* composition_info =
1025      GetCompositionRangeInfo();
1026  if (!composition_info || composition_info->range.is_empty()) {
1027    if (!requested_range.IsBoundedBy(selection->range()))
1028      return false;
1029    DCHECK(GetFocusedWidget());
1030    if (actual_range)
1031      *actual_range = selection->range();
1032    *rect = GetTextInputManager()
1033                ->GetSelectionRegion(GetFocusedWidget()->GetView())
1034                ->first_selection_rect;
1035    return true;
1036  }
1037
1038  const gfx::Range request_range_in_composition =
1039      ConvertCharacterRangeToCompositionRange(requested_range);
1040  if (request_range_in_composition == gfx::Range::InvalidRange())
1041    return false;
1042
1043  // If firstRectForCharacterRange in WebFrame is failed in renderer,
1044  // ImeCompositionRangeChanged will be sent with empty vector.
1045  if (!composition_info || composition_info->character_bounds.empty())
1046    return false;
1047  DCHECK_EQ(composition_info->character_bounds.size(),
1048            composition_info->range.length());
1049
1050  gfx::Range ui_actual_range;
1051  *rect = GetFirstRectForCompositionRange(request_range_in_composition,
1052                                          &ui_actual_range);
1053  if (actual_range) {
1054    *actual_range =
1055        gfx::Range(composition_info->range.start() + ui_actual_range.start(),
1056                   composition_info->range.start() + ui_actual_range.end());
1057  }
1058  return true;
1059}
1060
1061void RenderWidgetHostViewMac::FocusedNodeChanged(
1062    bool is_editable_node,
1063    const gfx::Rect& node_bounds_in_screen) {
1064  ns_view_->CancelComposition();
1065
1066  // If the Mac Zoom feature is enabled, update it with the bounds of the
1067  // current focused node so that it can ensure that it's scrolled into view.
1068  // Don't do anything if it's an editable node, as this will be handled by
1069  // OnSelectionBoundsChanged instead.
1070  if (UAZoomEnabled() && !is_editable_node) {
1071    NSRect bounds = NSRectFromCGRect(node_bounds_in_screen.ToCGRect());
1072    UAZoomChangeFocus(&bounds, NULL, kUAZoomFocusTypeOther);
1073  }
1074}
1075
1076void RenderWidgetHostViewMac::ResetFallbackToFirstNavigationSurface() {
1077  browser_compositor_->GetDelegatedFrameHost()
1078      ->ResetFallbackToFirstNavigationSurface();
1079}
1080
1081bool RenderWidgetHostViewMac::RequestRepaintForTesting() {
1082  return browser_compositor_->ForceNewSurfaceId();
1083}
1084
1085void RenderWidgetHostViewMac::TransformPointToRootSurface(gfx::PointF* point) {
1086  browser_compositor_->TransformPointToRootSurface(point);
1087}
1088
1089gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
1090  return window_frame_in_screen_dip_;
1091}
1092
1093blink::mojom::PointerLockResult RenderWidgetHostViewMac::LockMouse(
1094    bool request_unadjusted_movement) {
1095  if (mouse_locked_)
1096    return blink::mojom::PointerLockResult::kSuccess;
1097
1098  if (request_unadjusted_movement && !IsUnadjustedMouseMovementSupported()) {
1099    return blink::mojom::PointerLockResult::kUnsupportedOptions;
1100  }
1101
1102  mouse_locked_ = true;
1103  mouse_lock_unadjusted_movement_ = request_unadjusted_movement;
1104
1105  // Lock position of mouse cursor and hide it.
1106  ns_view_->SetCursorLockedUnacceleratedMovement(request_unadjusted_movement);
1107  ns_view_->SetCursorLocked(true);
1108
1109  // Clear the tooltip window.
1110  ns_view_->SetTooltipText(base::string16());
1111
1112  return blink::mojom::PointerLockResult::kSuccess;
1113}
1114
1115blink::mojom::PointerLockResult RenderWidgetHostViewMac::ChangeMouseLock(
1116    bool request_unadjusted_movement) {
1117  if (request_unadjusted_movement && !IsUnadjustedMouseMovementSupported()) {
1118    return blink::mojom::PointerLockResult::kUnsupportedOptions;
1119  }
1120
1121  mouse_lock_unadjusted_movement_ = request_unadjusted_movement;
1122  ns_view_->SetCursorLockedUnacceleratedMovement(request_unadjusted_movement);
1123  return blink::mojom::PointerLockResult::kSuccess;
1124}
1125
1126void RenderWidgetHostViewMac::UnlockMouse() {
1127  if (!mouse_locked_)
1128    return;
1129  mouse_locked_ = false;
1130  mouse_lock_unadjusted_movement_ = false;
1131  ns_view_->SetCursorLocked(false);
1132  ns_view_->SetCursorLockedUnacceleratedMovement(false);
1133
1134  if (host())
1135    host()->LostMouseLock();
1136}
1137
1138bool RenderWidgetHostViewMac::GetIsMouseLockedUnadjustedMovementForTesting() {
1139  return mouse_locked_ && mouse_lock_unadjusted_movement_;
1140}
1141
1142bool RenderWidgetHostViewMac::IsUnadjustedMouseMovementSupported() {
1143  // kCGEventUnacceleratedPointerMovementX/Y were first added as CGEventField
1144  // enum values in the 10.15.1 SDK.
1145  //
1146  // While they do seem to work at runtime back to 10.14, the safest approach is
1147  // to limit their use to 10.15.1 and newer to avoid relying on private or
1148  // undocumented APIs on earlier OSes.
1149  if (@available(macOS 10.15.1, *)) {
1150    return true;
1151  }
1152  return false;
1153}
1154
1155bool RenderWidgetHostViewMac::LockKeyboard(
1156    base::Optional<base::flat_set<ui::DomCode>> dom_codes) {
1157  base::Optional<std::vector<uint32_t>> uint_dom_codes;
1158  if (dom_codes) {
1159    uint_dom_codes.emplace();
1160    for (const auto& dom_code : *dom_codes)
1161      uint_dom_codes->push_back(static_cast<uint32_t>(dom_code));
1162  }
1163  is_keyboard_locked_ = true;
1164  ns_view_->LockKeyboard(uint_dom_codes);
1165  return true;
1166}
1167
1168void RenderWidgetHostViewMac::UnlockKeyboard() {
1169  if (!is_keyboard_locked_)
1170    return;
1171
1172  is_keyboard_locked_ = false;
1173  ns_view_->UnlockKeyboard();
1174}
1175
1176bool RenderWidgetHostViewMac::IsKeyboardLocked() {
1177  return is_keyboard_locked_;
1178}
1179
1180base::flat_map<std::string, std::string>
1181RenderWidgetHostViewMac::GetKeyboardLayoutMap() {
1182  return ui::GenerateDomKeyboardLayoutMap();
1183}
1184
1185void RenderWidgetHostViewMac::GestureEventAck(
1186    const WebGestureEvent& event,
1187    blink::mojom::InputEventResultState ack_result) {
1188  ForwardTouchpadZoomEventIfNecessary(event, ack_result);
1189
1190  // Stop flinging if a GSU event with momentum phase is sent to the renderer
1191  // but not consumed.
1192  StopFlingingIfNecessary(event, ack_result);
1193
1194  bool consumed = ack_result == blink::mojom::InputEventResultState::kConsumed;
1195  switch (event.GetType()) {
1196    case WebInputEvent::Type::kGestureScrollBegin:
1197    case WebInputEvent::Type::kGestureScrollUpdate:
1198    case WebInputEvent::Type::kGestureScrollEnd:
1199      [GetInProcessNSView() processedGestureScrollEvent:event
1200                                               consumed:consumed];
1201      return;
1202    default:
1203      break;
1204  }
1205  mouse_wheel_phase_handler_.GestureEventAck(event, ack_result);
1206}
1207
1208void RenderWidgetHostViewMac::ProcessAckedTouchEvent(
1209    const TouchEventWithLatencyInfo& touch,
1210    blink::mojom::InputEventResultState ack_result) {
1211  const bool event_consumed =
1212      ack_result == blink::mojom::InputEventResultState::kConsumed;
1213  gesture_provider_.OnTouchEventAck(
1214      touch.event.unique_touch_event_id, event_consumed,
1215      InputEventResultStateIsSetNonBlocking(ack_result));
1216  if (touch.event.touch_start_or_first_touch_move && event_consumed &&
1217      host()->delegate() && host()->delegate()->GetInputEventRouter()) {
1218    host()
1219        ->delegate()
1220        ->GetInputEventRouter()
1221        ->OnHandledTouchStartOrFirstTouchMove(
1222            touch.event.unique_touch_event_id);
1223  }
1224}
1225
1226void RenderWidgetHostViewMac::DidOverscroll(
1227    const ui::DidOverscrollParams& params) {
1228  [GetInProcessNSView() processedOverscroll:params];
1229}
1230
1231std::unique_ptr<SyntheticGestureTarget>
1232RenderWidgetHostViewMac::CreateSyntheticGestureTarget() {
1233  RenderWidgetHostImpl* host =
1234      RenderWidgetHostImpl::From(GetRenderWidgetHost());
1235  return std::unique_ptr<SyntheticGestureTarget>(
1236      new SyntheticGestureTargetMac(host, GetInProcessNSView()));
1237}
1238
1239const viz::LocalSurfaceId& RenderWidgetHostViewMac::GetLocalSurfaceId() const {
1240  return browser_compositor_->GetRendererLocalSurfaceId();
1241}
1242
1243const viz::FrameSinkId& RenderWidgetHostViewMac::GetFrameSinkId() const {
1244  return browser_compositor_->GetDelegatedFrameHost()->frame_sink_id();
1245}
1246
1247bool RenderWidgetHostViewMac::ShouldRouteEvents() const {
1248  // Event routing requires a valid frame sink (that is, that we be connected to
1249  // a ui::Compositor), which is not guaranteed to be the case.
1250  // https://crbug.com/844095
1251  if (!browser_compositor_->GetRootFrameSinkId().is_valid())
1252    return false;
1253
1254  return host()->delegate() && host()->delegate()->GetInputEventRouter();
1255}
1256
1257void RenderWidgetHostViewMac::SendTouchpadZoomEvent(
1258    const WebGestureEvent* event) {
1259  DCHECK(event->IsTouchpadZoomEvent());
1260  if (ShouldRouteEvents()) {
1261    host()->delegate()->GetInputEventRouter()->RouteGestureEvent(
1262        this, event, ui::LatencyInfo(ui::SourceEventType::TOUCHPAD));
1263    return;
1264  }
1265  host()->ForwardGestureEvent(*event);
1266}
1267
1268void RenderWidgetHostViewMac::InjectTouchEvent(
1269    const WebTouchEvent& event,
1270    const ui::LatencyInfo& latency_info) {
1271  ui::FilteredGestureProvider::TouchHandlingResult result =
1272      gesture_provider_.OnTouchEvent(MotionEventWeb(event));
1273  if (!result.succeeded)
1274    return;
1275
1276  if (ShouldRouteEvents()) {
1277    WebTouchEvent touch_event(event);
1278    host()->delegate()->GetInputEventRouter()->RouteTouchEvent(
1279        this, &touch_event, latency_info);
1280  } else {
1281    host()->ForwardTouchEventWithLatencyInfo(event, latency_info);
1282  }
1283}
1284
1285bool RenderWidgetHostViewMac::HasFallbackSurface() const {
1286  return browser_compositor_->GetDelegatedFrameHost()->HasFallbackSurface();
1287}
1288
1289bool RenderWidgetHostViewMac::TransformPointToCoordSpaceForView(
1290    const gfx::PointF& point,
1291    RenderWidgetHostViewBase* target_view,
1292    gfx::PointF* transformed_point) {
1293  if (target_view == this) {
1294    *transformed_point = point;
1295    return true;
1296  }
1297
1298  return target_view->TransformPointToLocalCoordSpace(
1299      point, GetCurrentSurfaceId(), transformed_point);
1300}
1301
1302viz::FrameSinkId RenderWidgetHostViewMac::GetRootFrameSinkId() {
1303  return browser_compositor_->GetRootFrameSinkId();
1304}
1305
1306viz::SurfaceId RenderWidgetHostViewMac::GetCurrentSurfaceId() const {
1307  // |browser_compositor_| could be null if this method is called during its
1308  // destruction.
1309  if (!browser_compositor_)
1310    return viz::SurfaceId();
1311  return browser_compositor_->GetDelegatedFrameHost()->GetCurrentSurfaceId();
1312}
1313
1314void RenderWidgetHostViewMac::ShutdownHost() {
1315  weak_factory_.InvalidateWeakPtrs();
1316  host()->ShutdownAndDestroyWidget(true);
1317  // Do not touch any members at this point, |this| has been deleted.
1318}
1319
1320void RenderWidgetHostViewMac::SetActive(bool active) {
1321  if (host()) {
1322    host()->SetActive(active);
1323    if (active) {
1324      if (HasFocus())
1325        host()->Focus();
1326    } else {
1327      host()->Blur();
1328    }
1329  }
1330  if (HasFocus())
1331    SetTextInputActive(active);
1332  if (!active)
1333    UnlockMouse();
1334}
1335
1336void RenderWidgetHostViewMac::ShowDefinitionForSelection() {
1337  // This will round-trip to the NSView to determine the selection range.
1338  ns_view_->ShowDictionaryOverlayForSelection();
1339}
1340
1341void RenderWidgetHostViewMac::UpdateBackgroundColor() {
1342  // This is called by the embedding code prior to the first frame appearing,
1343  // to set a reasonable color to show before the web content generates its
1344  // first frame. This will be overridden by the web contents.
1345  DCHECK(RenderWidgetHostViewBase::GetBackgroundColor());
1346  SkColor color = *RenderWidgetHostViewBase::GetBackgroundColor();
1347  SetBackgroundLayerColor(color);
1348  browser_compositor_->SetBackgroundColor(color);
1349}
1350
1351base::Optional<SkColor> RenderWidgetHostViewMac::GetBackgroundColor() {
1352  // This is used to specify a color to temporarily show while waiting for web
1353  // content. This should never return transparent, since that will cause bugs
1354  // where views are initialized as having a transparent background
1355  // inappropriately.
1356  // https://crbug.com/735407
1357  base::Optional<SkColor> color =
1358      RenderWidgetHostViewBase::GetBackgroundColor();
1359  return (color && *color == SK_ColorTRANSPARENT) ? SK_ColorWHITE : color;
1360}
1361
1362void RenderWidgetHostViewMac::SetBackgroundLayerColor(SkColor color) {
1363  if (color == background_layer_color_)
1364    return;
1365  background_layer_color_ = color;
1366  ns_view_->SetBackgroundColor(color);
1367}
1368
1369base::Optional<DisplayFeature> RenderWidgetHostViewMac::GetDisplayFeature() {
1370  return display_feature_;
1371}
1372
1373void RenderWidgetHostViewMac::SetDisplayFeatureForTesting(
1374    const DisplayFeature* display_feature) {
1375  if (display_feature)
1376    display_feature_ = *display_feature;
1377  else
1378    display_feature_ = base::nullopt;
1379}
1380
1381BrowserAccessibilityManager*
1382RenderWidgetHostViewMac::CreateBrowserAccessibilityManager(
1383    BrowserAccessibilityDelegate* delegate,
1384    bool for_root_frame) {
1385  return new BrowserAccessibilityManagerMac(
1386      BrowserAccessibilityManagerMac::GetEmptyDocument(), delegate);
1387}
1388
1389gfx::NativeViewAccessible
1390RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessible() {
1391  return GetInProcessNSView();
1392}
1393
1394gfx::NativeViewAccessible
1395RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessibleForWindow() {
1396  if (remote_window_accessible_)
1397    return remote_window_accessible_.get();
1398  return [GetInProcessNSView() window];
1399}
1400
1401void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
1402  const bool should_enable_password_input =
1403      active && GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD;
1404  if (should_enable_password_input)
1405    password_input_enabler_.reset(new ui::ScopedPasswordInputEnabler());
1406  else
1407    password_input_enabler_.reset();
1408}
1409
1410MouseWheelPhaseHandler* RenderWidgetHostViewMac::GetMouseWheelPhaseHandler() {
1411  return &mouse_wheel_phase_handler_;
1412}
1413
1414///////////////////////////////////////////////////////////////////////////////
1415// RenderWidgetHostNSViewHostHelper and mojom::RenderWidgetHostNSViewHost
1416// implementation:
1417
1418id RenderWidgetHostViewMac::GetRootBrowserAccessibilityElement() {
1419  if (auto* manager = host()->GetRootBrowserAccessibilityManager())
1420    return ToBrowserAccessibilityCocoa(manager->GetRoot());
1421  return nil;
1422}
1423
1424id RenderWidgetHostViewMac::GetFocusedBrowserAccessibilityElement() {
1425  return GetAccessibilityFocusedUIElement();
1426}
1427
1428void RenderWidgetHostViewMac::SetAccessibilityWindow(NSWindow* window) {
1429  // When running in-process, just use the NSView's NSWindow as its own
1430  // accessibility element.
1431  remote_window_accessible_.reset();
1432}
1433
1434bool RenderWidgetHostViewMac::SyncIsWidgetForMainFrame(
1435    bool* is_for_main_frame) {
1436  *is_for_main_frame = !!host()->owner_delegate();
1437  return true;
1438}
1439
1440void RenderWidgetHostViewMac::SyncIsWidgetForMainFrame(
1441    SyncIsWidgetForMainFrameCallback callback) {
1442  bool is_for_main_frame;
1443  SyncIsWidgetForMainFrame(&is_for_main_frame);
1444  std::move(callback).Run(is_for_main_frame);
1445}
1446
1447void RenderWidgetHostViewMac::RequestShutdown() {
1448  if (!weak_factory_.HasWeakPtrs()) {
1449    base::ThreadTaskRunnerHandle::Get()->PostTask(
1450        FROM_HERE, base::BindOnce(&RenderWidgetHostViewMac::ShutdownHost,
1451                                  weak_factory_.GetWeakPtr()));
1452  }
1453}
1454
1455void RenderWidgetHostViewMac::OnFirstResponderChanged(bool is_first_responder) {
1456  if (is_first_responder_ == is_first_responder)
1457    return;
1458  is_first_responder_ = is_first_responder;
1459  accessibility_focus_overrider_.SetViewIsFirstResponder(is_first_responder_);
1460  if (is_first_responder_) {
1461    host()->GotFocus();
1462    SetTextInputActive(true);
1463  } else {
1464    SetTextInputActive(false);
1465    host()->LostFocus();
1466  }
1467}
1468
1469void RenderWidgetHostViewMac::OnWindowIsKeyChanged(bool is_key) {
1470  if (is_window_key_ == is_key)
1471    return;
1472  is_window_key_ = is_key;
1473  accessibility_focus_overrider_.SetWindowIsKey(is_window_key_);
1474  if (is_first_responder_)
1475    SetActive(is_key);
1476}
1477
1478void RenderWidgetHostViewMac::OnBoundsInWindowChanged(
1479    const gfx::Rect& view_bounds_in_window_dip,
1480    bool attached_to_window) {
1481  bool view_size_changed =
1482      view_bounds_in_window_dip_.size() != view_bounds_in_window_dip.size();
1483
1484  if (attached_to_window) {
1485    view_bounds_in_window_dip_ = view_bounds_in_window_dip;
1486  } else {
1487    // If not attached to a window, do not update the bounds origin (since it is
1488    // meaningless, and the last value is the best guess at the next meaningful
1489    // value).
1490    view_bounds_in_window_dip_.set_size(view_bounds_in_window_dip.size());
1491  }
1492
1493  if (view_size_changed)
1494    UpdateNSViewAndDisplayProperties();
1495}
1496
1497void RenderWidgetHostViewMac::OnWindowFrameInScreenChanged(
1498    const gfx::Rect& window_frame_in_screen_dip) {
1499  if (window_frame_in_screen_dip_ == window_frame_in_screen_dip)
1500    return;
1501
1502  window_frame_in_screen_dip_ = window_frame_in_screen_dip;
1503  if (host()->delegate())
1504    host()->delegate()->SendScreenRects();
1505  else
1506    host()->SendScreenRects();
1507}
1508
1509void RenderWidgetHostViewMac::OnDisplayChanged(
1510    const display::Display& display) {
1511  display_ = display;
1512  UpdateNSViewAndDisplayProperties();
1513}
1514
1515void RenderWidgetHostViewMac::BeginKeyboardEvent() {
1516  DCHECK(!in_keyboard_event_);
1517  in_keyboard_event_ = true;
1518  RenderWidgetHostImpl* widget_host = host();
1519  if (widget_host && widget_host->delegate()) {
1520    widget_host =
1521        widget_host->delegate()->GetFocusedRenderWidgetHost(widget_host);
1522  }
1523  if (widget_host) {
1524    keyboard_event_widget_process_id_ = widget_host->GetProcess()->GetID();
1525    keyboard_event_widget_routing_id_ = widget_host->GetRoutingID();
1526  }
1527}
1528
1529void RenderWidgetHostViewMac::EndKeyboardEvent() {
1530  in_keyboard_event_ = false;
1531  keyboard_event_widget_process_id_ = 0;
1532  keyboard_event_widget_routing_id_ = 0;
1533}
1534
1535void RenderWidgetHostViewMac::ForwardKeyboardEvent(
1536    const NativeWebKeyboardEvent& key_event,
1537    const ui::LatencyInfo& latency_info) {
1538  if (auto* widget_host = GetWidgetForKeyboardEvent()) {
1539    widget_host->ForwardKeyboardEventWithLatencyInfo(key_event, latency_info);
1540  }
1541}
1542
1543void RenderWidgetHostViewMac::ForwardKeyboardEventWithCommands(
1544    const NativeWebKeyboardEvent& key_event,
1545    const ui::LatencyInfo& latency_info,
1546    std::vector<blink::mojom::EditCommandPtr> commands) {
1547  if (auto* widget_host = GetWidgetForKeyboardEvent()) {
1548    widget_host->ForwardKeyboardEventWithCommands(key_event, latency_info,
1549                                                  std::move(commands));
1550  }
1551}
1552
1553void RenderWidgetHostViewMac::RouteOrProcessMouseEvent(
1554    const blink::WebMouseEvent& const_web_event) {
1555  blink::WebMouseEvent web_event = const_web_event;
1556  ui::LatencyInfo latency_info(ui::SourceEventType::OTHER);
1557  latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT);
1558  if (ShouldRouteEvents()) {
1559    host()->delegate()->GetInputEventRouter()->RouteMouseEvent(this, &web_event,
1560                                                               latency_info);
1561  } else {
1562    ProcessMouseEvent(web_event, latency_info);
1563  }
1564}
1565
1566void RenderWidgetHostViewMac::RouteOrProcessTouchEvent(
1567    const blink::WebTouchEvent& const_web_event) {
1568  blink::WebTouchEvent web_event = const_web_event;
1569  ui::FilteredGestureProvider::TouchHandlingResult result =
1570      gesture_provider_.OnTouchEvent(MotionEventWeb(web_event));
1571  if (!result.succeeded)
1572    return;
1573
1574  ui::LatencyInfo latency_info(ui::SourceEventType::OTHER);
1575  latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT);
1576  if (ShouldRouteEvents()) {
1577    host()->delegate()->GetInputEventRouter()->RouteTouchEvent(this, &web_event,
1578                                                               latency_info);
1579  } else {
1580    ProcessTouchEvent(web_event, latency_info);
1581  }
1582}
1583
1584void RenderWidgetHostViewMac::RouteOrProcessWheelEvent(
1585    const blink::WebMouseWheelEvent& const_web_event) {
1586  blink::WebMouseWheelEvent web_event = const_web_event;
1587  ui::LatencyInfo latency_info(ui::SourceEventType::WHEEL);
1588  latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT);
1589  mouse_wheel_phase_handler_.AddPhaseIfNeededAndScheduleEndEvent(
1590      web_event, ShouldRouteEvents());
1591  if (web_event.phase == blink::WebMouseWheelEvent::kPhaseEnded) {
1592    // A wheel end event is scheduled and will get dispatched if momentum
1593    // phase doesn't start in 100ms. Don't sent the wheel end event
1594    // immediately.
1595    return;
1596  }
1597  if (ShouldRouteEvents()) {
1598    host()->delegate()->GetInputEventRouter()->RouteMouseWheelEvent(
1599        this, &web_event, latency_info);
1600  } else {
1601    ProcessMouseWheelEvent(web_event, latency_info);
1602  }
1603}
1604
1605void RenderWidgetHostViewMac::ForwardMouseEvent(
1606    const blink::WebMouseEvent& web_event) {
1607  if (host())
1608    host()->ForwardMouseEvent(web_event);
1609
1610  if (web_event.GetType() == WebInputEvent::Type::kMouseLeave)
1611    ns_view_->SetTooltipText(base::string16());
1612}
1613
1614void RenderWidgetHostViewMac::ForwardWheelEvent(
1615    const blink::WebMouseWheelEvent& const_web_event) {
1616  blink::WebMouseWheelEvent web_event = const_web_event;
1617  mouse_wheel_phase_handler_.AddPhaseIfNeededAndScheduleEndEvent(web_event,
1618                                                                 false);
1619}
1620
1621void RenderWidgetHostViewMac::GestureBegin(blink::WebGestureEvent begin_event,
1622                                           bool is_synthetically_injected) {
1623  gesture_begin_event_ = std::make_unique<WebGestureEvent>(begin_event);
1624
1625  // If the page is at the minimum zoom level, require a threshold be reached
1626  // before the pinch has an effect. Synthetic pinches are not subject to this
1627  // threshold.
1628  // TODO(crbug.com/1038683): |page_at_minimum_scale_| is always true, should it
1629  // be removed or correctly set based on RenderFrameMetadata?
1630  if (page_at_minimum_scale_) {
1631    pinch_has_reached_zoom_threshold_ = is_synthetically_injected;
1632    pinch_unused_amount_ = 1;
1633  }
1634}
1635
1636void RenderWidgetHostViewMac::GestureUpdate(
1637    blink::WebGestureEvent update_event) {
1638  // If, due to nesting of multiple gestures (e.g, from multiple touch
1639  // devices), the beginning of the gesture has been lost, skip the remainder
1640  // of the gesture.
1641  if (!gesture_begin_event_)
1642    return;
1643
1644  if (!pinch_has_reached_zoom_threshold_) {
1645    pinch_unused_amount_ *= update_event.data.pinch_update.scale;
1646    if (pinch_unused_amount_ < 0.667 || pinch_unused_amount_ > 1.5)
1647      pinch_has_reached_zoom_threshold_ = true;
1648  }
1649
1650  // Send a GesturePinchBegin event if none has been sent yet.
1651  if (!gesture_begin_pinch_sent_) {
1652    // Before starting a pinch sequence, send the pending wheel end event to
1653    // finish scrolling.
1654    mouse_wheel_phase_handler_.DispatchPendingWheelEndEvent();
1655    WebGestureEvent begin_event(*gesture_begin_event_);
1656    begin_event.SetType(WebInputEvent::Type::kGesturePinchBegin);
1657    begin_event.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
1658    begin_event.SetNeedsWheelEvent(true);
1659    SendTouchpadZoomEvent(&begin_event);
1660    gesture_begin_pinch_sent_ = YES;
1661  }
1662
1663  // Send a GesturePinchUpdate event.
1664  update_event.data.pinch_update.zoom_disabled =
1665      !pinch_has_reached_zoom_threshold_;
1666  SendTouchpadZoomEvent(&update_event);
1667}
1668
1669void RenderWidgetHostViewMac::GestureEnd(blink::WebGestureEvent end_event) {
1670  gesture_begin_event_.reset();
1671  if (gesture_begin_pinch_sent_) {
1672    SendTouchpadZoomEvent(&end_event);
1673    gesture_begin_pinch_sent_ = false;
1674  }
1675}
1676
1677void RenderWidgetHostViewMac::SmartMagnify(
1678    const blink::WebGestureEvent& smart_magnify_event) {
1679  SendTouchpadZoomEvent(&smart_magnify_event);
1680}
1681
1682void RenderWidgetHostViewMac::ImeSetComposition(
1683    const base::string16& text,
1684    const std::vector<ui::ImeTextSpan>& ime_text_spans,
1685    const gfx::Range& replacement_range,
1686    int selection_start,
1687    int selection_end) {
1688  if (auto* widget_host = GetWidgetForIme()) {
1689    widget_host->ImeSetComposition(text, ime_text_spans, replacement_range,
1690                                   selection_start, selection_end);
1691  }
1692}
1693
1694void RenderWidgetHostViewMac::ImeCommitText(
1695    const base::string16& text,
1696    const gfx::Range& replacement_range) {
1697  if (auto* widget_host = GetWidgetForIme()) {
1698    widget_host->ImeCommitText(text, std::vector<ui::ImeTextSpan>(),
1699                               replacement_range, 0);
1700  }
1701}
1702
1703void RenderWidgetHostViewMac::ImeFinishComposingText() {
1704  if (auto* widget_host = GetWidgetForIme()) {
1705    widget_host->ImeFinishComposingText(false);
1706  }
1707}
1708
1709void RenderWidgetHostViewMac::ImeCancelCompositionFromCocoa() {
1710  if (auto* widget_host = GetWidgetForIme()) {
1711    widget_host->ImeCancelComposition();
1712  }
1713}
1714
1715void RenderWidgetHostViewMac::LookUpDictionaryOverlayFromRange(
1716    const gfx::Range& range) {
1717  content::RenderWidgetHostViewBase* focused_view =
1718      GetFocusedViewForTextSelection();
1719  if (!focused_view)
1720    return;
1721
1722  RenderWidgetHostImpl* widget_host =
1723      RenderWidgetHostImpl::From(focused_view->GetRenderWidgetHost());
1724  if (!widget_host)
1725    return;
1726
1727  int32_t target_widget_process_id = widget_host->GetProcess()->GetID();
1728  int32_t target_widget_routing_id = widget_host->GetRoutingID();
1729  TextInputClientMac::GetInstance()->GetStringFromRange(
1730      widget_host, range,
1731      base::BindOnce(&RenderWidgetHostViewMac::OnGotStringForDictionaryOverlay,
1732                     weak_factory_.GetWeakPtr(), target_widget_process_id,
1733                     target_widget_routing_id));
1734}
1735
1736void RenderWidgetHostViewMac::LookUpDictionaryOverlayAtPoint(
1737    const gfx::PointF& root_point) {
1738  if (!host() || !host()->delegate() ||
1739      !host()->delegate()->GetInputEventRouter())
1740    return;
1741
1742  gfx::PointF transformed_point;
1743  RenderWidgetHostImpl* widget_host =
1744      host()->delegate()->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
1745          this, root_point, &transformed_point);
1746  if (!widget_host)
1747    return;
1748
1749  // For popups, do not support QuickLook.
1750  if (popup_parent_host_view_)
1751    return;
1752
1753  int32_t target_widget_process_id = widget_host->GetProcess()->GetID();
1754  int32_t target_widget_routing_id = widget_host->GetRoutingID();
1755  TextInputClientMac::GetInstance()->GetStringAtPoint(
1756      widget_host, gfx::ToFlooredPoint(transformed_point),
1757      base::BindOnce(&RenderWidgetHostViewMac::OnGotStringForDictionaryOverlay,
1758                     weak_factory_.GetWeakPtr(), target_widget_process_id,
1759                     target_widget_routing_id));
1760}
1761
1762bool RenderWidgetHostViewMac::SyncGetCharacterIndexAtPoint(
1763    const gfx::PointF& root_point,
1764    uint32_t* index) {
1765  *index = UINT32_MAX;
1766
1767  if (!host() || !host()->delegate() ||
1768      !host()->delegate()->GetInputEventRouter())
1769    return true;
1770
1771  gfx::PointF transformed_point;
1772  RenderWidgetHostImpl* widget_host =
1773      host()->delegate()->GetInputEventRouter()->GetRenderWidgetHostAtPoint(
1774          this, root_point, &transformed_point);
1775  if (!widget_host)
1776    return true;
1777
1778  *index = TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
1779      widget_host, gfx::ToFlooredPoint(transformed_point));
1780  return true;
1781}
1782
1783void RenderWidgetHostViewMac::SyncGetCharacterIndexAtPoint(
1784    const gfx::PointF& root_point,
1785    SyncGetCharacterIndexAtPointCallback callback) {
1786  uint32_t index;
1787  SyncGetCharacterIndexAtPoint(root_point, &index);
1788  std::move(callback).Run(index);
1789}
1790
1791bool RenderWidgetHostViewMac::SyncGetFirstRectForRange(
1792    const gfx::Range& requested_range,
1793    const gfx::Rect& in_rect,
1794    const gfx::Range& in_actual_range,
1795    gfx::Rect* rect,
1796    gfx::Range* actual_range,
1797    bool* success) {
1798  *rect = in_rect;
1799  *actual_range = in_actual_range;
1800  if (!GetFocusedWidget()) {
1801    *success = false;
1802    return true;
1803  }
1804  *success = true;
1805  if (!GetCachedFirstRectForCharacterRange(requested_range, rect,
1806                                           actual_range)) {
1807    // https://crbug.com/121917
1808    base::ScopedAllowBlocking allow_wait;
1809    *rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
1810        GetFocusedWidget(), requested_range);
1811    // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery.
1812    *actual_range = requested_range;
1813  }
1814  return true;
1815}
1816
1817void RenderWidgetHostViewMac::SyncGetFirstRectForRange(
1818    const gfx::Range& requested_range,
1819    const gfx::Rect& rect,
1820    const gfx::Range& actual_range,
1821    SyncGetFirstRectForRangeCallback callback) {
1822  gfx::Rect out_rect;
1823  gfx::Range out_actual_range;
1824  bool success;
1825  SyncGetFirstRectForRange(requested_range, rect, actual_range, &out_rect,
1826                           &out_actual_range, &success);
1827  std::move(callback).Run(out_rect, out_actual_range, success);
1828}
1829
1830void RenderWidgetHostViewMac::ExecuteEditCommand(const std::string& command) {
1831  if (host()->delegate()) {
1832    host()->delegate()->ExecuteEditCommand(command, base::nullopt);
1833  }
1834}
1835
1836void RenderWidgetHostViewMac::Undo() {
1837  if (auto* delegate = GetFocusedRenderWidgetHostDelegate()) {
1838    delegate->Undo();
1839  }
1840}
1841
1842void RenderWidgetHostViewMac::Redo() {
1843  if (auto* delegate = GetFocusedRenderWidgetHostDelegate()) {
1844    delegate->Redo();
1845  }
1846}
1847
1848void RenderWidgetHostViewMac::Cut() {
1849  if (auto* delegate = GetFocusedRenderWidgetHostDelegate()) {
1850    delegate->Cut();
1851  }
1852}
1853
1854void RenderWidgetHostViewMac::Copy() {
1855  if (auto* delegate = GetFocusedRenderWidgetHostDelegate()) {
1856    delegate->Copy();
1857  }
1858}
1859
1860void RenderWidgetHostViewMac::CopyToFindPboard() {
1861  WebContents* web_contents = GetWebContents();
1862  if (web_contents)
1863    web_contents->CopyToFindPboard();
1864}
1865
1866void RenderWidgetHostViewMac::Paste() {
1867  if (auto* delegate = GetFocusedRenderWidgetHostDelegate()) {
1868    delegate->Paste();
1869  }
1870}
1871
1872void RenderWidgetHostViewMac::PasteAndMatchStyle() {
1873  if (auto* delegate = GetFocusedRenderWidgetHostDelegate()) {
1874    delegate->PasteAndMatchStyle();
1875  }
1876}
1877
1878void RenderWidgetHostViewMac::SelectAll() {
1879  if (auto* delegate = GetFocusedRenderWidgetHostDelegate()) {
1880    delegate->SelectAll();
1881  }
1882}
1883
1884bool RenderWidgetHostViewMac::SyncIsSpeaking(bool* is_speaking) {
1885  *is_speaking = ui::TextServicesContextMenu::IsSpeaking();
1886  return true;
1887}
1888
1889void RenderWidgetHostViewMac::SyncIsSpeaking(SyncIsSpeakingCallback callback) {
1890  bool is_speaking;
1891  SyncIsSpeaking(&is_speaking);
1892  std::move(callback).Run(is_speaking);
1893}
1894
1895void RenderWidgetHostViewMac::StartSpeaking() {
1896  RenderWidgetHostView* target = this;
1897  WebContents* web_contents = GetWebContents();
1898  if (web_contents) {
1899    content::BrowserPluginGuestManager* guest_manager =
1900        web_contents->GetBrowserContext()->GetGuestManager();
1901    if (guest_manager) {
1902      content::WebContents* guest =
1903          guest_manager->GetFullPageGuest(web_contents);
1904      if (guest) {
1905        target = guest->GetRenderWidgetHostView();
1906      }
1907    }
1908  }
1909  target->SpeakSelection();
1910}
1911
1912void RenderWidgetHostViewMac::StopSpeaking() {
1913  ui::TextServicesContextMenu::StopSpeaking();
1914}
1915
1916void RenderWidgetHostViewMac::SetRemoteAccessibilityWindowToken(
1917    const std::vector<uint8_t>& window_token) {
1918  if (window_token.empty()) {
1919    remote_window_accessible_.reset();
1920  } else {
1921    remote_window_accessible_ =
1922        ui::RemoteAccessibility::GetRemoteElementFromToken(window_token);
1923  }
1924}
1925
1926///////////////////////////////////////////////////////////////////////////////
1927// mojom::RenderWidgetHostNSViewHost functions that translate events and
1928// forward them to the RenderWidgetHostNSViewHostHelper implementation:
1929
1930void RenderWidgetHostViewMac::ForwardKeyboardEventWithCommands(
1931    std::unique_ptr<blink::WebCoalescedInputEvent> input_event,
1932    const std::vector<uint8_t>& native_event_data,
1933    bool skip_in_browser,
1934    std::vector<blink::mojom::EditCommandPtr> edit_commands) {
1935  if (!input_event || !blink::WebInputEvent::IsKeyboardEventType(
1936                          input_event->Event().GetType())) {
1937    DLOG(ERROR) << "Absent or non-KeyboardEventType event.";
1938    return;
1939  }
1940  const blink::WebKeyboardEvent& keyboard_event =
1941      static_cast<const blink::WebKeyboardEvent&>(input_event->Event());
1942  NativeWebKeyboardEvent native_event(keyboard_event, nil);
1943  native_event.skip_in_browser = skip_in_browser;
1944  // The NSEvent constructed from the InputEvent sent over mojo is not even
1945  // close to the original NSEvent, resulting in all sorts of bugs. Use the
1946  // native event serialization to reconstruct the NSEvent.
1947  // https://crbug.com/919167,943197,964052
1948  [native_event.os_event release];
1949  native_event.os_event = [ui::EventFromData(native_event_data) retain];
1950  ForwardKeyboardEventWithCommands(native_event, input_event->latency_info(),
1951                                   std::move(edit_commands));
1952}
1953
1954void RenderWidgetHostViewMac::RouteOrProcessMouseEvent(
1955    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
1956  if (!input_event ||
1957      !blink::WebInputEvent::IsMouseEventType(input_event->Event().GetType())) {
1958    DLOG(ERROR) << "Absent or non-MouseEventType event.";
1959    return;
1960  }
1961  const blink::WebMouseEvent& mouse_event =
1962      static_cast<const blink::WebMouseEvent&>(input_event->Event());
1963  RouteOrProcessMouseEvent(mouse_event);
1964}
1965
1966void RenderWidgetHostViewMac::RouteOrProcessTouchEvent(
1967    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
1968  if (!input_event ||
1969      !blink::WebInputEvent::IsTouchEventType(input_event->Event().GetType())) {
1970    DLOG(ERROR) << "Absent or non-TouchEventType event.";
1971    return;
1972  }
1973  const blink::WebTouchEvent& touch_event =
1974      static_cast<const blink::WebTouchEvent&>(input_event->Event());
1975  RouteOrProcessTouchEvent(touch_event);
1976}
1977
1978void RenderWidgetHostViewMac::RouteOrProcessWheelEvent(
1979    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
1980  if (!input_event || input_event->Event().GetType() !=
1981                          blink::WebInputEvent::Type::kMouseWheel) {
1982    DLOG(ERROR) << "Absent or non-MouseWheel event.";
1983    return;
1984  }
1985  const blink::WebMouseWheelEvent& wheel_event =
1986      static_cast<const blink::WebMouseWheelEvent&>(input_event->Event());
1987  RouteOrProcessWheelEvent(wheel_event);
1988}
1989
1990void RenderWidgetHostViewMac::ForwardMouseEvent(
1991    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
1992  if (!input_event ||
1993      !blink::WebInputEvent::IsMouseEventType(input_event->Event().GetType())) {
1994    DLOG(ERROR) << "Absent or non-MouseEventType event.";
1995    return;
1996  }
1997  const blink::WebMouseEvent& mouse_event =
1998      static_cast<const blink::WebMouseEvent&>(input_event->Event());
1999  ForwardMouseEvent(mouse_event);
2000}
2001
2002void RenderWidgetHostViewMac::ForwardWheelEvent(
2003    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
2004  if (!input_event || input_event->Event().GetType() !=
2005                          blink::WebInputEvent::Type::kMouseWheel) {
2006    DLOG(ERROR) << "Absent or non-MouseWheel event.";
2007    return;
2008  }
2009  const blink::WebMouseWheelEvent& wheel_event =
2010      static_cast<const blink::WebMouseWheelEvent&>(input_event->Event());
2011  ForwardWheelEvent(wheel_event);
2012}
2013
2014void RenderWidgetHostViewMac::GestureBegin(
2015    std::unique_ptr<blink::WebCoalescedInputEvent> input_event,
2016    bool is_synthetically_injected) {
2017  if (!input_event || !blink::WebInputEvent::IsGestureEventType(
2018                          input_event->Event().GetType())) {
2019    DLOG(ERROR) << "Absent or non-GestureEventType event.";
2020    return;
2021  }
2022  blink::WebGestureEvent gesture_event =
2023      static_cast<const blink::WebGestureEvent&>(input_event->Event());
2024  // Strip the gesture type, because it is not known.
2025  gesture_event.SetType(blink::WebInputEvent::Type::kUndefined);
2026  GestureBegin(gesture_event, is_synthetically_injected);
2027}
2028
2029void RenderWidgetHostViewMac::GestureUpdate(
2030    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
2031  if (!input_event || !blink::WebInputEvent::IsGestureEventType(
2032                          input_event->Event().GetType())) {
2033    DLOG(ERROR) << "Absent or non-GestureEventType event.";
2034    return;
2035  }
2036  const blink::WebGestureEvent& gesture_event =
2037      static_cast<const blink::WebGestureEvent&>(input_event->Event());
2038  GestureUpdate(gesture_event);
2039}
2040
2041void RenderWidgetHostViewMac::GestureEnd(
2042    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
2043  if (!input_event || !blink::WebInputEvent::IsGestureEventType(
2044                          input_event->Event().GetType())) {
2045    DLOG(ERROR) << "Absent or non-GestureEventType event.";
2046    return;
2047  }
2048  blink::WebGestureEvent gesture_event =
2049      static_cast<const blink::WebGestureEvent&>(input_event->Event());
2050  GestureEnd(gesture_event);
2051}
2052
2053void RenderWidgetHostViewMac::SmartMagnify(
2054    std::unique_ptr<blink::WebCoalescedInputEvent> input_event) {
2055  if (!input_event || !blink::WebInputEvent::IsGestureEventType(
2056                          input_event->Event().GetType())) {
2057    DLOG(ERROR) << "Absent or non-GestureEventType event.";
2058    return;
2059  }
2060  const blink::WebGestureEvent& gesture_event =
2061      static_cast<const blink::WebGestureEvent&>(input_event->Event());
2062  SmartMagnify(gesture_event);
2063}
2064
2065void RenderWidgetHostViewMac::OnGotStringForDictionaryOverlay(
2066    int32_t target_widget_process_id,
2067    int32_t target_widget_routing_id,
2068    ui::mojom::AttributedStringPtr attributed_string,
2069    const gfx::Point& baseline_point) {
2070  if (!attributed_string || attributed_string->string.empty()) {
2071    // The PDF plugin does not support getting the attributed string at point.
2072    // Until it does, use NSPerformService(), which opens Dictionary.app.
2073    // TODO(shuchen): Support GetStringAtPoint() & GetStringFromRange() for PDF.
2074    // https://crbug.com/152438
2075    // This often just opens a blank dictionary, not the definition of |string|.
2076    // https://crbug.com/830047
2077    // This path will be taken, inappropriately, when a lookup gesture was
2078    // performed at a location that doesn't have text, but some text is
2079    // selected.
2080    // https://crbug.com/830906
2081    if (auto* selection = GetTextSelection()) {
2082      const base::string16& selected_text = selection->selected_text();
2083      NSString* ns_selected_text = base::SysUTF16ToNSString(selected_text);
2084      if ([ns_selected_text length] == 0)
2085        return;
2086      scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard;
2087      if ([pasteboard->get() writeObjects:@[ ns_selected_text ]]) {
2088        NSPerformService(@"Look Up in Dictionary", pasteboard->get());
2089      }
2090    }
2091  } else {
2092    // By the time we get here |widget_host| might have been destroyed.
2093    // https://crbug.com/737032
2094    auto* widget_host = content::RenderWidgetHost::FromID(
2095        target_widget_process_id, target_widget_routing_id);
2096    gfx::Point updated_baseline_point = baseline_point;
2097    if (widget_host) {
2098      if (auto* rwhv = widget_host->GetView()) {
2099        updated_baseline_point =
2100            rwhv->TransformPointToRootCoordSpace(baseline_point);
2101      }
2102    }
2103    ns_view_->ShowDictionaryOverlay(std::move(attributed_string),
2104                                    updated_baseline_point);
2105  }
2106}
2107
2108Class GetRenderWidgetHostViewCocoaClassForTesting() {
2109  return [RenderWidgetHostViewCocoa class];
2110}
2111
2112}  // namespace content
2113