[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)
|
||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||
#include "content/app/mac_init.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_APPLE)
|
||||
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||
#include "base/allocator/partition_allocator/shim/allocator_shim.h"
|
||||
#endif
|
||||
@ -201,7 +203,7 @@ RunContentProcess(ContentMainParams params,
|
||||
content_main_runner->ReInitializeParams(std::move(params));
|
||||
} else {
|
||||
is_initialized = true;
|
||||
#if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||
#if BUILDFLAG(IS_APPLE) && BUILDFLAG(USE_ALLOCATOR_SHIM)
|
||||
allocator_shim::InitializeAllocatorShim();
|
||||
#endif
|
||||
base::EnableTerminationOnOutOfMemory();
|
||||
@ -283,6 +285,15 @@ RunContentProcess(ContentMainParams params,
|
||||
InitializeMac();
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
// TODO(crbug.com/1412835): Remove this initialization on iOS. Everything
|
||||
// runs in process for now as we have no fork.
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
command_line->AppendSwitch(switches::kSingleProcess);
|
||||
command_line->AppendSwitch(switches::kEnableViewport);
|
||||
command_line->AppendSwitch(switches::kUseMobileUserAgent);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
|
||||
base::subtle::EnableFDOwnershipEnforcement(true);
|
||||
#endif
|
||||
|
@ -1100,6 +1100,9 @@ int NO_STACK_PROTECTOR ContentMainRunnerImpl::Run() {
|
||||
main_params.sandbox_info = content_main_params_->sandbox_info;
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
main_params.autorelease_pool = content_main_params_->autorelease_pool;
|
||||
#elif BUILDFLAG(IS_IOS)
|
||||
main_params.argc = content_main_params_->argc;
|
||||
main_params.argv = content_main_params_->argv;
|
||||
#endif
|
||||
|
||||
const bool start_minimal_browser = content_main_params_->minimal_browser_mode;
|
||||
|
@ -267,6 +267,7 @@ source_set("browser") {
|
||||
"//ui/base/ime/init",
|
||||
"//ui/color",
|
||||
"//ui/color:mixers",
|
||||
"//ui/compositor",
|
||||
"//ui/display",
|
||||
"//ui/display/types",
|
||||
"//ui/display/util",
|
||||
@ -2604,6 +2605,20 @@ source_set("browser") {
|
||||
weak_frameworks = [
|
||||
"UniformTypeIdentifiers.framework", # macOS 11
|
||||
]
|
||||
} else if (is_ios) {
|
||||
sources += [
|
||||
"child_process_launcher_helper_ios.cc",
|
||||
"renderer_host/browser_compositor_ios.h",
|
||||
"renderer_host/browser_compositor_ios.mm",
|
||||
"renderer_host/delegated_frame_host_client_ios.cc",
|
||||
"renderer_host/delegated_frame_host_client_ios.h",
|
||||
"renderer_host/render_widget_host_view_ios.h",
|
||||
"renderer_host/render_widget_host_view_ios.mm",
|
||||
"web_contents/web_contents_view_ios.h",
|
||||
"web_contents/web_contents_view_ios.mm",
|
||||
]
|
||||
frameworks += [ "IOSurface.framework" ]
|
||||
deps += [ "//ui/accelerated_widget_mac" ]
|
||||
} else {
|
||||
# Not Mac.
|
||||
deps += [ "//sandbox" ]
|
||||
@ -2896,6 +2911,15 @@ source_set("browser") {
|
||||
configs += [ "//build/config/linux/pangocairo" ]
|
||||
}
|
||||
|
||||
if (is_ios) {
|
||||
sources += [
|
||||
"renderer_host/input/web_input_event_builders_ios.h",
|
||||
"renderer_host/input/web_input_event_builders_ios.mm",
|
||||
"renderer_host/native_web_keyboard_event_ios.mm",
|
||||
"speech/tts_ios.mm",
|
||||
]
|
||||
}
|
||||
|
||||
if (is_android) {
|
||||
sources += [
|
||||
"accessibility/accessibility_tree_formatter_android.cc",
|
||||
|
@ -384,8 +384,11 @@ ChildProcessTerminationInfo BrowserChildProcessHostImpl::GetTerminationInfo(
|
||||
if (!child_process_) {
|
||||
// If the delegate doesn't use Launch() helper.
|
||||
ChildProcessTerminationInfo info;
|
||||
// TODO(crbug.com/1412835): iOS is single process mode for now.
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
info.status = base::GetTerminationStatus(data_.GetProcess().Handle(),
|
||||
&info.exit_code);
|
||||
#endif
|
||||
return info;
|
||||
}
|
||||
return child_process_->GetChildTerminationInfo(known_dead);
|
||||
|
@ -367,7 +367,7 @@ std::unique_ptr<base::MemoryPressureMonitor> CreateMemoryPressureMonitor(
|
||||
|
||||
std::unique_ptr<memory_pressure::MultiSourceMemoryPressureMonitor> monitor;
|
||||
|
||||
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || \
|
||||
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || \
|
||||
BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
|
||||
monitor =
|
||||
std::make_unique<memory_pressure::MultiSourceMemoryPressureMonitor>();
|
||||
|
@ -144,8 +144,11 @@ void ChildProcessLauncherHelper::LaunchOnLauncherThread() {
|
||||
|
||||
Process process;
|
||||
if (BeforeLaunchOnLauncherThread(*files_to_register, options_ptr)) {
|
||||
// TODO(crbug.com/1412835): iOS is single process mode for now.
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
base::FieldTrialList::PopulateLaunchOptionsWithFieldTrialState(
|
||||
command_line(), options_ptr);
|
||||
#endif
|
||||
process =
|
||||
LaunchProcessOnLauncherThread(options_ptr, std::move(files_to_register),
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
|
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());
|
||||
|
||||
// Mac shared memory doesn't use file descriptors.
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
#if !BUILDFLAG(IS_APPLE)
|
||||
int fd = base::FieldTrialList::GetFieldTrialDescriptor();
|
||||
DCHECK_NE(fd, -1);
|
||||
files_to_register->Share(kFieldTrialDescriptor, fd);
|
||||
|
@ -419,6 +419,8 @@ bool SwiftShaderAllowed() {
|
||||
[[maybe_unused]] bool MetalAllowed() {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
return base::FeatureList::IsEnabled(features::kMetal);
|
||||
#elif BUILDFLAG(IS_IOS)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
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() {
|
||||
#if defined(USE_AURA)
|
||||
if (host_->GetView() && host_->GetView()->GetNativeView() &&
|
||||
host_->GetView()->GetNativeView()->GetHost() &&
|
||||
host_->GetView()->GetNativeView()->GetHost()->compositor()) {
|
||||
return host_->GetView()->GetNativeView()->GetHost()->compositor();
|
||||
if (!host_->GetView()) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
return host_->GetView()->GetCompositor();
|
||||
}
|
||||
|
||||
void FlingScheduler::OnAnimationStep(base::TimeTicks timestamp) {
|
||||
|
@ -0,0 +1,41 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_IOS_H_
|
||||
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_IOS_H_
|
||||
|
||||
#include "content/common/content_export.h"
|
||||
#include "third_party/blink/public/common/input/web_gesture_event.h"
|
||||
#include "third_party/blink/public/common/input/web_input_event.h"
|
||||
#include "third_party/blink/public/common/input/web_keyboard_event.h"
|
||||
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
|
||||
#include "third_party/blink/public/common/input/web_touch_event.h"
|
||||
|
||||
@class UIEvent;
|
||||
@class UITouch;
|
||||
@class UIView;
|
||||
|
||||
namespace content {
|
||||
|
||||
class CONTENT_EXPORT WebKeyboardEventBuilder {
|
||||
public:
|
||||
static blink::WebKeyboardEvent Build(UIEvent* event);
|
||||
};
|
||||
|
||||
class CONTENT_EXPORT WebGestureEventBuilder {
|
||||
public:
|
||||
static blink::WebGestureEvent Build(UIEvent*, UIView*);
|
||||
};
|
||||
|
||||
class CONTENT_EXPORT WebTouchEventBuilder {
|
||||
public:
|
||||
static blink::WebTouchEvent Build(blink::WebInputEvent::Type type,
|
||||
UITouch* touch,
|
||||
UIEvent* event,
|
||||
UIView* view);
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
|
||||
#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_IOS_H_
|
@ -0,0 +1,225 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "content/browser/renderer_host/input/web_input_event_builders_ios.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include "third_party/blink/public/common/input/web_pointer_event.h"
|
||||
#include "third_party/blink/public/common/input/web_touch_point.h"
|
||||
#include "ui/events/base_event_utils.h"
|
||||
#include "ui/events/event_utils.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t MAX_POINTERS = blink::WebTouchEvent::kTouchesLengthCap;
|
||||
static UITouch* g_active_touches_map[MAX_POINTERS] = {};
|
||||
|
||||
size_t GetTouchPointerId(UITouch* touch) {
|
||||
for (size_t i = 0; i < MAX_POINTERS; ++i) {
|
||||
if (g_active_touches_map[i] == touch) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AddUITouch(UITouch* touch) {
|
||||
CHECK(GetTouchPointerId(touch) == 0);
|
||||
for (size_t i = 0; i < MAX_POINTERS; ++i) {
|
||||
if (!g_active_touches_map[i]) {
|
||||
g_active_touches_map[i] = [touch retain];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveUITouch(UITouch* touch) {
|
||||
for (size_t i = 0; i < MAX_POINTERS; ++i) {
|
||||
if (g_active_touches_map[i] == touch) {
|
||||
g_active_touches_map[i] = nullptr;
|
||||
[touch release];
|
||||
return;
|
||||
}
|
||||
}
|
||||
CHECK(false);
|
||||
}
|
||||
|
||||
int ModifiersFromEvent(UIEvent* event) {
|
||||
int modifiers = 0;
|
||||
UIKeyModifierFlags modifier_flags = [event modifierFlags];
|
||||
|
||||
if (modifier_flags & UIKeyModifierControl) {
|
||||
modifiers |= blink::WebInputEvent::kControlKey;
|
||||
}
|
||||
if (modifier_flags & UIKeyModifierShift) {
|
||||
modifiers |= blink::WebInputEvent::kShiftKey;
|
||||
}
|
||||
if (modifier_flags & UIKeyModifierAlternate) {
|
||||
modifiers |= blink::WebInputEvent::kAltKey;
|
||||
}
|
||||
if (modifier_flags & UIKeyModifierCommand) {
|
||||
modifiers |= blink::WebInputEvent::kMetaKey;
|
||||
}
|
||||
if (modifier_flags & UIKeyModifierAlphaShift) {
|
||||
modifiers |= blink::WebInputEvent::kCapsLockOn;
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
blink::WebTouchPoint::State ToWebTouchPointState(UITouch* event,
|
||||
bool was_changed) {
|
||||
// We will get an event for each actual changed phase.
|
||||
if (!was_changed) {
|
||||
return blink::WebTouchPoint::State::kStateStationary;
|
||||
}
|
||||
|
||||
switch ([event phase]) {
|
||||
case UITouchPhaseBegan:
|
||||
case UITouchPhaseRegionEntered:
|
||||
return blink::WebTouchPoint::State::kStatePressed;
|
||||
case UITouchPhaseMoved:
|
||||
case UITouchPhaseRegionMoved:
|
||||
return blink::WebTouchPoint::State::kStateMoved;
|
||||
case UITouchPhaseEnded:
|
||||
case UITouchPhaseRegionExited:
|
||||
return blink::WebTouchPoint::State::kStateReleased;
|
||||
case UITouchPhaseCancelled:
|
||||
return blink::WebTouchPoint::State::kStateCancelled;
|
||||
case UITouchPhaseStationary:
|
||||
return blink::WebTouchPoint::State::kStateStationary;
|
||||
}
|
||||
NOTREACHED() << "Invalid MotionEvent::Action.";
|
||||
return blink::WebTouchPoint::State::kStateUndefined;
|
||||
}
|
||||
|
||||
void SetWebPointerPropertiesFromMotionEventData(
|
||||
blink::WebPointerProperties& webPointerProperties,
|
||||
int pointer_id,
|
||||
float pressure) {
|
||||
webPointerProperties.id = pointer_id;
|
||||
webPointerProperties.force = pressure;
|
||||
webPointerProperties.tilt_x = webPointerProperties.tilt_y = 0;
|
||||
webPointerProperties.twist = 0;
|
||||
webPointerProperties.tangential_pressure = 0;
|
||||
webPointerProperties.button = blink::WebPointerProperties::Button::kNoButton;
|
||||
webPointerProperties.pointer_type =
|
||||
blink::WebPointerProperties::PointerType::kTouch;
|
||||
// TODO(dtapuska): Support stylus.
|
||||
}
|
||||
|
||||
blink::WebTouchPoint CreateWebTouchPoint(UIView* view,
|
||||
UITouch* event,
|
||||
bool was_changed) {
|
||||
blink::WebTouchPoint touch;
|
||||
|
||||
size_t pointer_index = GetTouchPointerId(event);
|
||||
CHECK(pointer_index != 0);
|
||||
|
||||
SetWebPointerPropertiesFromMotionEventData(touch, pointer_index,
|
||||
[event force]);
|
||||
|
||||
touch.state = ToWebTouchPointState(event, was_changed);
|
||||
CGPoint view_location = [event locationInView:view];
|
||||
touch.SetPositionInWidget(view_location.x, view_location.y);
|
||||
CGPoint window_location = [event locationInView:nil];
|
||||
touch.SetPositionInScreen(window_location.x, window_location.y);
|
||||
|
||||
float major_radius = event.majorRadius;
|
||||
float minor_radius = event.majorRadius;
|
||||
float orientation_deg = 0;
|
||||
|
||||
DCHECK_GE(major_radius, 0);
|
||||
DCHECK_GE(minor_radius, 0);
|
||||
DCHECK_GE(major_radius, minor_radius);
|
||||
// Orientation lies in [-180, 180] for a stylus, and [-90, 90] for other
|
||||
// touchscreen inputs. There are exceptions on Android when a device is
|
||||
// rotated, yielding touch orientations in the range of [-180, 180].
|
||||
// Regardless, normalise to [-90, 90), allowing a small tolerance to account
|
||||
// for floating point conversion.
|
||||
// TODO(e_hakkinen): Also pass unaltered stylus orientation, avoiding loss of
|
||||
// quadrant information, see crbug.com/493728.
|
||||
DCHECK_GT(orientation_deg, -180.01f);
|
||||
DCHECK_LT(orientation_deg, 180.01f);
|
||||
if (orientation_deg >= 90.f) {
|
||||
orientation_deg -= 180.f;
|
||||
} else if (orientation_deg < -90.f) {
|
||||
orientation_deg += 180.f;
|
||||
}
|
||||
if (orientation_deg >= 0) {
|
||||
// The case orientation_deg == 0 is handled here on purpose: although the
|
||||
// 'else' block is equivalent in this case, we want to pass the 0 value
|
||||
// unchanged (and 0 is the default value for many devices that don't
|
||||
// report elliptical touches).
|
||||
touch.radius_x = minor_radius;
|
||||
touch.radius_y = major_radius;
|
||||
touch.rotation_angle = orientation_deg;
|
||||
} else {
|
||||
touch.radius_x = major_radius;
|
||||
touch.radius_y = minor_radius;
|
||||
touch.rotation_angle = orientation_deg + 90;
|
||||
}
|
||||
|
||||
return touch;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
blink::WebKeyboardEvent WebKeyboardEventBuilder::Build(UIEvent*) {
|
||||
return blink::WebKeyboardEvent();
|
||||
}
|
||||
|
||||
blink::WebGestureEvent WebGestureEventBuilder::Build(UIEvent*, UIView*) {
|
||||
return blink::WebGestureEvent();
|
||||
}
|
||||
|
||||
blink::WebTouchEvent WebTouchEventBuilder::Build(
|
||||
blink::WebInputEvent::Type type,
|
||||
UITouch* touch,
|
||||
UIEvent* event,
|
||||
UIView* view) {
|
||||
blink::WebTouchEvent result(type, ModifiersFromEvent(event),
|
||||
ui::EventTimeStampFromSeconds([event timestamp]));
|
||||
// TODO(dtapuska): Enable
|
||||
// ui::ComputeEventLatencyOS(event);
|
||||
result.dispatch_type =
|
||||
result.GetType() == blink::WebInputEvent::Type::kTouchCancel
|
||||
? blink::WebInputEvent::DispatchType::kEventNonBlocking
|
||||
: blink::WebInputEvent::DispatchType::kBlocking;
|
||||
result.hovering = type == blink::WebInputEvent::Type::kTouchEnd;
|
||||
result.unique_touch_event_id = ui::GetNextTouchEventId();
|
||||
|
||||
size_t touch_index = 0;
|
||||
if (type == blink::WebInputEvent::Type::kTouchStart) {
|
||||
AddUITouch(touch);
|
||||
}
|
||||
result.touches[touch_index] =
|
||||
CreateWebTouchPoint(view, touch, /*was_changed=*/true);
|
||||
++touch_index;
|
||||
if (type == blink::WebInputEvent::Type::kTouchCancel ||
|
||||
type == blink::WebInputEvent::Type::kTouchEnd) {
|
||||
RemoveUITouch(touch);
|
||||
}
|
||||
|
||||
// We can't use `event.allTouches` here, because we need to generate a
|
||||
// WebTouchEvent for each touch point changing. But event.allTouches will have
|
||||
// it all already.
|
||||
for (size_t i = 0; i < MAX_POINTERS; ++i) {
|
||||
if (!g_active_touches_map[i] || g_active_touches_map[i] == touch) {
|
||||
continue;
|
||||
}
|
||||
result.touches[touch_index] = CreateWebTouchPoint(
|
||||
view, g_active_touches_map[i], /*was_changed=*/false);
|
||||
++touch_index;
|
||||
}
|
||||
result.touches_length = touch_index;
|
||||
DCHECK_GT(result.touches_length, 0U);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace content
|
@ -133,7 +133,6 @@ bool ShouldUseDesktopCaptureLacrosV2() {
|
||||
VideoCaptureDeviceProxyLacros::IsAvailable();
|
||||
}
|
||||
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
|
||||
#endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
|
||||
|
||||
// These values are persisted to logs. Entries should not be renumbered and
|
||||
// numeric values should never be reused.
|
||||
@ -214,7 +213,7 @@ DesktopCaptureImplementation CreatePlatformDependentVideoCaptureDevice(
|
||||
#endif
|
||||
return kNoImplementation;
|
||||
}
|
||||
|
||||
#endif // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
|
||||
} // anonymous namespace
|
||||
|
||||
InProcessVideoCaptureDeviceLauncher::InProcessVideoCaptureDeviceLauncher(
|
||||
|
@ -0,0 +1,56 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
|
||||
#include "content/browser/renderer_host/input/web_input_event_builders_ios.h"
|
||||
#include "ui/events/base_event_utils.h"
|
||||
#include "ui/events/event.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
NativeWebKeyboardEvent::NativeWebKeyboardEvent(blink::WebInputEvent::Type type,
|
||||
int modifiers,
|
||||
base::TimeTicks timestamp)
|
||||
: WebKeyboardEvent(type, modifiers, timestamp),
|
||||
os_event(NULL),
|
||||
skip_in_browser(false) {}
|
||||
|
||||
NativeWebKeyboardEvent::NativeWebKeyboardEvent(
|
||||
const blink::WebKeyboardEvent& web_event,
|
||||
gfx::NativeView native_view)
|
||||
: WebKeyboardEvent(web_event), os_event(nullptr), skip_in_browser(false) {}
|
||||
|
||||
NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event)
|
||||
: WebKeyboardEvent(WebKeyboardEventBuilder::Build(native_event)),
|
||||
os_event([native_event retain]),
|
||||
skip_in_browser(false) {}
|
||||
|
||||
NativeWebKeyboardEvent::NativeWebKeyboardEvent(const ui::KeyEvent& key_event)
|
||||
: NativeWebKeyboardEvent(key_event.native_event()) {}
|
||||
|
||||
NativeWebKeyboardEvent::NativeWebKeyboardEvent(
|
||||
const NativeWebKeyboardEvent& other)
|
||||
: WebKeyboardEvent(other),
|
||||
os_event([other.os_event retain]),
|
||||
skip_in_browser(other.skip_in_browser) {}
|
||||
|
||||
NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=(
|
||||
const NativeWebKeyboardEvent& other) {
|
||||
WebKeyboardEvent::operator=(other);
|
||||
|
||||
UIEvent* previous = os_event;
|
||||
os_event = [other.os_event retain];
|
||||
[previous release];
|
||||
|
||||
skip_in_browser = other.skip_in_browser;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NativeWebKeyboardEvent::~NativeWebKeyboardEvent() {
|
||||
[os_event release];
|
||||
}
|
||||
|
||||
} // namespace content
|
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.flags != new_state.flags ||
|
||||
old_state.can_compose_inline != new_state.can_compose_inline;
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
#elif BUILDFLAG(IS_APPLE)
|
||||
return old_state.type != new_state.type ||
|
||||
old_state.flags != new_state.flags ||
|
||||
old_state.can_compose_inline != new_state.can_compose_inline;
|
||||
|
@ -78,7 +78,7 @@ void NativeEventObserver::DidDispatchMSG(const MSG& msg) {
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
|
||||
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS)
|
||||
void NativeEventObserver::RegisterObserver() {}
|
||||
void NativeEventObserver::DeregisterObserver() {}
|
||||
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
|
||||
|
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)
|
||||
ui::ViewAndroid* view_android = GetNativeView();
|
||||
return view_android->bounds().size();
|
||||
#elif BUILDFLAG(IS_IOS)
|
||||
// TODO(crbug.com/1411704): Implement me.
|
||||
NOTREACHED();
|
||||
return gfx::Size();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
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) {
|
||||
sources += [
|
||||
"font_list_mac.mm",
|
||||
@ -367,7 +375,7 @@ source_set("common") {
|
||||
]
|
||||
}
|
||||
|
||||
if (is_mac || is_win || is_android || is_fuchsia) {
|
||||
if (is_mac || is_win || is_android || is_fuchsia || is_ios) {
|
||||
sources -= [ "font_list_fontconfig.cc" ]
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user