DevTools: cut host and port from webSocketDebuggerUrl in addition to ws:// prefix
[chromium-blink-merge.git] / content / browser / devtools / protocol / color_picker.cc
blob23d79a6dc981b251917e5c9ee781f8810e9346dc
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/color_picker.h"
7 #include "base/bind.h"
8 #include "content/browser/renderer_host/render_view_host_impl.h"
9 #include "content/browser/renderer_host/render_widget_host_view_base.h"
10 #include "content/common/cursors/webcursor.h"
11 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
12 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
13 #include "third_party/WebKit/public/web/WebInputEvent.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "ui/gfx/geometry/size_conversions.h"
17 namespace content {
18 namespace devtools {
19 namespace page {
21 ColorPicker::ColorPicker(ColorPickedCallback callback)
22 : callback_(callback),
23 enabled_(false),
24 last_cursor_x_(-1),
25 last_cursor_y_(-1),
26 host_(nullptr),
27 weak_factory_(this) {
28 mouse_event_callback_ = base::Bind(
29 &ColorPicker::HandleMouseEvent,
30 base::Unretained(this));
33 ColorPicker::~ColorPicker() {
36 void ColorPicker::SetRenderWidgetHost(RenderWidgetHostImpl* host) {
37 if (host_ == host)
38 return;
40 if (enabled_ && host_)
41 host_->RemoveMouseEventCallback(mouse_event_callback_);
42 ResetFrame();
43 host_ = host;
44 if (enabled_ && host)
45 host->AddMouseEventCallback(mouse_event_callback_);
48 void ColorPicker::SetEnabled(bool enabled) {
49 if (enabled_ == enabled)
50 return;
52 enabled_ = enabled;
53 if (!host_)
54 return;
56 if (enabled) {
57 host_->AddMouseEventCallback(mouse_event_callback_);
58 UpdateFrame();
59 } else {
60 host_->RemoveMouseEventCallback(mouse_event_callback_);
61 ResetFrame();
63 WebCursor pointer_cursor;
64 WebCursor::CursorInfo cursor_info;
65 cursor_info.type = blink::WebCursorInfo::TypePointer;
66 pointer_cursor.InitFromCursorInfo(cursor_info);
67 host_->SetCursor(pointer_cursor);
71 void ColorPicker::OnSwapCompositorFrame() {
72 if (enabled_)
73 UpdateFrame();
76 void ColorPicker::UpdateFrame() {
77 if (!host_)
78 return;
79 RenderWidgetHostViewBase* view =
80 static_cast<RenderWidgetHostViewBase*>(host_->GetView());
81 if (!view)
82 return;
84 gfx::Size size = view->GetViewBounds().size();
85 view->CopyFromCompositingSurface(
86 gfx::Rect(size), size,
87 base::Bind(&ColorPicker::FrameUpdated,
88 weak_factory_.GetWeakPtr()),
89 kN32_SkColorType);
92 void ColorPicker::ResetFrame() {
93 frame_.reset();
94 last_cursor_x_ = -1;
95 last_cursor_y_ = -1;
98 void ColorPicker::FrameUpdated(const SkBitmap& bitmap,
99 ReadbackResponse response) {
100 if (!enabled_)
101 return;
103 if (response == READBACK_SUCCESS) {
104 frame_ = bitmap;
105 UpdateCursor();
109 bool ColorPicker::HandleMouseEvent(const blink::WebMouseEvent& event) {
110 last_cursor_x_ = event.x;
111 last_cursor_y_ = event.y;
112 if (frame_.drawsNothing())
113 return true;
115 if (event.button == blink::WebMouseEvent::ButtonLeft &&
116 event.type == blink::WebInputEvent::MouseDown) {
117 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() ||
118 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) {
119 return true;
122 SkAutoLockPixels lock_image(frame_);
123 SkColor sk_color = frame_.getColor(last_cursor_x_, last_cursor_y_);
124 callback_.Run(SkColorGetR(sk_color), SkColorGetG(sk_color),
125 SkColorGetB(sk_color), SkColorGetA(sk_color));
127 UpdateCursor();
128 return true;
131 void ColorPicker::UpdateCursor() {
132 if (!host_ || frame_.drawsNothing())
133 return;
135 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() ||
136 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) {
137 return;
140 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
141 host_->GetView());
142 if (!view)
143 return;
145 // Due to platform limitations, we are using two different cursors
146 // depending on the platform. Mac and Win have large cursors with two circles
147 // for original spot and its magnified projection; Linux gets smaller (64 px)
148 // magnified projection only with centered hotspot.
149 // Mac Retina requires cursor to be > 120px in order to render smoothly.
151 #if defined(OS_LINUX)
152 const float kCursorSize = 63;
153 const float kDiameter = 63;
154 const float kHotspotOffset = 32;
155 const float kHotspotRadius = 0;
156 const float kPixelSize = 9;
157 #else
158 const float kCursorSize = 150;
159 const float kDiameter = 110;
160 const float kHotspotOffset = 25;
161 const float kHotspotRadius = 5;
162 const float kPixelSize = 10;
163 #endif
165 blink::WebScreenInfo screen_info;
166 view->GetScreenInfo(&screen_info);
167 double device_scale_factor = screen_info.deviceScaleFactor;
169 SkBitmap result;
170 result.allocN32Pixels(kCursorSize * device_scale_factor,
171 kCursorSize * device_scale_factor);
172 result.eraseARGB(0, 0, 0, 0);
174 SkCanvas canvas(result);
175 canvas.scale(device_scale_factor, device_scale_factor);
176 canvas.translate(0.5f, 0.5f);
178 SkPaint paint;
180 // Paint original spot with cross.
181 if (kHotspotRadius) {
182 paint.setStrokeWidth(1);
183 paint.setAntiAlias(false);
184 paint.setColor(SK_ColorDKGRAY);
185 paint.setStyle(SkPaint::kStroke_Style);
187 canvas.drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius,
188 kHotspotOffset, kHotspotOffset - kHotspotRadius,
189 paint);
190 canvas.drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius,
191 kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius,
192 paint);
193 canvas.drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset,
194 kHotspotOffset - kHotspotRadius, kHotspotOffset,
195 paint);
196 canvas.drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset,
197 kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset,
198 paint);
200 paint.setStrokeWidth(2);
201 paint.setAntiAlias(true);
202 canvas.drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint);
205 // Clip circle for magnified projection.
206 float padding = (kCursorSize - kDiameter) / 2;
207 SkPath clip_path;
208 clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter));
209 clip_path.close();
210 canvas.clipPath(clip_path, SkRegion::kIntersect_Op, true);
212 // Project pixels.
213 int pixel_count = kDiameter / kPixelSize;
214 SkRect src_rect = SkRect::MakeXYWH(last_cursor_x_ - pixel_count / 2,
215 last_cursor_y_ - pixel_count / 2,
216 pixel_count, pixel_count);
217 SkRect dst_rect = SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter);
218 canvas.drawBitmapRectToRect(frame_, &src_rect, dst_rect);
220 // Paint grid.
221 paint.setStrokeWidth(1);
222 paint.setAntiAlias(false);
223 paint.setColor(SK_ColorGRAY);
224 for (int i = 0; i < pixel_count; ++i) {
225 canvas.drawLine(padding + i * kPixelSize, padding,
226 padding + i * kPixelSize, kCursorSize - padding, paint);
227 canvas.drawLine(padding, padding + i * kPixelSize,
228 kCursorSize - padding, padding + i * kPixelSize, paint);
231 // Paint central pixel in red.
232 SkRect pixel = SkRect::MakeXYWH((kCursorSize - kPixelSize) / 2,
233 (kCursorSize - kPixelSize) / 2,
234 kPixelSize, kPixelSize);
235 paint.setColor(SK_ColorRED);
236 paint.setStyle(SkPaint::kStroke_Style);
237 canvas.drawRect(pixel, paint);
239 // Paint outline.
240 paint.setStrokeWidth(2);
241 paint.setColor(SK_ColorDKGRAY);
242 paint.setAntiAlias(true);
243 canvas.drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint);
245 WebCursor cursor;
246 WebCursor::CursorInfo cursor_info;
247 cursor_info.type = blink::WebCursorInfo::TypeCustom;
248 cursor_info.image_scale_factor = device_scale_factor;
249 cursor_info.custom_image = result;
250 cursor_info.hotspot =
251 gfx::Point(kHotspotOffset * device_scale_factor,
252 kHotspotOffset * device_scale_factor);
253 #if defined(OS_WIN)
254 cursor_info.external_handle = 0;
255 #endif
257 cursor.InitFromCursorInfo(cursor_info);
258 DCHECK(host_);
259 host_->SetCursor(cursor);
262 } // namespace page
263 } // namespace devtools
264 } // namespace content