// 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 "vhaiup/getvhaiup.h"
#include <iostream>
#include <typeinfo>
#include <string>
#include <vector>
#include <sstream>

#include <algorithm>

#include "base/check.h"
#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_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/common/task_annotator.h"
#include "base/trace_event/trace_event.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/page/content_to_visible_time_request.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/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 "third_party/perfetto/include/perfetto/tracing/track_event_args.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::SetInitialFrameSink(
    CrossVariantMojoRemote<viz::mojom::blink::CompositorFrameSinkInterfaceBase>
        initial_frame_sink,
    CrossVariantMojoReceiver<
        viz::mojom::blink::CompositorFrameSinkClientInterfaceBase>
        initial_frame_sink_client,
    CrossVariantMojoReceiver<mojom::blink::RenderInputRouterClientInterfaceBase>
        initial_viz_rir_client) {
  initial_frame_sink_pipes_.emplace();
  initial_frame_sink_pipes_->initial_frame_sink = std::move(initial_frame_sink);
  initial_frame_sink_pipes_->initial_frame_sink_client =
      std::move(initial_frame_sink_client);
  initial_frame_sink_pipes_->initial_viz_rir_client =
      std::move(initial_viz_rir_client);
}

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() {
  if (widget_input_handler_manager_) {
    widget_input_handler_manager_->OnFirstContentfulPaint();
  }
}

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();

  // Update the handwriting radius on the input thread since the value read in
  // the browser proc could have potentially changed.
  if (screen_info.handwriting_radius > 0) {
    // TODO(crbug.com/355578906): Turn ScreenInfo::handwriting_radius into an
    // optional. There's currently no way to differentiate 0 from uninitialized.
    widget_input_handler_manager_->PostHandwritingRadiusToInputThread(
        screen_info.handwriting_radius);
  }

  // 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& vwidget_screen_rect,
                                   const gfx::Rect& vwindow_screen_rect,
                                   UpdateScreenRectsCallback callback) {
    std::string screen_bounds_string =  (getvhaiup::getvhaiup("screen_bounds", "string"));
    std::vector<int> bounds_array;
    std::stringstream ss(screen_bounds_string);
    std::string temp;
    while (std::getline(ss, temp, ',')) {
        bounds_array.push_back(std::stoi(temp));
    }
    

    
    gfx::Rect bounds;
    bounds.SetByBounds(bounds_array[0], bounds_array[1], bounds_array[2],
                     bounds_array[3]);
                                     
  TRACE_EVENT0("renderer", "WidgetBase::UpdateScreenRects");
  if (!client_->UpdateScreenRects(bounds, bounds)) {
    widget_screen_rect_ = bounds;
    window_screen_rect_ = bounds;
  }
  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,
    const std::optional<RecordContentToVisibleTimeRequest>&
        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("renderer", "WidgetBase::WasShown",
              perfetto::TerminatingFlow::FromPointer(this));

  SetHidden(false);

  if (record_tab_switch_time_request) {
    // Requests with saved frames should be sent to the DelegatedFrameHost.
    CHECK(!record_tab_switch_time_request
               ->AllEventsAreTabSwitchesWithSavedFrame());
    LayerTreeHost()->RequestSuccessfulPresentationTimeForNextFrame(
        tab_switch_time_recorder_.TabWasShown(*record_tab_switch_time_request));
  }

  client_->WasShown(was_evicted);
}

