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