blob: 2d7ae9bbb5ba629310d365b84d218d28f7c6e37b [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/widget/widget_base.h"
#include "base/command_line.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/ukm_manager.h"
#include "components/viz/common/features.h"
#include "components/viz/common/switches.h"
#include "gpu/command_buffer/client/shared_memory_limits.h"
#include "gpu/command_buffer/common/context_creation_attribs.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
#include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/input/web_input_event_attribution.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/common/widget/screen_info.h"
#include "third_party/blink/public/mojom/input/pointer_lock_context.mojom-blink.h"
#include "third_party/blink/public/mojom/page/record_content_to_visible_time_request.mojom-blink.h"
#include "third_party/blink/public/mojom/widget/visual_properties.mojom-blink.h"
#include "third_party/blink/public/platform/cross_variant_mojo_util.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/public/platform/scheduler/web_widget_scheduler.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.h"
#include "third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h"
#include "third_party/blink/renderer/platform/widget/compositing/render_frame_metadata_observer_impl.h"
#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h"
#include "third_party/blink/renderer/platform/widget/frame_widget.h"
#include "third_party/blink/renderer/platform/widget/input/ime_event_guard.h"
#include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h"
#include "third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h"
#include "third_party/blink/renderer/platform/widget/widget_base_client.h"
#include "ui/base/ime/mojom/text_input_state.mojom-blink.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/presentation_feedback.h"
#if defined(OS_ANDROID)
#include "third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h"
#endif
namespace blink {
namespace {
#if defined(OS_ANDROID)
// Unique identifier for each output surface created.
uint32_t g_next_layer_tree_frame_sink_id = 1;
#endif
// Used for renderer compositor thread context, WebGL (when high priority is
// not requested by workaround), canvas, etc.
const gpu::SchedulingPriority kGpuStreamPriorityDefault =
gpu::SchedulingPriority::kNormal;
const uint32_t kGpuStreamIdDefault = 0;
static const int kInvalidNextPreviousFlagsValue = -1;
static const char kOOPIF[] = "OOPIF";
static const char kRenderer[] = "Renderer";
void OnDidPresentForceDrawFrame(
mojom::blink::Widget::ForceRedrawCallback callback,
const gfx::PresentationFeedback& feedback) {
std::move(callback).Run();
}
bool IsDateTimeInput(ui::TextInputType type) {
return type == ui::TEXT_INPUT_TYPE_DATE ||
type == ui::TEXT_INPUT_TYPE_DATE_TIME ||
type == ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL ||
type == ui::TEXT_INPUT_TYPE_MONTH ||
type == ui::TEXT_INPUT_TYPE_TIME || type == ui::TEXT_INPUT_TYPE_WEEK;
}
ui::TextInputType ConvertWebTextInputType(blink::WebTextInputType type) {
// Check the type is in the range representable by ui::TextInputType.
DCHECK_LE(type, static_cast<int>(ui::TEXT_INPUT_TYPE_MAX))
<< "blink::WebTextInputType and ui::TextInputType not synchronized";
return static_cast<ui::TextInputType>(type);
}
ui::TextInputMode ConvertWebTextInputMode(blink::WebTextInputMode mode) {
// Check the mode is in the range representable by ui::TextInputMode.
DCHECK_LE(mode, static_cast<int>(ui::TEXT_INPUT_MODE_MAX))
<< "blink::WebTextInputMode and ui::TextInputMode not synchronized";
return static_cast<ui::TextInputMode>(mode);
}
unsigned OrientationTypeToAngle(mojom::blink::ScreenOrientation type) {
unsigned angle;
// FIXME(ostap): This relationship between orientationType and
// orientationAngle is temporary. The test should be able to specify
// the angle in addition to the orientation type.
switch (type) {
case mojom::blink::ScreenOrientation::kLandscapePrimary:
angle = 90;
break;
case mojom::blink::ScreenOrientation::kLandscapeSecondary:
angle = 270;
break;
case mojom::blink::ScreenOrientation::kPortraitSecondary:
angle = 180;
break;
default:
angle = 0;
}
return angle;
}
std::unique_ptr<viz::SyntheticBeginFrameSource>
CreateSyntheticBeginFrameSource() {
base::SingleThreadTaskRunner* compositor_impl_side_task_runner =
Platform::Current()->CompositorThreadTaskRunner()
? Platform::Current()->CompositorThreadTaskRunner().get()
: base::ThreadTaskRunnerHandle::Get().get();
return std::make_unique<viz::BackToBackBeginFrameSource>(
std::make_unique<viz::DelayBasedTimeSource>(
compositor_impl_side_task_runner));
}
} // namespace
WidgetBase::WidgetBase(
WidgetBaseClient* client,
CrossVariantMojoAssociatedRemote<mojom::WidgetHostInterfaceBase>
widget_host,
CrossVariantMojoAssociatedReceiver<mojom::WidgetInterfaceBase> widget,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
bool hidden,
bool never_composited,
bool is_for_child_local_root)
: never_composited_(never_composited),
is_for_child_local_root_(is_for_child_local_root),
use_zoom_for_dsf_(Platform::Current()->IsUseZoomForDSFEnabled()),
client_(client),
widget_host_(std::move(widget_host), task_runner),
receiver_(this, std::move(widget), task_runner),
next_previous_flags_(kInvalidNextPreviousFlagsValue),
is_hidden_(hidden),
request_animation_after_delay_timer_(
std::move(task_runner),
this,
&WidgetBase::RequestAnimationAfterDelayTimerFired) {
if (auto* main_thread_scheduler =
scheduler::WebThreadScheduler::MainThreadScheduler()) {
render_widget_scheduling_state_ =
main_thread_scheduler->NewRenderWidgetSchedulingState();
render_widget_scheduling_state_->SetHidden(is_hidden_);
}
}
WidgetBase::~WidgetBase() {
// Ensure Shutdown was called.
DCHECK(!layer_tree_view_);
}
void WidgetBase::InitializeCompositing(
scheduler::WebAgentGroupScheduler& agent_group_scheduler,
cc::TaskGraphRunner* task_graph_runner,
bool for_child_local_root_frame,
const ScreenInfo& screen_info,
std::unique_ptr<cc::UkmRecorderFactory> ukm_recorder_factory,
const cc::LayerTreeSettings* settings,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler>
frame_widget_input_handler) {
scheduler::WebThreadScheduler* main_thread_scheduler =
&agent_group_scheduler.GetMainThreadScheduler();
main_thread_compositor_task_runner_ =
agent_group_scheduler.CompositorTaskRunner();
auto* compositing_thread_scheduler =
scheduler::WebThreadScheduler::CompositorThreadScheduler();
layer_tree_view_ =
std::make_unique<LayerTreeView>(this, main_thread_scheduler);
base::Optional<cc::LayerTreeSettings> default_settings;
if (!settings) {
default_settings = GenerateLayerTreeSettings(
compositing_thread_scheduler, for_child_local_root_frame,
screen_info.rect.size(), screen_info.device_scale_factor);
settings = &default_settings.value();
}
screen_info_ = screen_info;
layer_tree_view_->Initialize(
*settings, main_thread_compositor_task_runner_,
compositing_thread_scheduler
? compositing_thread_scheduler->DefaultTaskRunner()
: nullptr,
task_graph_runner, std::move(ukm_recorder_factory));
FrameWidget* frame_widget = client_->FrameWidget();
scheduler::WebThreadScheduler* compositor_thread_scheduler =
scheduler::WebThreadScheduler::CompositorThreadScheduler();
scoped_refptr<base::SingleThreadTaskRunner> compositor_input_task_runner;
// Use the compositor thread task runner unless this is a popup or other such
// non-frame widgets. The |compositor_thread_scheduler| can be null in tests
// without a compositor thread.
if (frame_widget && compositor_thread_scheduler) {
compositor_input_task_runner =
compositor_thread_scheduler->DefaultTaskRunner();
}
// We only use an external input handler for frame widgets because only
// frames use the compositor for input handling. Other kinds of widgets
// (e.g. popups, plugins) must forward their input directly through
// WidgetBaseInputHandler.
bool uses_input_handler = frame_widget;
widget_input_handler_manager_ = WidgetInputHandlerManager::Create(
weak_ptr_factory_.GetWeakPtr(), std::move(frame_widget_input_handler),
never_composited_, std::move(compositor_input_task_runner),
main_thread_scheduler, uses_input_handler);
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kAllowPreCommitInput))
widget_input_handler_manager_->AllowPreCommitInput();
UpdateScreenInfo(screen_info);
// If the widget is hidden, delay starting the compositor until the user
// shows it. Otherwise start the compositor immediately. If the widget is
// for a provisional frame, this importantly starts the compositor before
// the frame is inserted into the frame tree, which impacts first paint
// metrics.
if (!is_hidden_)
SetCompositorVisible(true);
}
void WidgetBase::Shutdown() {
scoped_refptr<base::SingleThreadTaskRunner> cleanup_runner =
base::ThreadTaskRunnerHandle::Get();
// The |input_event_queue_| is refcounted and will live while an event is
// being handled. This drops the connection back to this WidgetBase which
// is being destroyed.
if (widget_input_handler_manager_)
widget_input_handler_manager_->ClearClient();
// The LayerTreeHost may already be in the call stack, if this WidgetBase
// is being destroyed during an animation callback for instance. We can not
// delete it here and unwind the stack back up to it, or it will crash. So
// we post the deletion to another task, but disconnect the LayerTreeHost
// (via the LayerTreeView) from the destroying WidgetBase. The
// LayerTreeView owns the LayerTreeHost, and is its client, so they are kept
// alive together for a clean call stack.
if (layer_tree_view_) {
layer_tree_view_->Disconnect();
cleanup_runner->DeleteSoon(FROM_HERE, std::move(layer_tree_view_));
}
// The |widget_input_handler_manager_| needs to outlive the LayerTreeHost,
// which is destroyed asynchronously by DeleteSoon(). This needs to be a
// NonNestableTask as it needs to occur after DeleteSoon.
cleanup_runner->PostNonNestableTask(
FROM_HERE,
base::BindOnce([](scoped_refptr<WidgetInputHandlerManager> manager) {},
std::move(widget_input_handler_manager_)));
if (widget_compositor_) {
widget_compositor_->Shutdown();
widget_compositor_ = nullptr;
}
}
cc::LayerTreeHost* WidgetBase::LayerTreeHost() const {
return layer_tree_view_->layer_tree_host();
}
bool WidgetBase::IsComposited() const {
return !!layer_tree_view_;
}
cc::AnimationHost* WidgetBase::AnimationHost() const {
return layer_tree_view_->animation_host();
}
scheduler::WebRenderWidgetSchedulingState*
WidgetBase::RendererWidgetSchedulingState() const {
return render_widget_scheduling_state_.get();
}
void WidgetBase::ForceRedraw(
mojom::blink::Widget::ForceRedrawCallback callback) {
LayerTreeHost()->RequestPresentationTimeForNextFrame(
base::BindOnce(&OnDidPresentForceDrawFrame, std::move(callback)));
LayerTreeHost()->SetNeedsCommitWithForcedRedraw();
// ScheduleAnimationForWebTests() which is implemented by
// WebTestWebFrameWidgetImpl, providing the additional control over the
// lifecycle of compositing required by web tests. This will be a no-op on
// production.
client_->ScheduleAnimationForWebTests();
}
void WidgetBase::GetWidgetInputHandler(
mojo::PendingReceiver<mojom::blink::WidgetInputHandler> request,
mojo::PendingRemote<mojom::blink::WidgetInputHandlerHost> host) {
widget_input_handler_manager_->AddInterface(std::move(request),
std::move(host));
}
void WidgetBase::UpdateVisualProperties(
const VisualProperties& visual_properties_from_browser) {
TRACE_EVENT0("renderer", "WidgetBase::UpdateVisualProperties");
// UpdateVisualProperties is used to receive properties from the browser
// process for this WidgetBase. There are roughly 4 types of
// VisualProperties.
// TODO(danakj): Splitting these 4 types of properties apart and making them
// more explicit could be super useful to understanding this code.
// 1. Unique to each WidgetBase. Computed by the RenderWidgetHost and passed
// to the WidgetBase which consumes it here.
// Example: new_size.
// 2. Global properties, which are given to each WidgetBase (to maintain
// the requirement that a WidgetBase is updated atomically). These
// properties are usually the same for every WidgetBase, except when
// device emulation changes them in the main frame WidgetBase only.
// Example: screen_info.
// 3. Computed in the renderer of the main frame WebFrameWidgetImpl (in blink
// usually). Passed down through the waterfall dance to child frame
// WebFrameWidgetImpl. Here that step is performed by passing the value
// along to all RemoteFrame objects that are below this WebFrameWidgetImpl
// in the frame tree. The main frame (top level) WebFrameWidgetImpl ignores
// this value from its RenderWidgetHost since it is controlled in the
// renderer. Child frame WebFrameWidgetImpls consume the value from their
// RenderWidgetHost. Example: page_scale_factor.
// 4. Computed independently in the renderer for each WidgetBase (in blink
// usually). Passed down from the parent to the child WidgetBases through
// the waterfall dance, but the value only travels one step - the child
// frame WebFrameWidgetImpl would compute values for grandchild
// WebFrameWidgetImpls independently. Here the value is passed to child
// frame RenderWidgets by passing the value along to all RemoteFrame
// objects that are below this WebFrameWidgetImpl in the frame tree. Each
// WidgetBase consumes this value when it is received from its
// RenderWidgetHost. Example: compositor_viewport_pixel_rect.
// For each of these properties:
// If the WebView also knows these properties, each WebFrameWidgetImpl
// will pass them along to the WebView as it receives it, even if there
// are multiple WebFrameWidgetImpls related to the same WebView.
// However when the main frame in the renderer is the source of truth,
// then child widgets must not clobber that value! In all cases child frames
// do not need to update state in the WebView when a local main frame is
// present as it always sets the value first.
// TODO(danakj): This does create a race if there are multiple
// UpdateVisualProperties updates flowing through the WebFrameWidgetImpl
// tree at the same time, and it seems that only one WebFrameWidgetImpl for
// each WebView should be responsible for this update.
//
// TODO(danakj): A more explicit API to give values from here to RenderView
// and/or WebView would be nice. Also a more explicit API to give values to
// the RemoteFrame in one go, instead of setting each property
// independently, causing an update IPC from the
// RenderFrameProxy/RemoteFrame for each one.
//
// See also:
// https://docs.google.com/document/d/1G_fR1D_0c1yke8CqDMddoKrDGr3gy5t_ImEH4hKNIII/edit#
blink::VisualProperties visual_properties = visual_properties_from_browser;
// Web tests can override the device scale factor in the renderer.
if (auto scale_factor = client_->GetTestingDeviceScaleFactorOverride()) {
visual_properties.screen_info.device_scale_factor = scale_factor;
visual_properties.compositor_viewport_pixel_rect =
gfx::Rect(gfx::ScaleToCeiledSize(
visual_properties.new_size,
visual_properties.screen_info.device_scale_factor));
}
// Inform the rendering thread of the color space indicating the presence of
// HDR capabilities. The HDR bit happens to be globally true/false for all
// browser windows (on Windows OS) and thus would be the same for all
// RenderWidgets, so clobbering each other works out since only the HDR bit is
// used. See https://crbug.com/803451 and
// https://chromium-review.googlesource.com/c/chromium/src/+/852912/15#message-68bbd3e25c3b421a79cd028b2533629527d21fee
Platform::Current()->SetRenderingColorSpace(
visual_properties.screen_info.display_color_spaces
.GetScreenInfoColorSpace());
LayerTreeHost()->SetBrowserControlsParams(
visual_properties.browser_controls_params);
LayerTreeHost()->SetVisualDeviceViewportSize(gfx::ScaleToCeiledSize(
visual_properties.visible_viewport_size,
visual_properties.screen_info.device_scale_factor));
client_->UpdateVisualProperties(visual_properties);
}
void WidgetBase::UpdateScreenRects(const gfx::Rect& widget_screen_rect,
const gfx::Rect& window_screen_rect,
UpdateScreenRectsCallback callback) {
if (!client_->UpdateScreenRects(widget_screen_rect, window_screen_rect)) {
widget_screen_rect_ = widget_screen_rect;
window_screen_rect_ = window_screen_rect;
}
std::move(callback).Run();
}
void WidgetBase::WasHidden() {
// A provisional frame widget will never be hidden since that would require it
// to be shown first. A frame must be attached to the frame tree before
// changing visibility.
DCHECK(!IsForProvisionalFrame());
TRACE_EVENT0("renderer", "WidgetBase::WasHidden");
SetHidden(true);
tab_switch_time_recorder_.TabWasHidden();
client_->WasHidden();
}
void WidgetBase::WasShown(base::TimeTicks show_request_timestamp,
bool was_evicted,
mojom::blink::RecordContentToVisibleTimeRequestPtr
record_tab_switch_time_request) {
// The frame must be attached to the frame tree (which makes it no longer
// provisional) before changing visibility.
DCHECK(!IsForProvisionalFrame());
TRACE_EVENT_WITH_FLOW0("renderer", "WidgetBase::WasShown", this,
TRACE_EVENT_FLAG_FLOW_IN);
SetHidden(false);
if (record_tab_switch_time_request) {
LayerTreeHost()->RequestPresentationTimeForNextFrame(
tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
record_tab_switch_time_request->event_start_time,
record_tab_switch_time_request->destination_is_loaded,
record_tab_switch_time_request->show_reason_tab_switching,
record_tab_switch_time_request->show_reason_unoccluded,
record_tab_switch_time_request->show_reason_bfcache_restore,
show_request_timestamp));
}
client_->WasShown(was_evicted);
}
void WidgetBase::ApplyViewportChanges(
const cc::ApplyViewportChangesArgs& args) {
client_->ApplyViewportChanges(args);
}
void WidgetBase::UpdateCompositorScrollState(
const cc::CompositorCommitData& commit_data) {
client_->UpdateCompositorScrollState(commit_data);
}
void WidgetBase::OnDeferMainFrameUpdatesChanged(bool defer) {
// LayerTreeHost::CreateThreaded() will defer main frame updates immediately
// until it gets a LocalSurfaceId. That's before the
// |widget_input_handler_manager_| is created, so it can be null here.
// TODO(schenney): To avoid ping-ponging between defer main frame states
// during initialization, and requiring null checks here, we should probably
// pass the LocalSurfaceId to the compositor while it is
// initialized so that it doesn't have to immediately switch into deferred
// mode without being requested to.
if (!widget_input_handler_manager_)
return;
// The input handler wants to know about the mainframe update status to
// enable/disable input and for metrics.
widget_input_handler_manager_->OnDeferMainFrameUpdatesChanged(defer);
}
void WidgetBase::OnDeferCommitsChanged(bool defer) {
// The input handler wants to know about the commit status for metric purposes
// and to enable/disable input.
widget_input_handler_manager_->OnDeferCommitsChanged(defer);
}
void WidgetBase::DidBeginMainFrame() {
client_->DidBeginMainFrame();
}
void WidgetBase::RequestNewLayerTreeFrameSink(
LayerTreeFrameSinkCallback callback) {
// For widgets that are never visible, we don't start the compositor, so we
// never get a request for a cc::LayerTreeFrameSink.
DCHECK(!never_composited_);
// Provide a hook for testing to provide their own layer tree frame sink, if
// one is returned just run the callback.
if (std::unique_ptr<cc::LayerTreeFrameSink> sink =
client_->AllocateNewLayerTreeFrameSink()) {
std::move(callback).Run(std::move(sink), nullptr);
return;
}
KURL url = client_->GetURLForDebugTrace();
// The |url| is not always available, fallback to a fixed string.
if (url.IsEmpty())
url = KURL("chrome://gpu/WidgetBase::RequestNewLayerTreeFrameSink");
// TODO(danakj): This may not be accurate, depending on the intent. A child
// local root could be in the same process as the view, so if the client is
// meant to designate the process type, it seems kRenderer would be the
// correct choice. If client is meant to designate the widget type, then
// kOOPIF would denote that it is not for the main frame. However, kRenderer
// would also be used for other widgets such as popups.
const char* client_name = is_for_child_local_root_ ? kOOPIF : kRenderer;
const bool for_web_tests = WebTestMode();
// Misconfigured bots (eg. crbug.com/780757) could run web tests on a
// machine where gpu compositing doesn't work. Don't crash in that case.
if (for_web_tests && Platform::Current()->IsGpuCompositingDisabled()) {
LOG(FATAL) << "Web tests require gpu compositing, but it is disabled.";
return;
}
// TODO(jonross): Have this generated by the LayerTreeFrameSink itself, which
// would then handle binding.
mojo::PendingRemote<cc::mojom::blink::RenderFrameMetadataObserver>
render_frame_metadata_observer_remote;
mojo::PendingRemote<cc::mojom::blink::RenderFrameMetadataObserverClient>
render_frame_metadata_client_remote;
mojo::PendingReceiver<cc::mojom::blink::RenderFrameMetadataObserverClient>
render_frame_metadata_observer_client_receiver =
render_frame_metadata_client_remote.InitWithNewPipeAndPassReceiver();
auto render_frame_metadata_observer =
std::make_unique<RenderFrameMetadataObserverImpl>(
render_frame_metadata_observer_remote
.InitWithNewPipeAndPassReceiver(),
std::move(render_frame_metadata_client_remote));
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams params;
params.compositor_task_runner =
Platform::Current()->CompositorThreadTaskRunner();
if (for_web_tests && !params.compositor_task_runner) {
// The frame sink provider expects a compositor task runner, but we might
// not have that if we're running web tests in single threaded mode.
// Set it to be our thread's task runner instead.
params.compositor_task_runner = main_thread_compositor_task_runner_;
}
// The renderer runs animations and layout for animate_only BeginFrames.
params.wants_animate_only_begin_frames = true;
// In disable frame rate limit mode, also let the renderer tick as fast as it
// can. The top level begin frame source will also be running as a back to
// back begin frame source, but using a synthetic begin frame source here
// reduces latency when in this mode (at least for frames starting--it
// potentially increases it for input on the other hand.)
if (command_line.HasSwitch(::switches::kDisableFrameRateLimit))
params.synthetic_begin_frame_source = CreateSyntheticBeginFrameSource();
params.client_name = client_name;
mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSink>
compositor_frame_sink_receiver = CrossVariantMojoReceiver<
viz::mojom::blink::CompositorFrameSinkInterfaceBase>(
params.pipes.compositor_frame_sink_remote
.InitWithNewPipeAndPassReceiver());
mojo::PendingRemote<viz::mojom::blink::CompositorFrameSinkClient>
compositor_frame_sink_client;
params.pipes.client_receiver = CrossVariantMojoReceiver<
viz::mojom::blink::CompositorFrameSinkClientInterfaceBase>(
compositor_frame_sink_client.InitWithNewPipeAndPassReceiver());
if (Platform::Current()->IsGpuCompositingDisabled()) {
DCHECK(!for_web_tests);
widget_host_->CreateFrameSink(std::move(compositor_frame_sink_receiver),
std::move(compositor_frame_sink_client));
widget_host_->RegisterRenderFrameMetadataObserver(
std::move(render_frame_metadata_observer_client_receiver),
std::move(render_frame_metadata_observer_remote));
std::move(callback).Run(
std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
nullptr, nullptr, &params),
std::move(render_frame_metadata_observer));
return;
}
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
Platform::Current()->EstablishGpuChannelSync();
if (!gpu_channel_host) {
// Wait and try again. We may hear that the compositing mode has switched
// to software in the meantime.
std::move(callback).Run(nullptr, nullptr);
return;
}
scoped_refptr<viz::RasterContextProvider> worker_context_provider =
Platform::Current()->SharedCompositorWorkerContextProvider();
if (!worker_context_provider) {
// Cause the compositor to wait and try again.
std::move(callback).Run(nullptr, nullptr);
return;
}
// The renderer compositor context doesn't do a lot of stuff, so we don't
// expect it to need a lot of space for commands or transfer. Raster and
// uploads happen on the worker context instead.
gpu::SharedMemoryLimits limits = gpu::SharedMemoryLimits::ForMailboxContext();
// This is for an offscreen context for the compositor. So the default
// framebuffer doesn't need alpha, depth, stencil, antialiasing.
gpu::ContextCreationAttribs attributes;
attributes.alpha_size = -1;
attributes.depth_size = 0;
attributes.stencil_size = 0;
attributes.samples = 0;
attributes.sample_buffers = 0;
attributes.bind_generates_resource = false;
attributes.lose_context_when_out_of_memory = true;
attributes.enable_gles2_interface = true;
attributes.enable_raster_interface = false;
attributes.enable_oop_rasterization = false;
constexpr bool automatic_flushes = false;
constexpr bool support_locking = false;
constexpr bool support_grcontext = true;
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
Platform::Current()->GetGpuMemoryBufferManager();
scoped_refptr<viz::ContextProviderCommandBuffer> context_provider(
new viz::ContextProviderCommandBuffer(
gpu_channel_host, gpu_memory_buffer_manager, kGpuStreamIdDefault,
kGpuStreamPriorityDefault, gpu::kNullSurfaceHandle, url,
automatic_flushes, support_locking, support_grcontext, limits,
attributes,
viz::command_buffer_metrics::ContextType::RENDER_COMPOSITOR));
#if defined(OS_ANDROID)
if (Platform::Current()->IsSynchronousCompositingEnabledForAndroidWebView()) {
// TODO(ericrk): Collapse with non-webview registration below.
if (::features::IsUsingVizFrameSubmissionForWebView()) {
widget_host_->CreateFrameSink(std::move(compositor_frame_sink_receiver),
std::move(compositor_frame_sink_client));
}
widget_host_->RegisterRenderFrameMetadataObserver(
std::move(render_frame_metadata_observer_client_receiver),
std::move(render_frame_metadata_observer_remote));
std::move(callback).Run(
std::make_unique<SynchronousLayerTreeFrameSink>(
std::move(context_provider), std::move(worker_context_provider),
Platform::Current()->CompositorThreadTaskRunner(),
gpu_memory_buffer_manager, g_next_layer_tree_frame_sink_id++,
std::move(params.synthetic_begin_frame_source),
widget_input_handler_manager_->GetSynchronousCompositorRegistry(),
CrossVariantMojoRemote<
viz::mojom::blink::CompositorFrameSinkInterfaceBase>(
std::move(params.pipes.compositor_frame_sink_remote)),
CrossVariantMojoReceiver<
viz::mojom::blink::CompositorFrameSinkClientInterfaceBase>(
std::move(params.pipes.client_receiver))),
std::move(render_frame_metadata_observer));
return;
}
#endif
widget_host_->CreateFrameSink(std::move(compositor_frame_sink_receiver),
std::move(compositor_frame_sink_client));
widget_host_->RegisterRenderFrameMetadataObserver(
std::move(render_frame_metadata_observer_client_receiver),
std::move(render_frame_metadata_observer_remote));
params.gpu_memory_buffer_manager = gpu_memory_buffer_manager;
std::move(callback).Run(
std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
std::move(context_provider), std::move(worker_context_provider),
&params),
std::move(render_frame_metadata_observer));
}
void WidgetBase::DidCommitAndDrawCompositorFrame() {
// NOTE: Tests may break if this event is renamed or moved. See
// tab_capture_performancetest.cc.
TRACE_EVENT0("gpu", "WidgetBase::DidCommitAndDrawCompositorFrame");
client_->DidCommitAndDrawCompositorFrame();
}
void WidgetBase::DidObserveFirstScrollDelay(
base::TimeDelta first_scroll_delay,
base::TimeTicks first_scroll_timestamp) {
client_->DidObserveFirstScrollDelay(first_scroll_delay,
first_scroll_timestamp);
}
void WidgetBase::WillCommitCompositorFrame() {
client_->BeginCommitCompositorFrame();
}
void WidgetBase::DidCommitCompositorFrame(base::TimeTicks commit_start_time) {
client_->EndCommitCompositorFrame(commit_start_time);
}
void WidgetBase::DidCompletePageScaleAnimation() {
client_->DidCompletePageScaleAnimation();
}
void WidgetBase::RecordStartOfFrameMetrics() {
client_->RecordStartOfFrameMetrics();
}
void WidgetBase::RecordEndOfFrameMetrics(
base::TimeTicks frame_begin_time,
cc::ActiveFrameSequenceTrackers trackers) {
client_->RecordEndOfFrameMetrics(frame_begin_time, trackers);
}
std::unique_ptr<cc::BeginMainFrameMetrics>
WidgetBase::GetBeginMainFrameMetrics() {
return client_->GetBeginMainFrameMetrics();
}
std::unique_ptr<cc::WebVitalMetrics> WidgetBase::GetWebVitalMetrics() {
return client_->GetWebVitalMetrics();
}
void WidgetBase::BeginUpdateLayers() {
client_->BeginUpdateLayers();
}
void WidgetBase::EndUpdateLayers() {
client_->EndUpdateLayers();
}
void WidgetBase::WillBeginMainFrame() {
TRACE_EVENT0("gpu", "WidgetBase::WillBeginMainFrame");
client_->SetSuppressFrameRequestsWorkaroundFor704763Only(true);
client_->WillBeginMainFrame();
UpdateSelectionBounds();
// The UpdateTextInputState can result in further layout and possibly
// enable GPU acceleration so they need to be called before any painting
// is done.
UpdateTextInputState();
}
void WidgetBase::RunPaintBenchmark(int repeat_count,
cc::PaintBenchmarkResult& result) {
client_->RunPaintBenchmark(repeat_count, result);
}
void WidgetBase::SetCompositorVisible(bool visible) {
if (never_composited_)
return;
layer_tree_view_->SetVisible(visible);
}
void WidgetBase::UpdateVisualState() {
// When recording main frame metrics set the lifecycle reason to
// kBeginMainFrame, because this is the calller of UpdateLifecycle
// for the main frame. Otherwise, set the reason to kTests, which is
// the only other reason this method is called.
DocumentUpdateReason lifecycle_reason =
ShouldRecordBeginMainFrameMetrics()
? DocumentUpdateReason::kBeginMainFrame
: DocumentUpdateReason::kTest;
client_->UpdateLifecycle(WebLifecycleUpdate::kAll, lifecycle_reason);
client_->SetSuppressFrameRequestsWorkaroundFor704763Only(false);
}
void WidgetBase::BeginMainFrame(base::TimeTicks frame_time) {
base::TimeTicks raf_aligned_input_start_time;
if (ShouldRecordBeginMainFrameMetrics()) {
raf_aligned_input_start_time = base::TimeTicks::Now();
}
widget_input_handler_manager_->input_event_queue()->DispatchRafAlignedInput(
frame_time);
if (ShouldRecordBeginMainFrameMetrics()) {
client_->RecordDispatchRafAlignedInputTime(raf_aligned_input_start_time);
}
client_->BeginMainFrame(frame_time);
}
bool WidgetBase::ShouldRecordBeginMainFrameMetrics() {
// We record metrics only when running in multi-threaded mode, not
// single-thread mode for testing.
return Thread::CompositorThread();
}
void WidgetBase::AddPresentationCallback(
uint32_t frame_token,
base::OnceCallback<void(base::TimeTicks)> callback) {
layer_tree_view_->AddPresentationCallback(frame_token, std::move(callback));
}
void WidgetBase::SetCursor(const ui::Cursor& cursor) {
if (input_handler_.DidChangeCursor(cursor)) {
widget_host_->SetCursor(cursor);
}
}
void WidgetBase::SetToolTipText(const String& tooltip_text, TextDirection dir) {
widget_host_->SetToolTipText(tooltip_text.IsEmpty() ? "" : tooltip_text,
ToBaseTextDirection(dir));
}
void WidgetBase::ShowVirtualKeyboard() {
UpdateTextInputStateInternal(true, false);
}
void WidgetBase::UpdateTextInputState() {
UpdateTextInputStateInternal(false, false);
}
bool WidgetBase::CanComposeInline() {
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return true;
return frame_widget->CanComposeInline();
}
void WidgetBase::UpdateTextInputStateInternal(bool show_virtual_keyboard,
bool reply_to_request) {
TRACE_EVENT0("renderer", "WidgetBase::UpdateTextInputStateInternal");
if (ime_event_guard_) {
DCHECK(!reply_to_request);
if (show_virtual_keyboard)
ime_event_guard_->set_show_virtual_keyboard(true);
return;
}
ui::TextInputType new_type = GetTextInputType();
if (IsDateTimeInput(new_type))
return; // Not considered as a text input field in WebKit/Chromium.
FrameWidget* frame_widget = client_->FrameWidget();
blink::WebTextInputInfo new_info;
ui::mojom::VirtualKeyboardVisibilityRequest last_vk_visibility_request =
ui::mojom::VirtualKeyboardVisibilityRequest::NONE;
bool always_hide_ime = false;
base::Optional<gfx::Rect> control_bounds;
base::Optional<gfx::Rect> selection_bounds;
if (frame_widget) {
new_info = frame_widget->TextInputInfo();
// This will be used to decide whether or not to show VK when VK policy is
// manual.
last_vk_visibility_request =
frame_widget->GetLastVirtualKeyboardVisibilityRequest();
// Check whether the keyboard should always be hidden for the currently
// focused element.
always_hide_ime = frame_widget->ShouldSuppressKeyboardForFocusedElement();
frame_widget->GetEditContextBoundsInWindow(&control_bounds,
&selection_bounds);
}
const ui::TextInputMode new_mode =
ConvertWebTextInputMode(new_info.input_mode);
const ui::mojom::VirtualKeyboardPolicy new_vk_policy =
new_info.virtual_keyboard_policy;
bool new_can_compose_inline = CanComposeInline();
// Only sends text input params if they are changed or if the ime should be
// shown.
if (show_virtual_keyboard || reply_to_request ||
text_input_type_ != new_type || text_input_mode_ != new_mode ||
text_input_info_ != new_info || !new_info.ime_text_spans.empty() ||
can_compose_inline_ != new_can_compose_inline ||
always_hide_ime_ != always_hide_ime || vk_policy_ != new_vk_policy ||
(new_vk_policy == ui::mojom::VirtualKeyboardPolicy::MANUAL &&
(last_vk_visibility_request !=
ui::mojom::VirtualKeyboardVisibilityRequest::NONE)) ||
(control_bounds && frame_control_bounds_ != control_bounds) ||
(selection_bounds && frame_selection_bounds_ != selection_bounds)) {
ui::mojom::blink::TextInputStatePtr params =
ui::mojom::blink::TextInputState::New();
params->type = new_type;
params->mode = new_mode;
params->action = new_info.action;
params->flags = new_info.flags;
params->vk_policy = new_vk_policy;
params->last_vk_visibility_request = last_vk_visibility_request;
params->edit_context_control_bounds = control_bounds;
params->edit_context_selection_bounds = selection_bounds;
if (!new_info.ime_text_spans.empty()) {
params->ime_text_spans_info =
frame_widget->GetImeTextSpansInfo(new_info.ime_text_spans);
}
#if defined(OS_ANDROID)
if (next_previous_flags_ == kInvalidNextPreviousFlagsValue) {
// Due to a focus change, values will be reset by the frame.
// That case we only need fresh NEXT/PREVIOUS information.
// Also we won't send WidgetHostMsg_TextInputStateChanged if next/previous
// focusable status is changed.
if (frame_widget) {
next_previous_flags_ =
frame_widget->ComputeWebTextInputNextPreviousFlags();
} else {
// For safety in case GetInputMethodController() is null, because -1 is
// invalid value to send to browser process.
next_previous_flags_ = 0;
}
}
#else
next_previous_flags_ = 0;
#endif
params->flags |= next_previous_flags_;
params->value = new_info.value;
params->selection =
gfx::Range(new_info.selection_start, new_info.selection_end);
if (new_info.composition_start != -1) {
params->composition =
gfx::Range(new_info.composition_start, new_info.composition_end);
}
params->can_compose_inline = new_can_compose_inline;
// TODO(changwan): change instances of show_ime_if_needed to
// show_virtual_keyboard.
params->show_ime_if_needed = show_virtual_keyboard;
params->always_hide_ime = always_hide_ime;
params->reply_to_request = reply_to_request;
widget_host_->TextInputStateChanged(std::move(params));
text_input_info_ = new_info;
text_input_type_ = new_type;
text_input_mode_ = new_mode;
vk_policy_ = new_vk_policy;
can_compose_inline_ = new_can_compose_inline;
always_hide_ime_ = always_hide_ime;
text_input_flags_ = new_info.flags;
frame_control_bounds_ = control_bounds.value_or(gfx::Rect());
// Selection bounds are not populated in non-EditContext scenarios.
// It is communicated to IMEs via |WidgetBase::UpdateSelectionBounds|.
frame_selection_bounds_ = selection_bounds.value_or(gfx::Rect());
// Reset the show/hide state in the InputMethodController.
if (frame_widget) {
if (last_vk_visibility_request !=
ui::mojom::VirtualKeyboardVisibilityRequest::NONE) {
// Reset the visibility state.
frame_widget->ResetVirtualKeyboardVisibilityRequest();
}
}
#if defined(OS_ANDROID)
// If we send a new TextInputStateChanged message, we must also deliver a
// new RenderFrameMetadata, as the IME will need this info to be updated.
// TODO(ericrk): Consider folding the above IPC into RenderFrameMetadata.
// https://crbug.com/912309
// Compositing might not be initialized but input can still be dispatched
// to non-composited widgets so LayerTreeHost may be null.
if (IsComposited())
LayerTreeHost()->RequestForceSendMetadata();
#endif
}
}
void WidgetBase::ClearTextInputState() {
text_input_info_ = blink::WebTextInputInfo();
text_input_type_ = ui::TextInputType::TEXT_INPUT_TYPE_NONE;
text_input_mode_ = ui::TextInputMode::TEXT_INPUT_MODE_DEFAULT;
can_compose_inline_ = false;
text_input_flags_ = 0;
next_previous_flags_ = kInvalidNextPreviousFlagsValue;
}
void WidgetBase::ShowVirtualKeyboardOnElementFocus() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
// On ChromeOS, virtual keyboard is triggered only when users leave the
// mouse button or the finger and a text input element is focused at that
// time. Focus event itself shouldn't trigger virtual keyboard.
UpdateTextInputState();
#else
ShowVirtualKeyboard();
#endif
// TODO(rouslan): Fix ChromeOS and Windows 8 behavior of autofill popup with
// virtual keyboard.
#if !defined(OS_ANDROID)
client_->FocusChangeComplete();
#endif
}
void WidgetBase::ProcessTouchAction(cc::TouchAction touch_action) {
if (!input_handler_.ProcessTouchAction(touch_action))
return;
widget_input_handler_manager_->ProcessTouchAction(touch_action);
}
void WidgetBase::SetFocus(bool enable) {
has_focus_ = enable;
client_->FocusChanged(enable);
}
void WidgetBase::BindWidgetCompositor(
mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
if (widget_compositor_)
widget_compositor_->Shutdown();
widget_compositor_ = base::MakeRefCounted<WidgetCompositor>(
weak_ptr_factory_.GetWeakPtr(),
LayerTreeHost()->GetTaskRunnerProvider()->MainThreadTaskRunner(),
LayerTreeHost()->GetTaskRunnerProvider()->ImplThreadTaskRunner(),
std::move(receiver));
}
void WidgetBase::UpdateCompositionInfo(bool immediate_request) {
if (!monitor_composition_info_ && !immediate_request)
return; // Do not calculate composition info if not requested.
TRACE_EVENT0("renderer", "WidgetBase::UpdateCompositionInfo");
gfx::Range range;
Vector<gfx::Rect> character_bounds;
if (GetTextInputType() == ui::TextInputType::TEXT_INPUT_TYPE_NONE) {
// Composition information is only available on editable node.
range = gfx::Range::InvalidRange();
} else {
GetCompositionRange(&range);
GetCompositionCharacterBounds(&character_bounds);
}
if (!immediate_request &&
!ShouldUpdateCompositionInfo(range, character_bounds)) {
return;
}
composition_character_bounds_ = character_bounds;
composition_range_ = range;
if (mojom::blink::WidgetInputHandlerHost* host =
widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
host->ImeCompositionRangeChanged(composition_range_,
composition_character_bounds_);
}
}
void WidgetBase::ForceTextInputStateUpdate() {
#if defined(OS_ANDROID)
UpdateSelectionBounds();
UpdateTextInputStateInternal(false, true /* reply_to_request */);
#endif
}
void WidgetBase::RequestCompositionUpdates(bool immediate_request,
bool monitor_updates) {
monitor_composition_info_ = monitor_updates;
if (!immediate_request)
return;
UpdateCompositionInfo(true /* immediate request */);
}
void WidgetBase::GetCompositionRange(gfx::Range* range) {
*range = gfx::Range::InvalidRange();
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
*range = frame_widget->CompositionRange();
}
void WidgetBase::GetCompositionCharacterBounds(Vector<gfx::Rect>* bounds) {
DCHECK(bounds);
bounds->clear();
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
frame_widget->GetCompositionCharacterBoundsInWindow(bounds);
}
bool WidgetBase::ShouldUpdateCompositionInfo(const gfx::Range& range,
const Vector<gfx::Rect>& bounds) {
if (!range.IsValid())
return false;
if (composition_range_ != range)
return true;
if (bounds.size() != composition_character_bounds_.size())
return true;
for (size_t i = 0; i < bounds.size(); ++i) {
if (bounds[i] != composition_character_bounds_[i])
return true;
}
return false;
}
void WidgetBase::SetHidden(bool hidden) {
// A provisional frame widget will never be shown or hidden, as the frame must
// be attached to the frame tree before changing visibility.
DCHECK(!IsForProvisionalFrame());
if (is_hidden_ == hidden)
return;
// The status has changed. Tell the RenderThread about it and ensure
// throttled acks are released in case frame production ceases.
is_hidden_ = hidden;
if (auto* scheduler_state = RendererWidgetSchedulingState())
scheduler_state->SetHidden(hidden);
// If the renderer was hidden, resolve any pending synthetic gestures so they
// aren't blocked waiting for a compositor frame to be generated.
if (is_hidden_)
FlushInputProcessedCallback();
SetCompositorVisible(!is_hidden_);
}
ui::TextInputType WidgetBase::GetTextInputType() {
return ConvertWebTextInputType(client_->GetTextInputType());
}
void WidgetBase::UpdateSelectionBounds() {
TRACE_EVENT0("renderer", "WidgetBase::UpdateSelectionBounds");
if (ime_event_guard_)
return;
#if defined(USE_AURA)
// TODO(mohsen): For now, always send explicit selection IPC notifications for
// Aura beucause composited selection updates are not working for webview tags
// which regresses IME inside webview. Remove this when composited selection
// updates are fixed for webviews. See, http://crbug.com/510568.
bool send_ipc = true;
#else
// With composited selection updates, the selection bounds will be reported
// directly by the compositor, in which case explicit IPC selection
// notifications should be suppressed.
bool send_ipc = !RuntimeEnabledFeatures::CompositedSelectionUpdateEnabled();
#endif
if (send_ipc) {
bool is_anchor_first = false;
base::i18n::TextDirection focus_dir =
base::i18n::TextDirection::UNKNOWN_DIRECTION;
base::i18n::TextDirection anchor_dir =
base::i18n::TextDirection::UNKNOWN_DIRECTION;
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
if (frame_widget->GetSelectionBoundsInWindow(
&selection_focus_rect_, &selection_anchor_rect_, &focus_dir,
&anchor_dir, &is_anchor_first)) {
widget_host_->SelectionBoundsChanged(selection_anchor_rect_, anchor_dir,
selection_focus_rect_, focus_dir,
is_anchor_first);
}
}
UpdateCompositionInfo(false /* not an immediate request */);
}
void WidgetBase::MouseCaptureLost() {
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
frame_widget->MouseCaptureLost();
}
void WidgetBase::SetEditCommandsForNextKeyEvent(
Vector<mojom::blink::EditCommandPtr> edit_commands) {
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
frame_widget->SetEditCommandsForNextKeyEvent(std::move(edit_commands));
}
void WidgetBase::CursorVisibilityChange(bool is_visible) {
client_->SetCursorVisibilityState(is_visible);
}
void WidgetBase::SetMouseCapture(bool capture) {
if (mojom::blink::WidgetInputHandlerHost* host =
widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
host->SetMouseCapture(capture);
}
}
void WidgetBase::ImeSetComposition(
const String& text,
const Vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& replacement_range,
int selection_start,
int selection_end) {
if (!ShouldHandleImeEvents())
return;
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
if (frame_widget->ShouldDispatchImeEventsToPlugin()) {
frame_widget->ImeSetCompositionForPlugin(text, ime_text_spans,
replacement_range, selection_start,
selection_end);
return;
}
ImeEventGuard guard(weak_ptr_factory_.GetWeakPtr());
if (!frame_widget->SetComposition(text, ime_text_spans, replacement_range,
selection_start, selection_end)) {
// If we failed to set the composition text, then we need to let the browser
// process to cancel the input method's ongoing composition session, to make
// sure we are in a consistent state.
if (mojom::blink::WidgetInputHandlerHost* host =
widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
host->ImeCancelComposition();
}
}
UpdateCompositionInfo(false /* not an immediate request */);
}
void WidgetBase::ImeCommitText(const String& text,
const Vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& replacement_range,
int relative_cursor_pos) {
if (!ShouldHandleImeEvents())
return;
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
if (frame_widget->ShouldDispatchImeEventsToPlugin()) {
frame_widget->ImeCommitTextForPlugin(
text, ime_text_spans, replacement_range, relative_cursor_pos);
return;
}
ImeEventGuard guard(weak_ptr_factory_.GetWeakPtr());
input_handler_.set_handling_input_event(true);
frame_widget->CommitText(text, ime_text_spans, replacement_range,
relative_cursor_pos);
input_handler_.set_handling_input_event(false);
UpdateCompositionInfo(false /* not an immediate request */);
}
void WidgetBase::ImeFinishComposingText(bool keep_selection) {
if (!ShouldHandleImeEvents())
return;
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget)
return;
if (frame_widget->ShouldDispatchImeEventsToPlugin()) {
frame_widget->ImeFinishComposingTextForPlugin(keep_selection);
return;
}
ImeEventGuard guard(weak_ptr_factory_.GetWeakPtr());
input_handler_.set_handling_input_event(true);
frame_widget->FinishComposingText(keep_selection);
input_handler_.set_handling_input_event(false);
UpdateCompositionInfo(false /* not an immediate request */);
}
void WidgetBase::QueueSyntheticEvent(
std::unique_ptr<WebCoalescedInputEvent> event) {
client_->WillQueueSyntheticEvent(*event);
// TODO(acomminos): If/when we add support for gesture event attribution on
// the impl thread, have the caller provide attribution.
WebInputEventAttribution attribution;
widget_input_handler_manager_->input_event_queue()->HandleEvent(
std::move(event), MainThreadEventQueue::DispatchType::kNonBlocking,
mojom::blink::InputEventResultState::kNotConsumed, attribution, nullptr,
HandledEventCallback());
}
bool WidgetBase::IsForProvisionalFrame() {
auto* frame_widget = client_->FrameWidget();
if (!frame_widget)
return false;
return frame_widget->IsProvisional();
}
bool WidgetBase::ShouldHandleImeEvents() {
auto* frame_widget = client_->FrameWidget();
if (!frame_widget)
return false;
return frame_widget->ShouldHandleImeEvents();
}
void WidgetBase::RequestPresentationAfterScrollAnimationEnd(
mojom::blink::Widget::ForceRedrawCallback callback) {
LayerTreeHost()->RequestScrollAnimationEndNotification(
base::BindOnce(&WidgetBase::ForceRedraw, weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void WidgetBase::FlushInputProcessedCallback() {
widget_input_handler_manager_->InvokeInputProcessedCallback();
}
void WidgetBase::CancelCompositionForPepper() {
if (mojom::blink::WidgetInputHandlerHost* host =
widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
host->ImeCancelComposition();
}
#if defined(OS_MAC) || defined(USE_AURA)
UpdateCompositionInfo(false /* not an immediate request */);
#endif
}
void WidgetBase::OnImeEventGuardStart(ImeEventGuard* guard) {
if (!ime_event_guard_)
ime_event_guard_ = guard;
}
void WidgetBase::OnImeEventGuardFinish(ImeEventGuard* guard) {
if (ime_event_guard_ != guard)
return;
ime_event_guard_ = nullptr;
// While handling an ime event, text input state and selection bounds updates
// are ignored. These must explicitly be updated once finished handling the
// ime event.
UpdateSelectionBounds();
#if defined(OS_ANDROID)
if (guard->show_virtual_keyboard())
ShowVirtualKeyboard();
else
UpdateTextInputState();
#endif
}
void WidgetBase::RequestAnimationAfterDelay(const base::TimeDelta& delay) {
if (delay.is_zero()) {
client_->ScheduleAnimation();
return;
}
// Consolidate delayed animation frame requests to keep only the longest
// delay.
if (request_animation_after_delay_timer_.IsActive() &&
request_animation_after_delay_timer_.NextFireInterval() > delay) {
request_animation_after_delay_timer_.Stop();
}
if (!request_animation_after_delay_timer_.IsActive()) {
request_animation_after_delay_timer_.StartOneShot(delay, FROM_HERE);
}
}
void WidgetBase::RequestAnimationAfterDelayTimerFired(TimerBase*) {
client_->ScheduleAnimation();
}
void WidgetBase::UpdateSurfaceAndScreenInfo(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Rect& compositor_viewport_pixel_rect,
const ScreenInfo& new_screen_info_param) {
ScreenInfo new_screen_info = new_screen_info_param;
// If there is a screen orientation override apply it.
if (auto orientation_override = client_->ScreenOrientationOverride()) {
new_screen_info.orientation_type = orientation_override.value();
new_screen_info.orientation_angle =
OrientationTypeToAngle(new_screen_info.orientation_type);
}
// Same logic is used in RenderWidgetHostImpl::SynchronizeVisualProperties to
// detect if there is a screen orientation change.
bool orientation_changed =
screen_info_.orientation_angle != new_screen_info.orientation_angle ||
screen_info_.orientation_type != new_screen_info.orientation_type;
ScreenInfo previous_original_screen_info = client_->GetOriginalScreenInfo();
local_surface_id_from_parent_ = new_local_surface_id;
screen_info_ = new_screen_info;
// Note carefully that the DSF specified in |new_screen_info| is not the
// DSF used by the compositor during device emulation!
LayerTreeHost()->SetViewportRectAndScale(
compositor_viewport_pixel_rect,
client_->GetOriginalScreenInfo().device_scale_factor,
local_surface_id_from_parent_);
// The VisualDeviceViewportIntersectionRect derives from the LayerTreeView's
// viewport size, which is set above.
LayerTreeHost()->SetVisualDeviceViewportIntersectionRect(
client_->ViewportVisibleRect());
LayerTreeHost()->SetDisplayColorSpaces(screen_info_.display_color_spaces);
if (orientation_changed)
client_->OrientationChanged();
client_->DidUpdateSurfaceAndScreen(previous_original_screen_info);
}
void WidgetBase::UpdateScreenInfo(const ScreenInfo& new_screen_info) {
UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
CompositorViewportRect(), new_screen_info);
}
void WidgetBase::UpdateCompositorViewportAndScreenInfo(
const gfx::Rect& compositor_viewport_pixel_rect,
const ScreenInfo& new_screen_info) {
UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
compositor_viewport_pixel_rect, new_screen_info);
}
void WidgetBase::UpdateCompositorViewportRect(
const gfx::Rect& compositor_viewport_pixel_rect) {
UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
compositor_viewport_pixel_rect, screen_info_);
}
void WidgetBase::UpdateSurfaceAndCompositorRect(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Rect& compositor_viewport_pixel_rect) {
UpdateSurfaceAndScreenInfo(new_local_surface_id,
compositor_viewport_pixel_rect, screen_info_);
}
const ScreenInfo& WidgetBase::GetScreenInfo() {
return screen_info_;
}
void WidgetBase::SetScreenRects(const gfx::Rect& widget_screen_rect,
const gfx::Rect& window_screen_rect) {
widget_screen_rect_ = widget_screen_rect;
window_screen_rect_ = window_screen_rect;
}
void WidgetBase::SetPendingWindowRect(const gfx::Rect& rect) {
pending_window_rect_count_++;
pending_window_rect_ = rect;
// Popups don't get size updates back from the browser so just store the set
// values.
if (!client_->FrameWidget()) {
SetScreenRects(rect, rect);
}
}
void WidgetBase::AckPendingWindowRect() {
DCHECK(pending_window_rect_count_);
pending_window_rect_count_--;
if (pending_window_rect_count_ == 0)
pending_window_rect_.reset();
}
gfx::Rect WidgetBase::WindowRect() {
gfx::Rect rect;
if (pending_window_rect_) {
// NOTE(mbelshe): If there is a pending_window_rect_, then getting
// the RootWindowRect is probably going to return wrong results since the
// browser may not have processed the Move yet. There isn't really anything
// good to do in this case, and it shouldn't happen - since this size is
// only really needed for windowToScreen, which is only used for Popups.
rect = pending_window_rect_.value();
} else {
rect = window_screen_rect_;
}
client_->ScreenRectToEmulated(rect);
return rect;
}
gfx::Rect WidgetBase::ViewRect() {
gfx::Rect rect = widget_screen_rect_;
client_->ScreenRectToEmulated(rect);
return rect;
}
gfx::Rect WidgetBase::CompositorViewportRect() const {
return LayerTreeHost()->device_viewport_rect();
}
bool WidgetBase::ComputePreferCompositingToLCDText() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kDisablePreferCompositingToLCDText))
return false;
#if defined(OS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH)
// On Android, we never have subpixel antialiasing. On Chrome OS we prefer to
// composite all scrollers for better scrolling performance.
return true;
#else
// Prefer compositing if the device scale is high enough that losing subpixel
// antialiasing won't have a noticeable effect on text quality.
// Note: We should keep kHighDPIDeviceScaleFactorThreshold in
// cc/metrics/lcd_text_metrics_reporter.cc the same as the value below.
if (screen_info_.device_scale_factor >= 1.5f)
return true;
if (command_line.HasSwitch(switches::kEnablePreferCompositingToLCDText))
return true;
if (!Platform::Current()->IsLcdTextEnabled())
return true;
if (base::FeatureList::IsEnabled(features::kPreferCompositingToLCDText))
return true;
return false;
#endif
}
gfx::PointF WidgetBase::DIPsToBlinkSpace(const gfx::PointF& point) {
if (!use_zoom_for_dsf_)
return point;
// TODO(danakj): Should this be GetScreenInfo() so it changes under emulation?
return gfx::ScalePoint(point,
client_->GetOriginalScreenInfo().device_scale_factor);
}
gfx::Point WidgetBase::DIPsToRoundedBlinkSpace(const gfx::Point& point) {
if (!use_zoom_for_dsf_)
return point;
// TODO(danakj): Should this be GetScreenInfo() so it changes under emulation?
return gfx::ScaleToRoundedPoint(
point, client_->GetOriginalScreenInfo().device_scale_factor);
}
gfx::PointF WidgetBase::BlinkSpaceToDIPs(const gfx::PointF& point) {
if (!use_zoom_for_dsf_)
return point;
// TODO(danakj): Should this be GetScreenInfo() so it changes under emulation?
return gfx::ScalePoint(
point, 1.f / client_->GetOriginalScreenInfo().device_scale_factor);
}
gfx::Point WidgetBase::BlinkSpaceToFlooredDIPs(const gfx::Point& point) {
if (!use_zoom_for_dsf_)
return point;
// TODO(danakj): Should this be GetScreenInfo() so it changes under emulation?
// TODO(dtapuska): Determine if this should be a floor vs rounded.
float reverse = 1 / client_->GetOriginalScreenInfo().device_scale_factor;
return gfx::ScaleToFlooredPoint(point, reverse);
}
gfx::Size WidgetBase::DIPsToCeiledBlinkSpace(const gfx::Size& size) {
if (!use_zoom_for_dsf_)
return size;
return gfx::ScaleToCeiledSize(
size, client_->GetOriginalScreenInfo().device_scale_factor);
}
gfx::RectF WidgetBase::DIPsToBlinkSpace(const gfx::RectF& rect) {
if (!use_zoom_for_dsf_)
return rect;
// TODO(danakj): Should this be GetScreenInfo() so it changes under emulation?
return gfx::ScaleRect(rect,
client_->GetOriginalScreenInfo().device_scale_factor);
}
float WidgetBase::DIPsToBlinkSpace(float scalar) {
if (!use_zoom_for_dsf_)
return scalar;
// TODO(danakj): Should this be GetScreenInfo() so it changes under emulation?
return client_->GetOriginalScreenInfo().device_scale_factor * scalar;
}
gfx::Size WidgetBase::BlinkSpaceToFlooredDIPs(const gfx::Size& size) {
if (!use_zoom_for_dsf_)
return size;
float reverse = 1 / client_->GetOriginalScreenInfo().device_scale_factor;
return gfx::ScaleToFlooredSize(size, reverse);
}
gfx::Rect WidgetBase::BlinkSpaceToEnclosedDIPs(const gfx::Rect& rect) {
if (!use_zoom_for_dsf_)
return rect;
float reverse = 1 / client_->GetOriginalScreenInfo().device_scale_factor;
return gfx::ScaleToEnclosedRect(rect, reverse);
}
gfx::RectF WidgetBase::BlinkSpaceToDIPs(const gfx::RectF& rect) {
if (!use_zoom_for_dsf_)
return rect;
float reverse = 1 / client_->GetOriginalScreenInfo().device_scale_factor;
return gfx::ScaleRect(rect, reverse);
}
} // namespace blink