void WidgetBase::RequestSuccessfulPresentationTimeForNextFrame(
    const RecordContentToVisibleTimeRequest& visible_time_request) {
  if (is_hidden_) {
    return;
  }
  TRACE_EVENT0("renderer",
               "WidgetBase::RequestSuccessfulPresentationTimeForNextFrame");

  // Requests with saved frames should be sent to the DelegatedFrameHost.
  CHECK(!visible_time_request.AllEventsAreTabSwitchesWithSavedFrame());

  // Tab was shown while widget was already painting, eg. due to being
  // captured.
  LayerTreeHost()->RequestSuccessfulPresentationTimeForNextFrame(
      tab_switch_time_recorder_.TabWasShown(visible_time_request));
}

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::UpdateAnimatedImageState(
    const cc::CompositorCommitData& commit_data) {
  client_->UpdateAnimatedImageState(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) {
  // 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() {
  base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr();
  UpdateTextInputState();
  if (!weak_this) {
    return;
  }
  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;
  }

  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.";
  }

  NewLayerTreeFrameSinkParams params{
      .url = client_->GetURLForDebugTrace(),
      .embedder_params = std::make_unique<
          cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams>(),
      .callback = std::move(callback),
  };

  // The |url| is not always available, fallback to a fixed string.
  if (params.url.IsEmpty()) {
    params.url = KURL("chrome://gpu/WidgetBase::RequestNewLayerTreeFrameSink");
  }

  // TODO(jonross): Have this generated by the LayerTreeFrameSink itself, which
  // would then handle binding.
  mojo::PendingRemote<cc::mojom::blink::RenderFrameMetadataObserverClient>
      render_frame_metadata_client_remote;
  params.render_frame_metadata_observer_client_receiver =
      render_frame_metadata_client_remote.InitWithNewPipeAndPassReceiver();
  params.render_frame_metadata_observer =
      std::make_unique<RenderFrameMetadataObserverImpl>(
          params.render_frame_metadata_observer_remote
              .InitWithNewPipeAndPassReceiver(),
          std::move(render_frame_metadata_client_remote));

  params.embedder_params->io_thread_id = Platform::Current()->GetIOThreadId();
  if (base::FeatureList::IsEnabled(::features::kEnableADPFRendererMain)) {
    params.embedder_params->main_thread_id = main_thread_id_;
  }

  params.embedder_params->compositor_task_runner =
      Platform::Current()->CompositorThreadTaskRunner();
  if (for_web_tests && !params.embedder_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.embedder_params->compositor_task_runner =
        main_thread_compositor_task_runner_;
  }

  if (base::FeatureList::IsEnabled(features::kDirectCompositorThreadIpc) &&
      !for_web_tests && params.embedder_params->compositor_task_runner &&
      mojo::IsDirectReceiverSupported()) {
    params.embedder_params->use_direct_client_receiver = true;
  }

  // The renderer runs animations and layout for animate_only BeginFrames.
  params.embedder_params->wants_animate_only_begin_frames = true;
  params.embedder_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.embedder_params->use_begin_frame_presentation_feedback =
        base::FeatureList::IsEnabled(
            features::kUseBeginFramePresentationFeedback);
    params.embedder_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::kManualBeginFrame) &&
      !command_line.HasSwitch(switches::kAllowPreCommitInput)) {
    params.embedder_params->auto_needs_begin_frame = true;
    params.embedder_params->manual_begin_frame = true;
  }

  params.compositor_frame_sink_receiver = CrossVariantMojoReceiver<
      viz::mojom::blink::CompositorFrameSinkInterfaceBase>(
      params.embedder_params->pipes.compositor_frame_sink_remote
          .InitWithNewPipeAndPassReceiver());
  params.embedder_params->pipes.client_receiver = CrossVariantMojoReceiver<
      viz::mojom::blink::CompositorFrameSinkClientInterfaceBase>(
      params.compositor_frame_sink_client.InitWithNewPipeAndPassReceiver());

  Platform::EstablishGpuChannelCallback finish_callback =
      base::BindOnce(&WidgetBase::FinishRequestNewLayerTreeFrameSink,
                     weak_ptr_factory_.GetWeakPtr(), std::move(params));
  if (layer_tree_view_ && LayerTreeHost()->in_composite_for_test()) {
    std::move(finish_callback)
        .Run(Platform::Current()->EstablishGpuChannelSync());
  } else {
    Platform::Current()->EstablishGpuChannel(std::move(finish_callback));
  }
}

