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
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/inspector/inspector_emulation_agent.h"
6
7 #include "third_party/blink/public/common/input/web_touch_event.h"
8 #include "third_party/blink/public/platform/platform.h"
9 #include "third_party/blink/renderer/core/css/vision_deficiency.h"
10 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
11 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
12 #include "third_party/blink/renderer/core/frame/settings.h"
13 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
14 #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h"
15 #include "third_party/blink/renderer/core/inspector/locale_controller.h"
16 #include "third_party/blink/renderer/core/inspector/protocol/DOM.h"
17 #include "third_party/blink/renderer/core/loader/document_loader.h"
18 #include "third_party/blink/renderer/core/page/focus_controller.h"
19 #include "third_party/blink/renderer/core/page/page.h"
20 #include "third_party/blink/renderer/platform/geometry/double_rect.h"
21 #include "third_party/blink/renderer/platform/graphics/color.h"
22 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
23 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
24 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
25 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h"
26 #include "third_party/blink/renderer/platform/network/network_utils.h"
27 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
28 #include "third_party/blink/renderer/platform/scheduler/public/thread_cpu_throttler.h"
29
30 namespace blink {
31 using protocol::Maybe;
32 using protocol::Response;
33
InspectorEmulationAgent(WebLocalFrameImpl * web_local_frame_impl)34 InspectorEmulationAgent::InspectorEmulationAgent(
35 WebLocalFrameImpl* web_local_frame_impl)
36 : web_local_frame_(web_local_frame_impl),
37 default_background_color_override_rgba_(&agent_state_,
38 /*default_value=*/{}),
39 script_execution_disabled_(&agent_state_, /*default_value=*/false),
40 scrollbars_hidden_(&agent_state_, /*default_value=*/false),
41 document_cookie_disabled_(&agent_state_, /*default_value=*/false),
42 touch_event_emulation_enabled_(&agent_state_, /*default_value=*/false),
43 max_touch_points_(&agent_state_, /*default_value=*/1),
44 emulated_media_(&agent_state_, /*default_value=*/WTF::String()),
45 emulated_media_features_(&agent_state_, /*default_value=*/WTF::String()),
46 emulated_vision_deficiency_(&agent_state_,
47 /*default_value=*/WTF::String()),
48 navigator_platform_override_(&agent_state_,
49 /*default_value=*/WTF::String()),
50 user_agent_override_(&agent_state_, /*default_value=*/WTF::String()),
51 serialized_ua_metadata_override_(&agent_state_,
52 /*default_value=*/WTF::String()),
53 accept_language_override_(&agent_state_,
54 /*default_value=*/WTF::String()),
55 locale_override_(&agent_state_, /*default_value=*/WTF::String()),
56 virtual_time_budget_(&agent_state_, /*default_value*/ 0.0),
57 virtual_time_budget_initial_offset_(&agent_state_, /*default_value=*/0.0),
58 initial_virtual_time_(&agent_state_, /*default_value=*/0.0),
59 virtual_time_offset_(&agent_state_, /*default_value=*/0.0),
60 virtual_time_policy_(&agent_state_, /*default_value=*/WTF::String()),
61 virtual_time_task_starvation_count_(&agent_state_, /*default_value=*/0),
62 wait_for_navigation_(&agent_state_, /*default_value=*/false),
63 emulate_focus_(&agent_state_, /*default_value=*/false),
64 timezone_id_override_(&agent_state_, /*default_value=*/WTF::String()),
65 disabled_image_types_(&agent_state_, /*default_value=*/false) {}
66
67 InspectorEmulationAgent::~InspectorEmulationAgent() = default;
68
GetWebViewImpl()69 WebViewImpl* InspectorEmulationAgent::GetWebViewImpl() {
70 return web_local_frame_ ? web_local_frame_->ViewImpl() : nullptr;
71 }
72
Restore()73 void InspectorEmulationAgent::Restore() {
74 // Since serialized_ua_metadata_override_ can't directly be converted back
75 // to appropriate protocol message, we initially pass null and decode it
76 // directly.
77 WTF::String save_serialized_ua_metadata_override =
78 serialized_ua_metadata_override_.Get();
79 setUserAgentOverride(
80 user_agent_override_.Get(), accept_language_override_.Get(),
81 navigator_platform_override_.Get(),
82 protocol::Maybe<protocol::Emulation::UserAgentMetadata>());
83 ua_metadata_override_ = blink::UserAgentMetadata::Demarshal(
84 save_serialized_ua_metadata_override.Latin1());
85 serialized_ua_metadata_override_.Set(save_serialized_ua_metadata_override);
86
87 if (!locale_override_.Get().IsEmpty())
88 setLocaleOverride(locale_override_.Get());
89 if (!web_local_frame_)
90 return;
91
92 // Following code only runs for pages.
93 if (script_execution_disabled_.Get())
94 GetWebViewImpl()->GetDevToolsEmulator()->SetScriptExecutionDisabled(true);
95 if (scrollbars_hidden_.Get())
96 GetWebViewImpl()->GetDevToolsEmulator()->SetScrollbarsHidden(true);
97 if (document_cookie_disabled_.Get())
98 GetWebViewImpl()->GetDevToolsEmulator()->SetDocumentCookieDisabled(true);
99 setTouchEmulationEnabled(touch_event_emulation_enabled_.Get(),
100 max_touch_points_.Get());
101 auto features =
102 std::make_unique<protocol::Array<protocol::Emulation::MediaFeature>>();
103 for (auto const& name : emulated_media_features_.Keys()) {
104 auto const& value = emulated_media_features_.Get(name);
105 features->push_back(protocol::Emulation::MediaFeature::create()
106 .setName(name)
107 .setValue(value)
108 .build());
109 }
110 setEmulatedMedia(emulated_media_.Get(), std::move(features));
111 if (!emulated_vision_deficiency_.Get().IsNull())
112 setEmulatedVisionDeficiency(emulated_vision_deficiency_.Get());
113 auto status_or_rgba = protocol::DOM::RGBA::ReadFrom(
114 default_background_color_override_rgba_.Get());
115 if (status_or_rgba.ok())
116 setDefaultBackgroundColorOverride(std::move(status_or_rgba).value());
117 setFocusEmulationEnabled(emulate_focus_.Get());
118
119 if (!timezone_id_override_.Get().IsNull())
120 setTimezoneOverride(timezone_id_override_.Get());
121
122 if (virtual_time_policy_.Get().IsNull())
123 return;
124 // Tell the scheduler about the saved virtual time progress to ensure that
125 // virtual time monotonically advances despite the cross origin navigation.
126 // This should be done regardless of the virtual time mode.
127 web_local_frame_->View()->Scheduler()->SetInitialVirtualTimeOffset(
128 base::TimeDelta::FromMillisecondsD(virtual_time_offset_.Get()));
129
130 // Preserve wait for navigation in all modes.
131 bool wait_for_navigation = wait_for_navigation_.Get();
132
133 // Reinstate the stored policy.
134 double virtual_time_ticks_base_ms;
135
136 // For Pause, do not pass budget or starvation count.
137 if (virtual_time_policy_.Get() ==
138 protocol::Emulation::VirtualTimePolicyEnum::Pause) {
139 setVirtualTimePolicy(protocol::Emulation::VirtualTimePolicyEnum::Pause,
140 Maybe<double>(), Maybe<int>(), wait_for_navigation,
141 initial_virtual_time_.Get(),
142 &virtual_time_ticks_base_ms);
143 return;
144 }
145
146 // Calculate remaining budget for the advancing modes.
147 double budget_remaining = virtual_time_budget_.Get() +
148 virtual_time_budget_initial_offset_.Get() -
149 virtual_time_offset_.Get();
150 DCHECK_GE(budget_remaining, 0);
151
152 setVirtualTimePolicy(virtual_time_policy_.Get(), budget_remaining,
153 virtual_time_task_starvation_count_.Get(),
154 wait_for_navigation, initial_virtual_time_.Get(),
155 &virtual_time_ticks_base_ms);
156 }
157
disable()158 Response InspectorEmulationAgent::disable() {
159 if (enabled_) {
160 instrumenting_agents_->RemoveInspectorEmulationAgent(this);
161 enabled_ = false;
162 }
163
164 setUserAgentOverride(
165 String(), protocol::Maybe<String>(), protocol::Maybe<String>(),
166 protocol::Maybe<protocol::Emulation::UserAgentMetadata>());
167 if (!locale_override_.Get().IsEmpty())
168 setLocaleOverride(String());
169 if (!web_local_frame_)
170 return Response::Success();
171 setScriptExecutionDisabled(false);
172 setScrollbarsHidden(false);
173 setDocumentCookieDisabled(false);
174 setTouchEmulationEnabled(false, Maybe<int>());
175 // Clear emulated media features. Note that the current approach
176 // doesn't work well in cases where two clients have the same set of
177 // features overridden to the same value by two different clients
178 // (e.g. if we allowed two different front-ends with the same
179 // settings to attach to the same page). TODO: support this use case.
180 setEmulatedMedia(String(), {});
181 if (!emulated_vision_deficiency_.Get().IsNull())
182 setEmulatedVisionDeficiency(String("none"));
183 setCPUThrottlingRate(1);
184 setFocusEmulationEnabled(false);
185 setDefaultBackgroundColorOverride(Maybe<protocol::DOM::RGBA>());
186 disabled_image_types_.Clear();
187 return Response::Success();
188 }
189
resetPageScaleFactor()190 Response InspectorEmulationAgent::resetPageScaleFactor() {
191 Response response = AssertPage();
192 if (!response.IsSuccess())
193 return response;
194 GetWebViewImpl()->ResetScaleStateImmediately();
195 return response;
196 }
197
setPageScaleFactor(double page_scale_factor)198 Response InspectorEmulationAgent::setPageScaleFactor(double page_scale_factor) {
199 Response response = AssertPage();
200 if (!response.IsSuccess())
201 return response;
202 GetWebViewImpl()->SetPageScaleFactor(static_cast<float>(page_scale_factor));
203 return response;
204 }
205
setScriptExecutionDisabled(bool value)206 Response InspectorEmulationAgent::setScriptExecutionDisabled(bool value) {
207 Response response = AssertPage();
208 if (!response.IsSuccess())
209 return response;
210 if (script_execution_disabled_.Get() == value)
211 return response;
212 script_execution_disabled_.Set(value);
213 GetWebViewImpl()->GetDevToolsEmulator()->SetScriptExecutionDisabled(value);
214 return response;
215 }
216
setScrollbarsHidden(bool hidden)217 Response InspectorEmulationAgent::setScrollbarsHidden(bool hidden) {
218 Response response = AssertPage();
219 if (!response.IsSuccess())
220 return response;
221 if (scrollbars_hidden_.Get() == hidden)
222 return response;
223 scrollbars_hidden_.Set(hidden);
224 GetWebViewImpl()->GetDevToolsEmulator()->SetScrollbarsHidden(hidden);
225 return response;
226 }
227
setDocumentCookieDisabled(bool disabled)228 Response InspectorEmulationAgent::setDocumentCookieDisabled(bool disabled) {
229 Response response = AssertPage();
230 if (!response.IsSuccess())
231 return response;
232 if (document_cookie_disabled_.Get() == disabled)
233 return response;
234 document_cookie_disabled_.Set(disabled);
235 GetWebViewImpl()->GetDevToolsEmulator()->SetDocumentCookieDisabled(disabled);
236 return response;
237 }
238
setTouchEmulationEnabled(bool enabled,protocol::Maybe<int> max_touch_points)239 Response InspectorEmulationAgent::setTouchEmulationEnabled(
240 bool enabled,
241 protocol::Maybe<int> max_touch_points) {
242 Response response = AssertPage();
243 if (!response.IsSuccess())
244 return response;
245 int max_points = max_touch_points.fromMaybe(1);
246 if (max_points < 1 || max_points > WebTouchEvent::kTouchesLengthCap) {
247 String msg =
248 "Touch points must be between 1 and " +
249 String::Number(static_cast<uint16_t>(WebTouchEvent::kTouchesLengthCap));
250 return Response::InvalidParams(msg.Utf8());
251 }
252 touch_event_emulation_enabled_.Set(enabled);
253 max_touch_points_.Set(max_points);
254 GetWebViewImpl()->GetDevToolsEmulator()->SetTouchEventEmulationEnabled(
255 enabled, max_points);
256 return response;
257 }
258
setEmulatedMedia(Maybe<String> media,Maybe<protocol::Array<protocol::Emulation::MediaFeature>> features)259 Response InspectorEmulationAgent::setEmulatedMedia(
260 Maybe<String> media,
261 Maybe<protocol::Array<protocol::Emulation::MediaFeature>> features) {
262 Response response = AssertPage();
263 if (!response.IsSuccess())
264 return response;
265 if (media.isJust()) {
266 auto mediaValue = media.takeJust();
267 emulated_media_.Set(mediaValue);
268 GetWebViewImpl()->GetPage()->GetSettings().SetMediaTypeOverride(mediaValue);
269 } else {
270 emulated_media_.Set("");
271 GetWebViewImpl()->GetPage()->GetSettings().SetMediaTypeOverride("");
272 }
273 for (const WTF::String& feature : emulated_media_features_.Keys()) {
274 GetWebViewImpl()->GetPage()->SetMediaFeatureOverride(AtomicString(feature),
275 "");
276 }
277 emulated_media_features_.Clear();
278 if (features.isJust()) {
279 auto featuresValue = features.takeJust();
280 for (auto const& mediaFeature : *featuresValue.get()) {
281 auto const& name = mediaFeature->getName();
282 auto const& value = mediaFeature->getValue();
283 emulated_media_features_.Set(name, value);
284 GetWebViewImpl()->GetPage()->SetMediaFeatureOverride(AtomicString(name),
285 value);
286 }
287 }
288 return response;
289 }
290
setEmulatedVisionDeficiency(const String & type)291 Response InspectorEmulationAgent::setEmulatedVisionDeficiency(
292 const String& type) {
293 Response response = AssertPage();
294 if (!response.IsSuccess())
295 return response;
296
297 VisionDeficiency vision_deficiency;
298 namespace TypeEnum =
299 protocol::Emulation::SetEmulatedVisionDeficiency::TypeEnum;
300 if (type == TypeEnum::None)
301 vision_deficiency = VisionDeficiency::kNoVisionDeficiency;
302 else if (type == TypeEnum::Achromatopsia)
303 vision_deficiency = VisionDeficiency::kAchromatopsia;
304 else if (type == TypeEnum::BlurredVision)
305 vision_deficiency = VisionDeficiency::kBlurredVision;
306 else if (type == TypeEnum::Deuteranopia)
307 vision_deficiency = VisionDeficiency::kDeuteranopia;
308 else if (type == TypeEnum::Protanopia)
309 vision_deficiency = VisionDeficiency::kProtanopia;
310 else if (type == TypeEnum::Tritanopia)
311 vision_deficiency = VisionDeficiency::kTritanopia;
312 else
313 return Response::InvalidParams("Unknown vision deficiency type");
314
315 emulated_vision_deficiency_.Set(type);
316 GetWebViewImpl()->GetPage()->SetVisionDeficiency(vision_deficiency);
317 return response;
318 }
319
setCPUThrottlingRate(double rate)320 Response InspectorEmulationAgent::setCPUThrottlingRate(double rate) {
321 Response response = AssertPage();
322 if (!response.IsSuccess())
323 return response;
324 scheduler::ThreadCPUThrottler::GetInstance()->SetThrottlingRate(rate);
325 return response;
326 }
327
setFocusEmulationEnabled(bool enabled)328 Response InspectorEmulationAgent::setFocusEmulationEnabled(bool enabled) {
329 Response response = AssertPage();
330 if (!response.IsSuccess())
331 return response;
332 emulate_focus_.Set(enabled);
333 GetWebViewImpl()->GetPage()->GetFocusController().SetFocusEmulationEnabled(
334 enabled);
335 return response;
336 }
337
setVirtualTimePolicy(const String & policy,Maybe<double> virtual_time_budget_ms,protocol::Maybe<int> max_virtual_time_task_starvation_count,protocol::Maybe<bool> wait_for_navigation,protocol::Maybe<double> initial_virtual_time,double * virtual_time_ticks_base_ms)338 Response InspectorEmulationAgent::setVirtualTimePolicy(
339 const String& policy,
340 Maybe<double> virtual_time_budget_ms,
341 protocol::Maybe<int> max_virtual_time_task_starvation_count,
342 protocol::Maybe<bool> wait_for_navigation,
343 protocol::Maybe<double> initial_virtual_time,
344 double* virtual_time_ticks_base_ms) {
345 Response response = AssertPage();
346 if (!response.IsSuccess())
347 return response;
348 virtual_time_policy_.Set(policy);
349
350 PendingVirtualTimePolicy new_policy;
351 new_policy.policy = PageScheduler::VirtualTimePolicy::kPause;
352 if (protocol::Emulation::VirtualTimePolicyEnum::Advance == policy) {
353 new_policy.policy = PageScheduler::VirtualTimePolicy::kAdvance;
354 } else if (protocol::Emulation::VirtualTimePolicyEnum::
355 PauseIfNetworkFetchesPending == policy) {
356 new_policy.policy = PageScheduler::VirtualTimePolicy::kDeterministicLoading;
357 }
358
359 if (new_policy.policy == PageScheduler::VirtualTimePolicy::kPause &&
360 virtual_time_budget_ms.isJust()) {
361 LOG(ERROR) << "Can only specify virtual time budget for non-Pause policy";
362 return Response::InvalidParams(
363 "Can only specify budget for non-Pause policy");
364 }
365 if (new_policy.policy == PageScheduler::VirtualTimePolicy::kPause &&
366 max_virtual_time_task_starvation_count.isJust()) {
367 LOG(ERROR)
368 << "Can only specify virtual time starvation for non-Pause policy";
369 return Response::InvalidParams(
370 "Can only specify starvation count for non-Pause policy");
371 }
372
373 if (virtual_time_budget_ms.isJust()) {
374 new_policy.virtual_time_budget_ms = virtual_time_budget_ms.fromJust();
375 virtual_time_budget_.Set(*new_policy.virtual_time_budget_ms);
376 // Record the current virtual time offset so Restore can compute how much
377 // budget is left.
378 virtual_time_budget_initial_offset_.Set(virtual_time_offset_.Get());
379 } else {
380 virtual_time_budget_.Clear();
381 }
382
383 if (max_virtual_time_task_starvation_count.isJust()) {
384 new_policy.max_virtual_time_task_starvation_count =
385 max_virtual_time_task_starvation_count.fromJust();
386 virtual_time_task_starvation_count_.Set(
387 *new_policy.max_virtual_time_task_starvation_count);
388 } else {
389 virtual_time_task_starvation_count_.Clear();
390 }
391
392 InnerEnable();
393
394 // This needs to happen before we apply virtual time.
395 if (initial_virtual_time.isJust()) {
396 initial_virtual_time_.Set(initial_virtual_time.fromJust());
397 web_local_frame_->View()->Scheduler()->SetInitialVirtualTime(
398 base::Time::FromDoubleT(initial_virtual_time.fromJust()));
399 }
400
401 if (wait_for_navigation.fromMaybe(false)) {
402 wait_for_navigation_.Set(true);
403 pending_virtual_time_policy_ = std::move(new_policy);
404 } else {
405 ApplyVirtualTimePolicy(new_policy);
406 }
407
408 if (virtual_time_base_ticks_.is_null()) {
409 *virtual_time_ticks_base_ms = 0;
410 } else {
411 *virtual_time_ticks_base_ms =
412 (virtual_time_base_ticks_ - base::TimeTicks()).InMillisecondsF();
413 }
414
415 return response;
416 }
417
ApplyVirtualTimePolicy(const PendingVirtualTimePolicy & new_policy)418 void InspectorEmulationAgent::ApplyVirtualTimePolicy(
419 const PendingVirtualTimePolicy& new_policy) {
420 DCHECK(web_local_frame_);
421 web_local_frame_->View()->Scheduler()->SetVirtualTimePolicy(
422 new_policy.policy);
423 virtual_time_base_ticks_ =
424 web_local_frame_->View()->Scheduler()->EnableVirtualTime();
425 if (new_policy.virtual_time_budget_ms) {
426 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("renderer.scheduler", "VirtualTimeBudget",
427 TRACE_ID_LOCAL(this), "budget",
428 *new_policy.virtual_time_budget_ms);
429 base::TimeDelta budget_amount =
430 base::TimeDelta::FromMillisecondsD(*new_policy.virtual_time_budget_ms);
431 web_local_frame_->View()->Scheduler()->GrantVirtualTimeBudget(
432 budget_amount,
433 WTF::Bind(&InspectorEmulationAgent::VirtualTimeBudgetExpired,
434 WrapWeakPersistent(this)));
435 for (DocumentLoader* loader : pending_document_loaders_)
436 loader->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred);
437 pending_document_loaders_.clear();
438 }
439 if (new_policy.max_virtual_time_task_starvation_count) {
440 web_local_frame_->View()->Scheduler()->SetMaxVirtualTimeTaskStarvationCount(
441 *new_policy.max_virtual_time_task_starvation_count);
442 }
443 }
444
FrameStartedLoading(LocalFrame *)445 void InspectorEmulationAgent::FrameStartedLoading(LocalFrame*) {
446 if (pending_virtual_time_policy_) {
447 wait_for_navigation_.Set(false);
448 ApplyVirtualTimePolicy(*pending_virtual_time_policy_);
449 pending_virtual_time_policy_ = base::nullopt;
450 }
451 }
452
OverrideAcceptImageHeader(const HashSet<String> * disabled_image_types)453 AtomicString InspectorEmulationAgent::OverrideAcceptImageHeader(
454 const HashSet<String>* disabled_image_types) {
455 String header(ImageAcceptHeader());
456 for (String type : *disabled_image_types) {
457 // The header string is expected to be like
458 // `image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8` and is
459 // expected to be always ending with `image/*,*/*;q=xxx`, therefore, to
460 // remove a type we replace `image/x,` with empty string. Only webp and avif
461 // types can be disabled.
462 header.Replace(String(type + ","), "");
463 }
464 return AtomicString(header);
465 }
466
PrepareRequest(DocumentLoader * loader,ResourceRequest & request,ResourceLoaderOptions & options,ResourceType resource_type)467 void InspectorEmulationAgent::PrepareRequest(DocumentLoader* loader,
468 ResourceRequest& request,
469 ResourceLoaderOptions& options,
470 ResourceType resource_type) {
471 if (!accept_language_override_.Get().IsEmpty() &&
472 request.HttpHeaderField("Accept-Language").IsEmpty()) {
473 request.SetHttpHeaderField(
474 "Accept-Language",
475 AtomicString(network_utils::GenerateAcceptLanguageHeader(
476 accept_language_override_.Get())));
477 }
478
479 if (resource_type != ResourceType::kImage || disabled_image_types_.IsEmpty())
480 return;
481
482 if (!options.unsupported_image_mime_types) {
483 options.unsupported_image_mime_types =
484 base::MakeRefCounted<base::RefCountedData<HashSet<String>>>();
485 }
486
487 for (String type : disabled_image_types_.Keys()) {
488 options.unsupported_image_mime_types->data.insert(type);
489 }
490
491 request.SetHTTPAccept(
492 OverrideAcceptImageHeader(&options.unsupported_image_mime_types->data));
493 // Bypassing caching to prevent the use of the previously loaded and cached
494 // images.
495 request.SetCacheMode(mojom::blink::FetchCacheMode::kBypassCache);
496 }
497
setNavigatorOverrides(const String & platform)498 Response InspectorEmulationAgent::setNavigatorOverrides(
499 const String& platform) {
500 Response response = AssertPage();
501 if (!response.IsSuccess())
502 return response;
503 navigator_platform_override_.Set(platform);
504 GetWebViewImpl()->GetPage()->GetSettings().SetNavigatorPlatformOverride(
505 platform);
506 return response;
507 }
508
VirtualTimeBudgetExpired()509 void InspectorEmulationAgent::VirtualTimeBudgetExpired() {
510 TRACE_EVENT_NESTABLE_ASYNC_END0("renderer.scheduler", "VirtualTimeBudget",
511 TRACE_ID_LOCAL(this));
512 WebView* view = web_local_frame_->View();
513 if (!view)
514 return;
515
516 view->Scheduler()->SetVirtualTimePolicy(
517 PageScheduler::VirtualTimePolicy::kPause);
518 virtual_time_policy_.Set(protocol::Emulation::VirtualTimePolicyEnum::Pause);
519 // We could have been detached while VT was still running.
520 // TODO(caseq): should we rather force-pause the time upon Disable()?
521 if (auto* frontend = GetFrontend())
522 frontend->virtualTimeBudgetExpired();
523 }
524
setDefaultBackgroundColorOverride(Maybe<protocol::DOM::RGBA> color)525 Response InspectorEmulationAgent::setDefaultBackgroundColorOverride(
526 Maybe<protocol::DOM::RGBA> color) {
527 Response response = AssertPage();
528 if (!response.IsSuccess())
529 return response;
530 if (!color.isJust()) {
531 // Clear the override and state.
532 GetWebViewImpl()->ClearBaseBackgroundColorOverride();
533 default_background_color_override_rgba_.Clear();
534 return Response::Success();
535 }
536
537 blink::protocol::DOM::RGBA* rgba = color.fromJust();
538 default_background_color_override_rgba_.Set(rgba->Serialize());
539 // Clamping of values is done by Color() constructor.
540 int alpha = static_cast<int>(lroundf(255.0f * rgba->getA(1.0f)));
541 GetWebViewImpl()->SetBaseBackgroundColorOverride(
542 Color(rgba->getR(), rgba->getG(), rgba->getB(), alpha).Rgb());
543 return Response::Success();
544 }
545
setDeviceMetricsOverride(int width,int height,double device_scale_factor,bool mobile,Maybe<double> scale,Maybe<int> screen_width,Maybe<int> screen_height,Maybe<int> position_x,Maybe<int> position_y,Maybe<bool> dont_set_visible_size,Maybe<protocol::Emulation::ScreenOrientation>,Maybe<protocol::Page::Viewport>,Maybe<protocol::Emulation::DisplayFeature>)546 Response InspectorEmulationAgent::setDeviceMetricsOverride(
547 int width,
548 int height,
549 double device_scale_factor,
550 bool mobile,
551 Maybe<double> scale,
552 Maybe<int> screen_width,
553 Maybe<int> screen_height,
554 Maybe<int> position_x,
555 Maybe<int> position_y,
556 Maybe<bool> dont_set_visible_size,
557 Maybe<protocol::Emulation::ScreenOrientation>,
558 Maybe<protocol::Page::Viewport>,
559 Maybe<protocol::Emulation::DisplayFeature>) {
560 // We don't have to do anything other than reply to the client, as the
561 // emulation parameters should have already been updated by the handling of
562 // blink::mojom::FrameWidget::EnableDeviceEmulation.
563 return AssertPage();
564 }
565
clearDeviceMetricsOverride()566 Response InspectorEmulationAgent::clearDeviceMetricsOverride() {
567 // We don't have to do anything other than reply to the client, as the
568 // emulation parameters should have already been cleared by the handling of
569 // blink::mojom::FrameWidget::DisableDeviceEmulation.
570 return AssertPage();
571 }
572
setUserAgentOverride(const String & user_agent,protocol::Maybe<String> accept_language,protocol::Maybe<String> platform,protocol::Maybe<protocol::Emulation::UserAgentMetadata> ua_metadata_override)573 Response InspectorEmulationAgent::setUserAgentOverride(
574 const String& user_agent,
575 protocol::Maybe<String> accept_language,
576 protocol::Maybe<String> platform,
577 protocol::Maybe<protocol::Emulation::UserAgentMetadata>
578 ua_metadata_override) {
579 if (!user_agent.IsEmpty() || accept_language.isJust() || platform.isJust())
580 InnerEnable();
581 user_agent_override_.Set(user_agent);
582 accept_language_override_.Set(accept_language.fromMaybe(String()));
583 navigator_platform_override_.Set(platform.fromMaybe(String()));
584 if (web_local_frame_) {
585 GetWebViewImpl()->GetPage()->GetSettings().SetNavigatorPlatformOverride(
586 navigator_platform_override_.Get());
587 }
588
589 if (ua_metadata_override.isJust()) {
590 if (user_agent.IsEmpty()) {
591 ua_metadata_override_ = base::nullopt;
592 serialized_ua_metadata_override_.Set(WTF::String());
593 return Response::InvalidParams(
594 "Can't specify UserAgentMetadata but no UA string");
595 }
596 std::unique_ptr<protocol::Emulation::UserAgentMetadata> ua_metadata =
597 ua_metadata_override.takeJust();
598 ua_metadata_override_.emplace();
599 if (ua_metadata->getBrands()) {
600 for (const auto& bv : *ua_metadata->getBrands()) {
601 blink::UserAgentBrandVersion out_bv;
602 out_bv.brand = bv->getBrand().Ascii();
603 out_bv.major_version = bv->getVersion().Ascii();
604 ua_metadata_override_->brand_version_list.push_back(std::move(out_bv));
605 }
606 }
607 ua_metadata_override_->full_version = ua_metadata->getFullVersion().Ascii();
608 ua_metadata_override_->platform = ua_metadata->getPlatform().Ascii();
609 ua_metadata_override_->platform_version =
610 ua_metadata->getPlatformVersion().Ascii();
611 ua_metadata_override_->architecture =
612 ua_metadata->getArchitecture().Ascii();
613 ua_metadata_override_->model = ua_metadata->getModel().Ascii();
614 ua_metadata_override_->mobile = ua_metadata->getMobile();
615 } else {
616 ua_metadata_override_ = base::nullopt;
617 }
618
619 std::string marshalled =
620 blink::UserAgentMetadata::Marshal(ua_metadata_override_)
621 .value_or(std::string());
622 serialized_ua_metadata_override_.Set(
623 WTF::String(marshalled.data(), marshalled.size()));
624
625 return Response::Success();
626 }
627
setLocaleOverride(protocol::Maybe<String> maybe_locale)628 Response InspectorEmulationAgent::setLocaleOverride(
629 protocol::Maybe<String> maybe_locale) {
630 // Only allow resetting overrides set by the same agent.
631 if (locale_override_.Get().IsEmpty() &&
632 LocaleController::instance().has_locale_override()) {
633 return Response::ServerError(
634 "Another locale override is already in effect");
635 }
636 String locale = maybe_locale.fromMaybe(String());
637 String error = LocaleController::instance().SetLocaleOverride(locale);
638 if (!error.IsEmpty())
639 return Response::ServerError(error.Utf8());
640 locale_override_.Set(locale);
641 return Response::Success();
642 }
643
setTimezoneOverride(const String & timezone_id)644 Response InspectorEmulationAgent::setTimezoneOverride(
645 const String& timezone_id) {
646 if (timezone_id.IsEmpty()) {
647 timezone_override_.reset();
648 } else {
649 if (timezone_override_) {
650 timezone_override_->change(timezone_id);
651 } else {
652 timezone_override_ = TimeZoneController::SetTimeZoneOverride(timezone_id);
653 }
654 if (!timezone_override_) {
655 return TimeZoneController::HasTimeZoneOverride()
656 ? Response::ServerError(
657 "Timezone override is already in effect")
658 : Response::InvalidParams("Invalid timezone id");
659 }
660 }
661
662 timezone_id_override_.Set(timezone_id);
663
664 return Response::Success();
665 }
666
GetDisabledImageTypes(HashSet<String> * result)667 void InspectorEmulationAgent::GetDisabledImageTypes(HashSet<String>* result) {
668 if (disabled_image_types_.IsEmpty())
669 return;
670
671 for (String type : disabled_image_types_.Keys())
672 result->insert(type);
673 }
674
WillCommitLoad(LocalFrame *,DocumentLoader * loader)675 void InspectorEmulationAgent::WillCommitLoad(LocalFrame*,
676 DocumentLoader* loader) {
677 if (virtual_time_policy_.Get() !=
678 protocol::Emulation::VirtualTimePolicyEnum::Pause) {
679 return;
680 }
681 loader->SetDefersLoading(WebURLLoader::DeferType::kDeferred);
682 pending_document_loaders_.push_back(loader);
683 }
684
ApplyAcceptLanguageOverride(String * accept_lang)685 void InspectorEmulationAgent::ApplyAcceptLanguageOverride(String* accept_lang) {
686 if (!accept_language_override_.Get().IsEmpty())
687 *accept_lang = accept_language_override_.Get();
688 }
689
ApplyUserAgentOverride(String * user_agent)690 void InspectorEmulationAgent::ApplyUserAgentOverride(String* user_agent) {
691 if (!user_agent_override_.Get().IsEmpty())
692 *user_agent = user_agent_override_.Get();
693 }
694
ApplyUserAgentMetadataOverride(base::Optional<blink::UserAgentMetadata> * ua_metadata)695 void InspectorEmulationAgent::ApplyUserAgentMetadataOverride(
696 base::Optional<blink::UserAgentMetadata>* ua_metadata) {
697 // This applies when UA override is set.
698 if (!user_agent_override_.Get().IsEmpty()) {
699 *ua_metadata = ua_metadata_override_;
700 }
701 }
702
InnerEnable()703 void InspectorEmulationAgent::InnerEnable() {
704 if (enabled_)
705 return;
706 enabled_ = true;
707 instrumenting_agents_->AddInspectorEmulationAgent(this);
708 }
709
AssertPage()710 Response InspectorEmulationAgent::AssertPage() {
711 if (!web_local_frame_) {
712 LOG(ERROR) << "Can only enable virtual time for pages, not workers";
713 return Response::InvalidParams(
714 "Can only enable virtual time for pages, not workers");
715 }
716 return Response::Success();
717 }
718
Trace(Visitor * visitor) const719 void InspectorEmulationAgent::Trace(Visitor* visitor) const {
720 visitor->Trace(web_local_frame_);
721 visitor->Trace(pending_document_loaders_);
722 InspectorBaseAgent::Trace(visitor);
723 }
724
setDisabledImageTypes(std::unique_ptr<protocol::Array<protocol::Emulation::DisabledImageType>> disabled_types)725 protocol::Response InspectorEmulationAgent::setDisabledImageTypes(
726 std::unique_ptr<protocol::Array<protocol::Emulation::DisabledImageType>>
727 disabled_types) {
728 if (disabled_types->size() > 0 && !enabled_)
729 InnerEnable();
730 disabled_image_types_.Clear();
731 String prefix = "image/";
732 namespace DisabledImageTypeEnum = protocol::Emulation::DisabledImageTypeEnum;
733 for (protocol::Emulation::DisabledImageType type : *disabled_types) {
734 if (DisabledImageTypeEnum::Avif == type ||
735 DisabledImageTypeEnum::Webp == type) {
736 disabled_image_types_.Set(prefix + type, true);
737 continue;
738 }
739 disabled_image_types_.Clear();
740 return Response::InvalidParams("Invalid image type");
741 }
742 return Response::Success();
743 }
744
745 } // namespace blink
746