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