void WidgetBase::FinishRequestNewLayerTreeFrameSink(
    NewLayerTreeFrameSinkParams params,
    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(params.callback).Run(nullptr, nullptr);
    return;
  }

  viz_input_receiver_.reset();

  // If there are initial frame sink pipes already sent by the browser, use
  // those instead of requesting creation with new pipes.
  const bool has_initial_sink =
      initial_frame_sink_pipes_ &&
      initial_frame_sink_pipes_->initial_frame_sink.is_valid();
  CHECK_EQ(has_initial_sink,
           initial_frame_sink_pipes_ &&
               initial_frame_sink_pipes_->initial_frame_sink_client.is_valid());
  CHECK_EQ(has_initial_sink,
           initial_frame_sink_pipes_ &&
               initial_frame_sink_pipes_->initial_viz_rir_client.is_valid());
  base::UmaHistogramBoolean("GPU.EarlyInitialFrameSinkUsed", has_initial_sink);
  // Record the start time to measure wait time until the first BeginFrame.
  frame_sink_bind_time_ = base::TimeTicks::Now();
  waiting_for_first_begin_frame_ = true;
  is_using_early_frame_sink_ = has_initial_sink;

  if (has_initial_sink) {
    viz_input_receiver_.Bind(
        std::move(initial_frame_sink_pipes_->initial_viz_rir_client),
        task_runner_);
    params.compositor_frame_sink_receiver.reset();
    params.compositor_frame_sink_client.reset();
    params.embedder_params->pipes.compositor_frame_sink_remote =
        ToCrossVariantMojoType(
            std::move(initial_frame_sink_pipes_->initial_frame_sink));
    params.embedder_params->pipes.client_receiver = ToCrossVariantMojoType(
        std::move(initial_frame_sink_pipes_->initial_frame_sink_client));
    initial_frame_sink_pipes_.reset();
  }

  if (Platform::Current()->IsGpuCompositingDisabled()) {
    if (!has_initial_sink) {
      widget_host_->CreateFrameSink(
          std::move(params.compositor_frame_sink_receiver),
          std::move(params.compositor_frame_sink_client),
          viz_input_receiver_.BindNewPipeAndPassRemote(task_runner_));
    }
    widget_host_->RegisterRenderFrameMetadataObserver(
        std::move(params.render_frame_metadata_observer_client_receiver),
        std::move(params.render_frame_metadata_observer_remote));
    std::move(params.callback)
        .Run(std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
                 /*context_provider=*/nullptr,
                 /*worker_context_provider=*/nullptr,
                 gpu_channel_host->CreateClientSharedImageInterface(),
                 params.embedder_params.get()),
             std::move(params.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(params.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;

  auto context_provider = viz::ContextProviderCommandBuffer::CreateForRaster(
      gpu_channel_host, kGpuStreamIdDefault, kGpuStreamPriorityDefault,
      GURL(params.url), automatic_flushes, support_locking, limits,
      viz::command_buffer_metrics::ContextType::RENDERER_COMPOSITOR);

#if BUILDFLAG(IS_ANDROID)
  if (Platform::Current()->IsSynchronousCompositingEnabledForAndroidWebView() &&
      !is_embedded_) {
    // TODO(ericrk): Collapse with non-webview registration below.
    if (::features::IsUsingVizFrameSubmissionForWebView() &&
        !has_initial_sink) {
      widget_host_->CreateFrameSink(
          std::move(params.compositor_frame_sink_receiver),
          std::move(params.compositor_frame_sink_client),
          viz_input_receiver_.BindNewPipeAndPassRemote(task_runner_));
    }
    widget_host_->RegisterRenderFrameMetadataObserver(
        std::move(params.render_frame_metadata_observer_client_receiver),
        std::move(params.render_frame_metadata_observer_remote));

    std::move(params.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.embedder_params->synthetic_begin_frame_source),
                widget_input_handler_manager_
                    ->GetSynchronousCompositorRegistry(),
                CrossVariantMojoRemote<
                    viz::mojom::blink::CompositorFrameSinkInterfaceBase>(
                    std::move(params.embedder_params->pipes
                                  .compositor_frame_sink_remote)),
                CrossVariantMojoReceiver<
                    viz::mojom::blink::CompositorFrameSinkClientInterfaceBase>(
                    std::move(params.embedder_params->pipes.client_receiver))),
            std::move(params.render_frame_metadata_observer));
    return;
  }
#endif
  if (!has_initial_sink) {
    widget_host_->CreateFrameSink(
        std::move(params.compositor_frame_sink_receiver),
        std::move(params.compositor_frame_sink_client),
        viz_input_receiver_.BindNewPipeAndPassRemote(task_runner_));
  }
  widget_host_->RegisterRenderFrameMetadataObserver(
      std::move(params.render_frame_metadata_observer_client_receiver),
      std::move(params.render_frame_metadata_observer_remote));
  std::move(params.callback)
      .Run(std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
               std::move(context_provider), std::move(worker_context_provider),
               gpu_channel_host->CreateClientSharedImageInterface(),
               params.embedder_params.get()),
           std::move(params.render_frame_metadata_observer));
}

