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 "components/test_runner/mock_web_theme_engine.h"
7 #if !defined(OS_MACOSX)
9 #include "base/logging.h"
10 #include "skia/ext/platform_canvas.h"
11 #include "third_party/WebKit/public/platform/WebRect.h"
12 #include "third_party/WebKit/public/platform/WebSize.h"
13 #include "third_party/skia/include/core/SkPaint.h"
14 #include "third_party/skia/include/core/SkPath.h"
15 #include "third_party/skia/include/core/SkRect.h"
17 using blink::WebCanvas
;
18 using blink::WebColor
;
20 using blink::WebThemeEngine
;
22 namespace test_runner
{
26 const SkColor edgeColor
= SK_ColorBLACK
;
27 const SkColor readOnlyColor
= SkColorSetRGB(0xe9, 0xc2, 0xa6);
31 SkColor
bgColors(WebThemeEngine::State state
) {
33 case WebThemeEngine::StateDisabled
:
34 return SkColorSetRGB(0xc9, 0xc9, 0xc9);
35 case WebThemeEngine::StateHover
:
36 return SkColorSetRGB(0x43, 0xf9, 0xff);
37 case WebThemeEngine::StateNormal
:
38 return SkColorSetRGB(0x89, 0xc4, 0xff);
39 case WebThemeEngine::StatePressed
:
40 return SkColorSetRGB(0xa9, 0xff, 0x12);
41 case WebThemeEngine::StateFocused
:
42 return SkColorSetRGB(0x00, 0xf3, 0xac);
43 case WebThemeEngine::StateReadonly
:
44 return SkColorSetRGB(0xf3, 0xe0, 0xd0);
48 return SkColorSetRGB(0x00, 0x00, 0xff);
51 blink::WebSize
MockWebThemeEngine::getSize(WebThemeEngine::Part part
) {
52 // FIXME: We use this constant to indicate we are being asked for the size of
53 // a part that we don't expect to be asked about. We return a garbage value
54 // rather than just asserting because this code doesn't have access to either
55 // WTF or base to raise an assertion or do any logging :(.
56 const blink::WebSize invalidPartSize
= blink::WebSize(100, 100);
59 case WebThemeEngine::PartScrollbarLeftArrow
:
60 return blink::WebSize(17, 15);
61 case WebThemeEngine::PartScrollbarRightArrow
:
62 return invalidPartSize
;
63 case WebThemeEngine::PartScrollbarUpArrow
:
64 return blink::WebSize(15, 17);
65 case WebThemeEngine::PartScrollbarDownArrow
:
66 return invalidPartSize
;
67 case WebThemeEngine::PartScrollbarHorizontalThumb
:
68 return blink::WebSize(15, 15);
69 case WebThemeEngine::PartScrollbarVerticalThumb
:
70 return blink::WebSize(15, 15);
71 case WebThemeEngine::PartScrollbarHorizontalTrack
:
72 return blink::WebSize(0, 15);
73 case WebThemeEngine::PartScrollbarVerticalTrack
:
74 return blink::WebSize(15, 0);
75 case WebThemeEngine::PartCheckbox
:
76 case WebThemeEngine::PartRadio
:
77 return blink::WebSize(13, 13);
78 case WebThemeEngine::PartSliderThumb
:
79 return blink::WebSize(11, 21);
80 case WebThemeEngine::PartInnerSpinButton
:
81 return blink::WebSize(15, 8);
83 return invalidPartSize
;
87 static SkIRect
webRectToSkIRect(const WebRect
& webRect
) {
89 irect
.set(webRect
.x
, webRect
.y
, webRect
.x
+ webRect
.width
- 1,
90 webRect
.y
+ webRect
.height
- 1);
94 static SkIRect
validate(const SkIRect
& rect
, WebThemeEngine::Part part
) {
96 case WebThemeEngine::PartCheckbox
:
97 case WebThemeEngine::PartRadio
: {
98 SkIRect retval
= rect
;
100 // The maximum width and height is 13.
101 // Center the square in the passed rectangle.
102 const int maxControlSize
= 13;
103 int controlSize
= std::min(rect
.width(), rect
.height());
104 controlSize
= std::min(controlSize
, maxControlSize
);
106 retval
.fLeft
= rect
.fLeft
+ (rect
.width() / 2) - (controlSize
/ 2);
107 retval
.fRight
= retval
.fLeft
+ controlSize
- 1;
108 retval
.fTop
= rect
.fTop
+ (rect
.height() / 2) - (controlSize
/ 2);
109 retval
.fBottom
= retval
.fTop
+ controlSize
- 1;
118 void box(SkCanvas
* canvas
, const SkIRect
& rect
, SkColor fillColor
) {
121 paint
.setStyle(SkPaint::kFill_Style
);
122 paint
.setColor(fillColor
);
123 canvas
->drawIRect(rect
, paint
);
125 paint
.setColor(edgeColor
);
126 paint
.setStyle(SkPaint::kStroke_Style
);
127 canvas
->drawIRect(rect
, paint
);
130 void line(SkCanvas
* canvas
, int x0
, int y0
, int x1
, int y1
, SkColor color
) {
132 paint
.setColor(color
);
133 canvas
->drawLine(SkIntToScalar(x0
), SkIntToScalar(y0
), SkIntToScalar(x1
),
134 SkIntToScalar(y1
), paint
);
137 void triangle(SkCanvas
* canvas
,
148 paint
.setColor(color
);
149 paint
.setStyle(SkPaint::kFill_Style
);
151 path
.moveTo(SkIntToScalar(x0
), SkIntToScalar(y0
));
152 path
.lineTo(SkIntToScalar(x1
), SkIntToScalar(y1
));
153 path
.lineTo(SkIntToScalar(x2
), SkIntToScalar(y2
));
155 canvas
->drawPath(path
, paint
);
157 paint
.setColor(edgeColor
);
158 paint
.setStyle(SkPaint::kStroke_Style
);
159 canvas
->drawPath(path
, paint
);
162 void roundRect(SkCanvas
* canvas
, SkIRect irect
, SkColor color
) {
164 SkScalar radius
= SkIntToScalar(5);
168 paint
.setColor(color
);
169 paint
.setStyle(SkPaint::kFill_Style
);
170 canvas
->drawRoundRect(rect
, radius
, radius
, paint
);
172 paint
.setColor(edgeColor
);
173 paint
.setStyle(SkPaint::kStroke_Style
);
174 canvas
->drawRoundRect(rect
, radius
, radius
, paint
);
177 void oval(SkCanvas
* canvas
, SkIRect irect
, SkColor color
) {
182 paint
.setColor(color
);
183 paint
.setStyle(SkPaint::kFill_Style
);
184 canvas
->drawOval(rect
, paint
);
186 paint
.setColor(edgeColor
);
187 paint
.setStyle(SkPaint::kStroke_Style
);
188 canvas
->drawOval(rect
, paint
);
191 void circle(SkCanvas
* canvas
, SkIRect irect
, SkScalar radius
, SkColor color
) {
192 int left
= irect
.fLeft
;
193 int width
= irect
.width();
194 int height
= irect
.height();
195 int top
= irect
.fTop
;
197 SkScalar cy
= SkIntToScalar(top
+ height
/ 2);
198 SkScalar cx
= SkIntToScalar(left
+ width
/ 2);
201 paint
.setColor(color
);
202 paint
.setStyle(SkPaint::kFill_Style
);
203 canvas
->drawCircle(cx
, cy
, radius
, paint
);
205 paint
.setColor(edgeColor
);
206 paint
.setStyle(SkPaint::kStroke_Style
);
207 canvas
->drawCircle(cx
, cy
, radius
, paint
);
210 void nestedBoxes(SkCanvas
* canvas
,
217 SkColor innerColor
) {
219 box(canvas
, irect
, outerColor
);
220 lirect
.set(irect
.fLeft
+ indentLeft
, irect
.fTop
+ indentTop
,
221 irect
.fRight
- indentRight
, irect
.fBottom
- indentBottom
);
222 box(canvas
, lirect
, innerColor
);
225 void insetBox(SkCanvas
* canvas
,
233 lirect
.set(irect
.fLeft
+ indentLeft
, irect
.fTop
+ indentTop
,
234 irect
.fRight
- indentRight
, irect
.fBottom
- indentBottom
);
235 box(canvas
, lirect
, color
);
238 void markState(SkCanvas
* canvas
, SkIRect irect
, WebThemeEngine::State state
) {
239 int left
= irect
.fLeft
;
240 int right
= irect
.fRight
;
241 int top
= irect
.fTop
;
242 int bottom
= irect
.fBottom
;
244 // The length of a triangle side for the corner marks.
245 const int triangleSize
= 5;
248 case WebThemeEngine::StateDisabled
:
249 case WebThemeEngine::StateNormal
:
250 // Don't visually mark these states (color is enough).
253 case WebThemeEngine::StateReadonly
: {
254 // The horizontal lines in a read only control are spaced by this amount.
255 const int readOnlyLineOffset
= 5;
257 // Drawing lines across the control.
258 for (int i
= top
+ readOnlyLineOffset
; i
< bottom
;
259 i
+= readOnlyLineOffset
)
260 line(canvas
, left
+ 1, i
, right
- 1, i
, readOnlyColor
);
263 case WebThemeEngine::StateHover
:
264 // Draw a triangle in the upper left corner of the control. (Win's "hot")
265 triangle(canvas
, left
, top
, left
+ triangleSize
, top
, left
,
266 top
+ triangleSize
, edgeColor
);
269 case WebThemeEngine::StateFocused
:
270 // Draw a triangle in the bottom right corner of the control.
271 triangle(canvas
, right
, bottom
, right
- triangleSize
, bottom
, right
,
272 bottom
- triangleSize
, edgeColor
);
275 case WebThemeEngine::StatePressed
:
276 // Draw a triangle in the bottom left corner of the control.
277 triangle(canvas
, left
, bottom
, left
, bottom
- triangleSize
,
278 left
+ triangleSize
, bottom
, edgeColor
);
282 // FIXME: Should we do something here to indicate that we got an invalid
284 // Unfortunately, we can't assert because we don't have access to WTF or
290 void MockWebThemeEngine::paint(blink::WebCanvas
* canvas
,
291 WebThemeEngine::Part part
,
292 WebThemeEngine::State state
,
293 const blink::WebRect
& rect
,
294 const WebThemeEngine::ExtraParams
* extraParams
) {
295 SkIRect irect
= webRectToSkIRect(rect
);
298 // Indent amounts for the check in a checkbox or radio button.
299 const int checkIndent
= 3;
301 // Indent amounts for short and long sides of the scrollbar notches.
302 const int notchLongOffset
= 1;
303 const int notchShortOffset
= 4;
304 const int noOffset
= 0;
306 // Indent amounts for the short and long sides of a scroll thumb box.
307 const int thumbLongIndent
= 0;
308 const int thumbShortIndent
= 2;
310 // Indents for the crosshatch on a scroll grip.
311 const int gripLongIndent
= 3;
312 const int gripShortIndent
= 5;
314 // Indents for the the slider track.
315 const int sliderIndent
= 2;
317 int halfHeight
= irect
.height() / 2;
318 int halfWidth
= irect
.width() / 2;
319 int quarterHeight
= irect
.height() / 4;
320 int quarterWidth
= irect
.width() / 4;
321 int left
= irect
.fLeft
;
322 int right
= irect
.fRight
;
323 int top
= irect
.fTop
;
324 int bottom
= irect
.fBottom
;
327 case WebThemeEngine::PartScrollbarDownArrow
:
328 box(canvas
, irect
, bgColors(state
));
329 triangle(canvas
, left
+ quarterWidth
, top
+ quarterHeight
,
330 right
- quarterWidth
, top
+ quarterHeight
, left
+ halfWidth
,
331 bottom
- quarterHeight
, edgeColor
);
332 markState(canvas
, irect
, state
);
335 case WebThemeEngine::PartScrollbarLeftArrow
:
336 box(canvas
, irect
, bgColors(state
));
337 triangle(canvas
, right
- quarterWidth
, top
+ quarterHeight
,
338 right
- quarterWidth
, bottom
- quarterHeight
,
339 left
+ quarterWidth
, top
+ halfHeight
, edgeColor
);
342 case WebThemeEngine::PartScrollbarRightArrow
:
343 box(canvas
, irect
, bgColors(state
));
344 triangle(canvas
, left
+ quarterWidth
, top
+ quarterHeight
,
345 right
- quarterWidth
, top
+ halfHeight
, left
+ quarterWidth
,
346 bottom
- quarterHeight
, edgeColor
);
349 case WebThemeEngine::PartScrollbarUpArrow
:
350 box(canvas
, irect
, bgColors(state
));
351 triangle(canvas
, left
+ quarterWidth
, bottom
- quarterHeight
,
352 left
+ halfWidth
, top
+ quarterHeight
, right
- quarterWidth
,
353 bottom
- quarterHeight
, edgeColor
);
354 markState(canvas
, irect
, state
);
357 case WebThemeEngine::PartScrollbarHorizontalThumb
: {
358 // Draw a narrower box on top of the outside box.
359 nestedBoxes(canvas
, irect
, thumbLongIndent
, thumbShortIndent
,
360 thumbLongIndent
, thumbShortIndent
, bgColors(state
),
362 // Draw a horizontal crosshatch for the grip.
363 int longOffset
= halfWidth
- gripLongIndent
;
364 line(canvas
, left
+ gripLongIndent
, top
+ halfHeight
,
365 right
- gripLongIndent
, top
+ halfHeight
, edgeColor
);
366 line(canvas
, left
+ longOffset
, top
+ gripShortIndent
, left
+ longOffset
,
367 bottom
- gripShortIndent
, edgeColor
);
368 line(canvas
, right
- longOffset
, top
+ gripShortIndent
,
369 right
- longOffset
, bottom
- gripShortIndent
, edgeColor
);
370 markState(canvas
, irect
, state
);
374 case WebThemeEngine::PartScrollbarVerticalThumb
: {
375 // Draw a shorter box on top of the outside box.
376 nestedBoxes(canvas
, irect
, thumbShortIndent
, thumbLongIndent
,
377 thumbShortIndent
, thumbLongIndent
, bgColors(state
),
379 // Draw a vertical crosshatch for the grip.
380 int longOffset
= halfHeight
- gripLongIndent
;
381 line(canvas
, left
+ halfWidth
, top
+ gripLongIndent
, left
+ halfWidth
,
382 bottom
- gripLongIndent
, edgeColor
);
383 line(canvas
, left
+ gripShortIndent
, top
+ longOffset
,
384 right
- gripShortIndent
, top
+ longOffset
, edgeColor
);
385 line(canvas
, left
+ gripShortIndent
, bottom
- longOffset
,
386 right
- gripShortIndent
, bottom
- longOffset
, edgeColor
);
387 markState(canvas
, irect
, state
);
391 case WebThemeEngine::PartScrollbarHorizontalTrack
: {
392 int longOffset
= halfHeight
- notchLongOffset
;
393 int shortOffset
= irect
.width() - notchShortOffset
;
394 box(canvas
, irect
, bgColors(state
));
395 // back, notch on right
396 insetBox(canvas
, irect
, noOffset
, longOffset
, shortOffset
, longOffset
,
398 // forward, notch on right
399 insetBox(canvas
, irect
, shortOffset
, longOffset
, noOffset
, longOffset
,
401 markState(canvas
, irect
, state
);
405 case WebThemeEngine::PartScrollbarVerticalTrack
: {
406 int longOffset
= halfWidth
- notchLongOffset
;
407 int shortOffset
= irect
.height() - notchShortOffset
;
408 box(canvas
, irect
, bgColors(state
));
409 // back, notch at top
410 insetBox(canvas
, irect
, longOffset
, noOffset
, longOffset
, shortOffset
,
412 // forward, notch at bottom
413 insetBox(canvas
, irect
, longOffset
, shortOffset
, longOffset
, noOffset
,
415 markState(canvas
, irect
, state
);
419 case WebThemeEngine::PartScrollbarCorner
: {
420 SkIRect cornerRect
= {rect
.x
, rect
.y
, rect
.x
+ rect
.width
,
421 rect
.y
+ rect
.height
};
422 paint
.setColor(SK_ColorWHITE
);
423 paint
.setStyle(SkPaint::kFill_Style
);
424 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
425 paint
.setAntiAlias(true);
426 canvas
->drawIRect(cornerRect
, paint
);
430 case WebThemeEngine::PartCheckbox
:
431 if (extraParams
->button
.indeterminate
) {
432 nestedBoxes(canvas
, irect
, checkIndent
, halfHeight
, checkIndent
,
433 halfHeight
, bgColors(state
), edgeColor
);
434 } else if (extraParams
->button
.checked
) {
435 irect
= validate(irect
, part
);
436 nestedBoxes(canvas
, irect
, checkIndent
, checkIndent
, checkIndent
,
437 checkIndent
, bgColors(state
), edgeColor
);
439 irect
= validate(irect
, part
);
440 box(canvas
, irect
, bgColors(state
));
444 case WebThemeEngine::PartRadio
:
445 irect
= validate(irect
, part
);
446 halfHeight
= irect
.height() / 2;
447 if (extraParams
->button
.checked
) {
448 circle(canvas
, irect
, SkIntToScalar(halfHeight
), bgColors(state
));
449 circle(canvas
, irect
, SkIntToScalar(halfHeight
- checkIndent
),
452 circle(canvas
, irect
, SkIntToScalar(halfHeight
), bgColors(state
));
456 case WebThemeEngine::PartButton
:
457 roundRect(canvas
, irect
, bgColors(state
));
458 markState(canvas
, irect
, state
);
461 case WebThemeEngine::PartTextField
:
462 paint
.setColor(extraParams
->textField
.backgroundColor
);
463 paint
.setStyle(SkPaint::kFill_Style
);
464 canvas
->drawIRect(irect
, paint
);
466 paint
.setColor(edgeColor
);
467 paint
.setStyle(SkPaint::kStroke_Style
);
468 canvas
->drawIRect(irect
, paint
);
470 markState(canvas
, irect
, state
);
473 case WebThemeEngine::PartMenuList
:
474 if (extraParams
->menuList
.fillContentArea
) {
475 box(canvas
, irect
, extraParams
->menuList
.backgroundColor
);
478 paint
.setColor(edgeColor
);
479 paint
.setStyle(SkPaint::kStroke_Style
);
480 canvas
->drawIRect(irect
, paint
);
483 // clip the drop-down arrow to be inside the select box
484 if (extraParams
->menuList
.arrowX
- 4 > irect
.fLeft
)
485 irect
.fLeft
= extraParams
->menuList
.arrowX
- 4;
486 if (extraParams
->menuList
.arrowX
+ 12 < irect
.fRight
)
487 irect
.fRight
= extraParams
->menuList
.arrowX
+ 12;
489 irect
.fTop
= extraParams
->menuList
.arrowY
-
490 (extraParams
->menuList
.arrowHeight
) / 2;
491 irect
.fBottom
= extraParams
->menuList
.arrowY
+
492 (extraParams
->menuList
.arrowHeight
- 1) / 2;
493 halfWidth
= irect
.width() / 2;
494 quarterWidth
= irect
.width() / 4;
496 if (state
== WebThemeEngine::StateFocused
) // FIXME: draw differenty?
497 state
= WebThemeEngine::StateNormal
;
498 box(canvas
, irect
, bgColors(state
));
499 triangle(canvas
, irect
.fLeft
+ quarterWidth
, irect
.fTop
,
500 irect
.fRight
- quarterWidth
, irect
.fTop
, irect
.fLeft
+ halfWidth
,
501 irect
.fBottom
, edgeColor
);
505 case WebThemeEngine::PartSliderTrack
: {
506 SkIRect lirect
= irect
;
508 // Draw a narrow rect for the track plus box hatches on the ends.
509 if (state
== WebThemeEngine::StateFocused
) // FIXME: draw differently?
510 state
= WebThemeEngine::StateNormal
;
511 if (extraParams
->slider
.vertical
) {
512 lirect
.inset(halfWidth
- sliderIndent
, noOffset
);
513 box(canvas
, lirect
, bgColors(state
));
514 line(canvas
, left
, top
, right
, top
, edgeColor
);
515 line(canvas
, left
, bottom
, right
, bottom
, edgeColor
);
517 lirect
.inset(noOffset
, halfHeight
- sliderIndent
);
518 box(canvas
, lirect
, bgColors(state
));
519 line(canvas
, left
, top
, left
, bottom
, edgeColor
);
520 line(canvas
, right
, top
, right
, bottom
, edgeColor
);
525 case WebThemeEngine::PartSliderThumb
:
526 if (state
== WebThemeEngine::StateFocused
) // FIXME: draw differently?
527 state
= WebThemeEngine::StateNormal
;
528 oval(canvas
, irect
, bgColors(state
));
531 case WebThemeEngine::PartInnerSpinButton
: {
532 // stack half-height up and down arrows on top of each other
534 int halfHeight
= rect
.height
/ 2;
535 if (extraParams
->innerSpin
.readOnly
)
536 state
= blink::WebThemeEngine::StateDisabled
;
538 lirect
.set(rect
.x
, rect
.y
, rect
.x
+ rect
.width
- 1,
539 rect
.y
+ halfHeight
- 1);
540 box(canvas
, lirect
, bgColors(state
));
541 bottom
= lirect
.fBottom
;
542 quarterHeight
= lirect
.height() / 4;
543 triangle(canvas
, left
+ quarterWidth
, bottom
- quarterHeight
,
544 right
- quarterWidth
, bottom
- quarterHeight
, left
+ halfWidth
,
545 top
+ quarterHeight
, edgeColor
);
547 lirect
.set(rect
.x
, rect
.y
+ halfHeight
, rect
.x
+ rect
.width
- 1,
548 rect
.y
+ 2 * halfHeight
- 1);
550 bottom
= lirect
.fBottom
;
551 quarterHeight
= lirect
.height() / 4;
552 box(canvas
, lirect
, bgColors(state
));
553 triangle(canvas
, left
+ quarterWidth
, top
+ quarterHeight
,
554 right
- quarterWidth
, top
+ quarterHeight
, left
+ halfWidth
,
555 bottom
- quarterHeight
, edgeColor
);
556 markState(canvas
, irect
, state
);
559 case WebThemeEngine::PartProgressBar
: {
560 paint
.setColor(bgColors(state
));
561 paint
.setStyle(SkPaint::kFill_Style
);
562 canvas
->drawIRect(irect
, paint
);
565 SkIRect tofill
= irect
;
566 if (extraParams
->progressBar
.determinate
) {
567 tofill
.set(extraParams
->progressBar
.valueRectX
,
568 extraParams
->progressBar
.valueRectY
,
569 extraParams
->progressBar
.valueRectX
+
570 extraParams
->progressBar
.valueRectWidth
- 1,
571 extraParams
->progressBar
.valueRectY
+
572 extraParams
->progressBar
.valueRectHeight
);
575 if (!tofill
.intersect(irect
))
578 paint
.setColor(edgeColor
);
579 paint
.setStyle(SkPaint::kFill_Style
);
580 canvas
->drawIRect(tofill
, paint
);
582 markState(canvas
, irect
, state
);
586 // FIXME: Should we do something here to indicate that we got an invalid
588 // Unfortunately, we can't assert because we don't have access to WTF or
594 } // namespace test_runner
596 #endif // !defined(OS_MACOSX)