0

[ios] Initial RenderWidgetHostViewIOS implementation.

- Rudimentary implementation that allows touch events and keyboard
events to be delivered to a renderer.
- For each RenderWidgetHostViewIOS we create a BrowserCompositorIOS
which will attach the layers to a parent ui::Compositor. This
architecture is similar to how Mac works and is sufficient for
the initial prototype now.
- Provide a stub for a ChildProcessLauncher for iOS since we do not
have fork everything runs in process for now.

Bug: 1411704
Change-Id: I5f498dff7866f0c728bb593eb993235afb94a143
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4221595
Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1101174}
This commit is contained in:
Dave Tapuska
2023-02-03 20:43:24 +00:00
committed by Chromium LUCI CQ
parent 3fcbdb1504
commit a1cc370d96
27 changed files with 2326 additions and 16 deletions

@ -71,7 +71,9 @@
#if BUILDFLAG(IS_MAC)
#include "base/mac/scoped_nsautorelease_pool.h"
#include "content/app/mac_init.h"
#endif
#if BUILDFLAG(IS_APPLE)
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
#include "base/allocator/partition_allocator/shim/allocator_shim.h"
#endif
@ -201,7 +203,7 @@ RunContentProcess(ContentMainParams params,
content_main_runner->ReInitializeParams(std::move(params));
} else {
is_initialized = true;
#if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_ALLOCATOR_SHIM)
#if BUILDFLAG(IS_APPLE) && BUILDFLAG(USE_ALLOCATOR_SHIM)
allocator_shim::InitializeAllocatorShim();
#endif
base::EnableTerminationOnOutOfMemory();
@ -283,6 +285,15 @@ RunContentProcess(ContentMainParams params,
InitializeMac();
#endif
#if BUILDFLAG(IS_IOS)
// TODO(crbug.com/1412835): Remove this initialization on iOS. Everything
// runs in process for now as we have no fork.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitch(switches::kSingleProcess);
command_line->AppendSwitch(switches::kEnableViewport);
command_line->AppendSwitch(switches::kUseMobileUserAgent);
#endif
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
base::subtle::EnableFDOwnershipEnforcement(true);
#endif

@ -1100,6 +1100,9 @@ int NO_STACK_PROTECTOR ContentMainRunnerImpl::Run() {
main_params.sandbox_info = content_main_params_->sandbox_info;
#elif BUILDFLAG(IS_MAC)
main_params.autorelease_pool = content_main_params_->autorelease_pool;
#elif BUILDFLAG(IS_IOS)
main_params.argc = content_main_params_->argc;
main_params.argv = content_main_params_->argv;
#endif
const bool start_minimal_browser = content_main_params_->minimal_browser_mode;

@ -267,6 +267,7 @@ source_set("browser") {
"//ui/base/ime/init",
"//ui/color",
"//ui/color:mixers",
"//ui/compositor",
"//ui/display",
"//ui/display/types",
"//ui/display/util",
@ -2604,6 +2605,20 @@ source_set("browser") {
weak_frameworks = [
"UniformTypeIdentifiers.framework", # macOS 11
]
} else if (is_ios) {
sources += [
"child_process_launcher_helper_ios.cc",
"renderer_host/browser_compositor_ios.h",
"renderer_host/browser_compositor_ios.mm",
"renderer_host/delegated_frame_host_client_ios.cc",
"renderer_host/delegated_frame_host_client_ios.h",
"renderer_host/render_widget_host_view_ios.h",
"renderer_host/render_widget_host_view_ios.mm",
"web_contents/web_contents_view_ios.h",
"web_contents/web_contents_view_ios.mm",
]
frameworks += [ "IOSurface.framework" ]
deps += [ "//ui/accelerated_widget_mac" ]
} else {
# Not Mac.
deps += [ "//sandbox" ]
@ -2896,6 +2911,15 @@ source_set("browser") {
configs += [ "//build/config/linux/pangocairo" ]
}
if (is_ios) {
sources += [
"renderer_host/input/web_input_event_builders_ios.h",
"renderer_host/input/web_input_event_builders_ios.mm",
"renderer_host/native_web_keyboard_event_ios.mm",
"speech/tts_ios.mm",
]
}
if (is_android) {
sources += [
"accessibility/accessibility_tree_formatter_android.cc",

@ -384,8 +384,11 @@ ChildProcessTerminationInfo BrowserChildProcessHostImpl::GetTerminationInfo(
if (!child_process_) {
// If the delegate doesn't use Launch() helper.
ChildProcessTerminationInfo info;
// TODO(crbug.com/1412835): iOS is single process mode for now.
#if !BUILDFLAG(IS_IOS)
info.status = base::GetTerminationStatus(data_.GetProcess().Handle(),
&info.exit_code);
#endif
return info;
}
return child_process_->GetChildTerminationInfo(known_dead);

@ -367,7 +367,7 @@ std::unique_ptr<base::MemoryPressureMonitor> CreateMemoryPressureMonitor(
std::unique_ptr<memory_pressure::MultiSourceMemoryPressureMonitor> monitor;
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || \
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || \
BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
monitor =
std::make_unique<memory_pressure::MultiSourceMemoryPressureMonitor>();

@ -144,8 +144,11 @@ void ChildProcessLauncherHelper::LaunchOnLauncherThread() {
Process process;
if (BeforeLaunchOnLauncherThread(*files_to_register, options_ptr)) {
// TODO(crbug.com/1412835): iOS is single process mode for now.
#if !BUILDFLAG(IS_IOS)
base::FieldTrialList::PopulateLaunchOptionsWithFieldTrialState(
command_line(), options_ptr);
#endif
process =
LaunchProcessOnLauncherThread(options_ptr, std::move(files_to_register),
#if BUILDFLAG(IS_ANDROID)

@ -0,0 +1,102 @@
// 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/child_process_launcher.h"
#include "content/browser/child_process_launcher_helper.h"
#include "content/browser/child_process_launcher_helper_posix.h"
#include "content/public/browser/child_process_launcher_utils.h"
namespace content {
namespace internal {
// TODO(crbug.com/1412835): Fill this class out.
absl::optional<mojo::NamedPlatformChannel>
ChildProcessLauncherHelper::CreateNamedPlatformChannelOnClientThread() {
DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
return absl::nullopt;
}
void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
DCHECK(client_task_runner_->RunsTasksInCurrentSequence());
}
std::unique_ptr<PosixFileDescriptorInfo>
ChildProcessLauncherHelper::GetFilesToMap() {
DCHECK(CurrentlyOnProcessLauncherTaskRunner());
return CreateDefaultPosixFilesToMap(
child_process_id(), mojo_channel_->remote_endpoint(),
/*files_to_preload=*/{}, GetProcessType(), command_line());
}
bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
FileMappedForLaunch& files_to_register,
base::LaunchOptions* options) {
return true;
}
ChildProcessLauncherHelper::Process
ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
const base::LaunchOptions* options,
std::unique_ptr<PosixFileDescriptorInfo> files_to_register,
bool* is_synchronous_launch,
int* launch_result) {
DCHECK(!options);
*is_synchronous_launch = true;
ChildProcessLauncherHelper::Process process;
// process.process = base::LaunchProcess(*command_line(), *options);
*launch_result =
process.process.IsValid() ? LAUNCH_RESULT_SUCCESS : LAUNCH_RESULT_FAILURE;
return process;
}
bool ChildProcessLauncherHelper::IsUsingLaunchOptions() {
return false;
}
void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
const ChildProcessLauncherHelper::Process& process,
const base::LaunchOptions* options) {}
ChildProcessTerminationInfo ChildProcessLauncherHelper::GetTerminationInfo(
const ChildProcessLauncherHelper::Process& process,
bool known_dead) {
ChildProcessTerminationInfo info;
return info;
}
// static
bool ChildProcessLauncherHelper::TerminateProcess(const base::Process& process,
int exit_code) {
// TODO(https://crbug.com/818244): Determine whether we should also call
// EnsureProcessTerminated() to make sure of process-exit, and reap it.
// return process.Terminate(exit_code, false);
return false;
}
// static
void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync(
ChildProcessLauncherHelper::Process process) {
DCHECK(CurrentlyOnProcessLauncherTaskRunner());
// Client has gone away, so just kill the process. Using exit code 0 means
// that UMA won't treat this as a crash.
// process.process.Terminate(RESULT_CODE_NORMAL_EXIT, false);
// base::EnsureProcessTerminated(std::move(process.process));
}
void ChildProcessLauncherHelper::SetProcessBackgroundedOnLauncherThread(
base::Process process,
bool is_background) {}
// static
base::File OpenFileToShare(const base::FilePath& path,
base::MemoryMappedFile::Region* region) {
// Not used yet (until required files are described in the service manifest on
// iOS).
NOTREACHED();
return base::File();
}
} // namespace internal
} // namespace content

@ -59,7 +59,7 @@ std::unique_ptr<PosixFileDescriptorInfo> CreateDefaultPosixFilesToMap(
PosixFileDescriptorInfoImpl::Create());
// Mac shared memory doesn't use file descriptors.
#if !BUILDFLAG(IS_MAC)
#if !BUILDFLAG(IS_APPLE)
int fd = base::FieldTrialList::GetFieldTrialDescriptor();
DCHECK_NE(fd, -1);
files_to_register->Share(kFieldTrialDescriptor, fd);

@ -419,6 +419,8 @@ bool SwiftShaderAllowed() {
[[maybe_unused]] bool MetalAllowed() {
#if BUILDFLAG(IS_MAC)
return base::FeatureList::IsEnabled(features::kMetal);
#elif BUILDFLAG(IS_IOS)
return true;
#else
return false;
#endif

@ -0,0 +1,189 @@
// 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_BROWSER_COMPOSITOR_IOS_H_
#define CONTENT_BROWSER_RENDERER_HOST_BROWSER_COMPOSITOR_IOS_H_
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/surfaces/local_surface_id.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/common/surfaces/scoped_surface_id_allocator.h"
#include "content/browser/renderer_host/delegated_frame_host.h"
#include "content/common/content_export.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/compositor_observer.h"
#include "ui/compositor/layer_observer.h"
#include "ui/display/screen_info.h"
namespace content {
class BrowserCompositorIOSClient {
public:
virtual SkColor BrowserCompositorIOSGetGutterColor() = 0;
virtual void OnFrameTokenChanged(uint32_t frame_token,
base::TimeTicks activation_time) = 0;
virtual void DestroyCompositorForShutdown() = 0;
virtual bool OnBrowserCompositorSurfaceIdChanged() = 0;
virtual std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() = 0;
virtual display::ScreenInfo GetCurrentScreenInfo() const = 0;
virtual void SetCurrentDeviceScaleFactor(float device_scale_factor) = 0;
};
// This class owns a DelegatedFrameHost, and will dynamically attach and
// detach it from a ui::Compositor as needed. The ui::Compositor will be
// detached from the DelegatedFrameHost when the following conditions are
// all met:
// - The RenderWidgetHostImpl providing frames to the DelegatedFrameHost
// is visible.
// - The RenderWidgetHostViewIOS that is used to display these frames is
// attached to the UIView hierarchy of an UIWindow.
class CONTENT_EXPORT BrowserCompositorIOS : public DelegatedFrameHostClient,
public ui::LayerObserver {
public:
BrowserCompositorIOS(gfx::AcceleratedWidget accelerated_widget,
BrowserCompositorIOSClient* client,
bool render_widget_host_is_hidden,
const viz::FrameSinkId& frame_sink_id);
~BrowserCompositorIOS() override;
// These will not return nullptr until Destroy is called.
DelegatedFrameHost* GetDelegatedFrameHost();
// Force a new surface id to be allocated. Returns true if the
// RenderWidgetHostImpl sent the resulting surface id to the renderer.
bool ForceNewSurfaceId();
void SetBackgroundColor(SkColor background_color);
void UpdateVSyncParameters(const base::TimeTicks& timebase,
const base::TimeDelta& interval);
void TakeFallbackContentFrom(BrowserCompositorIOS* other);
// Update the renderer's SurfaceId to reflect the current dimensions of the
// UIView. This will allocate a new SurfaceId, so should only be called
// when necessary.
void UpdateSurfaceFromUIView(const gfx::Size& new_size_dip);
// Update the renderer's SurfaceId to reflect |new_size_in_pixels| in
// anticipation of the UIView resizing during auto-resize.
void UpdateSurfaceFromChild(
bool auto_resize_enabled,
float new_device_scale_factor,
const gfx::Size& new_size_in_pixels,
const viz::LocalSurfaceId& child_local_surface_id);
// This is used to ensure that the ui::Compositor be attached to the
// DelegatedFrameHost while the RWHImpl is visible.
// Note: This should be called before the RWHImpl is made visible and after
// it has been hidden, in order to ensure that thumbnailer notifications to
// initiate copies occur before the ui::Compositor be detached.
void SetRenderWidgetHostIsHidden(bool hidden);
// Specify if the ui::Layer should be visible or not.
void SetViewVisible(bool visible);
// Sets or clears the parent ui::Layer and updates state to reflect that
// we are now using the ui::Compositor from |parent_ui_layer| (if non-nullptr)
// or one from |recyclable_compositor_| (if a compositor is needed).
void SetParentUiLayer(ui::Layer* parent_ui_layer);
viz::FrameSinkId GetRootFrameSinkId();
const gfx::Size& GetRendererSize() const { return dfh_size_dip_; }
viz::ScopedSurfaceIdAllocator GetScopedRendererSurfaceIdAllocator(
base::OnceCallback<void()> allocation_task);
const viz::LocalSurfaceId& GetRendererLocalSurfaceId();
void TransformPointToRootSurface(gfx::PointF* point);
// DelegatedFrameHostClient implementation.
ui::Layer* DelegatedFrameHostGetLayer() const override;
bool DelegatedFrameHostIsVisible() const override;
SkColor DelegatedFrameHostGetGutterColor() const override;
void OnFrameTokenChanged(uint32_t frame_token,
base::TimeTicks activation_time) override;
float GetDeviceScaleFactor() const override;
void InvalidateLocalSurfaceIdOnEviction() override;
std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override;
bool ShouldShowStaleContentOnEviction() override;
base::WeakPtr<BrowserCompositorIOS> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void DidNavigate();
void ForceNewSurfaceForTesting();
ui::Compositor* GetCompositor() const;
private:
// ui::LayerObserver implementation:
void LayerDestroyed(ui::Layer* layer) override;
cc::DeadlinePolicy GetDeadlinePolicy(bool is_resize) const;
// The state of |delegated_frame_host_| and |recyclable_compositor_| to
// manage being visible, hidden, or drawn via a ui::Layer.
// The state of |recyclable_compositor_| and |parent_ui_layer_|.
enum State {
// We are drawing using |recyclable_compositor_|. This happens when the
// renderer, but no parent ui::Layer has been specified. This is used by
// content shell, popup windows (time/date picker), and when tab capturing
// a backgrounded tab.
HasOwnCompositor,
// There is no compositor. This is true when the renderer is not visible
// and no parent ui::Layer is specified.
HasNoCompositor,
// We are drawing using |parent_ui_layer_|'s compositor. This happens
// whenever |parent_ui_layer_| is non-nullptr.
UseParentLayerCompositor,
};
State state_ = HasNoCompositor;
void UpdateState();
void TransitionToState(State new_state);
void UpdateSurface(const gfx::Size& size_pixels,
float scale_factor,
const gfx::DisplayColorSpaces& display_color_spaces);
// Weak pointer to the layer supplied and reset via SetParentUiLayer. |this|
// is an observer of |parent_ui_layer_|, to ensure that |parent_ui_layer_|
// always be valid when non-null. The UpdateState function will re-parent
// |root_layer_| to be under |parent_ui_layer_|, if needed.
raw_ptr<ui::Layer> parent_ui_layer_ = nullptr;
bool render_widget_host_is_hidden_ = true;
raw_ptr<BrowserCompositorIOSClient> client_;
gfx::AcceleratedWidget accelerated_widget_;
std::unique_ptr<ui::Compositor> compositor_;
std::unique_ptr<DelegatedFrameHost> delegated_frame_host_;
std::unique_ptr<ui::Layer> root_layer_;
SkColor background_color_ = SK_ColorRED;
// The viz::ParentLocalSurfaceIdAllocator for the delegated frame host
// dispenses viz::LocalSurfaceIds that are rendered into by the renderer
// process. These values are not updated during resize.
viz::ParentLocalSurfaceIdAllocator dfh_local_surface_id_allocator_;
gfx::Size dfh_size_pixels_;
gfx::Size dfh_size_dip_;
float dfh_device_scale_factor_ = 1.f;
bool is_first_navigation_ = true;
viz::ParentLocalSurfaceIdAllocator local_surface_id_allocator_;
gfx::Size size_pixels_;
float scale_factor_ = 1.f;
gfx::DisplayColorSpaces display_color_spaces_;
base::WeakPtrFactory<BrowserCompositorIOS> weak_factory_;
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_BROWSER_COMPOSITOR_IOS_H_

@ -0,0 +1,423 @@
// 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/browser_compositor_ios.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/containers/circular_deque.h"
#include "base/lazy_instance.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/common/features.h"
#include "components/viz/common/surfaces/local_surface_id.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/context_factory.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/layout.h"
#include "ui/compositor/compositor_switches.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/size_conversions.h"
namespace content {
////////////////////////////////////////////////////////////////////////////////
// BrowserCompositorIOS
BrowserCompositorIOS::BrowserCompositorIOS(
gfx::AcceleratedWidget accelerated_widget,
BrowserCompositorIOSClient* client,
bool render_widget_host_is_hidden,
const viz::FrameSinkId& frame_sink_id)
: client_(client),
accelerated_widget_(accelerated_widget),
weak_factory_(this) {
root_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
// Ensure that this layer draws nothing when it does not not have delegated
// content (otherwise this solid color will be flashed during navigation).
root_layer_->SetColor(SK_ColorRED);
delegated_frame_host_ = std::make_unique<DelegatedFrameHost>(
frame_sink_id, this, true /* should_register_frame_sink_id */);
SetRenderWidgetHostIsHidden(render_widget_host_is_hidden);
}
BrowserCompositorIOS::~BrowserCompositorIOS() {
// Ensure that copy callbacks completed or cancelled during further tear-down
// do not call back into this.
weak_factory_.InvalidateWeakPtrs();
TransitionToState(HasNoCompositor);
delegated_frame_host_.reset();
root_layer_.reset();
}
DelegatedFrameHost* BrowserCompositorIOS::GetDelegatedFrameHost() {
DCHECK(delegated_frame_host_);
return delegated_frame_host_.get();
}
bool BrowserCompositorIOS::ForceNewSurfaceId() {
dfh_local_surface_id_allocator_.GenerateId();
delegated_frame_host_->EmbedSurface(
dfh_local_surface_id_allocator_.GetCurrentLocalSurfaceId(), dfh_size_dip_,
cc::DeadlinePolicy::UseExistingDeadline());
return client_->OnBrowserCompositorSurfaceIdChanged();
}
viz::FrameSinkId BrowserCompositorIOS::GetRootFrameSinkId() {
if (parent_ui_layer_) {
return parent_ui_layer_->GetCompositor()->frame_sink_id();
}
if (compositor_) {
return compositor_->frame_sink_id();
}
return viz::FrameSinkId();
}
void BrowserCompositorIOS::SetBackgroundColor(SkColor background_color) {
// background_color_ = background_color;
if (compositor_) {
compositor_->SetBackgroundColor(background_color_);
}
}
void BrowserCompositorIOS::UpdateSurfaceFromUIView(
const gfx::Size& new_size_dip) {
display::ScreenInfo current = client_->GetCurrentScreenInfo();
bool is_resize = !dfh_size_dip_.IsEmpty() && new_size_dip != dfh_size_dip_;
bool needs_new_surface_id =
new_size_dip != dfh_size_dip_ ||
current.device_scale_factor != dfh_device_scale_factor_;
dfh_size_dip_ = new_size_dip;
dfh_device_scale_factor_ = current.device_scale_factor;
// The device scale factor is always an integer, so the result here is also
// an integer.
dfh_size_pixels_ = gfx::ToRoundedSize(
gfx::ConvertSizeToPixels(dfh_size_dip_, current.device_scale_factor));
root_layer_->SetBounds(gfx::Rect(dfh_size_dip_));
if (needs_new_surface_id) {
dfh_local_surface_id_allocator_.GenerateId();
delegated_frame_host_->EmbedSurface(
dfh_local_surface_id_allocator_.GetCurrentLocalSurfaceId(),
dfh_size_dip_, GetDeadlinePolicy(is_resize));
}
if (compositor_) {
UpdateSurface(dfh_size_pixels_, current.device_scale_factor,
current.display_color_spaces);
}
}
void BrowserCompositorIOS::UpdateSurfaceFromChild(
bool auto_resize_enabled,
float new_device_scale_factor,
const gfx::Size& new_size_in_pixels,
const viz::LocalSurfaceId& child_local_surface_id) {
if (dfh_local_surface_id_allocator_.UpdateFromChild(child_local_surface_id)) {
if (auto_resize_enabled) {
client_->SetCurrentDeviceScaleFactor(new_device_scale_factor);
display::ScreenInfo current = client_->GetCurrentScreenInfo();
// TODO(danakj): We should avoid lossy conversions to integer DIPs.
dfh_size_dip_ = gfx::ToFlooredSize(gfx::ConvertSizeToDips(
new_size_in_pixels, current.device_scale_factor));
dfh_size_pixels_ = new_size_in_pixels;
dfh_device_scale_factor_ = new_device_scale_factor;
root_layer_->SetBounds(gfx::Rect(dfh_size_dip_));
if (compositor_) {
UpdateSurface(dfh_size_pixels_, current.device_scale_factor,
current.display_color_spaces);
}
}
delegated_frame_host_->EmbedSurface(
dfh_local_surface_id_allocator_.GetCurrentLocalSurfaceId(),
dfh_size_dip_, GetDeadlinePolicy(true /* is_resize */));
}
client_->OnBrowserCompositorSurfaceIdChanged();
}
void BrowserCompositorIOS::UpdateVSyncParameters(
const base::TimeTicks& timebase,
const base::TimeDelta& interval) {
ui::Compositor* compositor = nullptr;
if (compositor_) {
compositor = compositor_.get();
}
// TODO(ccameron): VSync parameters for a ui::Compositor should be tracked
// with the owner of that ui::Compositor (which, in the case of MacViews, is
// BridgedNativeView). For the moment, push the VSync parameters from here to
// the BridgedNativeView's ui::Compositor because that is a small change and
// is easy to merge.
// https://crbug.com/869129
if (parent_ui_layer_) {
compositor = parent_ui_layer_->GetCompositor();
}
if (compositor) {
compositor->SetDisplayVSyncParameters(timebase, interval);
}
}
void BrowserCompositorIOS::SetRenderWidgetHostIsHidden(bool hidden) {
render_widget_host_is_hidden_ = hidden;
UpdateState();
if (state_ == UseParentLayerCompositor) {
// UpdateState might not call WasShown when showing a frame using the same
// ParentLayerCompositor, since it returns early on a no-op state
// transition.
delegated_frame_host_->WasShown(GetRendererLocalSurfaceId(), dfh_size_dip_,
{} /* record_tab_switch_time_request */);
}
}
void BrowserCompositorIOS::SetViewVisible(bool visible) {
root_layer_->SetVisible(visible);
}
void BrowserCompositorIOS::UpdateState() {
// Always use the parent ui::Layer's ui::Compositor if available.
if (parent_ui_layer_) {
TransitionToState(UseParentLayerCompositor);
return;
}
// If the host is visible and a compositor is required then create one.
if (!render_widget_host_is_hidden_) {
TransitionToState(HasOwnCompositor);
return;
}
// Otherwise put the compositor up for recycling.
TransitionToState(HasNoCompositor);
}
void BrowserCompositorIOS::TransitionToState(State new_state) {
// Skip if there is no change to make.
bool is_no_op = false;
if (state_ == new_state) {
if (state_ == UseParentLayerCompositor) {
is_no_op = parent_ui_layer_ == root_layer_->parent();
} else {
is_no_op = true;
}
}
if (is_no_op) {
return;
}
// First, detach from the current compositor, if there is one.
delegated_frame_host_->DetachFromCompositor();
if (state_ == UseParentLayerCompositor) {
DCHECK(root_layer_->parent());
state_ = HasNoCompositor;
root_layer_->parent()->RemoveObserver(this);
root_layer_->parent()->Remove(root_layer_.get());
}
if (state_ == HasOwnCompositor) {
compositor_->SetRootLayer(nullptr);
compositor_.reset();
}
// The compositor is now detached. If this is the target state, we're done.
state_ = HasNoCompositor;
if (new_state == HasNoCompositor) {
// Don't transiently hide the DelegatedFrameHost because that can cause the
// current frame to be inappropriately evicted.
// https://crbug.com/897156
delegated_frame_host_->WasHidden(DelegatedFrameHost::HiddenCause::kOther);
return;
}
// Attach to the new compositor.
if (new_state == UseParentLayerCompositor) {
DCHECK(parent_ui_layer_);
parent_ui_layer_->Add(root_layer_.get());
parent_ui_layer_->AddObserver(this);
state_ = UseParentLayerCompositor;
}
if (new_state == HasOwnCompositor) {
ui::ContextFactory* context_factory = GetContextFactory();
compositor_ = std::make_unique<ui::Compositor>(
context_factory->AllocateFrameSinkId(), context_factory,
base::SingleThreadTaskRunner::GetCurrentDefault(),
ui::IsPixelCanvasRecordingEnabled());
display::ScreenInfo current = client_->GetCurrentScreenInfo();
UpdateSurface(dfh_size_pixels_, current.device_scale_factor,
current.display_color_spaces);
compositor_->SetRootLayer(root_layer_.get());
compositor_->SetBackgroundColor(background_color_);
compositor_->SetAcceleratedWidget(accelerated_widget_);
state_ = HasOwnCompositor;
}
DCHECK_EQ(state_, new_state);
delegated_frame_host_->AttachToCompositor(GetCompositor());
delegated_frame_host_->WasShown(GetRendererLocalSurfaceId(), dfh_size_dip_,
{} /* record_tab_switch_time_request */);
}
void BrowserCompositorIOS::TakeFallbackContentFrom(
BrowserCompositorIOS* other) {
delegated_frame_host_->TakeFallbackContentFrom(
other->delegated_frame_host_.get());
}
////////////////////////////////////////////////////////////////////////////////
// DelegatedFrameHost, public:
ui::Layer* BrowserCompositorIOS::DelegatedFrameHostGetLayer() const {
return root_layer_.get();
}
bool BrowserCompositorIOS::DelegatedFrameHostIsVisible() const {
return state_ != HasNoCompositor;
}
SkColor BrowserCompositorIOS::DelegatedFrameHostGetGutterColor() const {
return client_->BrowserCompositorIOSGetGutterColor();
}
void BrowserCompositorIOS::OnFrameTokenChanged(
uint32_t frame_token,
base::TimeTicks activation_time) {
client_->OnFrameTokenChanged(frame_token, activation_time);
}
float BrowserCompositorIOS::GetDeviceScaleFactor() const {
return dfh_device_scale_factor_;
}
void BrowserCompositorIOS::InvalidateLocalSurfaceIdOnEviction() {
dfh_local_surface_id_allocator_.Invalidate();
}
std::vector<viz::SurfaceId>
BrowserCompositorIOS::CollectSurfaceIdsForEviction() {
return client_->CollectSurfaceIdsForEviction();
}
bool BrowserCompositorIOS::ShouldShowStaleContentOnEviction() {
return false;
}
void BrowserCompositorIOS::DidNavigate() {
if (render_widget_host_is_hidden_) {
// Navigating while hidden should not allocate a new LocalSurfaceID. Once
// sizes are ready, or we begin to Show, we can then allocate the new
// LocalSurfaceId.
dfh_local_surface_id_allocator_.Invalidate();
} else {
// The first navigation does not need a new LocalSurfaceID. The renderer can
// use the ID that was already provided.
if (!is_first_navigation_) {
dfh_local_surface_id_allocator_.GenerateId();
}
delegated_frame_host_->EmbedSurface(
dfh_local_surface_id_allocator_.GetCurrentLocalSurfaceId(),
dfh_size_dip_, cc::DeadlinePolicy::UseExistingDeadline());
client_->OnBrowserCompositorSurfaceIdChanged();
}
delegated_frame_host_->DidNavigate();
is_first_navigation_ = false;
}
void BrowserCompositorIOS::SetParentUiLayer(ui::Layer* new_parent_ui_layer) {
if (new_parent_ui_layer) {
DCHECK(new_parent_ui_layer->GetCompositor());
}
// Set |parent_ui_layer_| to the new value, which potentially not match the
// value of |root_layer_->parent()|. The call to UpdateState will re-parent
// |root_layer_|.
DCHECK_EQ(root_layer_->parent(), parent_ui_layer_);
parent_ui_layer_ = new_parent_ui_layer;
UpdateState();
DCHECK_EQ(root_layer_->parent(), parent_ui_layer_);
}
void BrowserCompositorIOS::ForceNewSurfaceForTesting() {
float current_device_scale_factor =
client_->GetCurrentScreenInfo().device_scale_factor;
client_->SetCurrentDeviceScaleFactor(current_device_scale_factor * 2.0f);
UpdateSurfaceFromUIView(dfh_size_dip_);
}
viz::ScopedSurfaceIdAllocator
BrowserCompositorIOS::GetScopedRendererSurfaceIdAllocator(
base::OnceCallback<void()> allocation_task) {
return viz::ScopedSurfaceIdAllocator(&dfh_local_surface_id_allocator_,
std::move(allocation_task));
}
const viz::LocalSurfaceId& BrowserCompositorIOS::GetRendererLocalSurfaceId() {
if (!dfh_local_surface_id_allocator_.HasValidLocalSurfaceId()) {
dfh_local_surface_id_allocator_.GenerateId();
}
return dfh_local_surface_id_allocator_.GetCurrentLocalSurfaceId();
}
void BrowserCompositorIOS::TransformPointToRootSurface(gfx::PointF* point) {
gfx::Transform transform_to_root;
if (parent_ui_layer_) {
parent_ui_layer_->GetTargetTransformRelativeTo(nullptr, &transform_to_root);
}
*point = transform_to_root.MapPoint(*point);
}
void BrowserCompositorIOS::LayerDestroyed(ui::Layer* layer) {
DCHECK_EQ(layer, parent_ui_layer_);
SetParentUiLayer(nullptr);
}
ui::Compositor* BrowserCompositorIOS::GetCompositor() const {
if (parent_ui_layer_) {
return parent_ui_layer_->GetCompositor();
}
return compositor_.get();
}
cc::DeadlinePolicy BrowserCompositorIOS::GetDeadlinePolicy(
bool is_resize) const {
// Determined empirically for smoothness. Don't wait for non-resize frames,
// as it can cause jank at new tab creation.
// https://crbug.com/855364
uint32_t frames_to_wait = is_resize ? 8 : 0;
// When using the RecyclableCompositor, never wait for frames to arrive
// (surface sync is managed by the Suspend/Unsuspend lock).
if (compositor_) {
frames_to_wait = 0;
}
return cc::DeadlinePolicy::UseSpecifiedDeadline(frames_to_wait);
}
void BrowserCompositorIOS::UpdateSurface(
const gfx::Size& size_pixels,
float scale_factor,
const gfx::DisplayColorSpaces& display_color_spaces) {
if (size_pixels != size_pixels_ || scale_factor != scale_factor_) {
size_pixels_ = size_pixels;
scale_factor_ = scale_factor;
local_surface_id_allocator_.GenerateId();
viz::LocalSurfaceId local_surface_id =
local_surface_id_allocator_.GetCurrentLocalSurfaceId();
compositor_->SetScaleAndSize(scale_factor_, size_pixels_, local_surface_id);
}
if (display_color_spaces != display_color_spaces_) {
display_color_spaces_ = display_color_spaces;
compositor_->SetDisplayColorSpaces(display_color_spaces_);
}
}
} // namespace content

@ -0,0 +1,68 @@
// 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/delegated_frame_host_client_ios.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_ios.h"
#include "ui/compositor/layer.h"
namespace content {
DelegatedFrameHostClientIOS::DelegatedFrameHostClientIOS(
RenderWidgetHostViewIOS* render_widget_host_view)
: render_widget_host_view_(render_widget_host_view) {}
DelegatedFrameHostClientIOS::~DelegatedFrameHostClientIOS() {}
ui::Layer* DelegatedFrameHostClientIOS::DelegatedFrameHostGetLayer() const {
// TODO(crbug.com/1411704): Fix me
// return render_widget_host_view_->layer();
return nullptr;
}
bool DelegatedFrameHostClientIOS::DelegatedFrameHostIsVisible() const {
return !render_widget_host_view_->host()->is_hidden();
}
SkColor DelegatedFrameHostClientIOS::DelegatedFrameHostGetGutterColor() const {
// 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 (render_widget_host_view_->host()->delegate() &&
render_widget_host_view_->host()->delegate()->IsFullscreen()) {
return SK_ColorBLACK;
}
if (render_widget_host_view_->GetBackgroundColor()) {
return *render_widget_host_view_->GetBackgroundColor();
}
return SK_ColorWHITE;
}
void DelegatedFrameHostClientIOS::OnFrameTokenChanged(
uint32_t frame_token,
base::TimeTicks activation_time) {
render_widget_host_view_->OnFrameTokenChangedForView(frame_token,
activation_time);
}
float DelegatedFrameHostClientIOS::GetDeviceScaleFactor() const {
return 1.0f;
}
void DelegatedFrameHostClientIOS::InvalidateLocalSurfaceIdOnEviction() {
// TODO(crbug.com/1411704): Fix me
// render_widget_host_view_->InvalidateLocalSurfaceIdOnEviction();
}
std::vector<viz::SurfaceId>
DelegatedFrameHostClientIOS::CollectSurfaceIdsForEviction() {
return render_widget_host_view_->host()->CollectSurfaceIdsForEviction();
}
bool DelegatedFrameHostClientIOS::ShouldShowStaleContentOnEviction() {
return false;
}
} // namespace content

@ -0,0 +1,52 @@
// 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_DELEGATED_FRAME_HOST_CLIENT_IOS_H_
#define CONTENT_BROWSER_RENDERER_HOST_DELEGATED_FRAME_HOST_CLIENT_IOS_H_
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/delegated_frame_host.h"
#include "content/common/content_export.h"
namespace content {
class RenderWidgetHostViewIOS;
// DelegatedFrameHostClient implementation for iOS.
class CONTENT_EXPORT DelegatedFrameHostClientIOS
: public DelegatedFrameHostClient {
public:
explicit DelegatedFrameHostClientIOS(
RenderWidgetHostViewIOS* render_widget_host_view);
DelegatedFrameHostClientIOS(const DelegatedFrameHostClientIOS&) = delete;
DelegatedFrameHostClientIOS& operator=(const DelegatedFrameHostClientIOS&) =
delete;
~DelegatedFrameHostClientIOS() override;
protected:
RenderWidgetHostViewIOS* render_widget_host_view() {
return render_widget_host_view_;
}
// DelegatedFrameHostClient implementation.
ui::Layer* DelegatedFrameHostGetLayer() const override;
bool DelegatedFrameHostIsVisible() const override;
SkColor DelegatedFrameHostGetGutterColor() const override;
void OnFrameTokenChanged(uint32_t frame_token,
base::TimeTicks activation_time) override;
float GetDeviceScaleFactor() const override;
void InvalidateLocalSurfaceIdOnEviction() override;
std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override;
bool ShouldShowStaleContentOnEviction() override;
private:
raw_ptr<RenderWidgetHostViewIOS> render_widget_host_view_;
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_DELEGATED_FRAME_HOST_CLIENT_IOS_H_

@ -68,15 +68,10 @@ void FlingScheduler::ProgressFlingOnBeginFrameIfneeded(
}
ui::Compositor* FlingScheduler::GetCompositor() {
#if defined(USE_AURA)
if (host_->GetView() && host_->GetView()->GetNativeView() &&
host_->GetView()->GetNativeView()->GetHost() &&
host_->GetView()->GetNativeView()->GetHost()->compositor()) {
return host_->GetView()->GetNativeView()->GetHost()->compositor();
if (!host_->GetView()) {
return nullptr;
}
#endif
return nullptr;
return host_->GetView()->GetCompositor();
}
void FlingScheduler::OnAnimationStep(base::TimeTicks timestamp) {

@ -0,0 +1,41 @@
// 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_IOS_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_IOS_H_
#include "content/common/content_export.h"
#include "third_party/blink/public/common/input/web_gesture_event.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
#include "third_party/blink/public/common/input/web_touch_event.h"
@class UIEvent;
@class UITouch;
@class UIView;
namespace content {
class CONTENT_EXPORT WebKeyboardEventBuilder {
public:
static blink::WebKeyboardEvent Build(UIEvent* event);
};
class CONTENT_EXPORT WebGestureEventBuilder {
public:
static blink::WebGestureEvent Build(UIEvent*, UIView*);
};
class CONTENT_EXPORT WebTouchEventBuilder {
public:
static blink::WebTouchEvent Build(blink::WebInputEvent::Type type,
UITouch* touch,
UIEvent* event,
UIView* view);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_IOS_H_

@ -0,0 +1,225 @@
// 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/input/web_input_event_builders_ios.h"
#import <UIKit/UIKit.h>
#include "third_party/blink/public/common/input/web_pointer_event.h"
#include "third_party/blink/public/common/input/web_touch_point.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_utils.h"
namespace content {
namespace {
constexpr size_t MAX_POINTERS = blink::WebTouchEvent::kTouchesLengthCap;
static UITouch* g_active_touches_map[MAX_POINTERS] = {};
size_t GetTouchPointerId(UITouch* touch) {
for (size_t i = 0; i < MAX_POINTERS; ++i) {
if (g_active_touches_map[i] == touch) {
return i + 1;
}
}
return 0;
}
void AddUITouch(UITouch* touch) {
CHECK(GetTouchPointerId(touch) == 0);
for (size_t i = 0; i < MAX_POINTERS; ++i) {
if (!g_active_touches_map[i]) {
g_active_touches_map[i] = [touch retain];
return;
}
}
}
void RemoveUITouch(UITouch* touch) {
for (size_t i = 0; i < MAX_POINTERS; ++i) {
if (g_active_touches_map[i] == touch) {
g_active_touches_map[i] = nullptr;
[touch release];
return;
}
}
CHECK(false);
}
int ModifiersFromEvent(UIEvent* event) {
int modifiers = 0;
UIKeyModifierFlags modifier_flags = [event modifierFlags];
if (modifier_flags & UIKeyModifierControl) {
modifiers |= blink::WebInputEvent::kControlKey;
}
if (modifier_flags & UIKeyModifierShift) {
modifiers |= blink::WebInputEvent::kShiftKey;
}
if (modifier_flags & UIKeyModifierAlternate) {
modifiers |= blink::WebInputEvent::kAltKey;
}
if (modifier_flags & UIKeyModifierCommand) {
modifiers |= blink::WebInputEvent::kMetaKey;
}
if (modifier_flags & UIKeyModifierAlphaShift) {
modifiers |= blink::WebInputEvent::kCapsLockOn;
}
return modifiers;
}
blink::WebTouchPoint::State ToWebTouchPointState(UITouch* event,
bool was_changed) {
// We will get an event for each actual changed phase.
if (!was_changed) {
return blink::WebTouchPoint::State::kStateStationary;
}
switch ([event phase]) {
case UITouchPhaseBegan:
case UITouchPhaseRegionEntered:
return blink::WebTouchPoint::State::kStatePressed;
case UITouchPhaseMoved:
case UITouchPhaseRegionMoved:
return blink::WebTouchPoint::State::kStateMoved;
case UITouchPhaseEnded:
case UITouchPhaseRegionExited:
return blink::WebTouchPoint::State::kStateReleased;
case UITouchPhaseCancelled:
return blink::WebTouchPoint::State::kStateCancelled;
case UITouchPhaseStationary:
return blink::WebTouchPoint::State::kStateStationary;
}
NOTREACHED() << "Invalid MotionEvent::Action.";
return blink::WebTouchPoint::State::kStateUndefined;
}
void SetWebPointerPropertiesFromMotionEventData(
blink::WebPointerProperties& webPointerProperties,
int pointer_id,
float pressure) {
webPointerProperties.id = pointer_id;
webPointerProperties.force = pressure;
webPointerProperties.tilt_x = webPointerProperties.tilt_y = 0;
webPointerProperties.twist = 0;
webPointerProperties.tangential_pressure = 0;
webPointerProperties.button = blink::WebPointerProperties::Button::kNoButton;
webPointerProperties.pointer_type =
blink::WebPointerProperties::PointerType::kTouch;
// TODO(dtapuska): Support stylus.
}
blink::WebTouchPoint CreateWebTouchPoint(UIView* view,
UITouch* event,
bool was_changed) {
blink::WebTouchPoint touch;
size_t pointer_index = GetTouchPointerId(event);
CHECK(pointer_index != 0);
SetWebPointerPropertiesFromMotionEventData(touch, pointer_index,
[event force]);
touch.state = ToWebTouchPointState(event, was_changed);
CGPoint view_location = [event locationInView:view];
touch.SetPositionInWidget(view_location.x, view_location.y);
CGPoint window_location = [event locationInView:nil];
touch.SetPositionInScreen(window_location.x, window_location.y);
float major_radius = event.majorRadius;
float minor_radius = event.majorRadius;
float orientation_deg = 0;
DCHECK_GE(major_radius, 0);
DCHECK_GE(minor_radius, 0);
DCHECK_GE(major_radius, minor_radius);
// Orientation lies in [-180, 180] for a stylus, and [-90, 90] for other
// touchscreen inputs. There are exceptions on Android when a device is
// rotated, yielding touch orientations in the range of [-180, 180].
// Regardless, normalise to [-90, 90), allowing a small tolerance to account
// for floating point conversion.
// TODO(e_hakkinen): Also pass unaltered stylus orientation, avoiding loss of
// quadrant information, see crbug.com/493728.
DCHECK_GT(orientation_deg, -180.01f);
DCHECK_LT(orientation_deg, 180.01f);
if (orientation_deg >= 90.f) {
orientation_deg -= 180.f;
} else if (orientation_deg < -90.f) {
orientation_deg += 180.f;
}
if (orientation_deg >= 0) {
// The case orientation_deg == 0 is handled here on purpose: although the
// 'else' block is equivalent in this case, we want to pass the 0 value
// unchanged (and 0 is the default value for many devices that don't
// report elliptical touches).
touch.radius_x = minor_radius;
touch.radius_y = major_radius;
touch.rotation_angle = orientation_deg;
} else {
touch.radius_x = major_radius;
touch.radius_y = minor_radius;
touch.rotation_angle = orientation_deg + 90;
}
return touch;
}
} // namespace
blink::WebKeyboardEvent WebKeyboardEventBuilder::Build(UIEvent*) {
return blink::WebKeyboardEvent();
}
blink::WebGestureEvent WebGestureEventBuilder::Build(UIEvent*, UIView*) {
return blink::WebGestureEvent();
}
blink::WebTouchEvent WebTouchEventBuilder::Build(
blink::WebInputEvent::Type type,
UITouch* touch,
UIEvent* event,
UIView* view) {
blink::WebTouchEvent result(type, ModifiersFromEvent(event),
ui::EventTimeStampFromSeconds([event timestamp]));
// TODO(dtapuska): Enable
// ui::ComputeEventLatencyOS(event);
result.dispatch_type =
result.GetType() == blink::WebInputEvent::Type::kTouchCancel
? blink::WebInputEvent::DispatchType::kEventNonBlocking
: blink::WebInputEvent::DispatchType::kBlocking;
result.hovering = type == blink::WebInputEvent::Type::kTouchEnd;
result.unique_touch_event_id = ui::GetNextTouchEventId();
size_t touch_index = 0;
if (type == blink::WebInputEvent::Type::kTouchStart) {
AddUITouch(touch);
}
result.touches[touch_index] =
CreateWebTouchPoint(view, touch, /*was_changed=*/true);
++touch_index;
if (type == blink::WebInputEvent::Type::kTouchCancel ||
type == blink::WebInputEvent::Type::kTouchEnd) {
RemoveUITouch(touch);
}
// We can't use `event.allTouches` here, because we need to generate a
// WebTouchEvent for each touch point changing. But event.allTouches will have
// it all already.
for (size_t i = 0; i < MAX_POINTERS; ++i) {
if (!g_active_touches_map[i] || g_active_touches_map[i] == touch) {
continue;
}
result.touches[touch_index] = CreateWebTouchPoint(
view, g_active_touches_map[i], /*was_changed=*/false);
++touch_index;
}
result.touches_length = touch_index;
DCHECK_GT(result.touches_length, 0U);
return result;
}
} // namespace content

@ -133,7 +133,6 @@ bool ShouldUseDesktopCaptureLacrosV2() {
VideoCaptureDeviceProxyLacros::IsAvailable();
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
#endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
@ -214,7 +213,7 @@ DesktopCaptureImplementation CreatePlatformDependentVideoCaptureDevice(
#endif
return kNoImplementation;
}
#endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
} // anonymous namespace
InProcessVideoCaptureDeviceLauncher::InProcessVideoCaptureDeviceLauncher(

@ -0,0 +1,56 @@
// 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/public/browser/native_web_keyboard_event.h"
#include "content/browser/renderer_host/input/web_input_event_builders_ios.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
namespace content {
NativeWebKeyboardEvent::NativeWebKeyboardEvent(blink::WebInputEvent::Type type,
int modifiers,
base::TimeTicks timestamp)
: WebKeyboardEvent(type, modifiers, timestamp),
os_event(NULL),
skip_in_browser(false) {}
NativeWebKeyboardEvent::NativeWebKeyboardEvent(
const blink::WebKeyboardEvent& web_event,
gfx::NativeView native_view)
: WebKeyboardEvent(web_event), os_event(nullptr), skip_in_browser(false) {}
NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event)
: WebKeyboardEvent(WebKeyboardEventBuilder::Build(native_event)),
os_event([native_event retain]),
skip_in_browser(false) {}
NativeWebKeyboardEvent::NativeWebKeyboardEvent(const ui::KeyEvent& key_event)
: NativeWebKeyboardEvent(key_event.native_event()) {}
NativeWebKeyboardEvent::NativeWebKeyboardEvent(
const NativeWebKeyboardEvent& other)
: WebKeyboardEvent(other),
os_event([other.os_event retain]),
skip_in_browser(other.skip_in_browser) {}
NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=(
const NativeWebKeyboardEvent& other) {
WebKeyboardEvent::operator=(other);
UIEvent* previous = os_event;
os_event = [other.os_event retain];
[previous release];
skip_in_browser = other.skip_in_browser;
return *this;
}
NativeWebKeyboardEvent::~NativeWebKeyboardEvent() {
[os_event release];
}
} // namespace content

@ -0,0 +1,186 @@
// 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_IOS_H_
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_IOS_H_
#include "base/memory/raw_ptr.h"
#include "third_party/blink/public/mojom/input/input_handler.mojom-forward.h"
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "content/browser/renderer_host/browser_compositor_ios.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/text_input_manager.h"
#include "content/common/content_export.h"
#include "ui/accelerated_widget_mac/ca_layer_frame_sink.h"
#include "ui/events/gesture_detection/filtered_gesture_provider.h"
namespace ui {
enum class DomCode;
} // namespace ui
namespace content {
class RenderWidgetHost;
class UIViewHolder;
///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewIOS
//
// An object representing the "View" of a rendered web page. This object is
// responsible for displaying the content of the web page, and integrating with
// the UIView system. It is the implementation of the RenderWidgetHostView
// that the cross-platform RenderWidgetHost object uses
// to display the data.
//
// Comment excerpted from render_widget_host.h:
//
// "The lifetime of the RenderWidgetHost* is tied to the render process.
// If the render process dies, the RenderWidgetHost* goes away and all
// references to it must become NULL."
//
// RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
class CONTENT_EXPORT RenderWidgetHostViewIOS
: public RenderWidgetHostViewBase,
public BrowserCompositorIOSClient,
public TextInputManager::Observer,
public ui::GestureProviderClient,
public ui::CALayerFrameSink {
public:
// The view will associate itself with the given widget. The native view must
// be hooked up immediately to the view hierarchy, or else when it is
// deleted it will delete this out from under the caller.
RenderWidgetHostViewIOS(RenderWidgetHost* widget);
~RenderWidgetHostViewIOS() override;
RenderWidgetHostViewIOS(const RenderWidgetHostViewIOS&) = delete;
RenderWidgetHostViewIOS& operator=(const RenderWidgetHostViewIOS&) = delete;
// RenderWidgetHostView implementation.
void InitAsChild(gfx::NativeView parent_view) override;
void SetSize(const gfx::Size& size) override;
void SetBounds(const gfx::Rect& rect) override;
gfx::NativeView GetNativeView() override;
gfx::NativeViewAccessible GetNativeViewAccessible() override;
void Focus() override;
bool HasFocus() override;
void Hide() override;
bool IsShowing() override;
gfx::Rect GetViewBounds() override;
blink::mojom::PointerLockResult LockMouse(bool) override;
blink::mojom::PointerLockResult ChangeMouseLock(bool) override;
void UnlockMouse() override;
void EnsureSurfaceSynchronizedForWebTest() override;
uint32_t GetCaptureSequenceNumber() const override;
void TakeFallbackContentFrom(RenderWidgetHostView* view) override;
std::unique_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget()
override;
void ClearFallbackSurfaceForCommitPending() override;
void ResetFallbackToFirstNavigationSurface() override;
viz::FrameSinkId GetRootFrameSinkId() override;
const viz::FrameSinkId& GetFrameSinkId() const override;
const viz::LocalSurfaceId& GetLocalSurfaceId() const override;
viz::SurfaceId GetCurrentSurfaceId() const override;
void InitAsPopup(RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos,
const gfx::Rect& anchor_rect) override;
void UpdateCursor(const ui::Cursor& cursor) override;
void SetIsLoading(bool is_loading) override;
void RenderProcessGone() override;
void ShowWithVisibility(PageVisibilityState page_visibility) override;
gfx::Rect GetBoundsInRootWindow() override;
absl::optional<DisplayFeature> GetDisplayFeature() override;
void SetDisplayFeatureForTesting(
const DisplayFeature* display_feature) override;
void UpdateBackgroundColor() override;
void NotifyHostAndDelegateOnWasShown(
blink::mojom::RecordContentToVisibleTimeRequestPtr visible_time_request)
override;
void RequestSuccessfulPresentationTimeFromHostOrDelegate(
blink::mojom::RecordContentToVisibleTimeRequestPtr visible_time_request)
override;
void CancelSuccessfulPresentationTimeRequestForHostAndDelegate() override;
viz::ScopedSurfaceIdAllocator DidUpdateVisualProperties(
const cc::RenderFrameMetadata& metadata) override;
void DidNavigate() override;
bool RequestRepaintForTesting() override;
void Destroy() override;
ui::Compositor* GetCompositor() override;
// BrowserCompositorIOS overrides:
SkColor BrowserCompositorIOSGetGutterColor() override;
void OnFrameTokenChanged(uint32_t frame_token,
base::TimeTicks activation_time) override;
void DestroyCompositorForShutdown() override {}
bool OnBrowserCompositorSurfaceIdChanged() override;
std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override;
display::ScreenInfo GetCurrentScreenInfo() const override;
void SetCurrentDeviceScaleFactor(float device_scale_factor) override;
void UpdateScreenInfo() override;
void TransformPointToRootSurface(gfx::PointF* point) override;
void ProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch,
blink::mojom::InputEventResultState ack_result) override;
// ui::CALayerFrameSink overrides:
void UpdateCALayerTree(const gfx::CALayerParams& ca_layer_params) override;
// ui::GestureProviderClient implementation.
void OnGestureEvent(const ui::GestureEventData& gesture) override;
bool RequiresDoubleTapGestureEvents() const override;
// TextInputManager::Observer implementation.
void OnUpdateTextInputStateCalled(TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view,
bool did_update_state) override;
void SetActive(bool active);
void OnTouchEvent(blink::WebTouchEvent event);
void UpdateNativeViewTree(gfx::NativeView view);
void ImeSetComposition(const std::u16string& text,
const std::vector<ui::ImeTextSpan>& spans,
const gfx::Range& replacement_range,
int selection_start,
int selection_end);
void ImeCommitText(const std::u16string& text,
const gfx::Range& replacement_range,
int relative_position);
void ImeFinishComposingText(bool keep_selection);
void OnFirstResponderChanged();
private:
RenderWidgetHostImpl* GetActiveWidget();
void OnDidUpdateVisualPropertiesComplete(
const cc::RenderFrameMetadata& metadata);
bool ShouldRouteEvents() const;
void SendGestureEvent(const blink::WebGestureEvent& event);
// Provides gesture synthesis given a stream of touch events and touch event
// acks. This is for generating gesture events from injected touch events.
ui::FilteredGestureProvider gesture_provider_;
bool is_first_responder_ = false;
bool is_getting_focus_ = false;
bool is_visible_ = false;
// Latest capture sequence number which is incremented when the caller
// requests surfaces be synchronized via
// EnsureSurfaceSynchronizedForWebTest().
uint32_t latest_capture_sequence_number_ = 0u;
std::unique_ptr<BrowserCompositorIOS> browser_compositor_;
std::unique_ptr<UIViewHolder> ui_view_;
base::WeakPtrFactory<RenderWidgetHostViewIOS> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_IOS_H_

@ -0,0 +1,663 @@
// 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/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/cocoa/animation_utils.h"
#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
#include "ui/gfx/geometry/dip_util.h"
@interface CALayer (PrivateAPI)
- (void)setContentsChanged;
@end
// 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;
}
@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
- (instancetype)initWithWidget:(content::RenderWidgetHostViewIOS*)view {
_view = view;
self.multipleTouchEnabled = YES;
self.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
return [self init];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)hasText {
NOTIMPLEMENTED();
return NO;
}
- (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->OnFirstResponderChanged();
}
return result;
}
- (BOOL)resignFirstResponder {
BOOL result = [super resignFirstResponder];
if (result) {
_view->OnFirstResponderChanged();
}
return result;
}
- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
for (UITouch* touch in touches) {
_view->OnTouchEvent(content::WebTouchEventBuilder::Build(
blink::WebInputEvent::Type::kTouchStart, touch, event, self));
}
if (!_view->HasFocus()) {
if ([self becomeFirstResponder]) {
_view->OnFirstResponderChanged();
}
}
}
- (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_;
base::scoped_nsobject<CALayer> io_surface_layer_;
};
///////////////////////////////////////////////////////////////////////////////
// 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]);
browser_compositor_ = std::make_unique<BrowserCompositorIOS>(
ui_view_->view_.get(), this, host()->is_hidden(),
host()->GetFrameSinkId());
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);
}
RenderWidgetHostViewBase::Destroy();
delete this;
}
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 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() {
return nullptr;
}
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() {}
void RenderWidgetHostViewIOS::ShowWithVisibility(
PageVisibilityState page_visibility) {
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 gfx::Rect([ui_view_->view_ bounds]);
}
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(
gfx::Rect([ui_view_->view_ bounds]).size());
RenderWidgetHostViewBase::UpdateScreenInfo();
}
void RenderWidgetHostViewIOS::UpdateCALayerTree(
const gfx::CALayerParams& ca_layer_params) {
ScopedCAActionDisabler disabler;
base::ScopedCFTypeRef<IOSurfaceRef> io_surface(
IOSurfaceLookupFromMachPort(ca_layer_params.io_surface_mach_port));
if (!io_surface) {
return;
}
if (!ui_view_->io_surface_layer_) {
ui_view_->io_surface_layer_ =
base::scoped_nsobject<CALayer>([[CALayer alloc] init]);
[[ui_view_->view_ layer] addSublayer:ui_view_->io_surface_layer_];
[[ui_view_->view_ layer] setDrawsAsynchronously:YES];
}
id new_contents = static_cast<id>(io_surface.get());
if (new_contents && new_contents == [ui_view_->io_surface_layer_ contents]) {
[ui_view_->io_surface_layer_ setContentsChanged];
} else {
[ui_view_->io_surface_layer_ setContents:new_contents];
}
// TODO(danakj): We should avoid lossy conversions to integer DIPs. The OS
// wants a floating point value.
gfx::Size bounds_dip = gfx::ToFlooredSize(gfx::ConvertSizeToDips(
ca_layer_params.pixel_size, ca_layer_params.scale_factor));
[ui_view_->io_surface_layer_
setFrame:CGRectMake(0, 0, bounds_dip.width(), bounds_dip.height())];
if ([ui_view_->io_surface_layer_ contentsScale] !=
ca_layer_params.scale_factor) {
[ui_view_->io_surface_layer_ setContentsScale:ca_layer_params.scale_factor];
}
}
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);
}
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);
if (ShouldRouteEvents()) {
blink::WebGestureEvent gesture_event(event);
host()->delegate()->GetInputEventRouter()->RouteGestureEvent(
this, &gesture_event, latency_info);
} else {
host()->ForwardGestureEventWithLatencyInfo(event, latency_info);
}
}
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];
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 (!did_update_state) {
return;
}
const ui::mojom::TextInputState* state =
text_input_manager->GetTextInputState();
if (state) {
[ui_view_->view_ textInput].frame = [ui_view_->view_ bounds];
[[ui_view_->view_ textInput] becomeFirstResponder];
} else {
[[ui_view_->view_ textInput] resignFirstResponder];
}
}
ui::Compositor* RenderWidgetHostViewIOS::GetCompositor() {
return browser_compositor_->GetCompositor();
}
} // namespace content

@ -25,7 +25,7 @@ bool ShouldUpdateTextInputState(const ui::mojom::TextInputState& old_state,
old_state.type != new_state.type || old_state.mode != new_state.mode ||
old_state.flags != new_state.flags ||
old_state.can_compose_inline != new_state.can_compose_inline;
#elif BUILDFLAG(IS_MAC)
#elif BUILDFLAG(IS_APPLE)
return old_state.type != new_state.type ||
old_state.flags != new_state.flags ||
old_state.can_compose_inline != new_state.can_compose_inline;

@ -78,7 +78,7 @@ void NativeEventObserver::DidDispatchMSG(const MSG& msg) {
}
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS)
void NativeEventObserver::RegisterObserver() {}
void NativeEventObserver::DeregisterObserver() {}
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)

@ -0,0 +1,48 @@
// 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/speech/tts_platform_impl.h"
#include "base/functional/callback.h"
#include "base/no_destructor.h"
namespace content {
// Dummy implementation provide an implementation for iOS.
class TtsPlatformImplIOS : public TtsPlatformImpl {
public:
TtsPlatformImplIOS() = default;
TtsPlatformImplIOS(const TtsPlatformImplIOS&) = delete;
TtsPlatformImplIOS& operator=(const TtsPlatformImplIOS&) = delete;
// TtsPlatform implementation.
bool PlatformImplSupported() override { return false; }
bool PlatformImplInitialized() override { return false; }
void Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params,
base::OnceCallback<void(bool)> on_speak_finished) override {
std::move(on_speak_finished).Run(false);
}
bool StopSpeaking() override { return false; }
bool IsSpeaking() override { return false; }
void GetVoices(std::vector<VoiceData>* out_voices) override {}
void Pause() override {}
void Resume() override {}
// Get the single instance of this class.
static TtsPlatformImplIOS* GetInstance() {
static base::NoDestructor<TtsPlatformImplIOS> tts_platform;
return tts_platform.get();
}
};
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplIOS::GetInstance();
}
} // namespace content

@ -8512,6 +8512,10 @@ gfx::Size WebContentsImpl::GetSize() {
#elif BUILDFLAG(IS_ANDROID)
ui::ViewAndroid* view_android = GetNativeView();
return view_android->bounds().size();
#elif BUILDFLAG(IS_IOS)
// TODO(crbug.com/1411704): Implement me.
NOTREACHED();
return gfx::Size();
#endif
}

@ -0,0 +1,72 @@
// 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.
#ifndef CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_IOS_H_
#define CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_IOS_H_
#include "base/memory/raw_ptr.h"
#include "content/browser/renderer_host/render_view_host_delegate_view.h"
#include "content/browser/web_contents/web_contents_view.h"
#include "content/common/content_export.h"
#include "content/public/browser/visibility.h"
#include "ui/gfx/geometry/size.h"
namespace content {
class WebContentsImpl;
class WebContentsViewDelegate;
class WebContentsUIViewHolder;
// iOS-specific implementation of the WebContentsView. It owns an UIView that
// contains all of the contents of the tab and associated child views.
class WebContentsViewIOS : public WebContentsView,
public RenderViewHostDelegateView {
public:
// The corresponding WebContentsImpl is passed in the constructor, and manages
// our lifetime. This doesn't need to be the case, but is this way currently
// because that's what was easiest when they were split.
WebContentsViewIOS(WebContentsImpl* web_contents,
std::unique_ptr<WebContentsViewDelegate> delegate);
WebContentsViewIOS(const WebContentsViewIOS&) = delete;
WebContentsViewIOS& operator=(const WebContentsViewIOS&) = delete;
~WebContentsViewIOS() override;
// WebContentsView implementation --------------------------------------------
gfx::NativeView GetNativeView() const override;
gfx::NativeView GetContentNativeView() const override;
gfx::NativeWindow GetTopLevelNativeWindow() const override;
gfx::Rect GetContainerBounds() const override;
void Focus() override;
void SetInitialFocus() override;
void StoreFocus() override;
void RestoreFocus() override;
void FocusThroughTabTraversal(bool reverse) override;
DropData* GetDropData() const override;
gfx::Rect GetViewBounds() const override;
void CreateView(gfx::NativeView context) override;
RenderWidgetHostViewBase* CreateViewForWidget(
RenderWidgetHost* render_widget_host) override;
RenderWidgetHostViewBase* CreateViewForChildWidget(
RenderWidgetHost* render_widget_host) override;
void SetPageTitle(const std::u16string& title) override;
void RenderViewReady() override;
void RenderViewHostChanged(RenderViewHost* old_host,
RenderViewHost* new_host) override;
void SetOverscrollControllerEnabled(bool enabled) override;
void OnCapturerCountChanged() override;
void FullscreenStateChanged(bool is_fullscreen) override;
void UpdateWindowControlsOverlay(const gfx::Rect& bounding_rect) override;
private:
// The WebContentsImpl whose contents we display.
raw_ptr<WebContentsImpl> web_contents_;
std::unique_ptr<WebContentsUIViewHolder> ui_view_;
};
} // namespace content
#endif // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_IOS_H_

@ -0,0 +1,133 @@
// 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.
#import "content/browser/web_contents/web_contents_view_ios.h"
#import <UIKit/UIKit.h>
#include <memory>
#include <string>
#include <utility>
#include "base/mac/scoped_nsobject.h"
#include "content/browser/renderer_host/render_widget_host_view_ios.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "ui/base/cocoa/animation_utils.h"
namespace content {
// This class holds a scoped_nsobject so we don't leak that in the header
// of the WebContentsViewIOS.
class WebContentsUIViewHolder {
public:
base::scoped_nsobject<UIView> view_;
};
std::unique_ptr<WebContentsView> CreateWebContentsView(
WebContentsImpl* web_contents,
std::unique_ptr<WebContentsViewDelegate> delegate,
RenderViewHostDelegateView** render_view_host_delegate_view) {
auto rv =
std::make_unique<WebContentsViewIOS>(web_contents, std::move(delegate));
*render_view_host_delegate_view = rv.get();
return rv;
}
WebContentsViewIOS::WebContentsViewIOS(
WebContentsImpl* web_contents,
std::unique_ptr<WebContentsViewDelegate> delegate)
: web_contents_(web_contents) {
ui_view_ = std::make_unique<WebContentsUIViewHolder>();
ui_view_->view_ = base::scoped_nsobject<UIView>([[UIView alloc] init]);
[ui_view_->view_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight];
}
WebContentsViewIOS::~WebContentsViewIOS() {}
gfx::NativeView WebContentsViewIOS::GetNativeView() const {
return ui_view_->view_.get();
}
gfx::NativeView WebContentsViewIOS::GetContentNativeView() const {
RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
if (!rwhv) {
return nullptr;
}
return rwhv->GetNativeView();
}
gfx::NativeWindow WebContentsViewIOS::GetTopLevelNativeWindow() const {
return nullptr;
}
gfx::Rect WebContentsViewIOS::GetContainerBounds() const {
return gfx::Rect();
}
void WebContentsViewIOS::OnCapturerCountChanged() {}
void WebContentsViewIOS::FullscreenStateChanged(bool is_fullscreen) {}
void WebContentsViewIOS::UpdateWindowControlsOverlay(
const gfx::Rect& bounding_rect) {}
void WebContentsViewIOS::Focus() {}
void WebContentsViewIOS::SetInitialFocus() {}
void WebContentsViewIOS::StoreFocus() {}
void WebContentsViewIOS::RestoreFocus() {}
void WebContentsViewIOS::FocusThroughTabTraversal(bool reverse) {}
DropData* WebContentsViewIOS::GetDropData() const {
return nullptr;
}
gfx::Rect WebContentsViewIOS::GetViewBounds() const {
return gfx::Rect();
}
void WebContentsViewIOS::CreateView(gfx::NativeView context) {}
RenderWidgetHostViewBase* WebContentsViewIOS::CreateViewForWidget(
RenderWidgetHost* render_widget_host) {
return new RenderWidgetHostViewIOS(render_widget_host);
}
RenderWidgetHostViewBase* WebContentsViewIOS::CreateViewForChildWidget(
RenderWidgetHost* render_widget_host) {
return new RenderWidgetHostViewIOS(render_widget_host);
}
void WebContentsViewIOS::SetPageTitle(const std::u16string& title) {
// Meaningless on the Mac; widgets don't have a "title" attribute
}
void WebContentsViewIOS::RenderViewReady() {}
void WebContentsViewIOS::RenderViewHostChanged(RenderViewHost* old_host,
RenderViewHost* new_host) {
ScopedCAActionDisabler disabler;
if (old_host) {
auto* rwhv = old_host->GetWidget()->GetView();
if (rwhv && rwhv->GetNativeView()) {
static_cast<RenderWidgetHostViewIOS*>(rwhv)->UpdateNativeViewTree(
nullptr);
}
}
auto* rwhv = new_host->GetWidget()->GetView();
if (rwhv && rwhv->GetNativeView()) {
static_cast<RenderWidgetHostViewIOS*>(rwhv)->UpdateNativeViewTree(
GetNativeView());
}
}
void WebContentsViewIOS::SetOverscrollControllerEnabled(bool enabled) {}
} // namespace content

@ -307,6 +307,14 @@ source_set("common") {
}
}
if (is_ios) {
# TOOD: Create ios file instead of stealing android
sources += [
"cursors/webcursor_android.cc",
"font_list_android.cc",
]
}
if (is_mac) {
sources += [
"font_list_mac.mm",
@ -367,7 +375,7 @@ source_set("common") {
]
}
if (is_mac || is_win || is_android || is_fuchsia) {
if (is_mac || is_win || is_android || is_fuchsia || is_ios) {
sources -= [ "font_list_fontconfig.cc" ]
}