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