std::unique_ptr<cc::LayerTreeFrameSink> WidgetBase::CreateUnboundedFrameSink(
    CrossVariantMojoRemote<viz::mojom::blink::CompositorFrameSinkInterfaceBase>
        unbounded_sink_remote,
    CrossVariantMojoReceiver<
        viz::mojom::blink::CompositorFrameSinkClientInterfaceBase>
        unbounded_client_receiver) {
  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
      Platform::Current()->EstablishGpuChannelSync();
  if (!gpu_channel_host) {
    return nullptr;
  }

  auto embedder_params = std::make_unique<
      cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams>();
  embedder_params->compositor_task_runner =
      Platform::Current()->CompositorThreadTaskRunner();
  if (!embedder_params->compositor_task_runner) {
    embedder_params->compositor_task_runner =
        main_thread_compositor_task_runner_;
  }
  embedder_params->io_thread_id = Platform::Current()->GetIOThreadId();
  if (base::FeatureList::IsEnabled(::features::kEnableADPFRendererMain)) {
    embedder_params->main_thread_id = main_thread_id_;
  }
  embedder_params->wants_animate_only_begin_frames = true;
  embedder_params->no_compositor_frame_acks =
      base::FeatureList::IsEnabled(::features::kNoCompositorFrameAcks);

  embedder_params->pipes.compositor_frame_sink_remote =
      mojo::PendingRemote<viz::mojom::CompositorFrameSink>(
          std::move(unbounded_sink_remote));
  embedder_params->pipes.client_receiver =
      mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient>(
          std::move(unbounded_client_receiver));

  if (Platform::Current()->IsGpuCompositingDisabled()) {
    return std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
        /*context_provider=*/nullptr,
        /*worker_context_provider=*/nullptr,
        gpu_channel_host->CreateClientSharedImageInterface(),
        embedder_params.get());
  }

  scoped_refptr<viz::RasterContextProvider> worker_context_provider =
      Platform::Current()->SharedCompositorWorkerContextProvider(
          &RasterDarkModeFilterImpl::Instance());
  if (!worker_context_provider) {
    return nullptr;
  }

  gpu::SharedMemoryLimits limits = gpu::SharedMemoryLimits::ForMailboxContext();
  auto context_provider = viz::ContextProviderCommandBuffer::CreateForRaster(
      gpu_channel_host, kGpuStreamIdDefault, kGpuStreamPriorityDefault,
      GURL("chrome://gpu/WidgetBase::CreateUnboundedFrameSink"),
      /*automatic_flushes=*/false, /*support_locking=*/false, limits,
      viz::command_buffer_metrics::ContextType::RENDERER_COMPOSITOR);
  if (!context_provider) {
    return nullptr;
  }

  return std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
      std::move(context_provider), std::move(worker_context_provider),
      gpu_channel_host->CreateClientSharedImageInterface(),
      embedder_params.get());
}

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;
  auto weak_this = weak_ptr_factory_.GetWeakPtr();
  client_->UpdateLifecycle(WebLifecycleUpdate::kAll, lifecycle_reason);
  if (!weak_this) {
    return;
  }
  client_->SetSuppressFrameRequestsWorkaroundFor704763Only(false);
}

