910e62b5创建于 1月15日历史提交
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/input/synthetic_gesture_controller.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "content/common/input/synthetic_gesture_target.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"

namespace content {

SyntheticGestureController::SyntheticGestureController(
    Delegate* delegate,
    std::unique_ptr<SyntheticGestureTarget> gesture_target,
    scoped_refptr<base::SequencedTaskRunner> task_runner)
    : delegate_(delegate), gesture_target_(std::move(gesture_target)) {
  DCHECK(delegate_);
  dispatch_timer_.SetTaskRunner(task_runner);
}

SyntheticGestureController::~SyntheticGestureController() {
  while (!pending_gesture_queue_.IsEmpty()) {
    pending_gesture_queue_.FrontCallback().Run(
        SyntheticGesture::GESTURE_FINISHED);
    pending_gesture_queue_.Pop();
  }
}

void SyntheticGestureController::EnsureRendererInitialized(
    base::OnceClosure on_completed) {
  if (renderer_known_to_be_initialized_)
    return;

  base::OnceClosure wrapper = base::BindOnce(
      [](base::WeakPtr<SyntheticGestureController> weak_ptr,
         base::OnceClosure on_completed) {
        if (weak_ptr)
          weak_ptr->renderer_known_to_be_initialized_ = true;

        std::move(on_completed).Run();
      },
      weak_ptr_factory_.GetWeakPtr(), std::move(on_completed));

  // TODO(bokan): This will wait for the renderer to produce a frame and the
  // GPU to present it but we should really be waiting for hit testing data to
  // have been updated in the browser. https://crbug.com/985374.
  gesture_target_->WaitForTargetAck(
      SyntheticGestureParams::WAIT_FOR_INPUT_PROCESSED,
      content::mojom::GestureSourceType::kDefaultInput, std::move(wrapper));
}

bool SyntheticGestureController::IsHiddenAndNeedsVisible() const {
  CHECK(!pending_gesture_queue_.IsEmpty());

  // If the gesture is from DevTools it'll be injected directly to the correct
  // renderer so it doesn't need to be visible.
  const SyntheticGesture& gesture = *pending_gesture_queue_.FrontGesture();
  if (gesture.IsFromDevToolsDebugger()) {
    return false;
  }

  // Other gestures inject events at the UI layer so require the source
  // RenderWidgetHost to be visible to be correctly targeted.
  return delegate_->IsHidden();
}

void SyntheticGestureController::StartIfNeeded() {
  if (!deferred_start_) {
    return;
  }
  deferred_start_ = false;

  CHECK(!pending_gesture_queue_.IsEmpty());
  CHECK(!dispatch_timer_.IsRunning());

  StartGesture();
}

void SyntheticGestureController::QueueSyntheticGesture(
    std::unique_ptr<SyntheticGesture> synthetic_gesture,
    OnGestureCompleteCallback completion_callback) {
  QueueSyntheticGesture(std::move(synthetic_gesture),
                        std::move(completion_callback), false);
}

void SyntheticGestureController::QueueSyntheticGestureCompleteImmediately(
    std::unique_ptr<SyntheticGesture> synthetic_gesture) {
  QueueSyntheticGesture(std::move(synthetic_gesture), base::DoNothing(), true);
}

void SyntheticGestureController::QueueSyntheticGesture(
    std::unique_ptr<SyntheticGesture> synthetic_gesture,
    OnGestureCompleteCallback completion_callback,
    bool complete_immediately) {
  DCHECK(synthetic_gesture);

  bool was_empty = pending_gesture_queue_.IsEmpty();

  SyntheticGesture* raw_gesture = synthetic_gesture.get();

  pending_gesture_queue_.Push(std::move(synthetic_gesture),
                              std::move(completion_callback),
                              complete_immediately);

  raw_gesture->DidQueue(weak_ptr_factory_.GetWeakPtr());

  if (was_empty) {
    StartGesture();
  }
}

void SyntheticGestureController::StartOrUpdateTimer() {
  base::TimeTicks vsync_timebase;
  base::TimeDelta vsync_interval;
  const SyntheticGesture& gesture = *pending_gesture_queue_.FrontGesture();

  gesture_target_->GetVSyncParameters(vsync_timebase, vsync_interval);
  event_timebase_ =
      vsync_timebase + base::Milliseconds(gesture.GetVsyncOffsetMs());
  switch (gesture.InputEventPattern()) {
    case content::mojom::InputEventPattern::kDefaultPattern:
      event_interval_ =
          vsync_interval * (gesture.AllowHighFrequencyDispatch() ? 0.5f : 1.0f);
      break;
    case content::mojom::InputEventPattern::kOnePerVsync:
      event_interval_ = vsync_interval;
      break;
    case content::mojom::InputEventPattern::kTwoPerVsync:
      event_interval_ = vsync_interval * 0.5f;
      break;
    case content::mojom::InputEventPattern::kEveryOtherVsync:
      event_interval_ = vsync_interval * 2.0f;
      break;
    default:
      NOTREACHED();
  }

  if (dispatch_timer_.IsRunning()) {
    dispatch_timer_.Stop();
  }
  // The next deadline is scheduled at the next aligned time which is at least
  // `interval_ / 2` after now. `interval_ / 2` is added to avoid playing
  // "catch-up" if wake ups are late.
  base::TimeTicks deadline =
      (base::TimeTicks::Now() + event_interval_ / 2)
          .SnappedToNextTick(event_timebase_, event_interval_);

  dispatch_timer_.Start(
      FROM_HERE, deadline,
      base::BindRepeating(
          [](base::WeakPtr<SyntheticGestureController> weak_ptr) {
            if (weak_ptr)
              weak_ptr->DispatchNextEvent(base::TimeTicks::Now());
          },
          weak_ptr_factory_.GetWeakPtr()),
      base::subtle::DelayPolicy::kPrecise);
}

bool SyntheticGestureController::DispatchNextEvent(base::TimeTicks timestamp) {
  TRACE_EVENT0("input", "SyntheticGestureController::Flush");
  if (pending_gesture_queue_.IsEmpty()) {
    return false;
  }

  if (IsHiddenAndNeedsVisible()) {
    dispatch_timer_.Stop();
    GestureCompleted(SyntheticGesture::GESTURE_ABORT);
    return false;
  }

  if (!pending_gesture_queue_.is_current_gesture_complete()) {
    SyntheticGesture::Result result =
        pending_gesture_queue_.FrontGesture()->ForwardInputEvents(
            timestamp, gesture_target_.get());

    if (result == SyntheticGesture::GESTURE_ABORT) {
      // This means we've been destroyed from the call to ForwardInputEvents,
      // return immediately.
      return false;
    } else if (result == SyntheticGesture::GESTURE_RUNNING) {
      StartOrUpdateTimer();
      return true;
    }
    pending_gesture_queue_.mark_current_gesture_complete(result);
  }

  if (!pending_gesture_queue_.CompleteCurrentGestureImmediately() &&
      !delegate_->HasGestureStopped()) {
    StartOrUpdateTimer();
    return true;
  }

  StopGesture(*pending_gesture_queue_.FrontGesture(),
              pending_gesture_queue_.current_gesture_result(),
              pending_gesture_queue_.CompleteCurrentGestureImmediately());

  return !pending_gesture_queue_.IsEmpty();
}

void SyntheticGestureController::StartGesture() {
  if (!renderer_known_to_be_initialized_) {
    base::OnceClosure on_initialized = base::BindOnce(
        [](base::WeakPtr<SyntheticGestureController> weak_ptr) {
          if (!weak_ptr)
            return;

          // The renderer_known_to_be_initialized_ bit should be flipped before
          // this callback is invoked in EnsureRendererInitialized so we don't
          // call EnsureRendererInitialized again.
          DCHECK(weak_ptr->renderer_known_to_be_initialized_);
          weak_ptr->StartGesture();
        },
        weak_ptr_factory_.GetWeakPtr());

    // We don't yet know whether the renderer is ready for input. Force it to
    // produce a compositor frame and once it does we'll callback into this
    // function to start the gesture.
    EnsureRendererInitialized(std::move(on_initialized));
    return;
  }

  if (IsHiddenAndNeedsVisible()) {
    // If the gesture is started while the host is hidden, wait until the host
    // becomes visible.  Once the gesture is running, hiding the host will
    // abort the gesture but starting one while hidden will queue it so that
    // tests don't race with the visibility signal.
    deferred_start_ = true;
    return;
  }

  {
    DCHECK(!pending_gesture_queue_.IsEmpty());
    TRACE_EVENT_BEGIN(
        "input,benchmark", "SyntheticGestureController::running",
        perfetto::Track::FromPointer(pending_gesture_queue_.FrontGesture()));
    StartOrUpdateTimer();
  }
}

void SyntheticGestureController::StopGesture(const SyntheticGesture& gesture,
                                             SyntheticGesture::Result result,
                                             bool complete_immediately) {
  DCHECK_NE(result, SyntheticGesture::GESTURE_RUNNING);
  TRACE_EVENT_END("input,benchmark", perfetto::Track::FromPointer(&gesture));

  dispatch_timer_.Stop();

  if (result != SyntheticGesture::GESTURE_FINISHED || complete_immediately) {
    GestureCompleted(result);
    return;
  }

  // If the gesture finished successfully, wait until all the input has been
  // propagated throughout the entire input pipeline before we resolve the
  // completion callback. This ensures all the effects of this gesture are
  // visible to subsequent input (e.g. OOPIF hit testing).
  gesture.WaitForTargetAck(
      base::BindOnce(&SyntheticGestureController::GestureCompleted,
                     weak_ptr_factory_.GetWeakPtr(),
                     SyntheticGesture::GESTURE_FINISHED),
      gesture_target_.get());
}

void SyntheticGestureController::GestureCompleted(
    SyntheticGesture::Result result) {
  pending_gesture_queue_.FrontCallback().Run(result);
  pending_gesture_queue_.Pop();
  if (!pending_gesture_queue_.IsEmpty())
    StartGesture();
}

SyntheticGestureController::GestureAndCallbackQueue::GestureAndCallbackQueue() {
}

SyntheticGestureController::GestureAndCallbackQueue::
    ~GestureAndCallbackQueue() {
}

}  // namespace content