[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:

committed by
Chromium LUCI CQ

parent
3fcbdb1504
commit
a1cc370d96
content
app
browser
BUILD.gnbrowser_child_process_host_impl.ccbrowser_main_loop.ccchild_process_launcher_helper.ccchild_process_launcher_helper_ios.ccchild_process_launcher_helper_posix.cc
gpu
renderer_host
browser_compositor_ios.hbrowser_compositor_ios.mmdelegated_frame_host_client_ios.ccdelegated_frame_host_client_ios.h
input
media
native_web_keyboard_event_ios.mmrender_widget_host_view_ios.hrender_widget_host_view_ios.mmtext_input_manager.ccscheduler
responsiveness
speech
web_contents
common
@ -71,7 +71,9 @@
|
|||||||
#if BUILDFLAG(IS_MAC)
|
#if BUILDFLAG(IS_MAC)
|
||||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||||
#include "content/app/mac_init.h"
|
#include "content/app/mac_init.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_APPLE)
|
||||||
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
|
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||||
#include "base/allocator/partition_allocator/shim/allocator_shim.h"
|
#include "base/allocator/partition_allocator/shim/allocator_shim.h"
|
||||||
#endif
|
#endif
|
||||||
@ -201,7 +203,7 @@ RunContentProcess(ContentMainParams params,
|
|||||||
content_main_runner->ReInitializeParams(std::move(params));
|
content_main_runner->ReInitializeParams(std::move(params));
|
||||||
} else {
|
} else {
|
||||||
is_initialized = true;
|
is_initialized = true;
|
||||||
#if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_ALLOCATOR_SHIM)
|
#if BUILDFLAG(IS_APPLE) && BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||||
allocator_shim::InitializeAllocatorShim();
|
allocator_shim::InitializeAllocatorShim();
|
||||||
#endif
|
#endif
|
||||||
base::EnableTerminationOnOutOfMemory();
|
base::EnableTerminationOnOutOfMemory();
|
||||||
@ -283,6 +285,15 @@ RunContentProcess(ContentMainParams params,
|
|||||||
InitializeMac();
|
InitializeMac();
|
||||||
#endif
|
#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)
|
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
|
||||||
base::subtle::EnableFDOwnershipEnforcement(true);
|
base::subtle::EnableFDOwnershipEnforcement(true);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1100,6 +1100,9 @@ int NO_STACK_PROTECTOR ContentMainRunnerImpl::Run() {
|
|||||||
main_params.sandbox_info = content_main_params_->sandbox_info;
|
main_params.sandbox_info = content_main_params_->sandbox_info;
|
||||||
#elif BUILDFLAG(IS_MAC)
|
#elif BUILDFLAG(IS_MAC)
|
||||||
main_params.autorelease_pool = content_main_params_->autorelease_pool;
|
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
|
#endif
|
||||||
|
|
||||||
const bool start_minimal_browser = content_main_params_->minimal_browser_mode;
|
const bool start_minimal_browser = content_main_params_->minimal_browser_mode;
|
||||||
|
@ -267,6 +267,7 @@ source_set("browser") {
|
|||||||
"//ui/base/ime/init",
|
"//ui/base/ime/init",
|
||||||
"//ui/color",
|
"//ui/color",
|
||||||
"//ui/color:mixers",
|
"//ui/color:mixers",
|
||||||
|
"//ui/compositor",
|
||||||
"//ui/display",
|
"//ui/display",
|
||||||
"//ui/display/types",
|
"//ui/display/types",
|
||||||
"//ui/display/util",
|
"//ui/display/util",
|
||||||
@ -2604,6 +2605,20 @@ source_set("browser") {
|
|||||||
weak_frameworks = [
|
weak_frameworks = [
|
||||||
"UniformTypeIdentifiers.framework", # macOS 11
|
"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 {
|
} else {
|
||||||
# Not Mac.
|
# Not Mac.
|
||||||
deps += [ "//sandbox" ]
|
deps += [ "//sandbox" ]
|
||||||
@ -2896,6 +2911,15 @@ source_set("browser") {
|
|||||||
configs += [ "//build/config/linux/pangocairo" ]
|
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) {
|
if (is_android) {
|
||||||
sources += [
|
sources += [
|
||||||
"accessibility/accessibility_tree_formatter_android.cc",
|
"accessibility/accessibility_tree_formatter_android.cc",
|
||||||
|
@ -384,8 +384,11 @@ ChildProcessTerminationInfo BrowserChildProcessHostImpl::GetTerminationInfo(
|
|||||||
if (!child_process_) {
|
if (!child_process_) {
|
||||||
// If the delegate doesn't use Launch() helper.
|
// If the delegate doesn't use Launch() helper.
|
||||||
ChildProcessTerminationInfo info;
|
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.status = base::GetTerminationStatus(data_.GetProcess().Handle(),
|
||||||
&info.exit_code);
|
&info.exit_code);
|
||||||
|
#endif
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
return child_process_->GetChildTerminationInfo(known_dead);
|
return child_process_->GetChildTerminationInfo(known_dead);
|
||||||
|
@ -367,7 +367,7 @@ std::unique_ptr<base::MemoryPressureMonitor> CreateMemoryPressureMonitor(
|
|||||||
|
|
||||||
std::unique_ptr<memory_pressure::MultiSourceMemoryPressureMonitor> monitor;
|
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)
|
BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
|
||||||
monitor =
|
monitor =
|
||||||
std::make_unique<memory_pressure::MultiSourceMemoryPressureMonitor>();
|
std::make_unique<memory_pressure::MultiSourceMemoryPressureMonitor>();
|
||||||
|
@ -144,8 +144,11 @@ void ChildProcessLauncherHelper::LaunchOnLauncherThread() {
|
|||||||
|
|
||||||
Process process;
|
Process process;
|
||||||
if (BeforeLaunchOnLauncherThread(*files_to_register, options_ptr)) {
|
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(
|
base::FieldTrialList::PopulateLaunchOptionsWithFieldTrialState(
|
||||||
command_line(), options_ptr);
|
command_line(), options_ptr);
|
||||||
|
#endif
|
||||||
process =
|
process =
|
||||||
LaunchProcessOnLauncherThread(options_ptr, std::move(files_to_register),
|
LaunchProcessOnLauncherThread(options_ptr, std::move(files_to_register),
|
||||||
#if BUILDFLAG(IS_ANDROID)
|
#if BUILDFLAG(IS_ANDROID)
|
||||||
|
102
content/browser/child_process_launcher_helper_ios.cc
Normal file
102
content/browser/child_process_launcher_helper_ios.cc
Normal file
@ -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());
|
PosixFileDescriptorInfoImpl::Create());
|
||||||
|
|
||||||
// Mac shared memory doesn't use file descriptors.
|
// Mac shared memory doesn't use file descriptors.
|
||||||
#if !BUILDFLAG(IS_MAC)
|
#if !BUILDFLAG(IS_APPLE)
|
||||||
int fd = base::FieldTrialList::GetFieldTrialDescriptor();
|
int fd = base::FieldTrialList::GetFieldTrialDescriptor();
|
||||||
DCHECK_NE(fd, -1);
|
DCHECK_NE(fd, -1);
|
||||||
files_to_register->Share(kFieldTrialDescriptor, fd);
|
files_to_register->Share(kFieldTrialDescriptor, fd);
|
||||||
|
@ -419,6 +419,8 @@ bool SwiftShaderAllowed() {
|
|||||||
[[maybe_unused]] bool MetalAllowed() {
|
[[maybe_unused]] bool MetalAllowed() {
|
||||||
#if BUILDFLAG(IS_MAC)
|
#if BUILDFLAG(IS_MAC)
|
||||||
return base::FeatureList::IsEnabled(features::kMetal);
|
return base::FeatureList::IsEnabled(features::kMetal);
|
||||||
|
#elif BUILDFLAG(IS_IOS)
|
||||||
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
189
content/browser/renderer_host/browser_compositor_ios.h
Normal file
189
content/browser/renderer_host/browser_compositor_ios.h
Normal file
@ -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_
|
423
content/browser/renderer_host/browser_compositor_ios.mm
Normal file
423
content/browser/renderer_host/browser_compositor_ios.mm
Normal file
@ -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() {
|
ui::Compositor* FlingScheduler::GetCompositor() {
|
||||||
#if defined(USE_AURA)
|
if (!host_->GetView()) {
|
||||||
if (host_->GetView() && host_->GetView()->GetNativeView() &&
|
return nullptr;
|
||||||
host_->GetView()->GetNativeView()->GetHost() &&
|
|
||||||
host_->GetView()->GetNativeView()->GetHost()->compositor()) {
|
|
||||||
return host_->GetView()->GetNativeView()->GetHost()->compositor();
|
|
||||||
}
|
}
|
||||||
#endif
|
return host_->GetView()->GetCompositor();
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlingScheduler::OnAnimationStep(base::TimeTicks timestamp) {
|
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();
|
VideoCaptureDeviceProxyLacros::IsAvailable();
|
||||||
}
|
}
|
||||||
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
|
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
|
||||||
#endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
|
|
||||||
|
|
||||||
// These values are persisted to logs. Entries should not be renumbered and
|
// These values are persisted to logs. Entries should not be renumbered and
|
||||||
// numeric values should never be reused.
|
// numeric values should never be reused.
|
||||||
@ -214,7 +213,7 @@ DesktopCaptureImplementation CreatePlatformDependentVideoCaptureDevice(
|
|||||||
#endif
|
#endif
|
||||||
return kNoImplementation;
|
return kNoImplementation;
|
||||||
}
|
}
|
||||||
|
#endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
InProcessVideoCaptureDeviceLauncher::InProcessVideoCaptureDeviceLauncher(
|
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
|
186
content/browser/renderer_host/render_widget_host_view_ios.h
Normal file
186
content/browser/renderer_host/render_widget_host_view_ios.h
Normal file
@ -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_
|
663
content/browser/renderer_host/render_widget_host_view_ios.mm
Normal file
663
content/browser/renderer_host/render_widget_host_view_ios.mm
Normal file
@ -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.type != new_state.type || old_state.mode != new_state.mode ||
|
||||||
old_state.flags != new_state.flags ||
|
old_state.flags != new_state.flags ||
|
||||||
old_state.can_compose_inline != new_state.can_compose_inline;
|
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 ||
|
return old_state.type != new_state.type ||
|
||||||
old_state.flags != new_state.flags ||
|
old_state.flags != new_state.flags ||
|
||||||
old_state.can_compose_inline != new_state.can_compose_inline;
|
old_state.can_compose_inline != new_state.can_compose_inline;
|
||||||
|
@ -78,7 +78,7 @@ void NativeEventObserver::DidDispatchMSG(const MSG& msg) {
|
|||||||
}
|
}
|
||||||
#endif // BUILDFLAG(IS_WIN)
|
#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::RegisterObserver() {}
|
||||||
void NativeEventObserver::DeregisterObserver() {}
|
void NativeEventObserver::DeregisterObserver() {}
|
||||||
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
|
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
|
||||||
|
48
content/browser/speech/tts_ios.mm
Normal file
48
content/browser/speech/tts_ios.mm
Normal file
@ -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)
|
#elif BUILDFLAG(IS_ANDROID)
|
||||||
ui::ViewAndroid* view_android = GetNativeView();
|
ui::ViewAndroid* view_android = GetNativeView();
|
||||||
return view_android->bounds().size();
|
return view_android->bounds().size();
|
||||||
|
#elif BUILDFLAG(IS_IOS)
|
||||||
|
// TODO(crbug.com/1411704): Implement me.
|
||||||
|
NOTREACHED();
|
||||||
|
return gfx::Size();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
72
content/browser/web_contents/web_contents_view_ios.h
Normal file
72
content/browser/web_contents/web_contents_view_ios.h
Normal file
@ -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_
|
133
content/browser/web_contents/web_contents_view_ios.mm
Normal file
133
content/browser/web_contents/web_contents_view_ios.mm
Normal file
@ -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) {
|
if (is_mac) {
|
||||||
sources += [
|
sources += [
|
||||||
"font_list_mac.mm",
|
"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" ]
|
sources -= [ "font_list_fontconfig.cc" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user