| Index: content/browser/devtools/protocol/emulation_handler.cc
|
| diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc
|
| index a2f4cbd8082f9f2add3bce35701c566bbb2a45e0..e8916e4208ca2e6c2ba366ced7b1c17d918e3d80 100644
|
| --- a/content/browser/devtools/protocol/emulation_handler.cc
|
| +++ b/content/browser/devtools/protocol/emulation_handler.cc
|
| @@ -6,15 +6,25 @@
|
|
|
| #include <utility>
|
|
|
| +#include "base/bind.h"
|
| +#include "base/guid.h"
|
| +#include "base/memory/ptr_util.h"
|
| #include "base/strings/string_number_conversions.h"
|
| +#include "base/trace_event/trace_event.h"
|
| #include "build/build_config.h"
|
| +#include "cc/output/begin_frame_args.h"
|
| +#include "cc/surfaces/surface_id_allocator.h"
|
| +#include "cc/surfaces/surface_manager.h"
|
| +#include "content/browser/compositor/surface_utils.h"
|
| #include "content/browser/frame_host/render_frame_host_impl.h"
|
| #include "content/browser/renderer_host/render_widget_host_impl.h"
|
| #include "content/browser/web_contents/web_contents_impl.h"
|
| +#include "content/browser/web_contents/web_contents_view.h"
|
| #include "content/common/view_messages.h"
|
| #include "content/public/common/url_constants.h"
|
| #include "device/geolocation/geolocation_service_context.h"
|
| #include "device/geolocation/geoposition.h"
|
| +#include "ui/compositor/compositor.h"
|
|
|
| namespace content {
|
| namespace devtools {
|
| @@ -56,10 +66,67 @@ ui::GestureProviderConfigType TouchEmulationConfigurationToType(
|
|
|
| } // namespace
|
|
|
| +class EmulatedBeginFrameSource : public cc::ExternalBeginFrameSource {
|
| + public:
|
| + explicit EmulatedBeginFrameSource(cc::ExternalBeginFrameSourceClient* client)
|
| + : cc::ExternalBeginFrameSource(client) {}
|
| + ~EmulatedBeginFrameSource() override {}
|
| +
|
| + void DidFinishFrame(cc::BeginFrameObserver* obs,
|
| + size_t remaining_frames) override {
|
| + cc::ExternalBeginFrameSource::DidFinishFrame(obs, remaining_frames);
|
| + finished_observers_.insert(obs);
|
| + CheckFinished();
|
| + }
|
| +
|
| + void AddObserver(cc::BeginFrameObserver* obs) override {
|
| + cc::ExternalBeginFrameSource::AddObserver(obs);
|
| + sorted_observers_.insert(obs);
|
| + }
|
| +
|
| + void RemoveObserver(cc::BeginFrameObserver* obs) override {
|
| + cc::ExternalBeginFrameSource::RemoveObserver(obs);
|
| + sorted_observers_.erase(obs);
|
| + finished_observers_.erase(obs);
|
| + CheckFinished();
|
| + }
|
| +
|
| + void SendBeginFrame(const cc::BeginFrameArgs& args) {
|
| + finished_observers_.clear();
|
| + OnBeginFrame(args);
|
| +
|
| + for (cc::BeginFrameObserver* obs : sorted_observers_) {
|
| + // TODO(eseckler): Need sequence numbers in args to distinguish emulated
|
| + // args with equal timestamps.
|
| + if (obs->LastUsedBeginFrameArgs().frame_time != args.frame_time)
|
| + finished_observers_.insert(obs);
|
| + }
|
| + CheckFinished();
|
| + }
|
| +
|
| + void WhenFinished(base::Callback<void()> callback) { callback_ = callback; }
|
| +
|
| + private:
|
| + void CheckFinished() {
|
| + if (!base::STLIncludes(finished_observers_, sorted_observers_))
|
| + return;
|
| +
|
| + if (callback_)
|
| + callback_.Run();
|
| + }
|
| +
|
| + base::Callback<void()> callback_;
|
| + std::set<cc::BeginFrameObserver*> sorted_observers_;
|
| + std::set<cc::BeginFrameObserver*> finished_observers_;
|
| +};
|
| +
|
| EmulationHandler::EmulationHandler()
|
| : touch_emulation_enabled_(false),
|
| device_emulation_enabled_(false),
|
| - host_(nullptr) {
|
| + begin_frame_source_(nullptr),
|
| + needs_begin_frame_(false),
|
| + host_(nullptr),
|
| + weak_factory_(this) {
|
| }
|
|
|
| EmulationHandler::~EmulationHandler() {
|
| @@ -69,16 +136,23 @@ void EmulationHandler::SetRenderFrameHost(RenderFrameHostImpl* host) {
|
| if (host_ == host)
|
| return;
|
|
|
| + DisableBeginFrameControl();
|
| +
|
| host_ = host;
|
| UpdateTouchEventEmulationState();
|
| UpdateDeviceEmulationState();
|
| }
|
|
|
| +void EmulationHandler::SetClient(std::unique_ptr<Client> client) {
|
| + client_.swap(client);
|
| +}
|
| +
|
| void EmulationHandler::Detached() {
|
| touch_emulation_enabled_ = false;
|
| device_emulation_enabled_ = false;
|
| UpdateTouchEventEmulationState();
|
| UpdateDeviceEmulationState();
|
| + DisableBeginFrameControl();
|
| }
|
|
|
| Response EmulationHandler::SetGeolocationOverride(
|
| @@ -290,6 +364,142 @@ Response EmulationHandler::SetVirtualTimePolicy(
|
| return Response::FallThrough();
|
| }
|
|
|
| +#if defined(USE_AURA)
|
| +Response EmulationHandler::EnableBeginFrameControl() {
|
| + if (begin_frame_source_)
|
| + return Response::OK(); // already enabled.
|
| +
|
| + WebContentsImpl* web_contents = GetWebContents();
|
| + if (!web_contents)
|
| + return Response::InternalError("Could not connect to view");
|
| +
|
| + ui::Compositor* compositor = web_contents->GetView()->GetCompositor();
|
| + if (!compositor)
|
| + return Response::InternalError("Could not obtain Compositor for view");
|
| +
|
| + TRACE_EVENT_INSTANT2("cc", "EnableBeginFrameControl",
|
| + TRACE_EVENT_SCOPE_THREAD, "WebContents RoutingID",
|
| + web_contents->GetRoutingID(), "Compositor FrameSinkId",
|
| + compositor->frame_sink_id().ToString());
|
| +
|
| + // TODO(eseckler): Support that Display of WebContents may change (and be
|
| + // destroyed) while BFC is enabled.
|
| +
|
| + // Replace original BeginFrameSource by our DevTools-controlled one.
|
| + std::unique_ptr<cc::BeginFrameSource> begin_frame_source =
|
| + base::MakeUnique<EmulatedBeginFrameSource>(this);
|
| + begin_frame_source_ =
|
| + static_cast<EmulatedBeginFrameSource*>(begin_frame_source.get());
|
| + compositor->SwapBeginFrameSource(&begin_frame_source);
|
| + original_begin_frame_source_.swap(begin_frame_source);
|
| +
|
| + return Response::OK();
|
| +}
|
| +
|
| +Response EmulationHandler::DisableBeginFrameControl() {
|
| + if (!begin_frame_source_)
|
| + return Response::OK(); // already disabled.
|
| +
|
| + WebContentsImpl* web_contents = GetWebContents();
|
| + if (!web_contents)
|
| + return Response::InternalError("Could not connect to view");
|
| +
|
| + ui::Compositor* compositor = web_contents->GetView()->GetCompositor();
|
| + DCHECK(compositor);
|
| +
|
| + compositor->SwapBeginFrameSource(&original_begin_frame_source_);
|
| + original_begin_frame_source_.reset();
|
| + begin_frame_source_ = nullptr;
|
| + needs_begin_frame_ = false;
|
| + return Response::OK();
|
| +}
|
| +
|
| +Response EmulationHandler::SendBeginFrame(double interval,
|
| + const double* frame_time,
|
| + const double* deadline,
|
| + std::string* out_frame_id) {
|
| + if (!begin_frame_source_)
|
| + return Response::ServerError("BeginFrameControl not enabled");
|
| +
|
| + RenderWidgetHostImpl* widget_host =
|
| + host_ ? host_->GetRenderWidgetHost() : nullptr;
|
| + if (!widget_host)
|
| + return Response::ServerError("Target does not support SendBeginFrame");
|
| +
|
| + ui::Compositor* compositor = GetWebContents()->GetView()->GetCompositor();
|
| + DCHECK(compositor);
|
| +
|
| + base::TimeTicks frame_time_ticks =
|
| + frame_time
|
| + ? base::TimeTicks::Now()
|
| + : base::TimeTicks() + base::TimeDelta::FromMicroseconds(*frame_time);
|
| + base::TimeDelta interval_delta = base::TimeDelta::FromMicroseconds(interval);
|
| + base::TimeTicks deadline_ticks =
|
| + deadline
|
| + ? (base::TimeTicks() + base::TimeDelta::FromMicroseconds(*deadline))
|
| + : (frame_time_ticks + interval_delta);
|
| + auto args = cc::BeginFrameArgs::Create(
|
| + BEGINFRAME_FROM_HERE, frame_time_ticks, deadline_ticks, interval_delta,
|
| + cc::BeginFrameArgs::NORMAL);
|
| +
|
| + // TODO(eseckler): Expose via DevTools?
|
| + // Ensure that we give the renderer main thread(s) a chance to answer this
|
| + // BeginFrame (disables Begin(Main)Frame skipping).
|
| + args.allow_latency_optimizations = false;
|
| +
|
| + *out_frame_id = base::GenerateGUID();
|
| +
|
| + begin_frame_source_->WhenFinished(
|
| + base::Bind(&EmulationHandler::ClientFrameCommitted,
|
| + base::Unretained(this), *out_frame_id));
|
| + begin_frame_source_->SendBeginFrame(args);
|
| + return Response::OK();
|
| +}
|
| +
|
| +#else
|
| +Response EmulationHandler::EnableBeginFrameControl() {
|
| + return Response::InternalError("Only supported on Aura.");
|
| +}
|
| +
|
| +Response EmulationHandler::DisableBeginFrameControl() {
|
| + return Response::InternalError("Only supported on Aura.");
|
| +}
|
| +
|
| +Response EmulationHandler::SendBeginFrame(double interval,
|
| + const double* frame_time,
|
| + const double* deadline,
|
| + std::string* out_frame_id) {
|
| + return Response::InternalError("Only supported on Aura.");
|
| +}
|
| +#endif
|
| +
|
| +void EmulationHandler::OnNeedsBeginFrames(bool needs_begin_frames) {
|
| + if (needs_begin_frame_ == needs_begin_frames)
|
| + return;
|
| +
|
| + needs_begin_frame_ = needs_begin_frames;
|
| + ClientSetNeedsBeginFrame(needs_begin_frame_);
|
| +
|
| + // TODO(eseckler): Figure out if we need to capture needsBeginFrame updates
|
| + // elsewhere to avoid indicating (sole) presence of browser-level consumers.
|
| +}
|
| +
|
| +void EmulationHandler::ClientSetNeedsBeginFrame(bool needs_begin_frame) {
|
| + if (!client_ || !begin_frame_source_)
|
| + return;
|
| +
|
| + client_->SetNeedsBeginFrame(SetNeedsBeginFrameParams::Create()
|
| + ->set_needs_begin_frame(needs_begin_frame));
|
| +}
|
| +
|
| +void EmulationHandler::ClientFrameCommitted(const std::string& frame_id) {
|
| + if (!client_ || !begin_frame_source_)
|
| + return;
|
| +
|
| + client_->FrameCommitted(
|
| + FrameCommittedParams::Create()->set_frame_id(frame_id));
|
| +}
|
| +
|
| WebContentsImpl* EmulationHandler::GetWebContents() {
|
| return host_ ?
|
| static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(host_)) :
|
|
|