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