blob: 3b0ef487af7a7373d1898ce65586748fba3f0cd3 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h"
#include <utility>
#include "base/bind.h"
#include "base/containers/circular_deque.h"
#include "base/metrics/histogram_macros.h"
#include "cc/metrics/event_metrics.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
#include "third_party/blink/public/common/input/web_input_event_attribution.h"
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
namespace blink {
namespace {
constexpr base::TimeDelta kMaxRafDelay =
base::TimeDelta::FromMilliseconds(5 * 1000);
class QueuedClosure : public MainThreadEventQueueTask {
public:
QueuedClosure(base::OnceClosure closure) : closure_(std::move(closure)) {}
~QueuedClosure() override {}
FilterResult FilterNewEvent(MainThreadEventQueueTask* other_task) override {
return other_task->IsWebInputEvent() ? FilterResult::KeepIterating
: FilterResult::StopIterating;
}
bool IsWebInputEvent() const override { return false; }
void Dispatch(MainThreadEventQueue*) override { std::move(closure_).Run(); }
private:
base::OnceClosure closure_;
};
// Time interval at which touchmove events during scroll will be skipped
// during rAF signal.
constexpr base::TimeDelta kAsyncTouchMoveInterval =
base::TimeDelta::FromMilliseconds(200);
} // namespace
class QueuedWebInputEvent : public MainThreadEventQueueTask {
public:
QueuedWebInputEvent(std::unique_ptr<WebCoalescedInputEvent> event,
bool originally_cancelable,
HandledEventCallback callback,
bool known_by_scheduler,
const WebInputEventAttribution& attribution,
std::unique_ptr<cc::EventMetrics> metrics)
: event_(std::move(event)),
originally_cancelable_(originally_cancelable),
callback_(std::move(callback)),
known_by_scheduler_count_(known_by_scheduler ? 1 : 0),
attribution_(attribution),
metrics_(std::move(metrics)) {}
~QueuedWebInputEvent() override {}
static std::unique_ptr<QueuedWebInputEvent> CreateForRawEvent(
std::unique_ptr<WebCoalescedInputEvent> raw_event,
const WebInputEventAttribution& attribution,
const cc::EventMetrics* original_metrics) {
DCHECK_EQ(raw_event->Event().GetType(),
WebInputEvent::Type::kPointerRawUpdate);
std::unique_ptr<cc::EventMetrics> metrics =
cc::EventMetrics::CreateFromExisting(
raw_event->Event().GetTypeAsUiEventType(), base::nullopt,
raw_event->Event().GetScrollInputType(),
cc::EventMetrics::DispatchStage::kRendererCompositorFinished,
original_metrics);
return std::make_unique<QueuedWebInputEvent>(
std::move(raw_event), false, HandledEventCallback(), false, attribution,
std::move(metrics));
}
bool AreCoalescablePointerRawUpdateEvents(
const QueuedWebInputEvent& other_event) {
// There is no pointermove at this point in the queue.
DCHECK(event_->Event().GetType() != WebInputEvent::Type::kPointerMove &&
other_event.event_->Event().GetType() !=
WebInputEvent::Type::kPointerMove);
// Events with modifiers differing by kRelativeMotionEvent should not be
// coalesced. In case of a pointer lock, kRelativeMotionEvent is sent
// when the cursor is recentered. Events post the recentered event have
// a big delta compared to the previous events and hence should not be
// coalesced.
return event_->Event().GetType() ==
WebInputEvent::Type::kPointerRawUpdate &&
other_event.event_->Event().GetType() ==
WebInputEvent::Type::kPointerRawUpdate &&
((event_->Event().GetModifiers() &
blink::WebInputEvent::Modifiers::kRelativeMotionEvent) ==
(other_event.event_->Event().GetModifiers() &
blink::WebInputEvent::Modifiers::kRelativeMotionEvent));
}
FilterResult FilterNewEvent(MainThreadEventQueueTask* other_task) override {
if (!other_task->IsWebInputEvent())
return FilterResult::StopIterating;
QueuedWebInputEvent* other_event =
static_cast<QueuedWebInputEvent*>(other_task);
if (other_event->event_->Event().GetType() ==
WebInputEvent::Type::kTouchScrollStarted) {
return HandleTouchScrollStartQueued();
}
if (!event_->Event().IsSameEventClass(other_event->event_->Event()))
return FilterResult::KeepIterating;
if (!event_->CanCoalesceWith(*other_event->event_)) {
// Two pointerevents may not be able to coalesce but we should continue
// looking further down the queue if both of them were rawupdate or move
// events and only their pointer_type, id, or event_type was different.
if (AreCoalescablePointerRawUpdateEvents(*other_event))
return FilterResult::KeepIterating;
return FilterResult::StopIterating;
}
// If the other event was blocking store its callback to call later, but we
// also save the trace_id to ensure the flow events correct show the
// critical path.
if (other_event->callback_) {
blocking_coalesced_callbacks_.emplace_back(
std::move(other_event->callback_),
other_event->event_->latency_info().trace_id());
}
known_by_scheduler_count_ += other_event->known_by_scheduler_count_;
event_->CoalesceWith(*other_event->event_);
// The newest event (|other_item|) always wins when updating fields.
originally_cancelable_ = other_event->originally_cancelable_;
return FilterResult::CoalescedEvent;
}
bool IsWebInputEvent() const override { return true; }
void Dispatch(MainThreadEventQueue* queue) override {
HandledEventCallback callback =
base::BindOnce(&QueuedWebInputEvent::HandledEvent,
base::Unretained(this), base::RetainedRef(queue));
if (!queue->HandleEventOnMainThread(
*event_, attribution(), std::move(metrics_), std::move(callback))) {
// The |callback| won't be run, so our stored |callback_| should run
// indicating error.
HandledEvent(queue, mojom::blink::InputEventResultState::kNotConsumed,
event_->latency_info(), nullptr, base::nullopt);
}
}
void HandledEvent(MainThreadEventQueue* queue,
mojom::blink::InputEventResultState ack_result,
const ui::LatencyInfo& latency_info,
mojom::blink::DidOverscrollParamsPtr overscroll,
base::Optional<cc::TouchAction> touch_action) {
if (callback_) {
std::move(callback_).Run(ack_result, latency_info, std::move(overscroll),
touch_action);
} else {
DCHECK(!overscroll) << "Unexpected overscroll for un-acked event";
}
if (!blocking_coalesced_callbacks_.empty()) {
ui::LatencyInfo coalesced_latency_info = latency_info;
coalesced_latency_info.set_coalesced();
for (auto&& callback : blocking_coalesced_callbacks_) {
coalesced_latency_info.set_trace_id(callback.second);
std::move(callback.first)
.Run(ack_result, coalesced_latency_info, nullptr, base::nullopt);
}
}
if (queue->main_thread_scheduler_) {
// TODO(dtapuska): Change the scheduler API to take into account number of
// events processed.
for (size_t i = 0; i < known_by_scheduler_count_; ++i) {
queue->main_thread_scheduler_->DidHandleInputEventOnMainThread(
event_->Event(),
ack_result == blink::mojom::InputEventResultState::kConsumed
? WebInputEventResult::kHandledApplication
: WebInputEventResult::kNotHandled);
}
}
}
bool originally_cancelable() const { return originally_cancelable_; }
const WebInputEventAttribution& attribution() const { return attribution_; }
const WebInputEvent& Event() const { return event_->Event(); }
WebCoalescedInputEvent* mutable_coalesced_event() { return event_.get(); }
private:
FilterResult HandleTouchScrollStartQueued() {
// A TouchScrollStart will queued after this touch move which will make all
// previous touch moves that are queued uncancelable.
switch (event_->Event().GetType()) {
case WebInputEvent::Type::kTouchMove: {
WebTouchEvent* touch_event =
static_cast<WebTouchEvent*>(event_->EventPointer());
if (touch_event->dispatch_type ==
WebInputEvent::DispatchType::kBlocking) {
touch_event->dispatch_type =
WebInputEvent::DispatchType::kEventNonBlocking;
}
return FilterResult::KeepIterating;
}
case WebInputEvent::Type::kTouchStart:
case WebInputEvent::Type::kTouchEnd:
return FilterResult::StopIterating;
default:
return FilterResult::KeepIterating;
}
}
std::unique_ptr<WebCoalescedInputEvent> event_;
// Contains the pending callbacks to be called, along with their associated
// trace_ids.
base::circular_deque<std::pair<HandledEventCallback, int64_t>>
blocking_coalesced_callbacks_;
// Contains the number of non-blocking events coalesced.
// Whether the received event was originally cancelable or not. The compositor
// input handler can change the event based on presence of event handlers so
// this is the state at which the renderer received the event from the
// browser.
bool originally_cancelable_;
HandledEventCallback callback_;
size_t known_by_scheduler_count_;
const WebInputEventAttribution attribution_;
std::unique_ptr<cc::EventMetrics> metrics_;
};
MainThreadEventQueue::SharedState::SharedState()
: sent_main_frame_request_(false), sent_post_task_(false) {}
MainThreadEventQueue::SharedState::~SharedState() {}
MainThreadEventQueue::MainThreadEventQueue(
MainThreadEventQueueClient* client,
const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
scheduler::WebThreadScheduler* main_thread_scheduler,
bool allow_raf_aligned_input)
: client_(client),
last_touch_start_forced_nonblocking_due_to_fling_(false),
needs_low_latency_(false),
needs_unbuffered_input_for_debugger_(false),
allow_raf_aligned_input_(allow_raf_aligned_input),
main_task_runner_(main_task_runner),
main_thread_scheduler_(main_thread_scheduler) {
raf_fallback_timer_ = std::make_unique<base::OneShotTimer>();
raf_fallback_timer_->SetTaskRunner(main_task_runner);
event_predictor_ = std::make_unique<InputEventPrediction>(
base::FeatureList::IsEnabled(blink::features::kResamplingInputEvents));
}
MainThreadEventQueue::~MainThreadEventQueue() {}
void MainThreadEventQueue::HandleEvent(
std::unique_ptr<WebCoalescedInputEvent> event,
DispatchType original_dispatch_type,
mojom::blink::InputEventResultState ack_result,
const WebInputEventAttribution& attribution,
std::unique_ptr<cc::EventMetrics> metrics,
HandledEventCallback callback) {
TRACE_EVENT2("input", "MainThreadEventQueue::HandleEvent", "dispatch_type",
original_dispatch_type, "event_type", event->Event().GetType());
DCHECK(original_dispatch_type == DispatchType::kBlocking ||
original_dispatch_type == DispatchType::kNonBlocking);
DCHECK(ack_result == mojom::blink::InputEventResultState::kSetNonBlocking ||
ack_result ==
mojom::blink::InputEventResultState::kSetNonBlockingDueToFling ||
ack_result == mojom::blink::InputEventResultState::kNotConsumed);
bool is_blocking =
original_dispatch_type == DispatchType::kBlocking &&
ack_result != mojom::blink::InputEventResultState::kSetNonBlocking;
bool is_wheel = event->Event().GetType() == WebInputEvent::Type::kMouseWheel;
bool is_touch = WebInputEvent::IsTouchEventType(event->Event().GetType());
bool originally_cancelable = false;
if (is_touch) {
WebTouchEvent* touch_event =
static_cast<WebTouchEvent*>(event->EventPointer());
originally_cancelable =
touch_event->dispatch_type == WebInputEvent::DispatchType::kBlocking;
// Adjust the |dispatchType| on the event since the compositor
// determined all event listeners are passive.
if (!is_blocking) {
touch_event->dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
}
if (touch_event->GetType() == WebInputEvent::Type::kTouchStart)
last_touch_start_forced_nonblocking_due_to_fling_ = false;
if (touch_event->touch_start_or_first_touch_move &&
touch_event->dispatch_type == WebInputEvent::DispatchType::kBlocking) {
// If the touch start is forced to be passive due to fling, its following
// touch move should also be passive.
if (ack_result ==
mojom::blink::InputEventResultState::kSetNonBlockingDueToFling ||
last_touch_start_forced_nonblocking_due_to_fling_) {
touch_event->dispatch_type =
WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling;
is_blocking = false;
last_touch_start_forced_nonblocking_due_to_fling_ = true;
}
}
// If the event is non-cancelable ACK it right away.
if (is_blocking &&
touch_event->dispatch_type != WebInputEvent::DispatchType::kBlocking)
is_blocking = false;
}
if (is_wheel) {
WebMouseWheelEvent* wheel_event =
static_cast<WebMouseWheelEvent*>(event->EventPointer());
originally_cancelable =
wheel_event->dispatch_type == WebInputEvent::DispatchType::kBlocking;
if (!is_blocking) {
// Adjust the |dispatchType| on the event since the compositor
// determined all event listeners are passive.
wheel_event->dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
}
}
HandledEventCallback event_callback;
if (is_blocking) {
TRACE_EVENT_INSTANT0("input", "Blocking", TRACE_EVENT_SCOPE_THREAD);
event_callback = std::move(callback);
}
if (has_pointerrawupdate_handlers_) {
if (event->Event().GetType() == WebInputEvent::Type::kMouseMove) {
auto raw_event = std::make_unique<WebCoalescedInputEvent>(
std::make_unique<WebPointerEvent>(
WebInputEvent::Type::kPointerRawUpdate,
static_cast<const WebMouseEvent&>(event->Event())),
event->latency_info());
QueueEvent(QueuedWebInputEvent::CreateForRawEvent(
std::move(raw_event), attribution, metrics.get()));
} else if (event->Event().GetType() == WebInputEvent::Type::kTouchMove) {
const WebTouchEvent& touch_event =
static_cast<const WebTouchEvent&>(event->Event());
for (unsigned i = 0; i < touch_event.touches_length; ++i) {
const WebTouchPoint& touch_point = touch_event.touches[i];
if (touch_point.state == WebTouchPoint::State::kStateMoved) {
auto raw_event = std::make_unique<WebCoalescedInputEvent>(
std::make_unique<WebPointerEvent>(touch_event, touch_point),
event->latency_info());
raw_event->EventPointer()->SetType(
WebInputEvent::Type::kPointerRawUpdate);
QueueEvent(QueuedWebInputEvent::CreateForRawEvent(
std::move(raw_event), attribution, metrics.get()));
}
}
}
}
ui::LatencyInfo cloned_latency_info;
// Clone the latency info if we are calling the callback.
if (callback)
cloned_latency_info = event->latency_info();
auto queued_event = std::make_unique<QueuedWebInputEvent>(
std::move(event), originally_cancelable, std::move(event_callback),
IsForwardedAndSchedulerKnown(ack_result), attribution,
std::move(metrics));
QueueEvent(std::move(queued_event));
if (callback) {
std::move(callback).Run(ack_result, cloned_latency_info, nullptr,
base::nullopt);
}
}
void MainThreadEventQueue::QueueClosure(base::OnceClosure closure) {
bool needs_post_task = false;
std::unique_ptr<QueuedClosure> item(new QueuedClosure(std::move(closure)));
{
base::AutoLock lock(shared_state_lock_);
shared_state_.events_.Enqueue(std::move(item));
needs_post_task = !shared_state_.sent_post_task_;
shared_state_.sent_post_task_ = true;
}
if (needs_post_task)
PostTaskToMainThread();
}
void MainThreadEventQueue::PossiblyScheduleMainFrame() {
bool needs_main_frame = false;
{
base::AutoLock lock(shared_state_lock_);
if (!shared_state_.sent_main_frame_request_ &&
!shared_state_.events_.empty() &&
IsRafAlignedEvent(shared_state_.events_.front())) {
needs_main_frame = true;
shared_state_.sent_main_frame_request_ = true;
}
}
if (needs_main_frame)
SetNeedsMainFrame();
}
void MainThreadEventQueue::DispatchEvents() {
size_t events_to_process;
size_t queue_size;
// Record the queue size so that we only process
// that maximum number of events.
{
base::AutoLock lock(shared_state_lock_);
shared_state_.sent_post_task_ = false;
events_to_process = shared_state_.events_.size();
// Don't process rAF aligned events at tail of queue.
while (events_to_process > 0 &&
!ShouldFlushQueue(shared_state_.events_.at(events_to_process - 1))) {
--events_to_process;
}
}
while (events_to_process--) {
std::unique_ptr<MainThreadEventQueueTask> task;
{
base::AutoLock lock(shared_state_lock_);
if (shared_state_.events_.empty())
return;
task = shared_state_.events_.Pop();
}
HandleEventResampling(task, base::TimeTicks::Now());
// Dispatching the event is outside of critical section.
task->Dispatch(this);
}
// Dispatch all raw move events as well regardless of where they are in the
// queue
{
base::AutoLock lock(shared_state_lock_);
queue_size = shared_state_.events_.size();
}
for (size_t current_task_index = 0; current_task_index < queue_size;
++current_task_index) {
std::unique_ptr<MainThreadEventQueueTask> task;
{
base::AutoLock lock(shared_state_lock_);
while (current_task_index < queue_size &&
current_task_index < shared_state_.events_.size()) {
if (!IsRafAlignedEvent(shared_state_.events_.at(current_task_index)))
break;
current_task_index++;
}
if (current_task_index >= queue_size ||
current_task_index >= shared_state_.events_.size())
break;
if (IsRawUpdateEvent(shared_state_.events_.at(current_task_index))) {
task = shared_state_.events_.remove(current_task_index);
--queue_size;
--current_task_index;
} else if (!IsRafAlignedEvent(
shared_state_.events_.at(current_task_index))) {
// Do not pass a non-rAF-aligned event to avoid delivering raw move
// events and down/up events out of order to js.
break;
}
}
// Dispatching the event is outside of critical section.
if (task)
task->Dispatch(this);
}
PossiblyScheduleMainFrame();
}
static bool IsAsyncTouchMove(
const std::unique_ptr<MainThreadEventQueueTask>& queued_item) {
if (!queued_item->IsWebInputEvent())
return false;
const QueuedWebInputEvent* event =
static_cast<const QueuedWebInputEvent*>(queued_item.get());
if (event->Event().GetType() != WebInputEvent::Type::kTouchMove)
return false;
const WebTouchEvent& touch_event =
static_cast<const WebTouchEvent&>(event->Event());
return touch_event.moved_beyond_slop_region &&
!event->originally_cancelable();
}
void MainThreadEventQueue::RafFallbackTimerFired() {
// This fallback fires when the browser doesn't produce main frames for a
// variety of reasons. (eg. Tab gets hidden). We definitely don't want input
// to stay forever in the queue.
DispatchRafAlignedInput(base::TimeTicks::Now());
}
void MainThreadEventQueue::ClearRafFallbackTimerForTesting() {
raf_fallback_timer_.reset();
}
void MainThreadEventQueue::DispatchRafAlignedInput(base::TimeTicks frame_time) {
if (raf_fallback_timer_)
raf_fallback_timer_->Stop();
size_t queue_size_at_start;
// Record the queue size so that we only process
// that maximum number of events.
{
base::AutoLock lock(shared_state_lock_);
shared_state_.sent_main_frame_request_ = false;
queue_size_at_start = shared_state_.events_.size();
}
while (queue_size_at_start--) {
std::unique_ptr<MainThreadEventQueueTask> task;
{
base::AutoLock lock(shared_state_lock_);
if (shared_state_.events_.empty())
return;
if (IsRafAlignedEvent(shared_state_.events_.front())) {
// Throttle touchmoves that are async.
if (IsAsyncTouchMove(shared_state_.events_.front())) {
if (shared_state_.events_.size() == 1 &&
frame_time < shared_state_.last_async_touch_move_timestamp_ +
kAsyncTouchMoveInterval) {
break;
}
shared_state_.last_async_touch_move_timestamp_ = frame_time;
}
}
task = shared_state_.events_.Pop();
}
HandleEventResampling(task, frame_time);
// Dispatching the event is outside of critical section.
task->Dispatch(this);
}
PossiblyScheduleMainFrame();
}
void MainThreadEventQueue::PostTaskToMainThread() {
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MainThreadEventQueue::DispatchEvents, this));
}
void MainThreadEventQueue::QueueEvent(
std::unique_ptr<MainThreadEventQueueTask> event) {
bool is_raf_aligned = IsRafAlignedEvent(event);
bool needs_main_frame = false;
bool needs_post_task = false;
// Record the input event's type prior to enqueueing so that the scheduler
// can be notified of its dispatch (if the event is not coalesced).
bool is_input_event = event->IsWebInputEvent();
WebInputEvent::Type input_event_type = WebInputEvent::Type::kUndefined;
WebInputEventAttribution attribution;
if (is_input_event) {
auto* queued_input_event =
static_cast<const QueuedWebInputEvent*>(event.get());
input_event_type = queued_input_event->Event().GetType();
attribution = queued_input_event->attribution();
}
{
base::AutoLock lock(shared_state_lock_);
if (shared_state_.events_.Enqueue(std::move(event)) ==
MainThreadEventQueueTaskList::EnqueueResult::kEnqueued) {
if (!is_raf_aligned) {
needs_post_task = !shared_state_.sent_post_task_;
shared_state_.sent_post_task_ = true;
} else {
needs_main_frame = !shared_state_.sent_main_frame_request_;
shared_state_.sent_main_frame_request_ = true;
}
// Notify the scheduler that we'll enqueue a task to the main thread.
if (is_input_event && main_thread_scheduler_) {
main_thread_scheduler_->WillPostInputEventToMainThread(input_event_type,
attribution);
}
}
}
if (needs_post_task)
PostTaskToMainThread();
if (needs_main_frame)
SetNeedsMainFrame();
}
bool MainThreadEventQueue::IsRawUpdateEvent(
const std::unique_ptr<MainThreadEventQueueTask>& item) const {
return item->IsWebInputEvent() &&
static_cast<const QueuedWebInputEvent*>(item.get())
->Event()
.GetType() == WebInputEvent::Type::kPointerRawUpdate;
}
bool MainThreadEventQueue::ShouldFlushQueue(
const std::unique_ptr<MainThreadEventQueueTask>& item) const {
if (IsRawUpdateEvent(item))
return false;
return !IsRafAlignedEvent(item);
}
bool MainThreadEventQueue::IsRafAlignedEvent(
const std::unique_ptr<MainThreadEventQueueTask>& item) const {
if (!item->IsWebInputEvent())
return false;
const QueuedWebInputEvent* event =
static_cast<const QueuedWebInputEvent*>(item.get());
switch (event->Event().GetType()) {
case WebInputEvent::Type::kMouseMove:
case WebInputEvent::Type::kMouseWheel:
case WebInputEvent::Type::kTouchMove:
return allow_raf_aligned_input_ && !needs_low_latency_ &&
!needs_low_latency_until_pointer_up_ &&
!needs_unbuffered_input_for_debugger_;
default:
return false;
}
}
void MainThreadEventQueue::HandleEventResampling(
const std::unique_ptr<MainThreadEventQueueTask>& item,
base::TimeTicks frame_time) {
if (item->IsWebInputEvent() && allow_raf_aligned_input_ && event_predictor_) {
QueuedWebInputEvent* event = static_cast<QueuedWebInputEvent*>(item.get());
event_predictor_->HandleEvents(*event->mutable_coalesced_event(),
frame_time);
}
}
bool MainThreadEventQueue::HandleEventOnMainThread(
const WebCoalescedInputEvent& event,
const WebInputEventAttribution& attribution,
std::unique_ptr<cc::EventMetrics> metrics,
HandledEventCallback handled_callback) {
// Notify the scheduler that the main thread is about to execute handlers.
if (main_thread_scheduler_) {
main_thread_scheduler_->WillHandleInputEventOnMainThread(
event.Event().GetType(), attribution);
}
bool handled = false;
if (client_) {
handled = client_->HandleInputEvent(event, std::move(metrics),
std::move(handled_callback));
}
if (needs_low_latency_until_pointer_up_) {
// Reset the needs low latency until pointer up mode if necessary.
switch (event.Event().GetType()) {
case WebInputEvent::Type::kMouseUp:
case WebInputEvent::Type::kTouchCancel:
case WebInputEvent::Type::kTouchEnd:
case WebInputEvent::Type::kPointerCancel:
case WebInputEvent::Type::kPointerUp:
needs_low_latency_until_pointer_up_ = false;
break;
default:
break;
}
}
return handled;
}
void MainThreadEventQueue::SetNeedsMainFrame() {
if (main_task_runner_->BelongsToCurrentThread()) {
if (raf_fallback_timer_) {
raf_fallback_timer_->Start(
FROM_HERE, kMaxRafDelay,
base::BindOnce(&MainThreadEventQueue::RafFallbackTimerFired, this));
}
if (client_)
client_->SetNeedsMainFrame();
if (main_thread_scheduler_)
main_thread_scheduler_->OnMainFrameRequestedForInput();
return;
}
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MainThreadEventQueue::SetNeedsMainFrame, this));
}
void MainThreadEventQueue::ClearClient() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
client_ = nullptr;
raf_fallback_timer_.reset();
}
void MainThreadEventQueue::SetNeedsLowLatency(bool low_latency) {
needs_low_latency_ = low_latency;
}
void MainThreadEventQueue::SetNeedsUnbufferedInputForDebugger(bool unbuffered) {
needs_unbuffered_input_for_debugger_ = unbuffered;
}
void MainThreadEventQueue::HasPointerRawUpdateEventHandlers(bool has_handlers) {
has_pointerrawupdate_handlers_ = has_handlers;
}
void MainThreadEventQueue::RequestUnbufferedInputEvents() {
needs_low_latency_until_pointer_up_ = true;
}
} // namespace blink