Fix various typos, error handling for GN auto-roller.
[chromium-blink-merge.git] / content / browser / devtools / protocol / page_handler.cc
blob63550c016f0bd2c3c622178cddb059747c47007d
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.
5 #include "content/browser/devtools/protocol/page_handler.h"
7 #include <string>
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/threading/worker_pool.h"
17 #include "content/browser/devtools/protocol/color_picker.h"
18 #include "content/browser/renderer_host/render_widget_host_impl.h"
19 #include "content/browser/renderer_host/render_widget_host_view_base.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/common/view_messages.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/javascript_dialog_manager.h"
24 #include "content/public/browser/navigation_controller.h"
25 #include "content/public/browser/navigation_entry.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/storage_partition.h"
29 #include "content/public/browser/web_contents_delegate.h"
30 #include "content/public/common/referrer.h"
31 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
32 #include "third_party/skia/include/core/SkBitmap.h"
33 #include "ui/base/page_transition_types.h"
34 #include "ui/gfx/codec/jpeg_codec.h"
35 #include "ui/gfx/codec/png_codec.h"
36 #include "ui/gfx/geometry/size_conversions.h"
37 #include "ui/snapshot/snapshot.h"
38 #include "url/gurl.h"
40 namespace content {
41 namespace devtools {
42 namespace page {
44 namespace {
46 static const char kPng[] = "png";
47 static const char kJpeg[] = "jpeg";
48 static int kDefaultScreenshotQuality = 80;
49 static int kFrameRetryDelayMs = 100;
50 static int kCaptureRetryLimit = 2;
51 static int kMaxScreencastFramesInFlight = 2;
53 std::string EncodeScreencastFrame(const SkBitmap& bitmap,
54 const std::string& format,
55 int quality) {
56 std::vector<unsigned char> data;
57 SkAutoLockPixels lock_image(bitmap);
58 bool encoded;
59 if (format == kPng) {
60 encoded = gfx::PNGCodec::Encode(
61 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
62 gfx::PNGCodec::FORMAT_SkBitmap,
63 gfx::Size(bitmap.width(), bitmap.height()),
64 bitmap.width() * bitmap.bytesPerPixel(),
65 false, std::vector<gfx::PNGCodec::Comment>(), &data);
66 } else if (format == kJpeg) {
67 encoded = gfx::JPEGCodec::Encode(
68 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
69 gfx::JPEGCodec::FORMAT_SkBitmap,
70 bitmap.width(),
71 bitmap.height(),
72 bitmap.width() * bitmap.bytesPerPixel(),
73 quality, &data);
74 } else {
75 encoded = false;
78 if (!encoded)
79 return std::string();
81 std::string base_64_data;
82 base::Base64Encode(
83 base::StringPiece(reinterpret_cast<char*>(&data[0]), data.size()),
84 &base_64_data);
86 return base_64_data;
89 } // namespace
91 typedef DevToolsProtocolClient::Response Response;
93 PageHandler::PageHandler()
94 : enabled_(false),
95 screencast_enabled_(false),
96 screencast_quality_(kDefaultScreenshotQuality),
97 screencast_max_width_(-1),
98 screencast_max_height_(-1),
99 capture_retry_count_(0),
100 has_compositor_frame_metadata_(false),
101 screencast_frame_sent_(0),
102 screencast_frame_acked_(0),
103 processing_screencast_frame_(false),
104 color_picker_(new ColorPicker(base::Bind(
105 &PageHandler::OnColorPicked, base::Unretained(this)))),
106 host_(nullptr),
107 screencast_listener_(nullptr),
108 weak_factory_(this) {
111 PageHandler::~PageHandler() {
114 void PageHandler::SetRenderFrameHost(RenderFrameHostImpl* host) {
115 if (host_ == host)
116 return;
118 RenderWidgetHostImpl* widget_host =
119 host_ ? host_->GetRenderWidgetHost() : nullptr;
120 if (widget_host) {
121 registrar_.Remove(
122 this,
123 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
124 content::Source<RenderWidgetHost>(widget_host));
127 host_ = host;
128 widget_host = host_ ? host_->GetRenderWidgetHost() : nullptr;
129 color_picker_->SetRenderWidgetHost(widget_host);
131 if (widget_host) {
132 registrar_.Add(
133 this,
134 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
135 content::Source<RenderWidgetHost>(widget_host));
139 void PageHandler::SetClient(scoped_ptr<Client> client) {
140 client_.swap(client);
143 void PageHandler::Detached() {
144 Disable();
147 void PageHandler::OnSwapCompositorFrame(
148 const cc::CompositorFrameMetadata& frame_metadata) {
149 last_compositor_frame_metadata_ = has_compositor_frame_metadata_ ?
150 next_compositor_frame_metadata_ : frame_metadata;
151 next_compositor_frame_metadata_ = frame_metadata;
152 has_compositor_frame_metadata_ = true;
154 if (screencast_enabled_)
155 InnerSwapCompositorFrame();
156 color_picker_->OnSwapCompositorFrame();
159 void PageHandler::Observe(int type,
160 const NotificationSource& source,
161 const NotificationDetails& details) {
162 if (!screencast_enabled_)
163 return;
164 DCHECK(type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED);
165 bool visible = *Details<bool>(details).ptr();
166 NotifyScreencastVisibility(visible);
169 void PageHandler::DidAttachInterstitialPage() {
170 if (!enabled_)
171 return;
172 client_->InterstitialShown(InterstitialShownParams::Create());
175 void PageHandler::DidDetachInterstitialPage() {
176 if (!enabled_)
177 return;
178 client_->InterstitialHidden(InterstitialHiddenParams::Create());
181 void PageHandler::SetScreencastListener(ScreencastListener* listener) {
182 screencast_listener_ = listener;
185 Response PageHandler::Enable() {
186 enabled_ = true;
187 return Response::FallThrough();
190 Response PageHandler::Disable() {
191 enabled_ = false;
192 screencast_enabled_ = false;
193 color_picker_->SetEnabled(false);
194 if (screencast_listener_)
195 screencast_listener_->ScreencastEnabledChanged();
196 return Response::FallThrough();
199 Response PageHandler::Reload(const bool* ignoreCache,
200 const std::string* script_to_evaluate_on_load,
201 const std::string* script_preprocessor) {
202 WebContentsImpl* web_contents = GetWebContents();
203 if (!web_contents)
204 return Response::InternalError("Could not connect to view");
206 // Handle in browser only if it is crashed.
207 if (!web_contents->IsCrashed())
208 return Response::FallThrough();
210 web_contents->GetController().Reload(false);
211 return Response::OK();
214 Response PageHandler::Navigate(const std::string& url,
215 FrameId* frame_id) {
216 GURL gurl(url);
217 if (!gurl.is_valid())
218 return Response::InternalError("Cannot navigate to invalid URL");
220 WebContentsImpl* web_contents = GetWebContents();
221 if (!web_contents)
222 return Response::InternalError("Could not connect to view");
224 web_contents->GetController()
225 .LoadURL(gurl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
226 return Response::FallThrough();
229 Response PageHandler::GetNavigationHistory(int* current_index,
230 NavigationEntries* entries) {
231 WebContentsImpl* web_contents = GetWebContents();
232 if (!web_contents)
233 return Response::InternalError("Could not connect to view");
235 NavigationController& controller = web_contents->GetController();
236 *current_index = controller.GetCurrentEntryIndex();
237 for (int i = 0; i != controller.GetEntryCount(); ++i) {
238 entries->push_back(NavigationEntry::Create()
239 ->set_id(controller.GetEntryAtIndex(i)->GetUniqueID())
240 ->set_url(controller.GetEntryAtIndex(i)->GetURL().spec())
241 ->set_title(
242 base::UTF16ToUTF8(controller.GetEntryAtIndex(i)->GetTitle())));
244 return Response::OK();
247 Response PageHandler::NavigateToHistoryEntry(int entry_id) {
248 WebContentsImpl* web_contents = GetWebContents();
249 if (!web_contents)
250 return Response::InternalError("Could not connect to view");
252 NavigationController& controller = web_contents->GetController();
253 for (int i = 0; i != controller.GetEntryCount(); ++i) {
254 if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) {
255 controller.GoToIndex(i);
256 return Response::OK();
260 return Response::InvalidParams("No entry with passed id");
263 Response PageHandler::CaptureScreenshot(DevToolsCommandId command_id) {
264 if (!host_ || !host_->GetRenderWidgetHost())
265 return Response::InternalError("Could not connect to view");
267 host_->GetRenderWidgetHost()->GetSnapshotFromBrowser(
268 base::Bind(&PageHandler::ScreenshotCaptured,
269 weak_factory_.GetWeakPtr(), command_id));
270 return Response::OK();
273 Response PageHandler::CanScreencast(bool* result) {
274 #if defined(OS_ANDROID)
275 *result = true;
276 #else
277 *result = false;
278 #endif // defined(OS_ANDROID)
279 return Response::OK();
282 Response PageHandler::StartScreencast(const std::string* format,
283 const int* quality,
284 const int* max_width,
285 const int* max_height) {
286 RenderWidgetHostImpl* widget_host =
287 host_ ? host_->GetRenderWidgetHost() : nullptr;
288 if (!widget_host)
289 return Response::InternalError("Could not connect to view");
291 screencast_enabled_ = true;
292 screencast_format_ = format ? *format : kPng;
293 screencast_quality_ = quality ? *quality : kDefaultScreenshotQuality;
294 if (screencast_quality_ < 0 || screencast_quality_ > 100)
295 screencast_quality_ = kDefaultScreenshotQuality;
296 screencast_max_width_ = max_width ? *max_width : -1;
297 screencast_max_height_ = max_height ? *max_height : -1;
299 bool visible = !widget_host->is_hidden();
300 NotifyScreencastVisibility(visible);
301 if (visible) {
302 if (has_compositor_frame_metadata_) {
303 InnerSwapCompositorFrame();
304 } else {
305 widget_host->Send(
306 new ViewMsg_ForceRedraw(widget_host->GetRoutingID(), 0));
309 if (screencast_listener_)
310 screencast_listener_->ScreencastEnabledChanged();
311 return Response::FallThrough();
314 Response PageHandler::StopScreencast() {
315 screencast_enabled_ = false;
316 if (screencast_listener_)
317 screencast_listener_->ScreencastEnabledChanged();
318 return Response::FallThrough();
321 Response PageHandler::ScreencastFrameAck(int frame_number) {
322 screencast_frame_acked_ = frame_number;
323 return Response::OK();
326 Response PageHandler::HandleJavaScriptDialog(bool accept,
327 const std::string* prompt_text) {
328 base::string16 prompt_override;
329 if (prompt_text)
330 prompt_override = base::UTF8ToUTF16(*prompt_text);
332 WebContentsImpl* web_contents = GetWebContents();
333 if (!web_contents)
334 return Response::InternalError("Could not connect to view");
336 JavaScriptDialogManager* manager =
337 web_contents->GetDelegate()->GetJavaScriptDialogManager(web_contents);
338 if (manager && manager->HandleJavaScriptDialog(
339 web_contents, accept, prompt_text ? &prompt_override : nullptr)) {
340 return Response::OK();
343 return Response::InternalError("Could not handle JavaScript dialog");
346 Response PageHandler::QueryUsageAndQuota(DevToolsCommandId command_id,
347 const std::string& security_origin) {
348 return Response::OK();
351 Response PageHandler::SetColorPickerEnabled(bool enabled) {
352 if (!host_)
353 return Response::InternalError("Could not connect to view");
355 color_picker_->SetEnabled(enabled);
356 return Response::OK();
359 WebContentsImpl* PageHandler::GetWebContents() {
360 return host_ ?
361 static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(host_)) :
362 nullptr;
365 void PageHandler::NotifyScreencastVisibility(bool visible) {
366 if (visible)
367 capture_retry_count_ = kCaptureRetryLimit;
368 client_->ScreencastVisibilityChanged(
369 ScreencastVisibilityChangedParams::Create()->set_visible(visible));
372 void PageHandler::InnerSwapCompositorFrame() {
373 if (screencast_frame_sent_ - screencast_frame_acked_ >
374 kMaxScreencastFramesInFlight || processing_screencast_frame_) {
375 return;
378 if (!host_ || !host_->GetView())
379 return;
381 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
382 host_->GetView());
383 // TODO(vkuzkokov): do not use previous frame metadata.
384 cc::CompositorFrameMetadata& metadata = last_compositor_frame_metadata_;
386 gfx::SizeF viewport_size_dip = gfx::ScaleSize(
387 metadata.scrollable_viewport_size, metadata.page_scale_factor);
388 gfx::SizeF screen_size_dip = gfx::ScaleSize(view->GetPhysicalBackingSize(),
389 1 / metadata.device_scale_factor);
391 blink::WebScreenInfo screen_info;
392 view->GetScreenInfo(&screen_info);
393 double device_scale_factor = screen_info.deviceScaleFactor;
394 double scale = 1;
396 if (screencast_max_width_ > 0) {
397 double max_width_dip = screencast_max_width_ / device_scale_factor;
398 scale = std::min(scale, max_width_dip / screen_size_dip.width());
400 if (screencast_max_height_ > 0) {
401 double max_height_dip = screencast_max_height_ / device_scale_factor;
402 scale = std::min(scale, max_height_dip / screen_size_dip.height());
405 if (scale <= 0)
406 scale = 0.1;
408 gfx::Size snapshot_size_dip(gfx::ToRoundedSize(
409 gfx::ScaleSize(viewport_size_dip, scale)));
411 if (snapshot_size_dip.width() > 0 && snapshot_size_dip.height() > 0) {
412 processing_screencast_frame_ = true;
413 gfx::Rect viewport_bounds_dip(gfx::ToRoundedSize(viewport_size_dip));
414 view->CopyFromCompositingSurface(
415 viewport_bounds_dip,
416 snapshot_size_dip,
417 base::Bind(&PageHandler::ScreencastFrameCaptured,
418 weak_factory_.GetWeakPtr(),
419 last_compositor_frame_metadata_),
420 kN32_SkColorType);
424 void PageHandler::ScreencastFrameCaptured(
425 const cc::CompositorFrameMetadata& metadata,
426 const SkBitmap& bitmap,
427 ReadbackResponse response) {
428 if (response != READBACK_SUCCESS) {
429 processing_screencast_frame_ = false;
430 if (capture_retry_count_) {
431 --capture_retry_count_;
432 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
433 FROM_HERE, base::Bind(&PageHandler::InnerSwapCompositorFrame,
434 weak_factory_.GetWeakPtr()),
435 base::TimeDelta::FromMilliseconds(kFrameRetryDelayMs));
437 return;
439 base::PostTaskAndReplyWithResult(
440 base::WorkerPool::GetTaskRunner(true).get(),
441 FROM_HERE,
442 base::Bind(&EncodeScreencastFrame,
443 bitmap, screencast_format_, screencast_quality_),
444 base::Bind(&PageHandler::ScreencastFrameEncoded,
445 weak_factory_.GetWeakPtr(), metadata, base::Time::Now()));
448 void PageHandler::ScreencastFrameEncoded(
449 const cc::CompositorFrameMetadata& metadata,
450 const base::Time& timestamp,
451 const std::string& data) {
452 processing_screencast_frame_ = false;
454 // Consider metadata empty in case it has no device scale factor.
455 if (metadata.device_scale_factor == 0 || !host_ || data.empty())
456 return;
458 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
459 host_->GetView());
460 if (!view)
461 return;
463 gfx::SizeF screen_size_dip = gfx::ScaleSize(
464 view->GetPhysicalBackingSize(), 1 / metadata.device_scale_factor);
465 scoped_refptr<ScreencastFrameMetadata> param_metadata =
466 ScreencastFrameMetadata::Create()
467 ->set_page_scale_factor(metadata.page_scale_factor)
468 ->set_offset_top(metadata.location_bar_content_translation.y())
469 ->set_device_width(screen_size_dip.width())
470 ->set_device_height(screen_size_dip.height())
471 ->set_scroll_offset_x(metadata.root_scroll_offset.x())
472 ->set_scroll_offset_y(metadata.root_scroll_offset.y())
473 ->set_timestamp(timestamp.ToDoubleT());
474 client_->ScreencastFrame(ScreencastFrameParams::Create()
475 ->set_data(data)
476 ->set_metadata(param_metadata)
477 ->set_frame_number(++screencast_frame_sent_));
480 void PageHandler::ScreenshotCaptured(DevToolsCommandId command_id,
481 const unsigned char* png_data,
482 size_t png_size) {
483 if (!png_data || !png_size) {
484 client_->SendError(command_id,
485 Response::InternalError("Unable to capture screenshot"));
486 return;
489 std::string base_64_data;
490 base::Base64Encode(
491 base::StringPiece(reinterpret_cast<const char*>(png_data), png_size),
492 &base_64_data);
494 client_->SendCaptureScreenshotResponse(command_id,
495 CaptureScreenshotResponse::Create()->set_data(base_64_data));
498 void PageHandler::OnColorPicked(int r, int g, int b, int a) {
499 scoped_refptr<dom::RGBA> color =
500 dom::RGBA::Create()->set_r(r)->set_g(g)->set_b(b)->set_a(a);
501 client_->ColorPicked(ColorPickedParams::Create()->set_color(color));
504 } // namespace page
505 } // namespace devtools
506 } // namespace content