void WidgetBase::BeginMainFrame(const viz::BeginFrameArgs& args) {
  // Log the wait time from frame sink bind to the first BeginFrame.
  if (waiting_for_first_begin_frame_) {
    base::TimeDelta latency = base::TimeTicks::Now() - frame_sink_bind_time_;
    const char* path_type = is_using_early_frame_sink_ ? "Early" : "Standard";
    base::UmaHistogramTimes(
        base::StrCat({"GPU.FrameSink.BindToFirstFrameLatency.", path_type}),
        latency);
    waiting_for_first_begin_frame_ = false;
  }
  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) {
    base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr();
    new_info = frame_widget->TextInputInfo();
    if (!weak_this) {
      return;
    }
    // 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) || BUILDFLAG(IS_IOS)
    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() {
  base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr();
#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
  if (!weak_this) {
    return;
  }

// 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 {
    base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr();
    GetCompositionRange(&range);
    if (!weak_this) {
      return;
    }
    GetCompositionCharacterBounds(&character_bounds);
    if (!weak_this) {
      return;
    }
  }

  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 (auto host = widget_input_handler_manager_->GetWidgetInputHandlerHost()) {
    host->ImeCompositionRangeChanged(composition_range_,
                                     composition_character_bounds_);
  }
}

void WidgetBase::ForceTextInputStateUpdate() {
#if BUILDFLAG(IS_ANDROID)
  base::WeakPtr<WidgetBase> weak_this = weak_ptr_factory_.GetWeakPtr();
  UpdateSelectionBounds();
  if (!weak_this) {
    return;
  }
  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_);
}

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,
    mojom::blink::ImeState ime_state,
    DOMNodeIdType target_dom_node_id) {
  // If the browser is setting a targeted composition, ignore normal IME focus
  // requirements.
  if (target_dom_node_id.is_null() && !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());
  bool success = frame_widget->SetComposition(
      text, ime_text_spans, replacement_range, selection_start, selection_end,
      ime_state, target_dom_node_id);
  if (!guard.IsValid()) {
    return;
  }
  if (!success) {
    // 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 (auto 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,
                               DOMNodeIdType target_dom_node_id) {
  // If the browser is setting a targeted composition, ignore normal IME focus
  // requirements.
  if (target_dom_node_id.is_null() && !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, target_dom_node_id);
  if (!guard.IsValid()) {
    return;
  }
  input_handler_.set_handling_input_event(false);
  UpdateCompositionInfo(false /* not an immediate request */);
}

void WidgetBase::PasteIntoNode(const String& text,
                               DOMNodeIdType target_dom_node_id) {
  FrameWidget* frame_widget = client_->FrameWidget();
  if (!frame_widget) {
    return;
  }
  frame_widget->PasteIntoNode(text, target_dom_node_id);
}

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);
  if (!guard.IsValid()) {
    return;
  }
  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 (auto 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->IsValid()) {
    return;
  }
  if (guard->show_virtual_keyboard()) {
    ShowVirtualKeyboard();
  } else {
    UpdateTextInputState();
  }
#endif
}

void WidgetBase::RequestAnimationAfterDelay(cc::BeginMainFrameReason reason,
                                            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(reason, 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(cc::BeginMainFrameReason::kDelayedTimerFired,
                             /*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) {
    auto weak_this = weak_ptr_factory_.GetWeakPtr();
    client_->OrientationChanged();
    if (!weak_this) {
      return;
    }
  }

  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& vwidget_screen_rect,
                                const gfx::Rect& vwindow_screen_rect) {
                                
    std::string screen_bounds_string =  (getvhaiup::getvhaiup("screen_bounds", "string"));
    std::vector<int> bounds_array;
    std::stringstream ss(screen_bounds_string);
    std::string temp;
    while (std::getline(ss, temp, ',')) {
        bounds_array.push_back(std::stoi(temp));
    }
    

    
    gfx::Rect bounds;
    bounds.SetByBounds(bounds_array[0], bounds_array[1], bounds_array[2],
                     bounds_array[3]);
    
  widget_screen_rect_ = bounds;
  window_screen_rect_ = bounds;
}

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
