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