OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/devtools/protocol/emulation_handler.h" | 5 #include "content/browser/devtools/protocol/emulation_handler.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/guid.h" |
| 11 #include "base/memory/ptr_util.h" |
9 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "base/trace_event/trace_event.h" |
10 #include "build/build_config.h" | 14 #include "build/build_config.h" |
| 15 #include "cc/output/begin_frame_args.h" |
| 16 #include "cc/surfaces/surface_id_allocator.h" |
| 17 #include "cc/surfaces/surface_manager.h" |
| 18 #include "content/browser/compositor/surface_utils.h" |
11 #include "content/browser/frame_host/render_frame_host_impl.h" | 19 #include "content/browser/frame_host/render_frame_host_impl.h" |
12 #include "content/browser/renderer_host/render_widget_host_impl.h" | 20 #include "content/browser/renderer_host/render_widget_host_impl.h" |
13 #include "content/browser/web_contents/web_contents_impl.h" | 21 #include "content/browser/web_contents/web_contents_impl.h" |
| 22 #include "content/browser/web_contents/web_contents_view.h" |
14 #include "content/common/view_messages.h" | 23 #include "content/common/view_messages.h" |
15 #include "content/public/common/url_constants.h" | 24 #include "content/public/common/url_constants.h" |
16 #include "device/geolocation/geolocation_service_context.h" | 25 #include "device/geolocation/geolocation_service_context.h" |
17 #include "device/geolocation/geoposition.h" | 26 #include "device/geolocation/geoposition.h" |
| 27 #include "ui/compositor/compositor.h" |
18 | 28 |
19 namespace content { | 29 namespace content { |
20 namespace devtools { | 30 namespace devtools { |
21 namespace emulation { | 31 namespace emulation { |
22 | 32 |
23 using Response = DevToolsProtocolClient::Response; | 33 using Response = DevToolsProtocolClient::Response; |
24 using GeolocationServiceContext = device::GeolocationServiceContext; | 34 using GeolocationServiceContext = device::GeolocationServiceContext; |
25 using Geoposition = device::Geoposition; | 35 using Geoposition = device::Geoposition; |
26 | 36 |
27 namespace { | 37 namespace { |
(...skipping 21 matching lines...) Expand all Loading... |
49 } | 59 } |
50 if (protocol_value == | 60 if (protocol_value == |
51 set_touch_emulation_enabled::kConfigurationDesktop) { | 61 set_touch_emulation_enabled::kConfigurationDesktop) { |
52 result = ui::GestureProviderConfigType::GENERIC_DESKTOP; | 62 result = ui::GestureProviderConfigType::GENERIC_DESKTOP; |
53 } | 63 } |
54 return result; | 64 return result; |
55 } | 65 } |
56 | 66 |
57 } // namespace | 67 } // namespace |
58 | 68 |
| 69 class EmulatedBeginFrameSource : public cc::ExternalBeginFrameSource { |
| 70 public: |
| 71 explicit EmulatedBeginFrameSource(cc::ExternalBeginFrameSourceClient* client) |
| 72 : cc::ExternalBeginFrameSource(client) {} |
| 73 ~EmulatedBeginFrameSource() override {} |
| 74 |
| 75 void DidFinishFrame(cc::BeginFrameObserver* obs, |
| 76 size_t remaining_frames) override { |
| 77 cc::ExternalBeginFrameSource::DidFinishFrame(obs, remaining_frames); |
| 78 finished_observers_.insert(obs); |
| 79 CheckFinished(); |
| 80 } |
| 81 |
| 82 void AddObserver(cc::BeginFrameObserver* obs) override { |
| 83 cc::ExternalBeginFrameSource::AddObserver(obs); |
| 84 sorted_observers_.insert(obs); |
| 85 } |
| 86 |
| 87 void RemoveObserver(cc::BeginFrameObserver* obs) override { |
| 88 cc::ExternalBeginFrameSource::RemoveObserver(obs); |
| 89 sorted_observers_.erase(obs); |
| 90 finished_observers_.erase(obs); |
| 91 CheckFinished(); |
| 92 } |
| 93 |
| 94 void SendBeginFrame(const cc::BeginFrameArgs& args) { |
| 95 finished_observers_.clear(); |
| 96 OnBeginFrame(args); |
| 97 |
| 98 for (cc::BeginFrameObserver* obs : sorted_observers_) { |
| 99 // TODO(eseckler): Need sequence numbers in args to distinguish emulated |
| 100 // args with equal timestamps. |
| 101 if (obs->LastUsedBeginFrameArgs().frame_time != args.frame_time) |
| 102 finished_observers_.insert(obs); |
| 103 } |
| 104 CheckFinished(); |
| 105 } |
| 106 |
| 107 void WhenFinished(base::Callback<void()> callback) { callback_ = callback; } |
| 108 |
| 109 private: |
| 110 void CheckFinished() { |
| 111 if (!base::STLIncludes(finished_observers_, sorted_observers_)) |
| 112 return; |
| 113 |
| 114 if (callback_) |
| 115 callback_.Run(); |
| 116 } |
| 117 |
| 118 base::Callback<void()> callback_; |
| 119 std::set<cc::BeginFrameObserver*> sorted_observers_; |
| 120 std::set<cc::BeginFrameObserver*> finished_observers_; |
| 121 }; |
| 122 |
59 EmulationHandler::EmulationHandler() | 123 EmulationHandler::EmulationHandler() |
60 : touch_emulation_enabled_(false), | 124 : touch_emulation_enabled_(false), |
61 device_emulation_enabled_(false), | 125 device_emulation_enabled_(false), |
62 host_(nullptr) { | 126 begin_frame_source_(nullptr), |
| 127 needs_begin_frame_(false), |
| 128 host_(nullptr), |
| 129 weak_factory_(this) { |
63 } | 130 } |
64 | 131 |
65 EmulationHandler::~EmulationHandler() { | 132 EmulationHandler::~EmulationHandler() { |
66 } | 133 } |
67 | 134 |
68 void EmulationHandler::SetRenderFrameHost(RenderFrameHostImpl* host) { | 135 void EmulationHandler::SetRenderFrameHost(RenderFrameHostImpl* host) { |
69 if (host_ == host) | 136 if (host_ == host) |
70 return; | 137 return; |
71 | 138 |
| 139 DisableBeginFrameControl(); |
| 140 |
72 host_ = host; | 141 host_ = host; |
73 UpdateTouchEventEmulationState(); | 142 UpdateTouchEventEmulationState(); |
74 UpdateDeviceEmulationState(); | 143 UpdateDeviceEmulationState(); |
75 } | 144 } |
76 | 145 |
| 146 void EmulationHandler::SetClient(std::unique_ptr<Client> client) { |
| 147 client_.swap(client); |
| 148 } |
| 149 |
77 void EmulationHandler::Detached() { | 150 void EmulationHandler::Detached() { |
78 touch_emulation_enabled_ = false; | 151 touch_emulation_enabled_ = false; |
79 device_emulation_enabled_ = false; | 152 device_emulation_enabled_ = false; |
80 UpdateTouchEventEmulationState(); | 153 UpdateTouchEventEmulationState(); |
81 UpdateDeviceEmulationState(); | 154 UpdateDeviceEmulationState(); |
| 155 DisableBeginFrameControl(); |
82 } | 156 } |
83 | 157 |
84 Response EmulationHandler::SetGeolocationOverride( | 158 Response EmulationHandler::SetGeolocationOverride( |
85 double* latitude, double* longitude, double* accuracy) { | 159 double* latitude, double* longitude, double* accuracy) { |
86 if (!GetWebContents()) | 160 if (!GetWebContents()) |
87 return Response::InternalError("Could not connect to view"); | 161 return Response::InternalError("Could not connect to view"); |
88 | 162 |
89 GeolocationServiceContext* geolocation_context = | 163 GeolocationServiceContext* geolocation_context = |
90 GetWebContents()->GetGeolocationServiceContext(); | 164 GetWebContents()->GetGeolocationServiceContext(); |
91 std::unique_ptr<Geoposition> geoposition(new Geoposition()); | 165 std::unique_ptr<Geoposition> geoposition(new Geoposition()); |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
283 Response EmulationHandler::SetCPUThrottlingRate(double rate) { | 357 Response EmulationHandler::SetCPUThrottlingRate(double rate) { |
284 return Response::FallThrough(); | 358 return Response::FallThrough(); |
285 } | 359 } |
286 | 360 |
287 Response EmulationHandler::SetVirtualTimePolicy( | 361 Response EmulationHandler::SetVirtualTimePolicy( |
288 const std::string& policy, | 362 const std::string& policy, |
289 const int* budget) { | 363 const int* budget) { |
290 return Response::FallThrough(); | 364 return Response::FallThrough(); |
291 } | 365 } |
292 | 366 |
| 367 #if defined(USE_AURA) |
| 368 Response EmulationHandler::EnableBeginFrameControl() { |
| 369 if (begin_frame_source_) |
| 370 return Response::OK(); // already enabled. |
| 371 |
| 372 WebContentsImpl* web_contents = GetWebContents(); |
| 373 if (!web_contents) |
| 374 return Response::InternalError("Could not connect to view"); |
| 375 |
| 376 ui::Compositor* compositor = web_contents->GetView()->GetCompositor(); |
| 377 if (!compositor) |
| 378 return Response::InternalError("Could not obtain Compositor for view"); |
| 379 |
| 380 TRACE_EVENT_INSTANT2("cc", "EnableBeginFrameControl", |
| 381 TRACE_EVENT_SCOPE_THREAD, "WebContents RoutingID", |
| 382 web_contents->GetRoutingID(), "Compositor FrameSinkId", |
| 383 compositor->frame_sink_id().ToString()); |
| 384 |
| 385 // TODO(eseckler): Support that Display of WebContents may change (and be |
| 386 // destroyed) while BFC is enabled. |
| 387 |
| 388 // Replace original BeginFrameSource by our DevTools-controlled one. |
| 389 std::unique_ptr<cc::BeginFrameSource> begin_frame_source = |
| 390 base::MakeUnique<EmulatedBeginFrameSource>(this); |
| 391 begin_frame_source_ = |
| 392 static_cast<EmulatedBeginFrameSource*>(begin_frame_source.get()); |
| 393 compositor->SwapBeginFrameSource(&begin_frame_source); |
| 394 original_begin_frame_source_.swap(begin_frame_source); |
| 395 |
| 396 return Response::OK(); |
| 397 } |
| 398 |
| 399 Response EmulationHandler::DisableBeginFrameControl() { |
| 400 if (!begin_frame_source_) |
| 401 return Response::OK(); // already disabled. |
| 402 |
| 403 WebContentsImpl* web_contents = GetWebContents(); |
| 404 if (!web_contents) |
| 405 return Response::InternalError("Could not connect to view"); |
| 406 |
| 407 ui::Compositor* compositor = web_contents->GetView()->GetCompositor(); |
| 408 DCHECK(compositor); |
| 409 |
| 410 compositor->SwapBeginFrameSource(&original_begin_frame_source_); |
| 411 original_begin_frame_source_.reset(); |
| 412 begin_frame_source_ = nullptr; |
| 413 needs_begin_frame_ = false; |
| 414 return Response::OK(); |
| 415 } |
| 416 |
| 417 Response EmulationHandler::SendBeginFrame(double interval, |
| 418 const double* frame_time, |
| 419 const double* deadline, |
| 420 std::string* out_frame_id) { |
| 421 if (!begin_frame_source_) |
| 422 return Response::ServerError("BeginFrameControl not enabled"); |
| 423 |
| 424 RenderWidgetHostImpl* widget_host = |
| 425 host_ ? host_->GetRenderWidgetHost() : nullptr; |
| 426 if (!widget_host) |
| 427 return Response::ServerError("Target does not support SendBeginFrame"); |
| 428 |
| 429 ui::Compositor* compositor = GetWebContents()->GetView()->GetCompositor(); |
| 430 DCHECK(compositor); |
| 431 |
| 432 base::TimeTicks frame_time_ticks = |
| 433 frame_time |
| 434 ? base::TimeTicks::Now() |
| 435 : base::TimeTicks() + base::TimeDelta::FromMicroseconds(*frame_time); |
| 436 base::TimeDelta interval_delta = base::TimeDelta::FromMicroseconds(interval); |
| 437 base::TimeTicks deadline_ticks = |
| 438 deadline |
| 439 ? (base::TimeTicks() + base::TimeDelta::FromMicroseconds(*deadline)) |
| 440 : (frame_time_ticks + interval_delta); |
| 441 auto args = cc::BeginFrameArgs::Create( |
| 442 BEGINFRAME_FROM_HERE, frame_time_ticks, deadline_ticks, interval_delta, |
| 443 cc::BeginFrameArgs::NORMAL); |
| 444 |
| 445 // TODO(eseckler): Expose via DevTools? |
| 446 // Ensure that we give the renderer main thread(s) a chance to answer this |
| 447 // BeginFrame (disables Begin(Main)Frame skipping). |
| 448 args.allow_latency_optimizations = false; |
| 449 |
| 450 *out_frame_id = base::GenerateGUID(); |
| 451 |
| 452 begin_frame_source_->WhenFinished( |
| 453 base::Bind(&EmulationHandler::ClientFrameCommitted, |
| 454 base::Unretained(this), *out_frame_id)); |
| 455 begin_frame_source_->SendBeginFrame(args); |
| 456 return Response::OK(); |
| 457 } |
| 458 |
| 459 #else |
| 460 Response EmulationHandler::EnableBeginFrameControl() { |
| 461 return Response::InternalError("Only supported on Aura."); |
| 462 } |
| 463 |
| 464 Response EmulationHandler::DisableBeginFrameControl() { |
| 465 return Response::InternalError("Only supported on Aura."); |
| 466 } |
| 467 |
| 468 Response EmulationHandler::SendBeginFrame(double interval, |
| 469 const double* frame_time, |
| 470 const double* deadline, |
| 471 std::string* out_frame_id) { |
| 472 return Response::InternalError("Only supported on Aura."); |
| 473 } |
| 474 #endif |
| 475 |
| 476 void EmulationHandler::OnNeedsBeginFrames(bool needs_begin_frames) { |
| 477 if (needs_begin_frame_ == needs_begin_frames) |
| 478 return; |
| 479 |
| 480 needs_begin_frame_ = needs_begin_frames; |
| 481 ClientSetNeedsBeginFrame(needs_begin_frame_); |
| 482 |
| 483 // TODO(eseckler): Figure out if we need to capture needsBeginFrame updates |
| 484 // elsewhere to avoid indicating (sole) presence of browser-level consumers. |
| 485 } |
| 486 |
| 487 void EmulationHandler::ClientSetNeedsBeginFrame(bool needs_begin_frame) { |
| 488 if (!client_ || !begin_frame_source_) |
| 489 return; |
| 490 |
| 491 client_->SetNeedsBeginFrame(SetNeedsBeginFrameParams::Create() |
| 492 ->set_needs_begin_frame(needs_begin_frame)); |
| 493 } |
| 494 |
| 495 void EmulationHandler::ClientFrameCommitted(const std::string& frame_id) { |
| 496 if (!client_ || !begin_frame_source_) |
| 497 return; |
| 498 |
| 499 client_->FrameCommitted( |
| 500 FrameCommittedParams::Create()->set_frame_id(frame_id)); |
| 501 } |
| 502 |
293 WebContentsImpl* EmulationHandler::GetWebContents() { | 503 WebContentsImpl* EmulationHandler::GetWebContents() { |
294 return host_ ? | 504 return host_ ? |
295 static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(host_)) : | 505 static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(host_)) : |
296 nullptr; | 506 nullptr; |
297 } | 507 } |
298 | 508 |
299 void EmulationHandler::UpdateTouchEventEmulationState() { | 509 void EmulationHandler::UpdateTouchEventEmulationState() { |
300 RenderWidgetHostImpl* widget_host = | 510 RenderWidgetHostImpl* widget_host = |
301 host_ ? host_->GetRenderWidgetHost() : nullptr; | 511 host_ ? host_->GetRenderWidgetHost() : nullptr; |
302 if (!widget_host) | 512 if (!widget_host) |
(...skipping 16 matching lines...) Expand all Loading... |
319 widget_host->GetRoutingID(), device_emulation_params_)); | 529 widget_host->GetRoutingID(), device_emulation_params_)); |
320 } else { | 530 } else { |
321 widget_host->Send(new ViewMsg_DisableDeviceEmulation( | 531 widget_host->Send(new ViewMsg_DisableDeviceEmulation( |
322 widget_host->GetRoutingID())); | 532 widget_host->GetRoutingID())); |
323 } | 533 } |
324 } | 534 } |
325 | 535 |
326 } // namespace emulation | 536 } // namespace emulation |
327 } // namespace devtools | 537 } // namespace devtools |
328 } // namespace content | 538 } // namespace content |
OLD | NEW |