blob: 5f7928bf5311f236499ee105f9cdb62fdd219f19 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/render_widget_host_view_ios.h"
#import <UIKit/UIKit.h>
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "components/viz/common/surfaces/frame_sink_id_allocator.h"
#include "content/browser/renderer_host/browser_compositor_ios.h"
#include "content/browser/renderer_host/input/motion_event_web.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target_ios.h"
#include "content/browser/renderer_host/input/web_input_event_builders_ios.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
#include "content/browser/renderer_host/text_input_manager.h"
#include "content/browser/renderer_host/ui_events_helper.h"
#include "content/common/content_switches_internal.h"
#include "content/public/browser/browser_task_traits.h"
#include "ui/accelerated_widget_mac/ca_layer_frame_sink_provider.h"
#include "ui/accelerated_widget_mac/display_ca_layer_tree.h"
#include "ui/base/ime/text_input_mode.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
// Used for settng the requested renderer size when testing.
constexpr int kDefaultWidthForTesting = 800;
constexpr int kDefaultHeightForTesting = 600;
@interface UIApplication (Testing)
- (BOOL)isRunningTests;
@end
@implementation UIApplication (Testing)
- (BOOL)isRunningTests {
return NO;
}
@end
namespace {
bool IsTesting() {
return [[UIApplication sharedApplication] isRunningTests];
}
} // namespace
// TODO(dtapuska): Change this to be UITextInput and handle the other
// events to implement the composition and selection ranges.
@interface RenderWidgetUIViewTextInput : UIView <UIKeyInput> {
raw_ptr<content::RenderWidgetHostViewIOS> _view;
}
- (void)onUpdateTextInputState:(const ui::mojom::TextInputState&)state
withBounds:(CGRect)bounds;
- (void)showKeyboard:(bool)has_text withBounds:(CGRect)bounds;
- (void)hideKeyboard;
@end
@interface RenderWidgetUIView : CALayerFrameSinkProvider {
raw_ptr<content::RenderWidgetHostViewIOS> _view;
}
// TextInput state.
@property(nonatomic, strong) RenderWidgetUIViewTextInput* textInput;
@end
@implementation CALayerFrameSinkProvider
- (ui::CALayerFrameSink*)frameSink {
return nil;
}
@end
@implementation RenderWidgetUIViewTextInput {
BOOL _hasText;
}
- (instancetype)initWithWidget:(content::RenderWidgetHostViewIOS*)view {
_view = view;
_hasText = NO;
self.multipleTouchEnabled = YES;
self.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
return [self init];
}
- (void)onUpdateTextInputState:(const ui::mojom::TextInputState&)state
withBounds:(CGRect)bounds {
// Check for the visibility request and policy if VK APIs are enabled.
if (state.vk_policy == ui::mojom::VirtualKeyboardPolicy::MANUAL) {
// policy is manual.
if (state.last_vk_visibility_request ==
ui::mojom::VirtualKeyboardVisibilityRequest::SHOW) {
[self showKeyboard:!state.value->empty() withBounds:bounds];
} else if (state.last_vk_visibility_request ==
ui::mojom::VirtualKeyboardVisibilityRequest::HIDE) {
[self hideKeyboard];
}
} else {
bool hide = state.always_hide_ime ||
state.mode == ui::TextInputMode::TEXT_INPUT_MODE_NONE ||
state.type == ui::TextInputType::TEXT_INPUT_TYPE_NONE;
if (hide) {
[self hideKeyboard];
} else if (state.show_ime_if_needed) {
[self showKeyboard:!state.value->empty() withBounds:bounds];
}
}
}
- (void)showKeyboard:(bool)has_text withBounds:(CGRect)bounds {
self.frame = bounds;
[self becomeFirstResponder];
_hasText = has_text;
}
- (void)hideKeyboard {
[self resignFirstResponder];
_hasText = NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)hasText {
return _hasText;
}
- (void)insertText:(NSString*)text {
_view->ImeCommitText(base::SysNSStringToUTF16(text),
gfx::Range::InvalidRange(), 0);
}
- (void)deleteBackward {
std::vector<ui::ImeTextSpan> ime_text_spans;
_view->ImeSetComposition(std::u16string(), ime_text_spans,
gfx::Range::InvalidRange(), -1, 0);
_view->ImeCommitText(std::u16string(), gfx::Range::InvalidRange(), 0);
}
- (BOOL)becomeFirstResponder {
BOOL result = [super becomeFirstResponder];
if (result) {
_view->OnFirstResponderChanged();
}
return result;
}
- (BOOL)resignFirstResponder {
BOOL result = [super resignFirstResponder];
if (result) {
_view->OnFirstResponderChanged();
}
return result;
}
@end
@implementation RenderWidgetUIView
@synthesize textInput = _text_input;
- (instancetype)initWithWidget:(content::RenderWidgetHostViewIOS*)view {
self = [self init];
if (self) {
_view = view;
self.multipleTouchEnabled = YES;
self.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_text_input = [[RenderWidgetUIViewTextInput alloc] initWithWidget:view];
[self addSubview:_text_input];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
_view->UpdateScreenInfo();
// TODO(dtapuska): This isn't correct, we need to figure out when the window
// gains/loses focus.
_view->SetActive(true);
}
- (ui::CALayerFrameSink*)frameSink {
return _view;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)becomeFirstResponder {
BOOL result = [super becomeFirstResponder];
if (result || _view->CanBecomeFirstResponderForTesting()) {
_view->OnFirstResponderChanged();
}
return result;
}
- (BOOL)resignFirstResponder {
BOOL result = [super resignFirstResponder];
if (result || _view->CanResignFirstResponderForTesting()) {
_view->OnFirstResponderChanged();
}
return result;
}
- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
if (!_view->HasFocus()) {
if ([self becomeFirstResponder]) {
_view->OnFirstResponderChanged();
}
}
for (UITouch* touch in touches) {
_view->OnTouchEvent(content::WebTouchEventBuilder::Build(
blink::WebInputEvent::Type::kTouchStart, touch, event, self));
}
}
- (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
for (UITouch* touch in touches) {
_view->OnTouchEvent(content::WebTouchEventBuilder::Build(
blink::WebInputEvent::Type::kTouchEnd, touch, event, self));
}
}
- (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
for (UITouch* touch in touches) {
_view->OnTouchEvent(content::WebTouchEventBuilder::Build(
blink::WebInputEvent::Type::kTouchMove, touch, event, self));
}
}
- (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
for (UITouch* touch in touches) {
_view->OnTouchEvent(content::WebTouchEventBuilder::Build(
blink::WebInputEvent::Type::kTouchCancel, touch, event, self));
}
}
@end
namespace content {
// This class holds a scoped_nsobject so we don't leak that in the header
// of the RenderWidgetHostViewIOS.
class UIViewHolder {
public:
base::scoped_nsobject<RenderWidgetUIView> view_;
};
///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewIOS, public:
RenderWidgetHostViewIOS::RenderWidgetHostViewIOS(RenderWidgetHost* widget)
: RenderWidgetHostViewBase(widget),
gesture_provider_(
ui::GetGestureProviderConfig(
ui::GestureProviderConfigType::CURRENT_PLATFORM,
content::GetUIThreadTaskRunner({BrowserTaskType::kUserInput})),
this) {
ui_view_ = std::make_unique<UIViewHolder>();
ui_view_->view_ = base::scoped_nsobject<RenderWidgetUIView>(
[[RenderWidgetUIView alloc] initWithWidget:this]);
display_tree_ =
std::make_unique<ui::DisplayCALayerTree>([ui_view_->view_ layer]);
browser_compositor_ = std::make_unique<BrowserCompositorIOS>(
ui_view_->view_.get(), this, host()->is_hidden(),
host()->GetFrameSinkId());
if (IsTesting()) {
browser_compositor_->UpdateSurfaceFromUIView(
gfx::Size(kDefaultWidthForTesting, kDefaultHeightForTesting));
}
CHECK(host()->GetFrameSinkId().is_valid());
// Let the page-level input event router know about our surface ID
// namespace for surface-based hit testing.
if (ShouldRouteEvents()) {
host()->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner(
GetFrameSinkId(), this);
}
if (GetTextInputManager()) {
text_input_manager_->AddObserver(this);
}
host()->SetView(this);
}
RenderWidgetHostViewIOS::~RenderWidgetHostViewIOS() = default;
void RenderWidgetHostViewIOS::Destroy() {
if (text_input_manager_) {
text_input_manager_->RemoveObserver(this);
}
browser_compositor_.reset();
// Call this before the derived class is destroyed so that virtual function
// calls back into `this` still work.
NotifyObserversAboutShutdown();
RenderWidgetHostViewBase::Destroy();
delete this;
}
bool RenderWidgetHostViewIOS::IsSurfaceAvailableForCopy() {
return browser_compositor_->GetDelegatedFrameHost()
->CanCopyFromCompositingSurface();
}
void RenderWidgetHostViewIOS::CopyFromSurface(
const gfx::Rect& src_rect,
const gfx::Size& dst_size,
base::OnceCallback<void(const SkBitmap&)> callback) {
base::WeakPtr<RenderWidgetHostImpl> popup_host;
base::WeakPtr<DelegatedFrameHost> popup_frame_host;
RenderWidgetHostViewBase::CopyMainAndPopupFromSurface(
host()->GetWeakPtr(),
browser_compositor_->GetDelegatedFrameHost()->GetWeakPtr(), popup_host,
popup_frame_host, src_rect, dst_size, GetDeviceScaleFactor(),
std::move(callback));
}
void RenderWidgetHostViewIOS::InitAsChild(gfx::NativeView parent_view) {}
void RenderWidgetHostViewIOS::SetSize(const gfx::Size& size) {}
void RenderWidgetHostViewIOS::SetBounds(const gfx::Rect& rect) {}
gfx::NativeView RenderWidgetHostViewIOS::GetNativeView() {
return ui_view_->view_.get();
}
gfx::NativeViewAccessible RenderWidgetHostViewIOS::GetNativeViewAccessible() {
return {};
}
void RenderWidgetHostViewIOS::Focus() {
// Ignore redundant calls, as they can cause unending loops of focus-setting.
// crbug.com/998123, crbug.com/804184.
if (is_first_responder_ || is_getting_focus_) {
return;
}
base::AutoReset<bool> is_getting_focus_bit(&is_getting_focus_, true);
[ui_view_->view_ becomeFirstResponder];
}
bool RenderWidgetHostViewIOS::HasFocus() {
return is_first_responder_;
}
gfx::Rect RenderWidgetHostViewIOS::GetViewBounds() {
return IsTesting()
? gfx::Rect(kDefaultWidthForTesting, kDefaultHeightForTesting)
: gfx::Rect([ui_view_->view_ bounds]);
}
blink::mojom::PointerLockResult RenderWidgetHostViewIOS::LockMouse(bool) {
return {};
}
blink::mojom::PointerLockResult RenderWidgetHostViewIOS::ChangeMouseLock(bool) {
return {};
}
void RenderWidgetHostViewIOS::UnlockMouse() {}
uint32_t RenderWidgetHostViewIOS::GetCaptureSequenceNumber() const {
return latest_capture_sequence_number_;
}
void RenderWidgetHostViewIOS::EnsureSurfaceSynchronizedForWebTest() {
++latest_capture_sequence_number_;
browser_compositor_->ForceNewSurfaceId();
}
void RenderWidgetHostViewIOS::TakeFallbackContentFrom(
RenderWidgetHostView* view) {}
std::unique_ptr<SyntheticGestureTarget>
RenderWidgetHostViewIOS::CreateSyntheticGestureTarget() {
RenderWidgetHostImpl* host =
RenderWidgetHostImpl::From(GetRenderWidgetHost());
return std::make_unique<SyntheticGestureTargetIOS>(host);
}
const viz::LocalSurfaceId& RenderWidgetHostViewIOS::GetLocalSurfaceId() const {
return browser_compositor_->GetRendererLocalSurfaceId();
}
const viz::FrameSinkId& RenderWidgetHostViewIOS::GetFrameSinkId() const {
return browser_compositor_->GetDelegatedFrameHost()->frame_sink_id();
}
viz::FrameSinkId RenderWidgetHostViewIOS::GetRootFrameSinkId() {
return browser_compositor_->GetRootFrameSinkId();
}
viz::SurfaceId RenderWidgetHostViewIOS::GetCurrentSurfaceId() const {
// |browser_compositor_| could be null if this method is called during its
// destruction.
if (!browser_compositor_) {
return viz::SurfaceId();
}
return browser_compositor_->GetDelegatedFrameHost()->GetCurrentSurfaceId();
}
void RenderWidgetHostViewIOS::InitAsPopup(
RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos,
const gfx::Rect& anchor_rect) {}
void RenderWidgetHostViewIOS::UpdateCursor(const ui::Cursor& cursor) {}
void RenderWidgetHostViewIOS::SetIsLoading(bool is_loading) {}
void RenderWidgetHostViewIOS::RenderProcessGone() {
Destroy();
}
void RenderWidgetHostViewIOS::ShowWithVisibility(
PageVisibilityState page_visibility) {
if (IsTesting() && !is_visible_) {
// There is some circularity in how UpdateScreenInfo works. The base class
// sets up some state needed by the browser compositor. The base class also
// depends on an update from the browser compositor. In practice this is a
// non issue because the function is called many times and values converge,
// but this is not necessarily the case in tests. This could be resolved
// by rewriting UpdateScreenInfo to interleave the work (see the mac
// implementation, eg), but for now we will simply may another call to the
// base class.
RenderWidgetHostViewBase::UpdateScreenInfo();
UpdateScreenInfo();
}
is_visible_ = true;
browser_compositor_->SetViewVisible(is_visible_);
OnShowWithPageVisibility(page_visibility);
}
void RenderWidgetHostViewIOS::NotifyHostAndDelegateOnWasShown(
blink::mojom::RecordContentToVisibleTimeRequestPtr visible_time_request) {
browser_compositor_->SetRenderWidgetHostIsHidden(false);
host()->WasShown(blink::mojom::RecordContentToVisibleTimeRequestPtr());
}
void RenderWidgetHostViewIOS::Hide() {
is_visible_ = false;
browser_compositor_->SetViewVisible(is_visible_);
browser_compositor_->SetRenderWidgetHostIsHidden(true);
if (!host() || host()->is_hidden()) {
return;
}
// Inform the renderer that we are being hidden so it can reduce its resource
// utilization.
host()->WasHidden();
}
bool RenderWidgetHostViewIOS::IsShowing() {
return is_visible_ && [ui_view_->view_ window];
}
gfx::Rect RenderWidgetHostViewIOS::GetBoundsInRootWindow() {
return IsTesting()
? gfx::Rect(kDefaultWidthForTesting, kDefaultHeightForTesting)
: gfx::Rect([ui_view_->view_ bounds]);
}
gfx::Size RenderWidgetHostViewIOS::GetRequestedRendererSize() {
// When testing, we will not have a windowScene and, as a consequence, we will
// not have an intrinsic renderer size. This will cause tests to fail, though,
// so we will instead set a default size.
return !IsTesting()
? browser_compositor_->GetRendererSize()
: gfx::Size(kDefaultWidthForTesting, kDefaultHeightForTesting);
}
absl::optional<DisplayFeature> RenderWidgetHostViewIOS::GetDisplayFeature() {
return absl::nullopt;
}
void RenderWidgetHostViewIOS::SetDisplayFeatureForTesting(
const DisplayFeature* display_feature) {}
void RenderWidgetHostViewIOS::UpdateBackgroundColor() {}
void RenderWidgetHostViewIOS::
RequestSuccessfulPresentationTimeFromHostOrDelegate(
blink::mojom::RecordContentToVisibleTimeRequestPtr
visible_time_request) {
host()->RequestSuccessfulPresentationTimeForNextFrame(
std::move(visible_time_request));
}
void RenderWidgetHostViewIOS::
CancelSuccessfulPresentationTimeRequestForHostAndDelegate() {
host()->CancelSuccessfulPresentationTimeRequest();
}
SkColor RenderWidgetHostViewIOS::BrowserCompositorIOSGetGutterColor() {
// When making an element on the page fullscreen the element's background
// may not match the page's, so use black as the gutter color to avoid
// flashes of brighter colors during the transition.
if (host()->delegate() && host()->delegate()->IsFullscreen()) {
return SK_ColorBLACK;
}
if (GetBackgroundColor()) {
return *GetBackgroundColor();
}
return SK_ColorWHITE;
}
bool RenderWidgetHostViewIOS::OnBrowserCompositorSurfaceIdChanged() {
return host()->SynchronizeVisualProperties();
}
void RenderWidgetHostViewIOS::OnFrameTokenChanged(
uint32_t frame_token,
base::TimeTicks activation_time) {
OnFrameTokenChangedForView(frame_token, activation_time);
}
std::vector<viz::SurfaceId>
RenderWidgetHostViewIOS::CollectSurfaceIdsForEviction() {
return {};
}
void RenderWidgetHostViewIOS::UpdateScreenInfo() {
browser_compositor_->UpdateSurfaceFromUIView(GetViewBounds().size());
RenderWidgetHostViewBase::UpdateScreenInfo();
}
void RenderWidgetHostViewIOS::UpdateCALayerTree(
const gfx::CALayerParams& ca_layer_params) {
DCHECK(display_tree_);
display_tree_->UpdateCALayerTree(ca_layer_params);
}
void RenderWidgetHostViewIOS::DidNavigate() {
browser_compositor_->DidNavigate();
}
viz::ScopedSurfaceIdAllocator
RenderWidgetHostViewIOS::DidUpdateVisualProperties(
const cc::RenderFrameMetadata& metadata) {
base::OnceCallback<void()> allocation_task = base::BindOnce(
base::IgnoreResult(
&RenderWidgetHostViewIOS::OnDidUpdateVisualPropertiesComplete),
weak_factory_.GetWeakPtr(), metadata);
return browser_compositor_->GetScopedRendererSurfaceIdAllocator(
std::move(allocation_task));
}
void RenderWidgetHostViewIOS::OnDidUpdateVisualPropertiesComplete(
const cc::RenderFrameMetadata& metadata) {
browser_compositor_->UpdateSurfaceFromChild(
host()->auto_resize_enabled(), metadata.device_scale_factor,
metadata.viewport_size_in_pixels,
metadata.local_surface_id.value_or(viz::LocalSurfaceId()));
}
void RenderWidgetHostViewIOS::ClearFallbackSurfaceForCommitPending() {
browser_compositor_->GetDelegatedFrameHost()
->ClearFallbackSurfaceForCommitPending();
browser_compositor_->InvalidateLocalSurfaceIdOnEviction();
}
void RenderWidgetHostViewIOS::ResetFallbackToFirstNavigationSurface() {
browser_compositor_->GetDelegatedFrameHost()
->ResetFallbackToFirstNavigationSurface();
}
bool RenderWidgetHostViewIOS::RequestRepaintForTesting() {
return browser_compositor_->ForceNewSurfaceId();
}
void RenderWidgetHostViewIOS::TransformPointToRootSurface(gfx::PointF* point) {
browser_compositor_->TransformPointToRootSurface(point);
}
bool RenderWidgetHostViewIOS::TransformPointToCoordSpaceForView(
const gfx::PointF& point,
RenderWidgetHostViewBase* target_view,
gfx::PointF* transformed_point) {
if (target_view == this) {
*transformed_point = point;
return true;
}
return target_view->TransformPointToLocalCoordSpace(
point, GetCurrentSurfaceId(), transformed_point);
}
display::ScreenInfo RenderWidgetHostViewIOS::GetCurrentScreenInfo() const {
return screen_infos_.current();
}
void RenderWidgetHostViewIOS::SetCurrentDeviceScaleFactor(
float device_scale_factor) {
// TODO(https://crbug.com/1337094): does this need to be upscaled by
// scale_override_for_capture_ for HiDPI capture mode?
screen_infos_.mutable_current().device_scale_factor = device_scale_factor;
}
void RenderWidgetHostViewIOS::SetActive(bool active) {
if (host()) {
UpdateActiveState(active);
if (active) {
if (HasFocus()) {
host()->Focus();
}
} else {
host()->Blur();
}
}
// if (HasFocus())
// SetTextInputActive(active);
if (!active) {
UnlockMouse();
}
}
bool RenderWidgetHostViewIOS::ShouldRouteEvents() const {
DCHECK(host());
return host()->delegate() && host()->delegate()->GetInputEventRouter();
}
void RenderWidgetHostViewIOS::OnTouchEvent(blink::WebTouchEvent web_event) {
ui::FilteredGestureProvider::TouchHandlingResult result =
gesture_provider_.OnTouchEvent(MotionEventWeb(web_event));
if (!result.succeeded) {
return;
}
web_event.moved_beyond_slop_region = result.moved_beyond_slop_region;
ui::LatencyInfo latency_info(ui::SourceEventType::TOUCH);
latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT);
if (ShouldRouteEvents()) {
host()->delegate()->GetInputEventRouter()->RouteTouchEvent(this, &web_event,
latency_info);
} else {
host()->ForwardTouchEventWithLatencyInfo(web_event, latency_info);
}
}
void RenderWidgetHostViewIOS::ProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch,
blink::mojom::InputEventResultState ack_result) {
const bool event_consumed =
ack_result == blink::mojom::InputEventResultState::kConsumed;
gesture_provider_.OnTouchEventAck(
touch.event.unique_touch_event_id, event_consumed,
InputEventResultStateIsSetNonBlocking(ack_result));
if (touch.event.touch_start_or_first_touch_move && event_consumed &&
ShouldRouteEvents()) {
host()
->delegate()
->GetInputEventRouter()
->OnHandledTouchStartOrFirstTouchMove(
touch.event.unique_touch_event_id);
}
}
void RenderWidgetHostViewIOS::OnGestureEvent(
const ui::GestureEventData& gesture) {
if ((gesture.type() == ui::ET_GESTURE_PINCH_BEGIN ||
gesture.type() == ui::ET_GESTURE_PINCH_UPDATE ||
gesture.type() == ui::ET_GESTURE_PINCH_END) &&
!IsPinchToZoomEnabled()) {
return;
}
blink::WebGestureEvent web_gesture =
ui::CreateWebGestureEventFromGestureEventData(gesture);
SendGestureEvent(web_gesture);
}
bool RenderWidgetHostViewIOS::RequiresDoubleTapGestureEvents() const {
return true;
}
void RenderWidgetHostViewIOS::SendGestureEvent(
const blink::WebGestureEvent& event) {
ui::LatencyInfo latency_info =
ui::WebInputEventTraits::CreateLatencyInfoForWebGestureEvent(event);
InjectGestureEvent(event, latency_info);
}
void RenderWidgetHostViewIOS::InjectTouchEvent(
const blink::WebTouchEvent& event,
const ui::LatencyInfo& latency_info) {
ui::FilteredGestureProvider::TouchHandlingResult result =
gesture_provider_.OnTouchEvent(MotionEventWeb(event));
if (!result.succeeded) {
return;
}
if (ShouldRouteEvents()) {
blink::WebTouchEvent touch_event(event);
host()->delegate()->GetInputEventRouter()->RouteTouchEvent(
this, &touch_event, latency_info);
} else {
host()->ForwardTouchEventWithLatencyInfo(event, latency_info);
}
}
void RenderWidgetHostViewIOS::InjectGestureEvent(
const blink::WebGestureEvent& event,
const ui::LatencyInfo& latency_info) {
if (ShouldRouteEvents()) {
blink::WebGestureEvent gesture_event(event);
host()->delegate()->GetInputEventRouter()->RouteGestureEvent(
this, &gesture_event, latency_info);
} else {
host()->ForwardGestureEventWithLatencyInfo(event, latency_info);
}
}
void RenderWidgetHostViewIOS::InjectMouseEvent(
const blink::WebMouseEvent& web_mouse,
const ui::LatencyInfo& latency_info) {
NOTIMPLEMENTED();
}
void RenderWidgetHostViewIOS::InjectMouseWheelEvent(
const blink::WebMouseWheelEvent& web_wheel,
const ui::LatencyInfo& latency_info) {
NOTIMPLEMENTED();
}
bool RenderWidgetHostViewIOS::CanBecomeFirstResponderForTesting() const {
return IsTesting() && !is_first_responder_ && is_getting_focus_;
}
bool RenderWidgetHostViewIOS::CanResignFirstResponderForTesting() const {
return IsTesting() && is_first_responder_;
}
void RenderWidgetHostViewIOS::UpdateNativeViewTree(gfx::NativeView view) {
if (view) {
[view addSubview:ui_view_->view_];
ui_view_->view_.get().frame = [view bounds];
} else {
[ui_view_->view_ removeFromSuperview];
}
}
void RenderWidgetHostViewIOS::ImeSetComposition(
const std::u16string& text,
const std::vector<ui::ImeTextSpan>& spans,
const gfx::Range& replacement_range,
int selection_start,
int selection_end) {
if (auto* widget_host = GetActiveWidget()) {
widget_host->ImeSetComposition(text, spans, replacement_range,
selection_start, selection_end);
}
}
void RenderWidgetHostViewIOS::ImeCommitText(const std::u16string& text,
const gfx::Range& replacement_range,
int relative_position) {
if (auto* widget_host = GetActiveWidget()) {
widget_host->ImeCommitText(text, std::vector<ui::ImeTextSpan>(),
replacement_range, relative_position);
}
}
void RenderWidgetHostViewIOS::ImeFinishComposingText(bool keep_selection) {
if (auto* widget_host = GetActiveWidget()) {
widget_host->ImeFinishComposingText(keep_selection);
}
}
RenderWidgetHostImpl* RenderWidgetHostViewIOS::GetActiveWidget() {
return text_input_manager_ ? text_input_manager_->GetActiveWidget() : nullptr;
}
void RenderWidgetHostViewIOS::OnFirstResponderChanged() {
bool is_first_responder = [ui_view_->view_ isFirstResponder] ||
[[ui_view_->view_ textInput] isFirstResponder] ||
(IsTesting() && is_getting_focus_);
if (is_first_responder_ == is_first_responder) {
return;
}
is_first_responder_ = is_first_responder;
if (is_first_responder_) {
host()->GotFocus();
} else {
host()->LostFocus();
}
}
void RenderWidgetHostViewIOS::OnUpdateTextInputStateCalled(
TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view,
bool did_update_state) {
if (text_input_manager->GetActiveWidget()) {
[[ui_view_->view_ textInput]
onUpdateTextInputState:*text_input_manager->GetTextInputState()
withBounds:[ui_view_->view_ bounds]];
} else {
// If there are no active widgets, the TextInputState.type should be
// reported as none.
[[ui_view_->view_ textInput]
onUpdateTextInputState:ui::mojom::TextInputState()
withBounds:[ui_view_->view_ bounds]];
}
}
ui::Compositor* RenderWidgetHostViewIOS::GetCompositor() {
return browser_compositor_->GetCompositor();
}
} // namespace content