blob: 0896ed985458e0dc15a62e81b3053a7586d71f56 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// 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 <algorithm>
#include "base/command_line.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/common/task_annotator.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/base/features.h"
#include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
#include "cc/raster/categorized_worker_pool.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/paint_holding_reason.h"
#include "components/viz/common/features.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 "media/base/media_switches.h"
#include "mojo/public/cpp/bindings/direct_receiver.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/mojom/input/pointer_lock_context.mojom-blink.h"
#include "third_party/blink/public/mojom/widget/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/web/blink.h"
#include "third_party/blink/renderer/platform/graphics/raster_dark_mode_filter_impl.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/compositor_thread_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/widget_scheduler.h"
#include "third_party/blink/renderer/platform/widget/compositing/blink_categorized_worker_pool_delegate.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/base/mojom/menu_source_type.mojom-blink-forward.h"
#include "ui/display/display.h"
#include "ui/display/screen_info.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/presentation_feedback.h"
#if BUILDFLAG(IS_ANDROID)
#include "third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h"
#endif
namespace blink {
namespace {
#if BUILDFLAG(IS_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;
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(display::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 display::mojom::blink::ScreenOrientation::kLandscapePrimary:
angle = 90;
break;
case display::mojom::blink::ScreenOrientation::kLandscapeSecondary:
angle = 270;
break;
case display::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::SingleThreadTaskRunner::GetCurrentDefault().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_embedded,
bool is_for_scalable_page)
: never_composited_(never_composited),
is_embedded_(is_embedded),
is_for_scalable_page_(is_for_scalable_page),
client_(client),
widget_host_(std::move(widget_host), task_runner),
receiver_(this, std::move(widget), task_runner),
next_previous_flags_(kInvalidNextPreviousFlagsValue),
is_hidden_(hidden),
task_runner_(task_runner),
request_animation_after_delay_timer_(
std::move(task_runner),
this,
&WidgetBase::RequestAnimationAfterDelayTimerFired) {}
WidgetBase::~WidgetBase() {
// Ensure Shutdown was called.
DCHECK(!layer_tree_view_);
}
void WidgetBase::InitializeCompositing(
PageScheduler& page_scheduler,
const display::ScreenInfos& screen_infos,
const cc::LayerTreeSettings* settings,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler>
frame_widget_input_handler,
WidgetBase* previous_widget) {
DCHECK(!initialized_);
widget_scheduler_ = page_scheduler.CreateWidgetScheduler(this);
widget_scheduler_->SetHidden(is_hidden_);
main_thread_compositor_task_runner_ =
page_scheduler.GetAgentGroupScheduler().CompositorTaskRunner();
main_thread_id_ = base::PlatformThread::CurrentId();
auto* compositing_thread_scheduler =
ThreadScheduler::CompositorThreadScheduler();
if (previous_widget) {
CHECK(previous_widget->layer_tree_view_);
CHECK(!settings);
AssertAreCompatible(*this, *previous_widget);
// `screen_infos` is applied to this LayerTreeView below.
previous_widget->DisconnectLayerTreeView(this, /*delay_release=*/false);
CHECK(layer_tree_view_);
} else {
layer_tree_view_ = std::make_unique<LayerTreeView>(this, widget_scheduler_);
std::optional<cc::LayerTreeSettings> default_settings;
if (!settings) {
const display::ScreenInfo& screen_info = screen_infos.current();
default_settings = GenerateLayerTreeSettings(
compositing_thread_scheduler, is_embedded_, is_for_scalable_page_,
screen_info.rect.size(), screen_info.device_scale_factor);
settings = &default_settings.value();
}
layer_tree_view_->Initialize(
*settings, main_thread_compositor_task_runner_,
compositing_thread_scheduler
? compositing_thread_scheduler->DefaultTaskRunner()
: nullptr,
cc::CategorizedWorkerPool::GetOrCreate(
&BlinkCategorizedWorkerPoolDelegate::Get()));
}
screen_infos_ = screen_infos;
max_render_buffer_bounds_sw_ =
LayerTreeHost()->GetSettings().max_render_buffer_bounds_for_sw;
FrameWidget* frame_widget = client_->FrameWidget();
// Even if we have a |compositing_thread_scheduler| we do not process input
// on the compositor thread for widgets that are not frames. (ie. popups).
auto* widget_compositing_thread_scheduler =
frame_widget ? compositing_thread_scheduler : nullptr;
// 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;
base::PlatformThreadId io_thread_id = Platform::Current()->GetIOThreadId();
widget_input_handler_manager_ = WidgetInputHandlerManager::Create(
weak_ptr_factory_.GetWeakPtr(), std::move(frame_widget_input_handler),
never_composited_, widget_compositing_thread_scheduler, widget_scheduler_,
uses_input_handler, client_->AllowsScrollResampling(), io_thread_id,
main_thread_id_);
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kAllowPreCommitInput))
widget_input_handler_manager_->AllowPreCommitInput();
UpdateScreenInfo(screen_infos);
// 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);
if (Platform::Current()->IsThreadedAnimationEnabled()) {
DCHECK(AnimationHost());
scroll_animation_timeline_ = cc::AnimationTimeline::Create(
cc::AnimationIdProvider::NextTimelineId());
AnimationHost()->AddAnimationTimeline(scroll_animation_timeline_);
}
initialized_ = true;
}
void WidgetBase::InitializeNonCompositing() {
DCHECK(!initialized_);
// WidgetBase users implicitly expect one default ScreenInfo to exist.
screen_infos_ = display::ScreenInfos(display::ScreenInfo());
initialized_ = true;
}
void WidgetBase::OnFirstContentfulPaint(
const base::TimeTicks& first_paint_time) {
if (widget_input_handler_manager_) {
widget_input_handler_manager_->OnFirstContentfulPaint(first_paint_time);
}
}
void WidgetBase::Shutdown(bool delay_release) {
// 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();
DisconnectLayerTreeView(nullptr, delay_release);
// The `widget_scheduler_` must be deleted last because the
// `widget_input_handler_manager_` may request to post a task on the
// InputTaskQueue. The `widget_input_handler_manager_` must outlive
// the `layer_tree_view_` because it's `LayerTreeHost` holds a raw ptr to
// the `InputHandlerProxy` interface on the compositor thread. The
// `LayerTreeHost` destruction is synchronous and will join with the
// compositor thread
if (widget_scheduler_) {
widget_scheduler_->WillShutdown();
scoped_refptr<base::SingleThreadTaskRunner> cleanup_runner =
base::SingleThreadTaskRunner::GetCurrentDefault();
base::TimeDelta task_delay(base::Seconds(0));
if (delay_release) {
#if BUILDFLAG(IS_ANDROID)
CHECK(!Platform::Current()
->IsSynchronousCompositingEnabledForAndroidWebView());
#endif
CHECK(base::FeatureList::IsEnabled(
blink::features::kDelayLayerTreeViewDeletionOnLocalSwap));
task_delay =
features::kDelayLayerTreeViewDeletionOnLocalSwapTaskDelayParam.Get();
}
cleanup_runner->PostNonNestableDelayedTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<scheduler::WidgetScheduler> scheduler,
scoped_refptr<WidgetInputHandlerManager> manager,
std::unique_ptr<LayerTreeView> view) {
view.reset();
manager.reset();
scheduler->Shutdown();
},
std::move(widget_scheduler_),
std::move(widget_input_handler_manager_),
std::move(layer_tree_view_)),
task_delay);
}
if (widget_compositor_) {
widget_compositor_->Shutdown();
widget_compositor_ = nullptr;
}
}
void WidgetBase::DisconnectLayerTreeView(WidgetBase* new_widget,
bool delay_release) {
will_be_destroyed_ = true;
if (!layer_tree_view_) {
CHECK(!new_widget);
return;
}
// 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 (ScrollAnimationTimeline()) {
DCHECK(AnimationHost());
AnimationHost()->RemoveAnimationTimeline(ScrollAnimationTimeline());
}
if (new_widget) {
// Reattach to `new_widget`.
layer_tree_view_->ClearPreviousDelegateAndReattachIfNeeded(
new_widget, widget_scheduler_);
new_widget->layer_tree_view_ = std::move(layer_tree_view_);
layer_tree_view_ = nullptr;
} else if (delay_release) {
#if BUILDFLAG(IS_ANDROID)
CHECK(!Platform::Current()
->IsSynchronousCompositingEnabledForAndroidWebView());
#endif
CHECK(base::FeatureList::IsEnabled(
blink::features::kDelayLayerTreeViewDeletionOnLocalSwap));
// Detach the LayerTreeView now without attaching it to anything else. The
// actual release of the LayerTreeView and its resources will happen later,
// see also the task posted in `Shutdown()`.
layer_tree_view_->ClearPreviousDelegateAndReattachIfNeeded(nullptr,
nullptr);
} else {
// Disconnect and release now.
layer_tree_view_->Disconnect();
}
}
cc::LayerTreeHost* WidgetBase::LayerTreeHost() const {
return layer_tree_view_ ? layer_tree_view_->layer_tree_host() : nullptr;
}
cc::AnimationHost* WidgetBase::AnimationHost() const {
return layer_tree_view_ ? layer_tree_view_->animation_host() : nullptr;
}
cc::AnimationTimeline* WidgetBase::ScrollAnimationTimeline() const {
return scroll_animation_timeline_.get();
}
scheduler::WidgetScheduler* WidgetBase::WidgetScheduler() {
return widget_scheduler_.get();
}
void WidgetBase::ForceRedraw(
mojom::blink::Widget::ForceRedrawCallback callback) {
TRACE_EVENT0("renderer", "WidgetBase::ForceRedraw");
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,
bool from_viz) {
// Viz-initiated request.
if (from_viz) {
widget_input_handler_manager_->SetVizHost(std::move(host));
// Hold back binding Viz side receiver until we have processed Browser side
// `GetWidgetInputHandler` request.
if (!widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
pending_viz_widget_input_handler_.emplace(std::move(request));
return;
}
// Proceed with immediate binding since the browser-side
// WidgetInputHandlerHost has been bounded.
widget_input_handler_manager_->AddInterface(std::move(request));
} else {
// Browser initiated request.
widget_input_handler_manager_->SetHost(std::move(host));
widget_input_handler_manager_->AddInterface(std::move(request));
// If there's a pending Viz-side receiver, bind it now that the browser-side
// host is available.
if (pending_viz_widget_input_handler_.has_value()) {
widget_input_handler_manager_->AddInterface(
std::move(*pending_viz_widget_input_handler_));
pending_viz_widget_input_handler_.reset();
}
}
}
void WidgetBase::ShowContextMenu(ui::mojom::blink::MenuSourceType source_type,
const gfx::Point& location) {
client_->ShowContextMenu(source_type, location);
}
void WidgetBase::BindInputTargetClient(
mojo::PendingReceiver<viz::mojom::blink::InputTargetClient> host) {
client_->BindInputTargetClient(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#
VisualProperties visual_properties = visual_properties_from_browser;
auto& screen_info = visual_properties.screen_infos.mutable_current();
// Web tests can override the device scale factor in the renderer.
if (auto scale_factor = client_->GetTestingDeviceScaleFactorOverride()) {
screen_info.device_scale_factor = 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(
screen_info.display_color_spaces.GetScreenInfoColorSpace());
LayerTreeHost()->SetBrowserControlsParams(
visual_properties.browser_controls_params);
LayerTreeHost()->SetVisualDeviceViewportSize(
gfx::ScaleToCeiledSize(visual_properties.visible_viewport_size_device_px,
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) {
TRACE_EVENT0("renderer", "WidgetBase::UpdateScreenRects");
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(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()->RequestSuccessfulPresentationTimeForNextFrame(
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_bfcache_restore));
}
client_->WasShown(was_evicted);
}
void WidgetBase::RequestSuccessfulPresentationTimeForNextFrame(
mojom::blink::RecordContentToVisibleTimeRequestPtr visible_time_request) {
DCHECK(visible_time_request);
if (is_hidden_) {
return;
}
TRACE_EVENT0("renderer",
"WidgetBase::RequestSuccessfulPresentationTimeForNextFrame");
if (visible_time_request->show_reason_unfolding) {
LayerTreeHost()->RequestSuccessfulPresentationTimeForNextFrame(
tab_switch_time_recorder_.GetCallbackForNextFrameAfterUnfold(
visible_time_request->event_start_time));
return;
}
// Tab was shown while widget was already painting, eg. due to being
// captured.
LayerTreeHost()->RequestSuccessfulPresentationTimeForNextFrame(
tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */, visible_time_request->event_start_time,
visible_time_request->destination_is_loaded,
visible_time_request->show_reason_tab_switching,
visible_time_request->show_reason_bfcache_restore));
}
void WidgetBase::CancelSuccessfulPresentationTimeRequest() {
if (is_hidden_) {
return;
}
TRACE_EVENT0("renderer",
"WidgetBase::CancelSuccessfulPresentationTimeRequest");
// Tab was hidden while widget keeps painting, eg. due to being captured.
tab_switch_time_recorder_.TabWasHidden();
}
void WidgetBase::SetupBrowserRenderInputRouterConnections(
mojo::PendingReceiver<mojom::blink::RenderInputRouterClient>
browser_request) {
TRACE_EVENT("renderer",
"WidgetBase::SetupBrowserRenderInputRouterConnections");
// TODO(b/322833330): Investigate binding |browser_input_receiver_| on
// RendererCompositor to break dependency on CrRendererMain and avoiding
// contention with javascript during method calls.
browser_input_receiver_.Bind(std::move(browser_request), task_runner_);
}
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(rendering-core): 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,
cc::PaintHoldingReason reason,
std::optional<cc::PaintHoldingCommitTrigger> trigger) {
// The input handler wants to know about the commit status for metric purposes
// and to enable/disable input.
widget_input_handler_manager_->OnDeferCommitsChanged(defer, reason);
}
void WidgetBase::OnCommitRequested() {
client_->OnCommitRequested();
}
void WidgetBase::DidBeginMainFrame() {
UpdateTextInputState();
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");
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. LOG(FATAL) in that case.
if (for_web_tests && Platform::Current()->IsGpuCompositingDisabled() &&
!Platform::Current()->CompositorThreadTaskRunner()) {
LOG(FATAL) << "Web tests require gpu compositing in single thread mode, "
"but it is disabled.";
}
// 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));
auto params = std::make_unique<
cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams>();
params->io_thread_id = Platform::Current()->GetIOThreadId();
params->main_thread_id = main_thread_id_;
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_;
}
if (base::FeatureList::IsEnabled(features::kDirectCompositorThreadIpc) &&
!for_web_tests && params->compositor_task_runner &&
mojo::IsDirectReceiverSupported()) {
params->use_direct_client_receiver = true;
}
// The renderer runs animations and layout for animate_only BeginFrames.
params->wants_animate_only_begin_frames = true;
params->no_compositor_frame_acks =
base::FeatureList::IsEnabled(::features::kNoCompositorFrameAcks);
// 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.)
// TODO(b/221220344): Support dynamically setting the BeginFrameSource per VRR
// state changes.
const cc::LayerTreeSettings& settings = LayerTreeHost()->GetSettings();
if (settings.disable_frame_rate_limit ||
settings.enable_variable_refresh_rate) {
params->use_begin_frame_presentation_feedback =
base::FeatureList::IsEnabled(
features::kUseBeginFramePresentationFeedback);
params->synthetic_begin_frame_source = CreateSyntheticBeginFrameSource();
}
// Don't enable the cc side internal begin frame source if using headless,
// since cc won't receive ExternalBeginFrame issued by headless tests when
// internal begin frame source is started.
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (base::FeatureList::IsEnabled(
::features::kInternalBeginFrameSourceOnManyDidNotProduceFrame) &&
!params->synthetic_begin_frame_source &&
!settings.single_thread_proxy_scheduler &&
!settings.using_synchronous_renderer_compositor &&
!command_line.HasSwitch(switches::kAllowPreCommitInput)) {
static const uint64_t num_did_not_produce_frame = static_cast<uint64_t>(
::features::kNumDidNotProduceFrameBeforeInternalBeginFrameSource.Get());
params->num_did_not_produce_frame_before_internal_begin_frame_source =
num_did_not_produce_frame;
params->auto_needs_begin_frame = true;
}
if (base::FeatureList::IsEnabled(::features::kManualBeginFrame) &&
!command_line.HasSwitch(switches::kAllowPreCommitInput)) {
params->auto_needs_begin_frame = true;
params->manual_begin_frame = true;
}
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());
Platform::EstablishGpuChannelCallback finish_callback =
base::BindOnce(&WidgetBase::FinishRequestNewLayerTreeFrameSink,
weak_ptr_factory_.GetWeakPtr(), url,
std::move(compositor_frame_sink_receiver),
std::move(compositor_frame_sink_client),
std::move(render_frame_metadata_observer_client_receiver),
std::move(render_frame_metadata_observer_remote),
std::move(render_frame_metadata_observer),
std::move(params), std::move(callback));
bool needs_sync_composite_for_test =
layer_tree_view_ && LayerTreeHost()->in_composite_for_test();
if (base::FeatureList::IsEnabled(features::kEstablishGpuChannelAsync) &&
!needs_sync_composite_for_test) {
Platform::Current()->EstablishGpuChannel(std::move(finish_callback));
} else {
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
Platform::Current()->EstablishGpuChannelSync();
std::move(finish_callback).Run(gpu_channel_host);
}
}
void WidgetBase::FinishRequestNewLayerTreeFrameSink(
const KURL& url,
mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSink>
compositor_frame_sink_receiver,
mojo::PendingRemote<viz::mojom::blink::CompositorFrameSinkClient>
compositor_frame_sink_client,
mojo::PendingReceiver<cc::mojom::blink::RenderFrameMetadataObserverClient>
render_frame_metadata_observer_client_receiver,
mojo::PendingRemote<cc::mojom::blink::RenderFrameMetadataObserver>
render_frame_metadata_observer_remote,
std::unique_ptr<RenderFrameMetadataObserverImpl>
render_frame_metadata_observer,
std::unique_ptr<cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams>
params,
LayerTreeFrameSinkCallback callback,
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
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;
}
viz_input_receiver_.reset();
if (Platform::Current()->IsGpuCompositingDisabled()) {
widget_host_->CreateFrameSink(
std::move(compositor_frame_sink_receiver),
std::move(compositor_frame_sink_client),
viz_input_receiver_.BindNewPipeAndPassRemote(task_runner_));
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>(
/*context_provider=*/nullptr, /*worker_context_provider=*/nullptr,
gpu_channel_host->CreateClientSharedImageInterface(), params.get()),
std::move(render_frame_metadata_observer));
return;
}
scoped_refptr<viz::RasterContextProvider> worker_context_provider =
Platform::Current()->SharedCompositorWorkerContextProvider(
&RasterDarkModeFilterImpl::Instance());
if (!worker_context_provider) {
// Cause the compositor to wait and try again.
std::move(callback).Run(nullptr, nullptr);
return;
}
{
viz::RasterContextProvider::ScopedRasterContextLock scoped_context(
worker_context_provider.get());
max_render_buffer_bounds_gpu_ =
worker_context_provider->ContextCapabilities().max_texture_size;
}
// 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();
constexpr bool automatic_flushes = false;
constexpr bool support_locking = false;
constexpr bool enable_gpu_rasterization = false;
constexpr bool lose_context_when_out_of_memory = true;
auto context_provider = viz::ContextProviderCommandBuffer::CreateForRaster(
gpu_channel_host, kGpuStreamIdDefault, kGpuStreamPriorityDefault,
GURL(url), automatic_flushes, support_locking, limits,
viz::command_buffer_metrics::ContextType::RENDERER_COMPOSITOR,
enable_gpu_rasterization, lose_context_when_out_of_memory);
#if BUILDFLAG(IS_ANDROID)
if (Platform::Current()->IsSynchronousCompositingEnabledForAndroidWebView() &&
!is_embedded_) {
// 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),
viz_input_receiver_.BindNewPipeAndPassRemote(task_runner_));
}
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(),
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),
viz_input_receiver_.BindNewPipeAndPassRemote(task_runner_));
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>(
std::move(context_provider), std::move(worker_context_provider),
gpu_channel_host->CreateClientSharedImageInterface(), params.get()),
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");
}
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,
base::TimeTicks commit_finish_time) {
client_->EndCommitCompositorFrame(commit_start_time, commit_finish_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();
}
void WidgetBase::BeginUpdateLayers() {
client_->BeginUpdateLayers();
}
void WidgetBase::EndUpdateLayers() {
client_->EndUpdateLayers();
}
void WidgetBase::WillBeginMainFrame() {
TRACE_EVENT0("gpu", "WidgetBase::WillBeginMainFrame");
client_->SetSuppressFrameRequestsWorkaroundFor704763Only(true);
client_->WillBeginMainFrame();
UpdateSelectionBounds();
}
void WidgetBase::RunPaintBenchmark(int repeat_count,
cc::PaintBenchmarkResult& result) {
client_->RunPaintBenchmark(repeat_count, result);
}
void WidgetBase::ScheduleAnimationForWebTests() {
client_->ScheduleAnimationForWebTests();
}
std::unique_ptr<cc::RenderFrameMetadataObserver>
WidgetBase::CreateRenderFrameObserver() {
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));
widget_host_->RegisterRenderFrameMetadataObserver(
std::move(render_frame_metadata_observer_client_receiver),
std::move(render_frame_metadata_observer_remote));
return render_frame_metadata_observer;
}
void WidgetBase::SetCompositorVisible(bool visible) {
if (never_composited_)
return;
layer_tree_view_->SetVisible(visible);
}
void WidgetBase::WarmUpCompositor() {
if (never_composited_) {
return;
}
layer_tree_view_->SetShouldWarmUp();
}
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(const viz::BeginFrameArgs& args) {
base::TimeTicks raf_aligned_input_start_time;
if (ShouldRecordBeginMainFrameMetrics()) {
raf_aligned_input_start_time = base::TimeTicks::Now();
}
auto weak_this = weak_ptr_factory_.GetWeakPtr();
widget_input_handler_manager_->input_event_queue()->DispatchRafAlignedInput(
args.frame_time);
// DispatchRafAlignedInput could have detached the frame.
if (!weak_this)
return;
if (ShouldRecordBeginMainFrameMetrics()) {
client_->RecordDispatchRafAlignedInputTime(raf_aligned_input_start_time);
}
client_->BeginMainFrame(args);
}
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(const viz::FrameTimingDetails&)> callback) {
layer_tree_view_->AddPresentationCallback(frame_token, std::move(callback));
}
#if BUILDFLAG(IS_APPLE)
void WidgetBase::AddCoreAnimationErrorCodeCallback(
uint32_t frame_token,
base::OnceCallback<void(gfx::CALayerResult)> callback) {
layer_tree_view_->AddCoreAnimationErrorCodeCallback(frame_token,
std::move(callback));
}
#endif
void WidgetBase::SetCursor(const ui::Cursor& cursor) {
if (input_handler_.DidChangeCursor(cursor)) {
widget_host_->SetCursor(cursor);
}
}
void WidgetBase::UpdateTooltipUnderCursor(const String& tooltip_text,
TextDirection dir) {
widget_host_->UpdateTooltipUnderCursor(
tooltip_text.empty() ? "" : tooltip_text, ToBaseTextDirection(dir));
}
void WidgetBase::UpdateTooltipFromKeyboard(const String& tooltip_text,
TextDirection dir,
const gfx::Rect& bounds) {
widget_host_->UpdateTooltipFromKeyboard(
tooltip_text.empty() ? "" : tooltip_text, ToBaseTextDirection(dir),
BlinkSpaceToEnclosedDIPs(bounds));
}
void WidgetBase::ClearKeyboardTriggeredTooltip() {
widget_host_->ClearKeyboardTriggeredTooltip();
}
void WidgetBase::ShowVirtualKeyboard() {
UpdateTextInputStateInternal(true, false);
}
void WidgetBase::UpdateTextInputState() {
UpdateTextInputStateInternal(false, false);
}
// static
void WidgetBase::AssertAreCompatible(const WidgetBase& a, const WidgetBase& b) {
CHECK_EQ(a.is_embedded_, b.is_embedded_);
CHECK_EQ(a.is_for_scalable_page_, b.is_for_scalable_page_);
CHECK_EQ(a.main_thread_compositor_task_runner_,
b.main_thread_compositor_task_runner_);
}
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;
std::optional<gfx::Rect> control_bounds;
std::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.
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 ||
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->node_id = new_info.node_id;
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() && frame_widget) {
params->ime_text_spans_info =
frame_widget->GetImeTextSpansInfo(new_info.ime_text_spans);
}
#if BUILDFLAG(IS_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->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;
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 BUILDFLAG(IS_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 (layer_tree_view_)
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) || BUILDFLAG(IS_IOS_TVOS)
// 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.
// On tvOS, the system keyboard takes the entire screen, so we want to show
// it only when an input field is explicitly tapped rather than when an
// element is focused.
UpdateTextInputState();
#else
ShowVirtualKeyboard();
#endif
// TODO(rouslan): Fix ChromeOS and Windows 8 behavior of autofill popup with
// virtual keyboard.
#if !BUILDFLAG(IS_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(mojom::blink::FocusState focus_state) {
has_focus_ = focus_state == mojom::blink::FocusState::kFocused;
client_->FocusChanged(focus_state);
}
void WidgetBase::BindWidgetCompositor(
mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
if (widget_compositor_)
widget_compositor_->Shutdown();
widget_compositor_ = WidgetCompositor::Create(
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.
FrameWidget* frame_widget = client_->FrameWidget();
if (!frame_widget) {
return;
}
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 the new pipeline for CursorAnchorInfo data is available, send data from
// the frame widget instead.
if (frame_widget->HasImeRenderWidgetHost()) {
frame_widget->UpdateCursorAnchorInfo(/*update_requested=*/true);
return;
}
if (mojom::blink::WidgetInputHandlerHost* host =
widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
host->ImeCompositionRangeChanged(composition_range_,
composition_character_bounds_);
}
}
void WidgetBase::ForceTextInputStateUpdate() {
#if BUILDFLAG(IS_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 (wtf_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 (widget_scheduler_)
widget_scheduler_->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_);
if (widget_input_handler_manager_) {
widget_input_handler_manager_->SetHidden(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_,
&selection_bounding_box_, &focus_dir, &anchor_dir,
&is_anchor_first)) {
widget_host_->SelectionBoundsChanged(
selection_anchor_rect_, anchor_dir, selection_focus_rect_, focus_dir,
selection_bounding_box_, 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::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);
// Popups, which don't have a threaded input handler, are allowed to queue up
// main thread gesture scroll events.
bool uses_input_handler = client_->FrameWidget();
// 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(), !uses_input_handler);
}
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::CancelComposition() {
if (mojom::blink::WidgetInputHandlerHost* host =
widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
host->ImeCancelComposition();
}
#if BUILDFLAG(IS_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 BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
if (guard->show_virtual_keyboard())
ShowVirtualKeyboard();
else
UpdateTextInputState();
#endif
}
void WidgetBase::RequestAnimationAfterDelay(const base::TimeDelta& delay,
bool urgent) {
if (delay.is_zero()) {
// See the comment in MainThreadEventQueue::QueueEvent() explaining why we
// use "IsEligibleForThrottleMainFrameTo60Hz()".
bool urgent_for_input =
input_handler_.handling_input_event() &&
::features::IsEligibleForThrottleMainFrameTo60Hz() &&
base::FeatureList::IsEnabled(features::kUrgentMainFrameForInput);
client_->ScheduleAnimation(urgent || urgent_for_input);
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*) {
bool urgent_for_input =
input_handler_.handling_input_event() &&
base::FeatureList::IsEnabled(features::kUrgentMainFrameForInput);
client_->ScheduleAnimation(/*urgent=*/urgent_for_input);
}
float WidgetBase::GetOriginalDeviceScaleFactor() const {
return client_->GetOriginalScreenInfos().current().device_scale_factor;
}
void WidgetBase::UpdateSurfaceAndScreenInfo(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Rect& compositor_viewport_pixel_rect,
const display::ScreenInfos& screen_infos) {
display::ScreenInfos new_screen_infos = screen_infos;
display::ScreenInfo& new_screen_info = new_screen_infos.mutable_current();
// 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);
}
// RenderWidgetHostImpl::SynchronizeVisualProperties uses similar logic to
// detect orientation changes on the display currently showing the widget.
const display::ScreenInfo& previous_screen_info = screen_infos_.current();
bool orientation_changed =
previous_screen_info.orientation_angle !=
new_screen_info.orientation_angle ||
previous_screen_info.orientation_type != new_screen_info.orientation_type;
display::ScreenInfos previous_original_screen_infos =
client_->GetOriginalScreenInfos();
local_surface_id_from_parent_ = new_local_surface_id;
screen_infos_ = new_screen_infos;
// 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,
GetOriginalDeviceScaleFactor(),
local_surface_id_from_parent_);
// The VisualDeviceViewportIntersectionRect derives from the LayerTreeView's
// viewport size, which is set above.
LayerTreeHost()->SetVisualDeviceViewportIntersectionRect(
client_->ViewportVisibleRect());
if (display::Display::HasForceRasterColorProfile()) {
LayerTreeHost()->SetDisplayColorSpaces(gfx::DisplayColorSpaces(
display::Display::GetForcedRasterColorProfile()));
} else {
LayerTreeHost()->SetDisplayColorSpaces(
screen_infos_.current().display_color_spaces);
}
if (orientation_changed)
client_->OrientationChanged();
client_->DidUpdateSurfaceAndScreen(previous_original_screen_infos);
}
void WidgetBase::UpdateScreenInfo(
const display::ScreenInfos& new_screen_infos) {
UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
CompositorViewportRect(), new_screen_infos);
}
void WidgetBase::UpdateCompositorViewportAndScreenInfo(
const gfx::Rect& compositor_viewport_pixel_rect,
const display::ScreenInfos& new_screen_infos) {
UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
compositor_viewport_pixel_rect, new_screen_infos);
}
void WidgetBase::UpdateCompositorViewportRect(
const gfx::Rect& compositor_viewport_pixel_rect) {
UpdateSurfaceAndScreenInfo(local_surface_id_from_parent_,
compositor_viewport_pixel_rect, screen_infos_);
}
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_infos_);
}
const display::ScreenInfo& WidgetBase::GetScreenInfo() {
return screen_infos_.current();
}
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();
}
LCDTextPreference WidgetBase::ComputeLCDTextPreference() const {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kDisablePreferCompositingToLCDText)) {
return LCDTextPreference::kStronglyPreferred;
}
if (!Platform::Current()->IsLcdTextEnabled()) {
return LCDTextPreference::kIgnored;
}
// 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_infos_.current().device_scale_factor >= 1.5f) {
return LCDTextPreference::kIgnored;
}
if (command_line.HasSwitch(switches::kEnablePreferCompositingToLCDText) ||
base::FeatureList::IsEnabled(features::kPreferCompositingToLCDText)) {
return LCDTextPreference::kWeaklyPreferred;
}
return LCDTextPreference::kStronglyPreferred;
}
void WidgetBase::CountDroppedPointerDownForEventTiming(unsigned count) {
client_->CountDroppedPointerDownForEventTiming(count);
}
gfx::PointF WidgetBase::DIPsToBlinkSpace(const gfx::PointF& point) {
// TODO(danakj): Should this use non-original scale factor so it changes under
// emulation?
return gfx::ScalePoint(point, GetOriginalDeviceScaleFactor());
}
gfx::Point WidgetBase::DIPsToRoundedBlinkSpace(const gfx::Point& point) {
// TODO(danakj): Should this use non-original scale factor so it changes under
// emulation?
return gfx::ScaleToRoundedPoint(point, GetOriginalDeviceScaleFactor());
}
gfx::PointF WidgetBase::BlinkSpaceToDIPs(const gfx::PointF& point) {
// TODO(danakj): Should this use non-original scale factor so it changes under
// emulation?
return gfx::ScalePoint(point, 1.f / GetOriginalDeviceScaleFactor());
}
gfx::Point WidgetBase::BlinkSpaceToFlooredDIPs(const gfx::Point& point) {
// TODO(danakj): Should this use non-original scale factor so it changes under
// emulation?
float reverse = 1 / GetOriginalDeviceScaleFactor();
return gfx::ScaleToFlooredPoint(point, reverse);
}
gfx::Size WidgetBase::DIPsToCeiledBlinkSpace(const gfx::Size& size) {
return gfx::ScaleToCeiledSize(size, GetOriginalDeviceScaleFactor());
}
gfx::RectF WidgetBase::DIPsToBlinkSpace(const gfx::RectF& rect) {
// TODO(danakj): Should this use non-original scale factor so it changes under
// emulation?
return gfx::ScaleRect(rect, GetOriginalDeviceScaleFactor());
}
float WidgetBase::DIPsToBlinkSpace(float scalar) {
// TODO(danakj): Should this use non-original scale factor so it changes under
// emulation?
return GetOriginalDeviceScaleFactor() * scalar;
}
gfx::Size WidgetBase::BlinkSpaceToFlooredDIPs(const gfx::Size& size) {
float reverse = 1 / GetOriginalDeviceScaleFactor();
return gfx::ScaleToFlooredSize(size, reverse);
}
gfx::Rect WidgetBase::BlinkSpaceToEnclosedDIPs(const gfx::Rect& rect) {
float reverse = 1 / GetOriginalDeviceScaleFactor();
return gfx::ScaleToEnclosedRect(rect, reverse);
}
gfx::RectF WidgetBase::BlinkSpaceToDIPs(const gfx::RectF& rect) {
float reverse = 1 / GetOriginalDeviceScaleFactor();
return gfx::ScaleRect(rect, reverse);
}
std::optional<int> WidgetBase::GetMaxRenderBufferBounds() const {
return Platform::Current()->IsGpuCompositingDisabled()
? max_render_buffer_bounds_sw_
: max_render_buffer_bounds_gpu_;
}
void WidgetBase::OnDevToolsSessionConnectionChanged(bool attached) {
if (widget_input_handler_manager_) {
widget_input_handler_manager_->OnDevToolsSessionConnectionChanged(attached);
}
}
void WidgetBase::RequestBeginMainFrameNotExpected(bool requested) {
LayerTreeHost()->RequestBeginMainFrameNotExpected(requested);
}
bool WidgetBase::AreMainFramesPausedOrDeferred() const {
cc::LayerTreeHost* host = LayerTreeHost();
CHECK(host);
return host->MainFrameUpdatesAreDeferred() || host->IsRenderingPaused();
}
} // namespace blink