1 // Copyright 2014 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 "content/browser/devtools/protocol/page_handler.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/location.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/ref_counted_memory.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/process/process_handle.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/post_task.h"
24 #include "base/task/thread_pool.h"
25 #include "base/threading/thread_task_runner_handle.h"
26 #include "build/build_config.h"
27 #include "content/browser/child_process_security_policy_impl.h"
28 #include "content/browser/devtools/devtools_agent_host_impl.h"
29 #include "content/browser/devtools/protocol/browser_handler.h"
30 #include "content/browser/devtools/protocol/devtools_mhtml_helper.h"
31 #include "content/browser/devtools/protocol/emulation_handler.h"
32 #include "content/browser/devtools/protocol/handler_helpers.h"
33 #include "content/browser/manifest/manifest_manager_host.h"
34 #include "content/browser/renderer_host/navigation_request.h"
35 #include "content/browser/renderer_host/navigator.h"
36 #include "content/browser/renderer_host/render_widget_host_impl.h"
37 #include "content/browser/renderer_host/render_widget_host_view_base.h"
38 #include "content/browser/web_contents/web_contents_impl.h"
39 #include "content/browser/web_contents/web_contents_view.h"
40 #include "content/public/browser/browser_context.h"
41 #include "content/public/browser/browser_thread.h"
42 #include "content/public/browser/download_manager.h"
43 #include "content/public/browser/file_select_listener.h"
44 #include "content/public/browser/javascript_dialog_manager.h"
45 #include "content/public/browser/navigation_controller.h"
46 #include "content/public/browser/navigation_entry.h"
47 #include "content/public/browser/navigation_handle.h"
48 #include "content/public/browser/storage_partition.h"
49 #include "content/public/browser/web_contents_delegate.h"
50 #include "content/public/common/referrer.h"
51 #include "content/public/common/result_codes.h"
52 #include "content/public/common/use_zoom_for_dsf_policy.h"
53 #include "net/base/filename_util.h"
54 #include "third_party/skia/include/core/SkBitmap.h"
55 #include "ui/base/page_transition_types.h"
56 #include "ui/gfx/codec/jpeg_codec.h"
57 #include "ui/gfx/codec/png_codec.h"
58 #include "ui/gfx/geometry/size_conversions.h"
59 #include "ui/gfx/image/image.h"
60 #include "ui/gfx/image/image_util.h"
61 #include "ui/gfx/skbitmap_operations.h"
62 #include "ui/snapshot/snapshot.h"
63 
64 #ifdef OS_ANDROID
65 #include "content/browser/renderer_host/compositor_impl_android.h"
66 #endif
67 
68 namespace content {
69 namespace protocol {
70 
71 namespace {
72 
73 constexpr const char* kMhtml = "mhtml";
74 constexpr const char* kPng = "png";
75 constexpr const char* kJpeg = "jpeg";
76 constexpr int kDefaultScreenshotQuality = 80;
77 constexpr int kFrameRetryDelayMs = 100;
78 constexpr int kCaptureRetryLimit = 2;
79 constexpr int kMaxScreencastFramesInFlight = 2;
80 constexpr char kCommandIsOnlyAvailableAtTopTarget[] =
81     "Command can only be executed on top-level targets";
82 
EncodeImage(const gfx::Image & image,const std::string & format,int quality)83 Binary EncodeImage(const gfx::Image& image,
84                    const std::string& format,
85                    int quality) {
86   DCHECK(!image.IsEmpty());
87 
88   scoped_refptr<base::RefCountedMemory> data;
89   if (format == kPng) {
90     data = image.As1xPNGBytes();
91   } else if (format == kJpeg) {
92     scoped_refptr<base::RefCountedBytes> bytes(new base::RefCountedBytes());
93     if (gfx::JPEG1xEncodedDataFromImage(image, quality, &bytes->data()))
94       data = bytes;
95   }
96 
97   if (!data || !data->front())
98     return protocol::Binary();
99 
100   return Binary::fromRefCounted(data);
101 }
102 
EncodeSkBitmap(const SkBitmap & image,const std::string & format,int quality)103 Binary EncodeSkBitmap(const SkBitmap& image,
104                       const std::string& format,
105                       int quality) {
106   return EncodeImage(gfx::Image::CreateFrom1xBitmap(image), format, quality);
107 }
108 
BuildScreencastFrameMetadata(const gfx::Size & surface_size,float device_scale_factor,float page_scale_factor,const gfx::Vector2dF & root_scroll_offset,float top_controls_visible_height)109 std::unique_ptr<Page::ScreencastFrameMetadata> BuildScreencastFrameMetadata(
110     const gfx::Size& surface_size,
111     float device_scale_factor,
112     float page_scale_factor,
113     const gfx::Vector2dF& root_scroll_offset,
114     float top_controls_visible_height) {
115   if (surface_size.IsEmpty() || device_scale_factor == 0)
116     return nullptr;
117 
118   const gfx::SizeF content_size_dip =
119       gfx::ScaleSize(gfx::SizeF(surface_size), 1 / device_scale_factor);
120   float top_offset_dip = top_controls_visible_height;
121   gfx::Vector2dF root_scroll_offset_dip = root_scroll_offset;
122   if (IsUseZoomForDSFEnabled()) {
123     top_offset_dip /= device_scale_factor;
124     root_scroll_offset_dip.Scale(1 / device_scale_factor);
125   }
126   std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata =
127       Page::ScreencastFrameMetadata::Create()
128           .SetPageScaleFactor(page_scale_factor)
129           .SetOffsetTop(top_offset_dip)
130           .SetDeviceWidth(content_size_dip.width())
131           .SetDeviceHeight(content_size_dip.height())
132           .SetScrollOffsetX(root_scroll_offset_dip.x())
133           .SetScrollOffsetY(root_scroll_offset_dip.y())
134           .SetTimestamp(base::Time::Now().ToDoubleT())
135           .Build();
136   return page_metadata;
137 }
138 
139 // Determines the snapshot size that best-fits the Surface's content to the
140 // remote's requested image size.
DetermineSnapshotSize(const gfx::Size & surface_size,int screencast_max_width,int screencast_max_height)141 gfx::Size DetermineSnapshotSize(const gfx::Size& surface_size,
142                                 int screencast_max_width,
143                                 int screencast_max_height) {
144   if (surface_size.IsEmpty())
145     return gfx::Size();  // Nothing to copy (and avoid divide-by-zero below).
146 
147   double scale = 1;
148   if (screencast_max_width > 0) {
149     scale = std::min(scale, static_cast<double>(screencast_max_width) /
150                                 surface_size.width());
151   }
152   if (screencast_max_height > 0) {
153     scale = std::min(scale, static_cast<double>(screencast_max_height) /
154                                 surface_size.height());
155   }
156   return gfx::ToRoundedSize(gfx::ScaleSize(gfx::SizeF(surface_size), scale));
157 }
158 
GetMetadataFromFrame(const media::VideoFrame & frame,double * device_scale_factor,double * page_scale_factor,gfx::Vector2dF * root_scroll_offset,double * top_controls_visible_height)159 void GetMetadataFromFrame(const media::VideoFrame& frame,
160                           double* device_scale_factor,
161                           double* page_scale_factor,
162                           gfx::Vector2dF* root_scroll_offset,
163                           double* top_controls_visible_height) {
164   // Get metadata from |frame|. This will CHECK if metadata is missing.
165   *device_scale_factor = *frame.metadata()->device_scale_factor;
166   *page_scale_factor = *frame.metadata()->page_scale_factor;
167   root_scroll_offset->set_x(*frame.metadata()->root_scroll_offset_x);
168   root_scroll_offset->set_y(*frame.metadata()->root_scroll_offset_y);
169   *top_controls_visible_height = *frame.metadata()->top_controls_visible_height;
170 }
171 
172 template <typename ProtocolCallback>
CanExecuteGlobalCommands(RenderFrameHost * host,const std::unique_ptr<ProtocolCallback> & callback)173 bool CanExecuteGlobalCommands(
174     RenderFrameHost* host,
175     const std::unique_ptr<ProtocolCallback>& callback) {
176   if (!host || !host->GetParent())
177     return true;
178   callback->sendFailure(
179       Response::ServerError(kCommandIsOnlyAvailableAtTopTarget));
180   return false;
181 }
182 
183 }  // namespace
184 
PageHandler(EmulationHandler * emulation_handler,BrowserHandler * browser_handler,bool allow_file_access)185 PageHandler::PageHandler(EmulationHandler* emulation_handler,
186                          BrowserHandler* browser_handler,
187                          bool allow_file_access)
188     : DevToolsDomainHandler(Page::Metainfo::domainName),
189       enabled_(false),
190       screencast_enabled_(false),
191       screencast_quality_(kDefaultScreenshotQuality),
192       screencast_max_width_(-1),
193       screencast_max_height_(-1),
194       capture_every_nth_frame_(1),
195       capture_retry_count_(0),
196       session_id_(0),
197       frame_counter_(0),
198       frames_in_flight_(0),
199       video_consumer_(nullptr),
200       last_surface_size_(gfx::Size()),
201       host_(nullptr),
202       emulation_handler_(emulation_handler),
203       browser_handler_(browser_handler) {
204   bool create_video_consumer = true;
205 #ifdef OS_ANDROID
206   // Video capture doesn't work on Android WebView. Use CopyFromSurface instead.
207   if (!CompositorImpl::IsInitialized())
208     create_video_consumer = false;
209 #endif
210   if (create_video_consumer) {
211     video_consumer_ = std::make_unique<DevToolsVideoConsumer>(
212         base::BindRepeating(&PageHandler::OnFrameFromVideoConsumer,
213                             weak_factory_.GetWeakPtr()));
214   }
215   DCHECK(emulation_handler_);
216 }
217 
218 PageHandler::~PageHandler() = default;
219 
220 // static
EnabledForWebContents(WebContentsImpl * contents)221 std::vector<PageHandler*> PageHandler::EnabledForWebContents(
222     WebContentsImpl* contents) {
223   if (!DevToolsAgentHost::HasFor(contents))
224     return std::vector<PageHandler*>();
225   std::vector<PageHandler*> result;
226   for (auto* handler :
227        PageHandler::ForAgentHost(static_cast<DevToolsAgentHostImpl*>(
228            DevToolsAgentHost::GetOrCreateFor(contents).get()))) {
229     if (handler->enabled_)
230       result.push_back(handler);
231   }
232   return result;
233 }
234 
235 // static
ForAgentHost(DevToolsAgentHostImpl * host)236 std::vector<PageHandler*> PageHandler::ForAgentHost(
237     DevToolsAgentHostImpl* host) {
238   return host->HandlersByName<PageHandler>(Page::Metainfo::domainName);
239 }
240 
SetRenderer(int process_host_id,RenderFrameHostImpl * frame_host)241 void PageHandler::SetRenderer(int process_host_id,
242                               RenderFrameHostImpl* frame_host) {
243   if (host_ == frame_host)
244     return;
245 
246   RenderWidgetHostImpl* widget_host =
247       host_ ? host_->GetRenderWidgetHost() : nullptr;
248   if (widget_host && observation_.IsObservingSource(widget_host))
249     observation_.RemoveObservation();
250 
251   host_ = frame_host;
252   widget_host = host_ ? host_->GetRenderWidgetHost() : nullptr;
253 
254   if (widget_host)
255     observation_.Observe(widget_host);
256 
257   if (video_consumer_ && frame_host) {
258     video_consumer_->SetFrameSinkId(
259         frame_host->GetRenderWidgetHost()->GetFrameSinkId());
260   }
261 }
262 
Wire(UberDispatcher * dispatcher)263 void PageHandler::Wire(UberDispatcher* dispatcher) {
264   frontend_.reset(new Page::Frontend(dispatcher->channel()));
265   Page::Dispatcher::wire(dispatcher, this);
266 }
267 
OnSynchronousSwapCompositorFrame(const cc::RenderFrameMetadata & frame_metadata)268 void PageHandler::OnSynchronousSwapCompositorFrame(
269     const cc::RenderFrameMetadata& frame_metadata) {
270   // Cache |frame_metadata_| as InnerSwapCompositorFrame may also be called on
271   // screencast start.
272   frame_metadata_ = frame_metadata;
273   if (screencast_enabled_)
274     InnerSwapCompositorFrame();
275 }
276 
RenderWidgetHostVisibilityChanged(RenderWidgetHost * widget_host,bool became_visible)277 void PageHandler::RenderWidgetHostVisibilityChanged(
278     RenderWidgetHost* widget_host,
279     bool became_visible) {
280   if (!screencast_enabled_)
281     return;
282   NotifyScreencastVisibility(became_visible);
283 }
284 
RenderWidgetHostDestroyed(RenderWidgetHost * widget_host)285 void PageHandler::RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) {
286   DCHECK(observation_.IsObservingSource(widget_host));
287   observation_.RemoveObservation();
288 }
289 
DidAttachInterstitialPage()290 void PageHandler::DidAttachInterstitialPage() {
291   if (!enabled_)
292     return;
293   frontend_->InterstitialShown();
294 }
295 
DidDetachInterstitialPage()296 void PageHandler::DidDetachInterstitialPage() {
297   if (!enabled_)
298     return;
299   frontend_->InterstitialHidden();
300 }
301 
DidRunJavaScriptDialog(const GURL & url,const base::string16 & message,const base::string16 & default_prompt,JavaScriptDialogType dialog_type,bool has_non_devtools_handlers,JavaScriptDialogCallback callback)302 void PageHandler::DidRunJavaScriptDialog(const GURL& url,
303                                          const base::string16& message,
304                                          const base::string16& default_prompt,
305                                          JavaScriptDialogType dialog_type,
306                                          bool has_non_devtools_handlers,
307                                          JavaScriptDialogCallback callback) {
308   if (!enabled_)
309     return;
310   DCHECK(pending_dialog_.is_null());
311   pending_dialog_ = std::move(callback);
312   std::string type = Page::DialogTypeEnum::Alert;
313   if (dialog_type == JAVASCRIPT_DIALOG_TYPE_CONFIRM)
314     type = Page::DialogTypeEnum::Confirm;
315   if (dialog_type == JAVASCRIPT_DIALOG_TYPE_PROMPT)
316     type = Page::DialogTypeEnum::Prompt;
317   frontend_->JavascriptDialogOpening(url.spec(), base::UTF16ToUTF8(message),
318                                      type, has_non_devtools_handlers,
319                                      base::UTF16ToUTF8(default_prompt));
320 }
321 
DidRunBeforeUnloadConfirm(const GURL & url,bool has_non_devtools_handlers,JavaScriptDialogCallback callback)322 void PageHandler::DidRunBeforeUnloadConfirm(const GURL& url,
323                                             bool has_non_devtools_handlers,
324                                             JavaScriptDialogCallback callback) {
325   if (!enabled_)
326     return;
327   DCHECK(pending_dialog_.is_null());
328   pending_dialog_ = std::move(callback);
329   frontend_->JavascriptDialogOpening(url.spec(), std::string(),
330                                      Page::DialogTypeEnum::Beforeunload,
331                                      has_non_devtools_handlers, std::string());
332 }
333 
DidCloseJavaScriptDialog(bool success,const base::string16 & user_input)334 void PageHandler::DidCloseJavaScriptDialog(bool success,
335                                            const base::string16& user_input) {
336   if (!enabled_)
337     return;
338   pending_dialog_.Reset();
339   frontend_->JavascriptDialogClosed(success, base::UTF16ToUTF8(user_input));
340 }
341 
Enable()342 Response PageHandler::Enable() {
343   enabled_ = true;
344   return Response::FallThrough();
345 }
346 
Disable()347 Response PageHandler::Disable() {
348   enabled_ = false;
349   screencast_enabled_ = false;
350 
351   if (video_consumer_)
352     video_consumer_->StopCapture();
353 
354   if (!pending_dialog_.is_null()) {
355     WebContentsImpl* web_contents = GetWebContents();
356     // Leave dialog hanging if there is a manager that can take care of it,
357     // cancel and send ack otherwise.
358     bool has_dialog_manager =
359         web_contents && web_contents->GetDelegate() &&
360         web_contents->GetDelegate()->GetJavaScriptDialogManager(web_contents);
361     if (!has_dialog_manager)
362       std::move(pending_dialog_).Run(false, base::string16());
363     pending_dialog_.Reset();
364   }
365 
366   for (auto* item : pending_downloads_)
367     item->RemoveObserver(this);
368   navigate_callbacks_.clear();
369   return Response::FallThrough();
370 }
371 
Crash()372 Response PageHandler::Crash() {
373   WebContents* web_contents = WebContents::FromRenderFrameHost(host_);
374   if (!web_contents)
375     return Response::ServerError("Not attached to a page");
376   if (web_contents->IsCrashed())
377     return Response::ServerError("The target has already crashed");
378   if (host_->frame_tree_node()->navigation_request())
379     return Response::ServerError("Page has pending navigations, not killing");
380   return Response::FallThrough();
381 }
382 
Close()383 Response PageHandler::Close() {
384   WebContentsImpl* web_contents = GetWebContents();
385   if (!web_contents)
386     return Response::ServerError("Not attached to a page");
387   web_contents->DispatchBeforeUnload(false /* auto_cancel */);
388   return Response::Success();
389 }
390 
Reload(Maybe<bool> bypassCache,Maybe<std::string> script_to_evaluate_on_load,std::unique_ptr<ReloadCallback> callback)391 void PageHandler::Reload(Maybe<bool> bypassCache,
392                          Maybe<std::string> script_to_evaluate_on_load,
393                          std::unique_ptr<ReloadCallback> callback) {
394   WebContentsImpl* web_contents = GetWebContents();
395   if (!web_contents) {
396     callback->sendFailure(Response::InternalError());
397     return;
398   }
399 
400   // In the case of inspecting a GuestView (e.g. a PDF), we should reload
401   // the outer web contents (embedder), since otherwise reloading the guest by
402   // itself will fail.
403   if (web_contents->GetOuterWebContents())
404     web_contents = web_contents->GetOuterWebContents();
405 
406   // It is important to fallback before triggering reload, so that
407   // renderer could prepare beforehand.
408   callback->fallThrough();
409   web_contents->GetController().Reload(bypassCache.fromMaybe(false)
410                                            ? ReloadType::BYPASSING_CACHE
411                                            : ReloadType::NORMAL,
412                                        false);
413 }
414 
ParsePolicyFromString(const std::string & policy)415 static network::mojom::ReferrerPolicy ParsePolicyFromString(
416     const std::string& policy) {
417   if (policy == Page::ReferrerPolicyEnum::NoReferrer)
418     return network::mojom::ReferrerPolicy::kNever;
419   if (policy == Page::ReferrerPolicyEnum::NoReferrerWhenDowngrade)
420     return network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade;
421   if (policy == Page::ReferrerPolicyEnum::Origin)
422     return network::mojom::ReferrerPolicy::kOrigin;
423   if (policy == Page::ReferrerPolicyEnum::OriginWhenCrossOrigin)
424     return network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin;
425   if (policy == Page::ReferrerPolicyEnum::SameOrigin)
426     return network::mojom::ReferrerPolicy::kSameOrigin;
427   if (policy == Page::ReferrerPolicyEnum::StrictOrigin)
428     return network::mojom::ReferrerPolicy::kStrictOrigin;
429   if (policy == Page::ReferrerPolicyEnum::StrictOriginWhenCrossOrigin) {
430     return network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin;
431   }
432   if (policy == Page::ReferrerPolicyEnum::UnsafeUrl)
433     return network::mojom::ReferrerPolicy::kAlways;
434 
435   DCHECK(policy.empty());
436   return network::mojom::ReferrerPolicy::kDefault;
437 }
438 
Navigate(const std::string & url,Maybe<std::string> referrer,Maybe<std::string> maybe_transition_type,Maybe<std::string> frame_id,Maybe<std::string> referrer_policy,std::unique_ptr<NavigateCallback> callback)439 void PageHandler::Navigate(const std::string& url,
440                            Maybe<std::string> referrer,
441                            Maybe<std::string> maybe_transition_type,
442                            Maybe<std::string> frame_id,
443                            Maybe<std::string> referrer_policy,
444                            std::unique_ptr<NavigateCallback> callback) {
445   GURL gurl(url);
446   if (!gurl.is_valid()) {
447     callback->sendFailure(
448         Response::ServerError("Cannot navigate to invalid URL"));
449     return;
450   }
451 
452   if (!host_) {
453     callback->sendFailure(Response::InternalError());
454     return;
455   }
456 
457   ui::PageTransition type;
458   std::string transition_type =
459       maybe_transition_type.fromMaybe(Page::TransitionTypeEnum::Typed);
460   if (transition_type == Page::TransitionTypeEnum::Link)
461     type = ui::PAGE_TRANSITION_LINK;
462   else if (transition_type == Page::TransitionTypeEnum::Typed)
463     type = ui::PAGE_TRANSITION_TYPED;
464   else if (transition_type == Page::TransitionTypeEnum::Address_bar)
465     type = ui::PAGE_TRANSITION_FROM_ADDRESS_BAR;
466   else if (transition_type == Page::TransitionTypeEnum::Auto_bookmark)
467     type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
468   else if (transition_type == Page::TransitionTypeEnum::Auto_subframe)
469     type = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
470   else if (transition_type == Page::TransitionTypeEnum::Manual_subframe)
471     type = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
472   else if (transition_type == Page::TransitionTypeEnum::Generated)
473     type = ui::PAGE_TRANSITION_GENERATED;
474   else if (transition_type == Page::TransitionTypeEnum::Auto_toplevel)
475     type = ui::PAGE_TRANSITION_AUTO_TOPLEVEL;
476   else if (transition_type == Page::TransitionTypeEnum::Form_submit)
477     type = ui::PAGE_TRANSITION_FORM_SUBMIT;
478   else if (transition_type == Page::TransitionTypeEnum::Reload)
479     type = ui::PAGE_TRANSITION_RELOAD;
480   else if (transition_type == Page::TransitionTypeEnum::Keyword)
481     type = ui::PAGE_TRANSITION_KEYWORD;
482   else if (transition_type == Page::TransitionTypeEnum::Keyword_generated)
483     type = ui::PAGE_TRANSITION_KEYWORD_GENERATED;
484   else
485     type = ui::PAGE_TRANSITION_TYPED;
486 
487   std::string out_frame_id = frame_id.fromMaybe(
488       host_->frame_tree_node()->devtools_frame_token().ToString());
489   FrameTreeNode* frame_tree_node = FrameTreeNodeFromDevToolsFrameToken(
490       host_->frame_tree_node(), out_frame_id);
491 
492   if (!frame_tree_node) {
493     callback->sendFailure(
494         Response::ServerError("No frame with given id found"));
495     return;
496   }
497 
498   NavigationController::LoadURLParams params(gurl);
499   network::mojom::ReferrerPolicy policy =
500       ParsePolicyFromString(referrer_policy.fromMaybe(""));
501   params.referrer = Referrer(GURL(referrer.fromMaybe("")), policy);
502   params.transition_type = type;
503   params.frame_tree_node_id = frame_tree_node->frame_tree_node_id();
504   frame_tree_node->navigator().GetController()->LoadURLWithParams(params);
505 
506   base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token();
507   auto navigate_callback = navigate_callbacks_.find(frame_token);
508   if (navigate_callback != navigate_callbacks_.end()) {
509     std::string error_string = net::ErrorToString(net::ERR_ABORTED);
510     navigate_callback->second->sendSuccess(out_frame_id, Maybe<std::string>(),
511                                            Maybe<std::string>(error_string));
512   }
513   if (frame_tree_node->navigation_request()) {
514     navigate_callbacks_[frame_token] = std::move(callback);
515   } else {
516     callback->sendSuccess(out_frame_id, Maybe<std::string>(),
517                           Maybe<std::string>());
518   }
519 }
520 
NavigationReset(NavigationRequest * navigation_request)521 void PageHandler::NavigationReset(NavigationRequest* navigation_request) {
522   auto navigate_callback = navigate_callbacks_.find(
523       navigation_request->frame_tree_node()->devtools_frame_token());
524   if (navigate_callback == navigate_callbacks_.end())
525     return;
526   std::string frame_id =
527       navigation_request->frame_tree_node()->devtools_frame_token().ToString();
528   bool success = navigation_request->GetNetErrorCode() == net::OK;
529   std::string error_string =
530       net::ErrorToString(navigation_request->GetNetErrorCode());
531   navigate_callback->second->sendSuccess(
532       frame_id,
533       Maybe<std::string>(
534           navigation_request->devtools_navigation_token().ToString()),
535       success ? Maybe<std::string>() : Maybe<std::string>(error_string));
536   navigate_callbacks_.erase(navigate_callback);
537 }
538 
DownloadWillBegin(FrameTreeNode * ftn,download::DownloadItem * item)539 void PageHandler::DownloadWillBegin(FrameTreeNode* ftn,
540                                     download::DownloadItem* item) {
541   if (!enabled_)
542     return;
543 
544   // The filename the end user sees may differ. This is an attempt to eagerly
545   // determine the filename at the beginning of the download; see
546   // DownloadTargetDeterminer:DownloadTargetDeterminer::Result
547   // and DownloadTargetDeterminer::GenerateFileName in
548   // chrome/browser/download/download_target_determiner.cc
549   // for the more comprehensive logic.
550   const base::string16 likely_filename = net::GetSuggestedFilename(
551       item->GetURL(), item->GetContentDisposition(), std::string(),
552       item->GetSuggestedFilename(), item->GetMimeType(), "download");
553 
554   frontend_->DownloadWillBegin(ftn->devtools_frame_token().ToString(),
555                                item->GetGuid(), item->GetURL().spec(),
556                                base::UTF16ToUTF8(likely_filename));
557 
558   item->AddObserver(this);
559   pending_downloads_.insert(item);
560 }
561 
OnDownloadDestroyed(download::DownloadItem * item)562 void PageHandler::OnDownloadDestroyed(download::DownloadItem* item) {
563   pending_downloads_.erase(item);
564 }
565 
OnDownloadUpdated(download::DownloadItem * item)566 void PageHandler::OnDownloadUpdated(download::DownloadItem* item) {
567   if (!enabled_)
568     return;
569   std::string state = Page::DownloadProgress::StateEnum::InProgress;
570   if (item->GetState() == download::DownloadItem::COMPLETE)
571     state = Page::DownloadProgress::StateEnum::Completed;
572   else if (item->GetState() == download::DownloadItem::CANCELLED)
573     state = Page::DownloadProgress::StateEnum::Canceled;
574   frontend_->DownloadProgress(item->GetGuid(), item->GetTotalBytes(),
575                               item->GetReceivedBytes(), state);
576   if (state != Page::DownloadProgress::StateEnum::InProgress) {
577     item->RemoveObserver(this);
578     pending_downloads_.erase(item);
579   }
580 }
581 
TransitionTypeName(ui::PageTransition type)582 static const char* TransitionTypeName(ui::PageTransition type) {
583   int32_t t = type & ~ui::PAGE_TRANSITION_QUALIFIER_MASK;
584   switch (t) {
585     case ui::PAGE_TRANSITION_LINK:
586       return Page::TransitionTypeEnum::Link;
587     case ui::PAGE_TRANSITION_TYPED:
588       return Page::TransitionTypeEnum::Typed;
589     case ui::PAGE_TRANSITION_AUTO_BOOKMARK:
590       return Page::TransitionTypeEnum::Auto_bookmark;
591     case ui::PAGE_TRANSITION_AUTO_SUBFRAME:
592       return Page::TransitionTypeEnum::Auto_subframe;
593     case ui::PAGE_TRANSITION_MANUAL_SUBFRAME:
594       return Page::TransitionTypeEnum::Manual_subframe;
595     case ui::PAGE_TRANSITION_GENERATED:
596       return Page::TransitionTypeEnum::Generated;
597     case ui::PAGE_TRANSITION_AUTO_TOPLEVEL:
598       return Page::TransitionTypeEnum::Auto_toplevel;
599     case ui::PAGE_TRANSITION_FORM_SUBMIT:
600       return Page::TransitionTypeEnum::Form_submit;
601     case ui::PAGE_TRANSITION_RELOAD:
602       return Page::TransitionTypeEnum::Reload;
603     case ui::PAGE_TRANSITION_KEYWORD:
604       return Page::TransitionTypeEnum::Keyword;
605     case ui::PAGE_TRANSITION_KEYWORD_GENERATED:
606       return Page::TransitionTypeEnum::Keyword_generated;
607     default:
608       return Page::TransitionTypeEnum::Other;
609   }
610 }
611 
GetNavigationHistory(int * current_index,std::unique_ptr<NavigationEntries> * entries)612 Response PageHandler::GetNavigationHistory(
613     int* current_index,
614     std::unique_ptr<NavigationEntries>* entries) {
615   WebContentsImpl* web_contents = GetWebContents();
616   if (!web_contents)
617     return Response::InternalError();
618 
619   NavigationController& controller = web_contents->GetController();
620   *current_index = controller.GetCurrentEntryIndex();
621   *entries = std::make_unique<NavigationEntries>();
622   for (int i = 0; i != controller.GetEntryCount(); ++i) {
623     auto* entry = controller.GetEntryAtIndex(i);
624     (*entries)->emplace_back(
625         Page::NavigationEntry::Create()
626             .SetId(entry->GetUniqueID())
627             .SetUrl(entry->GetURL().spec())
628             .SetUserTypedURL(entry->GetUserTypedURL().spec())
629             .SetTitle(base::UTF16ToUTF8(entry->GetTitle()))
630             .SetTransitionType(TransitionTypeName(entry->GetTransitionType()))
631             .Build());
632   }
633   return Response::Success();
634 }
635 
NavigateToHistoryEntry(int entry_id)636 Response PageHandler::NavigateToHistoryEntry(int entry_id) {
637   WebContentsImpl* web_contents = GetWebContents();
638   if (!web_contents)
639     return Response::InternalError();
640 
641   NavigationController& controller = web_contents->GetController();
642   for (int i = 0; i != controller.GetEntryCount(); ++i) {
643     if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) {
644       controller.GoToIndex(i);
645       return Response::Success();
646     }
647   }
648 
649   return Response::InvalidParams("No entry with passed id");
650 }
651 
ReturnTrue(NavigationEntry * entry)652 static bool ReturnTrue(NavigationEntry* entry) {
653   return true;
654 }
655 
ResetNavigationHistory()656 Response PageHandler::ResetNavigationHistory() {
657   WebContentsImpl* web_contents = GetWebContents();
658   if (!web_contents)
659     return Response::InternalError();
660 
661   NavigationController& controller = web_contents->GetController();
662   controller.DeleteNavigationEntries(base::BindRepeating(&ReturnTrue));
663   return Response::Success();
664 }
665 
CaptureSnapshot(Maybe<std::string> format,std::unique_ptr<CaptureSnapshotCallback> callback)666 void PageHandler::CaptureSnapshot(
667     Maybe<std::string> format,
668     std::unique_ptr<CaptureSnapshotCallback> callback) {
669   if (!CanExecuteGlobalCommands(host_, callback))
670     return;
671   std::string snapshot_format = format.fromMaybe(kMhtml);
672   if (snapshot_format != kMhtml) {
673     callback->sendFailure(Response::ServerError("Unsupported snapshot format"));
674     return;
675   }
676   DevToolsMHTMLHelper::Capture(weak_factory_.GetWeakPtr(), std::move(callback));
677 }
678 
CaptureScreenshot(Maybe<std::string> format,Maybe<int> quality,Maybe<Page::Viewport> clip,Maybe<bool> from_surface,std::unique_ptr<CaptureScreenshotCallback> callback)679 void PageHandler::CaptureScreenshot(
680     Maybe<std::string> format,
681     Maybe<int> quality,
682     Maybe<Page::Viewport> clip,
683     Maybe<bool> from_surface,
684     std::unique_ptr<CaptureScreenshotCallback> callback) {
685   if (!host_ || !host_->GetRenderWidgetHost() ||
686       !host_->GetRenderWidgetHost()->GetView()) {
687     callback->sendFailure(Response::InternalError());
688     return;
689   }
690   if (!CanExecuteGlobalCommands(host_, callback))
691     return;
692   if (clip.isJust()) {
693     if (clip.fromJust()->GetWidth() == 0) {
694       callback->sendFailure(
695           Response::ServerError("Cannot take screenshot with 0 width."));
696       return;
697     }
698     if (clip.fromJust()->GetHeight() == 0) {
699       callback->sendFailure(
700           Response::ServerError("Cannot take screenshot with 0 height."));
701       return;
702     }
703   }
704 
705   RenderWidgetHostImpl* widget_host = host_->GetRenderWidgetHost();
706   std::string screenshot_format = format.fromMaybe(kPng);
707   int screenshot_quality = quality.fromMaybe(kDefaultScreenshotQuality);
708 
709   // We don't support clip/emulation when capturing from window, bail out.
710   if (!from_surface.fromMaybe(true)) {
711     widget_host->GetSnapshotFromBrowser(
712         base::BindOnce(&PageHandler::ScreenshotCaptured,
713                        weak_factory_.GetWeakPtr(), std::move(callback),
714                        screenshot_format, screenshot_quality, gfx::Size(),
715                        gfx::Size(), blink::DeviceEmulationParams()),
716         false);
717     return;
718   }
719 
720   // Welcome to the neural net of capturing screenshot while emulating device
721   // metrics!
722   bool emulation_enabled = emulation_handler_->device_emulation_enabled();
723   blink::DeviceEmulationParams original_params =
724       emulation_handler_->GetDeviceEmulationParams();
725   blink::DeviceEmulationParams modified_params = original_params;
726 
727   // Capture original view size if we know we are going to destroy it. We use
728   // it in ScreenshotCaptured to restore.
729   gfx::Size original_view_size =
730       emulation_enabled || clip.isJust()
731           ? widget_host->GetView()->GetViewBounds().size()
732           : gfx::Size();
733   gfx::Size emulated_view_size = modified_params.view_size;
734 
735   double dpfactor = 1;
736   float widget_host_device_scale_factor = widget_host->GetDeviceScaleFactor();
737   if (emulation_enabled) {
738     // When emulating, emulate again and scale to make resulting image match
739     // physical DP resolution. If view_size is not overriden, use actual view
740     // size.
741     float original_scale =
742         original_params.scale > 0 ? original_params.scale : 1;
743     if (!modified_params.view_size.width()) {
744       emulated_view_size.set_width(
745           ceil(original_view_size.width() / original_scale));
746     }
747     if (!modified_params.view_size.height()) {
748       emulated_view_size.set_height(
749           ceil(original_view_size.height() / original_scale));
750     }
751 
752     dpfactor = modified_params.device_scale_factor
753                    ? modified_params.device_scale_factor /
754                          widget_host_device_scale_factor
755                    : 1;
756     // When clip is specified, we scale viewport via clip, otherwise we use
757     // scale.
758     modified_params.scale = clip.isJust() ? 1 : dpfactor;
759     modified_params.view_size = emulated_view_size;
760   } else if (clip.isJust()) {
761     // When not emulating, still need to emulate the page size.
762     modified_params.view_size = original_view_size;
763     modified_params.screen_size = gfx::Size();
764     modified_params.device_scale_factor = 0;
765     modified_params.scale = 1;
766   }
767 
768   // Set up viewport in renderer.
769   if (clip.isJust()) {
770     modified_params.viewport_offset.SetPoint(clip.fromJust()->GetX(),
771                                              clip.fromJust()->GetY());
772     modified_params.viewport_scale = clip.fromJust()->GetScale() * dpfactor;
773     if (IsUseZoomForDSFEnabled()) {
774       modified_params.viewport_offset.Scale(widget_host_device_scale_factor);
775     }
776   }
777 
778   // We use DeviceEmulationParams to either emulate, set viewport or both.
779   emulation_handler_->SetDeviceEmulationParams(modified_params);
780 
781   // Set view size for the screenshot right after emulating.
782   if (clip.isJust()) {
783     double scale = dpfactor * clip.fromJust()->GetScale();
784     widget_host->GetView()->SetSize(
785         gfx::Size(base::ClampRound(clip.fromJust()->GetWidth() * scale),
786                   base::ClampRound(clip.fromJust()->GetHeight() * scale)));
787   } else if (emulation_enabled) {
788     widget_host->GetView()->SetSize(
789         gfx::ScaleToFlooredSize(emulated_view_size, dpfactor));
790   }
791   gfx::Size requested_image_size = gfx::Size();
792   if (emulation_enabled || clip.isJust()) {
793     if (clip.isJust()) {
794       requested_image_size =
795           gfx::Size(clip.fromJust()->GetWidth(), clip.fromJust()->GetHeight());
796     } else {
797       requested_image_size = emulated_view_size;
798     }
799     double scale = emulation_enabled ? original_params.device_scale_factor
800                                      : widget_host_device_scale_factor;
801     if (clip.isJust())
802       scale *= clip.fromJust()->GetScale();
803     requested_image_size = gfx::ScaleToRoundedSize(requested_image_size, scale);
804   }
805 
806   widget_host->GetSnapshotFromBrowser(
807       base::BindOnce(&PageHandler::ScreenshotCaptured,
808                      weak_factory_.GetWeakPtr(), std::move(callback),
809                      screenshot_format, screenshot_quality, original_view_size,
810                      requested_image_size, original_params),
811       true);
812 }
813 
PrintToPDF(Maybe<bool> landscape,Maybe<bool> display_header_footer,Maybe<bool> print_background,Maybe<double> scale,Maybe<double> paper_width,Maybe<double> paper_height,Maybe<double> margin_top,Maybe<double> margin_bottom,Maybe<double> margin_left,Maybe<double> margin_right,Maybe<String> page_ranges,Maybe<bool> ignore_invalid_page_ranges,Maybe<String> header_template,Maybe<String> footer_template,Maybe<bool> prefer_css_page_size,Maybe<String> transfer_mode,std::unique_ptr<PrintToPDFCallback> callback)814 void PageHandler::PrintToPDF(Maybe<bool> landscape,
815                              Maybe<bool> display_header_footer,
816                              Maybe<bool> print_background,
817                              Maybe<double> scale,
818                              Maybe<double> paper_width,
819                              Maybe<double> paper_height,
820                              Maybe<double> margin_top,
821                              Maybe<double> margin_bottom,
822                              Maybe<double> margin_left,
823                              Maybe<double> margin_right,
824                              Maybe<String> page_ranges,
825                              Maybe<bool> ignore_invalid_page_ranges,
826                              Maybe<String> header_template,
827                              Maybe<String> footer_template,
828                              Maybe<bool> prefer_css_page_size,
829                              Maybe<String> transfer_mode,
830                              std::unique_ptr<PrintToPDFCallback> callback) {
831   callback->sendFailure(Response::ServerError("PrintToPDF is not implemented"));
832   return;
833 }
834 
StartScreencast(Maybe<std::string> format,Maybe<int> quality,Maybe<int> max_width,Maybe<int> max_height,Maybe<int> every_nth_frame)835 Response PageHandler::StartScreencast(Maybe<std::string> format,
836                                       Maybe<int> quality,
837                                       Maybe<int> max_width,
838                                       Maybe<int> max_height,
839                                       Maybe<int> every_nth_frame) {
840   WebContentsImpl* web_contents = GetWebContents();
841   if (!web_contents)
842     return Response::InternalError();
843   RenderWidgetHostImpl* widget_host =
844       host_ ? host_->GetRenderWidgetHost() : nullptr;
845   if (!widget_host)
846     return Response::InternalError();
847 
848   screencast_enabled_ = true;
849   screencast_format_ = format.fromMaybe(kPng);
850   screencast_quality_ = quality.fromMaybe(kDefaultScreenshotQuality);
851   if (screencast_quality_ < 0 || screencast_quality_ > 100)
852     screencast_quality_ = kDefaultScreenshotQuality;
853   screencast_max_width_ = max_width.fromMaybe(-1);
854   screencast_max_height_ = max_height.fromMaybe(-1);
855   ++session_id_;
856   frame_counter_ = 0;
857   frames_in_flight_ = 0;
858   capture_every_nth_frame_ = every_nth_frame.fromMaybe(1);
859   bool visible = !widget_host->is_hidden();
860   NotifyScreencastVisibility(visible);
861 
862   if (video_consumer_) {
863     gfx::Size surface_size = gfx::Size();
864     RenderWidgetHostViewBase* const view =
865         static_cast<RenderWidgetHostViewBase*>(host_->GetView());
866     if (view) {
867       surface_size = view->GetCompositorViewportPixelSize();
868       last_surface_size_ = surface_size;
869     }
870 
871     gfx::Size snapshot_size = DetermineSnapshotSize(
872         surface_size, screencast_max_width_, screencast_max_height_);
873     if (!snapshot_size.IsEmpty())
874       video_consumer_->SetMinAndMaxFrameSize(snapshot_size, snapshot_size);
875 
876     video_consumer_->StartCapture();
877     return Response::FallThrough();
878   }
879 
880   if (!visible)
881     return Response::FallThrough();
882 
883   if (frame_metadata_) {
884     InnerSwapCompositorFrame();
885   } else {
886     widget_host->RequestForceRedraw(0);
887   }
888   return Response::FallThrough();
889 }
890 
StopScreencast()891 Response PageHandler::StopScreencast() {
892   screencast_enabled_ = false;
893   if (video_consumer_)
894     video_consumer_->StopCapture();
895   return Response::FallThrough();
896 }
897 
ScreencastFrameAck(int session_id)898 Response PageHandler::ScreencastFrameAck(int session_id) {
899   if (session_id == session_id_)
900     --frames_in_flight_;
901   return Response::Success();
902 }
903 
HandleJavaScriptDialog(bool accept,Maybe<std::string> prompt_text)904 Response PageHandler::HandleJavaScriptDialog(bool accept,
905                                              Maybe<std::string> prompt_text) {
906   WebContentsImpl* web_contents = GetWebContents();
907   if (!web_contents)
908     return Response::InternalError();
909 
910   if (pending_dialog_.is_null())
911     return Response::InvalidParams("No dialog is showing");
912 
913   base::string16 prompt_override;
914   if (prompt_text.isJust())
915     prompt_override = base::UTF8ToUTF16(prompt_text.fromJust());
916   std::move(pending_dialog_).Run(accept, prompt_override);
917 
918   // Clean up the dialog UI if any.
919   if (web_contents->GetDelegate()) {
920     JavaScriptDialogManager* manager =
921         web_contents->GetDelegate()->GetJavaScriptDialogManager(web_contents);
922     if (manager) {
923       manager->HandleJavaScriptDialog(
924           web_contents, accept,
925           prompt_text.isJust() ? &prompt_override : nullptr);
926     }
927   }
928 
929   return Response::Success();
930 }
931 
BringToFront()932 Response PageHandler::BringToFront() {
933   WebContentsImpl* wc = GetWebContents();
934   if (wc) {
935     wc->Activate();
936     wc->Focus();
937     return Response::Success();
938   }
939   return Response::InternalError();
940 }
941 
SetDownloadBehavior(const std::string & behavior,Maybe<std::string> download_path)942 Response PageHandler::SetDownloadBehavior(const std::string& behavior,
943                                           Maybe<std::string> download_path) {
944   BrowserContext* browser_context =
945       host_ ? host_->GetProcess()->GetBrowserContext() : nullptr;
946   if (!browser_context)
947     return Response::ServerError("Could not fetch browser context");
948   if (host_ && host_->GetParent())
949     return Response::ServerError(kCommandIsOnlyAvailableAtTopTarget);
950   return browser_handler_->DoSetDownloadBehavior(behavior, browser_context,
951                                                  std::move(download_path));
952 }
953 
GetAppManifest(std::unique_ptr<GetAppManifestCallback> callback)954 void PageHandler::GetAppManifest(
955     std::unique_ptr<GetAppManifestCallback> callback) {
956   if (!host_) {
957     callback->sendFailure(Response::ServerError("Cannot retrieve manifest"));
958     return;
959   }
960   if (!CanExecuteGlobalCommands(host_, callback))
961     return;
962   ManifestManagerHost::GetOrCreateForCurrentDocument(host_->GetMainFrame())
963       ->RequestManifestDebugInfo(base::BindOnce(&PageHandler::GotManifest,
964                                                 weak_factory_.GetWeakPtr(),
965                                                 std::move(callback)));
966 }
967 
GetWebContents()968 WebContentsImpl* PageHandler::GetWebContents() {
969   return host_ && !host_->frame_tree_node()->parent()
970              ? static_cast<WebContentsImpl*>(
971                    WebContents::FromRenderFrameHost(host_))
972              : nullptr;
973 }
974 
NotifyScreencastVisibility(bool visible)975 void PageHandler::NotifyScreencastVisibility(bool visible) {
976   if (visible)
977     capture_retry_count_ = kCaptureRetryLimit;
978   frontend_->ScreencastVisibilityChanged(visible);
979 }
980 
ShouldCaptureNextScreencastFrame()981 bool PageHandler::ShouldCaptureNextScreencastFrame() {
982   return frames_in_flight_ <= kMaxScreencastFramesInFlight &&
983          !(++frame_counter_ % capture_every_nth_frame_);
984 }
985 
InnerSwapCompositorFrame()986 void PageHandler::InnerSwapCompositorFrame() {
987   if (!host_)
988     return;
989 
990   if (!ShouldCaptureNextScreencastFrame())
991     return;
992 
993   RenderWidgetHostViewBase* const view =
994       static_cast<RenderWidgetHostViewBase*>(host_->GetView());
995   if (!view || !view->IsSurfaceAvailableForCopy())
996     return;
997 
998   const gfx::Size surface_size = view->GetCompositorViewportPixelSize();
999   if (surface_size.IsEmpty())
1000     return;
1001 
1002   const gfx::Size snapshot_size = DetermineSnapshotSize(
1003       surface_size, screencast_max_width_, screencast_max_height_);
1004   if (snapshot_size.IsEmpty())
1005     return;
1006 
1007   double top_controls_visible_height =
1008       frame_metadata_->top_controls_height *
1009       frame_metadata_->top_controls_shown_ratio;
1010 
1011   std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata =
1012       BuildScreencastFrameMetadata(
1013           surface_size, frame_metadata_->device_scale_factor,
1014           frame_metadata_->page_scale_factor,
1015           frame_metadata_->root_scroll_offset.value_or(gfx::Vector2dF()),
1016           top_controls_visible_height);
1017   if (!page_metadata)
1018     return;
1019 
1020   // Request a copy of the surface as a scaled SkBitmap.
1021   view->CopyFromSurface(
1022       gfx::Rect(), snapshot_size,
1023       base::BindOnce(&PageHandler::ScreencastFrameCaptured,
1024                      weak_factory_.GetWeakPtr(), std::move(page_metadata)));
1025   frames_in_flight_++;
1026 }
1027 
OnFrameFromVideoConsumer(scoped_refptr<media::VideoFrame> frame)1028 void PageHandler::OnFrameFromVideoConsumer(
1029     scoped_refptr<media::VideoFrame> frame) {
1030   if (!host_)
1031     return;
1032 
1033   if (!ShouldCaptureNextScreencastFrame())
1034     return;
1035 
1036   RenderWidgetHostViewBase* const view =
1037       static_cast<RenderWidgetHostViewBase*>(host_->GetView());
1038   if (!view)
1039     return;
1040 
1041   const gfx::Size surface_size = view->GetCompositorViewportPixelSize();
1042   if (surface_size.IsEmpty())
1043     return;
1044 
1045   // If window has been resized, set the new dimensions.
1046   if (surface_size != last_surface_size_) {
1047     last_surface_size_ = surface_size;
1048     gfx::Size snapshot_size = DetermineSnapshotSize(
1049         surface_size, screencast_max_width_, screencast_max_height_);
1050     if (!snapshot_size.IsEmpty())
1051       video_consumer_->SetMinAndMaxFrameSize(snapshot_size, snapshot_size);
1052     return;
1053   }
1054 
1055   double device_scale_factor, page_scale_factor;
1056   double top_controls_visible_height;
1057   gfx::Vector2dF root_scroll_offset;
1058   GetMetadataFromFrame(*frame, &device_scale_factor, &page_scale_factor,
1059                        &root_scroll_offset, &top_controls_visible_height);
1060   std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata =
1061       BuildScreencastFrameMetadata(surface_size, device_scale_factor,
1062                                    page_scale_factor, root_scroll_offset,
1063                                    top_controls_visible_height);
1064   if (!page_metadata)
1065     return;
1066 
1067   frames_in_flight_++;
1068   ScreencastFrameCaptured(std::move(page_metadata),
1069                           DevToolsVideoConsumer::GetSkBitmapFromFrame(frame));
1070 }
1071 
ScreencastFrameCaptured(std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,const SkBitmap & bitmap)1072 void PageHandler::ScreencastFrameCaptured(
1073     std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,
1074     const SkBitmap& bitmap) {
1075   if (bitmap.drawsNothing()) {
1076     if (capture_retry_count_) {
1077       --capture_retry_count_;
1078       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1079           FROM_HERE,
1080           base::BindOnce(&PageHandler::InnerSwapCompositorFrame,
1081                          weak_factory_.GetWeakPtr()),
1082           base::TimeDelta::FromMilliseconds(kFrameRetryDelayMs));
1083     }
1084     --frames_in_flight_;
1085     return;
1086   }
1087   base::ThreadPool::PostTaskAndReplyWithResult(
1088       FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
1089       base::BindOnce(&EncodeSkBitmap, bitmap, screencast_format_,
1090                      screencast_quality_),
1091       base::BindOnce(&PageHandler::ScreencastFrameEncoded,
1092                      weak_factory_.GetWeakPtr(), std::move(page_metadata)));
1093 }
1094 
ScreencastFrameEncoded(std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,const protocol::Binary & data)1095 void PageHandler::ScreencastFrameEncoded(
1096     std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,
1097     const protocol::Binary& data) {
1098   if (data.size() == 0) {
1099     --frames_in_flight_;
1100     return;  // Encode failed.
1101   }
1102 
1103   frontend_->ScreencastFrame(data, std::move(page_metadata), session_id_);
1104 }
1105 
ScreenshotCaptured(std::unique_ptr<CaptureScreenshotCallback> callback,const std::string & format,int quality,const gfx::Size & original_view_size,const gfx::Size & requested_image_size,const blink::DeviceEmulationParams & original_emulation_params,const gfx::Image & image)1106 void PageHandler::ScreenshotCaptured(
1107     std::unique_ptr<CaptureScreenshotCallback> callback,
1108     const std::string& format,
1109     int quality,
1110     const gfx::Size& original_view_size,
1111     const gfx::Size& requested_image_size,
1112     const blink::DeviceEmulationParams& original_emulation_params,
1113     const gfx::Image& image) {
1114   if (original_view_size.width()) {
1115     RenderWidgetHostImpl* widget_host = host_->GetRenderWidgetHost();
1116     widget_host->GetView()->SetSize(original_view_size);
1117     emulation_handler_->SetDeviceEmulationParams(original_emulation_params);
1118   }
1119 
1120   if (image.IsEmpty()) {
1121     callback->sendFailure(
1122         Response::ServerError("Unable to capture screenshot"));
1123     return;
1124   }
1125 
1126   if (!requested_image_size.IsEmpty() &&
1127       (image.Width() != requested_image_size.width() ||
1128        image.Height() != requested_image_size.height())) {
1129     const SkBitmap* bitmap = image.ToSkBitmap();
1130     SkBitmap cropped = SkBitmapOperations::CreateTiledBitmap(
1131         *bitmap, 0, 0, requested_image_size.width(),
1132         requested_image_size.height());
1133     gfx::Image croppedImage = gfx::Image::CreateFrom1xBitmap(cropped);
1134     callback->sendSuccess(EncodeImage(croppedImage, format, quality));
1135   } else {
1136     callback->sendSuccess(EncodeImage(image, format, quality));
1137   }
1138 }
1139 
GotManifest(std::unique_ptr<GetAppManifestCallback> callback,const GURL & manifest_url,const::blink::Manifest & parsed_manifest,blink::mojom::ManifestDebugInfoPtr debug_info)1140 void PageHandler::GotManifest(std::unique_ptr<GetAppManifestCallback> callback,
1141                               const GURL& manifest_url,
1142                               const ::blink::Manifest& parsed_manifest,
1143                               blink::mojom::ManifestDebugInfoPtr debug_info) {
1144   auto errors = std::make_unique<protocol::Array<Page::AppManifestError>>();
1145   bool failed = true;
1146   if (debug_info) {
1147     failed = false;
1148     for (const auto& error : debug_info->errors) {
1149       errors->emplace_back(Page::AppManifestError::Create()
1150                                .SetMessage(error->message)
1151                                .SetCritical(error->critical)
1152                                .SetLine(error->line)
1153                                .SetColumn(error->column)
1154                                .Build());
1155       if (error->critical)
1156         failed = true;
1157     }
1158   }
1159 
1160   std::unique_ptr<Page::AppManifestParsedProperties> parsed;
1161   if (!parsed_manifest.IsEmpty()) {
1162     parsed = Page::AppManifestParsedProperties::Create()
1163                  .SetScope(parsed_manifest.scope.possibly_invalid_spec())
1164                  .Build();
1165   }
1166 
1167   callback->sendSuccess(
1168       manifest_url.possibly_invalid_spec(), std::move(errors),
1169       failed ? Maybe<std::string>() : debug_info->raw_manifest,
1170       std::move(parsed));
1171 }
1172 
StopLoading()1173 Response PageHandler::StopLoading() {
1174   WebContentsImpl* web_contents = GetWebContents();
1175   if (!web_contents)
1176     return Response::InternalError();
1177   web_contents->Stop();
1178   return Response::Success();
1179 }
1180 
SetWebLifecycleState(const std::string & state)1181 Response PageHandler::SetWebLifecycleState(const std::string& state) {
1182   WebContentsImpl* web_contents = GetWebContents();
1183   if (!web_contents)
1184     return Response::ServerError("Not attached to a page");
1185   if (state == Page::SetWebLifecycleState::StateEnum::Frozen) {
1186     // TODO(fmeawad): Instead of forcing a visibility change, only allow
1187     // freezing a page if it was already hidden.
1188     web_contents->WasHidden();
1189     web_contents->SetPageFrozen(true);
1190     return Response::Success();
1191   }
1192   if (state == Page::SetWebLifecycleState::StateEnum::Active) {
1193     web_contents->SetPageFrozen(false);
1194     return Response::Success();
1195   }
1196   return Response::ServerError("Unidentified lifecycle state");
1197 }
1198 
GetInstallabilityErrors(std::unique_ptr<GetInstallabilityErrorsCallback> callback)1199 void PageHandler::GetInstallabilityErrors(
1200     std::unique_ptr<GetInstallabilityErrorsCallback> callback) {
1201   auto installability_errors =
1202       std::make_unique<protocol::Array<Page::InstallabilityError>>();
1203   // TODO: Use InstallableManager once it moves into content/.
1204   // Until then, this code is only used to return empty array in the tests.
1205   callback->sendSuccess(std::move(installability_errors));
1206 }
1207 
GetManifestIcons(std::unique_ptr<GetManifestIconsCallback> callback)1208 void PageHandler::GetManifestIcons(
1209     std::unique_ptr<GetManifestIconsCallback> callback) {
1210   // TODO: Use InstallableManager once it moves into content/.
1211   // Until then, this code is only used to return no image data in the tests.
1212   callback->sendSuccess(Maybe<Binary>());
1213 }
1214 
1215 }  // namespace protocol
1216 }  // namespace content
1217