1 // Copyright 2013 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/shell/renderer/test_runner/WebTestThemeEngineMock.h"
7 #include "base/logging.h"
8 #include "skia/ext/platform_canvas.h"
9 #include "third_party/WebKit/public/platform/WebRect.h"
10 #include "third_party/WebKit/public/platform/WebSize.h"
11 #include "third_party/skia/include/core/SkRect.h"
13 using blink::WebCanvas
;
14 using blink::WebColor
;
16 using blink::WebThemeEngine
;
18 namespace WebTestRunner
{
20 static const SkColor edgeColor
= SK_ColorBLACK
;
21 static const SkColor readOnlyColor
= SkColorSetRGB(0xe9, 0xc2, 0xa6);
23 SkColor
bgColors(WebThemeEngine::State state
) {
25 case WebThemeEngine::StateDisabled
:
26 return SkColorSetRGB(0xc9, 0xc9, 0xc9);
27 case WebThemeEngine::StateHover
:
28 return SkColorSetRGB(0x43, 0xf9, 0xff);
29 case WebThemeEngine::StateNormal
:
30 return SkColorSetRGB(0x89, 0xc4, 0xff);
31 case WebThemeEngine::StatePressed
:
32 return SkColorSetRGB(0xa9, 0xff, 0x12);
33 case WebThemeEngine::StateFocused
:
34 return SkColorSetRGB(0x00, 0xf3, 0xac);
35 case WebThemeEngine::StateReadonly
:
36 return SkColorSetRGB(0xf3, 0xe0, 0xd0);
40 return SkColorSetRGB(0x00, 0x00, 0xff);
43 blink::WebSize
WebTestThemeEngineMock::getSize(WebThemeEngine::Part part
)
45 // FIXME: We use this constant to indicate we are being asked for the size of
46 // a part that we don't expect to be asked about. We return a garbage value
47 // rather than just asserting because this code doesn't have access to either
48 // WTF or base to raise an assertion or do any logging :(.
49 const blink::WebSize invalidPartSize
= blink::WebSize(100, 100);
52 case WebThemeEngine::PartScrollbarLeftArrow
:
53 return blink::WebSize(17, 15);
54 case WebThemeEngine::PartScrollbarRightArrow
:
55 return invalidPartSize
;
56 case WebThemeEngine::PartScrollbarUpArrow
:
57 return blink::WebSize(15, 17);
58 case WebThemeEngine::PartScrollbarDownArrow
:
59 return invalidPartSize
;
60 case WebThemeEngine::PartScrollbarHorizontalThumb
:
61 return blink::WebSize(15, 15);
62 case WebThemeEngine::PartScrollbarVerticalThumb
:
63 return blink::WebSize(15, 15);
64 case WebThemeEngine::PartScrollbarHorizontalTrack
:
65 return blink::WebSize(0, 15);
66 case WebThemeEngine::PartScrollbarVerticalTrack
:
67 return blink::WebSize(15, 0);
68 case WebThemeEngine::PartCheckbox
:
69 case WebThemeEngine::PartRadio
:
70 return blink::WebSize(13, 13);
71 case WebThemeEngine::PartSliderThumb
:
72 return blink::WebSize(11, 21);
73 case WebThemeEngine::PartInnerSpinButton
:
74 return blink::WebSize(15, 8);
76 return invalidPartSize
;
80 static SkIRect
webRectToSkIRect(const WebRect
& webRect
)
83 irect
.set(webRect
.x
, webRect
.y
,
84 webRect
.x
+ webRect
.width
- 1, webRect
.y
+ webRect
.height
- 1);
88 static SkIRect
validate(const SkIRect
& rect
, WebThemeEngine::Part part
)
91 case WebThemeEngine::PartCheckbox
:
92 case WebThemeEngine::PartRadio
: {
93 SkIRect retval
= rect
;
95 // The maximum width and height is 13.
96 // Center the square in the passed rectangle.
97 const int maxControlSize
= 13;
98 int controlSize
= std::min(rect
.width(), rect
.height());
99 controlSize
= std::min(controlSize
, maxControlSize
);
101 retval
.fLeft
= rect
.fLeft
+ (rect
.width() / 2) - (controlSize
/ 2);
102 retval
.fRight
= retval
.fLeft
+ controlSize
- 1;
103 retval
.fTop
= rect
.fTop
+ (rect
.height() / 2) - (controlSize
/ 2);
104 retval
.fBottom
= retval
.fTop
+ controlSize
- 1;
114 void box(SkCanvas
*canvas
, const SkIRect
& rect
, SkColor fillColor
)
118 paint
.setStyle(SkPaint::kFill_Style
);
119 paint
.setColor(fillColor
);
120 canvas
->drawIRect(rect
, paint
);
122 paint
.setColor(edgeColor
);
123 paint
.setStyle(SkPaint::kStroke_Style
);
124 canvas
->drawIRect(rect
, paint
);
127 void line(SkCanvas
*canvas
, int x0
, int y0
, int x1
, int y1
, SkColor color
)
130 paint
.setColor(color
);
131 canvas
->drawLine(SkIntToScalar(x0
), SkIntToScalar(y0
),
132 SkIntToScalar(x1
), SkIntToScalar(y1
), paint
);
135 void triangle(SkCanvas
*canvas
,
144 paint
.setColor(color
);
145 paint
.setStyle(SkPaint::kFill_Style
);
147 path
.moveTo(SkIntToScalar(x0
), SkIntToScalar(y0
));
148 path
.lineTo(SkIntToScalar(x1
), SkIntToScalar(y1
));
149 path
.lineTo(SkIntToScalar(x2
), SkIntToScalar(y2
));
151 canvas
->drawPath(path
, paint
);
153 paint
.setColor(edgeColor
);
154 paint
.setStyle(SkPaint::kStroke_Style
);
155 canvas
->drawPath(path
, paint
);
158 void roundRect(SkCanvas
*canvas
, SkIRect irect
, SkColor color
)
161 SkScalar radius
= SkIntToScalar(5);
165 paint
.setColor(color
);
166 paint
.setStyle(SkPaint::kFill_Style
);
167 canvas
->drawRoundRect(rect
, radius
, radius
, paint
);
169 paint
.setColor(edgeColor
);
170 paint
.setStyle(SkPaint::kStroke_Style
);
171 canvas
->drawRoundRect(rect
, radius
, radius
, paint
);
174 void oval(SkCanvas
* canvas
, SkIRect irect
, SkColor color
)
180 paint
.setColor(color
);
181 paint
.setStyle(SkPaint::kFill_Style
);
182 canvas
->drawOval(rect
, paint
);
184 paint
.setColor(edgeColor
);
185 paint
.setStyle(SkPaint::kStroke_Style
);
186 canvas
->drawOval(rect
, paint
);
189 void circle(SkCanvas
*canvas
, SkIRect irect
, SkScalar radius
, SkColor color
)
191 int left
= irect
.fLeft
;
192 int width
= irect
.width();
193 int height
= irect
.height();
194 int top
= irect
.fTop
;
196 SkScalar cy
= SkIntToScalar(top
+ height
/ 2);
197 SkScalar cx
= SkIntToScalar(left
+ width
/ 2);
200 paint
.setColor(color
);
201 paint
.setStyle(SkPaint::kFill_Style
);
202 canvas
->drawCircle(cx
, cy
, radius
, paint
);
204 paint
.setColor(edgeColor
);
205 paint
.setStyle(SkPaint::kStroke_Style
);
206 canvas
->drawCircle(cx
, cy
, radius
, paint
);
209 void nestedBoxes(SkCanvas
*canvas
,
219 box(canvas
, irect
, outerColor
);
220 lirect
.set(irect
.fLeft
+ indentLeft
,
221 irect
.fTop
+ indentTop
,
222 irect
.fRight
- indentRight
,
223 irect
.fBottom
- indentBottom
);
224 box(canvas
, lirect
, innerColor
);
227 void insetBox(SkCanvas
* canvas
,
235 lirect
.set(irect
.fLeft
+ indentLeft
,
236 irect
.fTop
+ indentTop
,
237 irect
.fRight
- indentRight
,
238 irect
.fBottom
- indentBottom
);
239 box(canvas
, lirect
, color
);
242 void markState(SkCanvas
*canvas
, SkIRect irect
, WebThemeEngine::State state
)
244 int left
= irect
.fLeft
;
245 int right
= irect
.fRight
;
246 int top
= irect
.fTop
;
247 int bottom
= irect
.fBottom
;
249 // The length of a triangle side for the corner marks.
250 const int triangleSize
= 5;
253 case WebThemeEngine::StateDisabled
:
254 case WebThemeEngine::StateNormal
:
255 // Don't visually mark these states (color is enough).
258 case WebThemeEngine::StateReadonly
: {
259 // The horizontal lines in a read only control are spaced by this amount.
260 const int readOnlyLineOffset
= 5;
262 // Drawing lines across the control.
263 for (int i
= top
+ readOnlyLineOffset
; i
< bottom
; i
+= readOnlyLineOffset
)
264 line(canvas
, left
+ 1, i
, right
- 1, i
, readOnlyColor
);
267 case WebThemeEngine::StateHover
:
268 // Draw a triangle in the upper left corner of the control. (Win's "hot")
271 left
+ triangleSize
, top
,
272 left
, top
+ triangleSize
,
276 case WebThemeEngine::StateFocused
:
277 // Draw a triangle in the bottom right corner of the control.
280 right
- triangleSize
, bottom
,
281 right
, bottom
- triangleSize
,
285 case WebThemeEngine::StatePressed
:
286 // Draw a triangle in the bottom left corner of the control.
289 left
, bottom
- triangleSize
,
290 left
+ triangleSize
, bottom
,
295 // FIXME: Should we do something here to indicate that we got an invalid state?
296 // Unfortunately, we can't assert because we don't have access to WTF or base.
301 void WebTestThemeEngineMock::paint(
302 blink::WebCanvas
* canvas
,
303 WebThemeEngine::Part part
,
304 WebThemeEngine::State state
,
305 const blink::WebRect
& rect
,
306 const WebThemeEngine::ExtraParams
* extraParams
)
308 SkIRect irect
= webRectToSkIRect(rect
);
311 // Indent amounts for the check in a checkbox or radio button.
312 const int checkIndent
= 3;
314 // Indent amounts for short and long sides of the scrollbar notches.
315 const int notchLongOffset
= 1;
316 const int notchShortOffset
= 4;
317 const int noOffset
= 0;
319 // Indent amounts for the short and long sides of a scroll thumb box.
320 const int thumbLongIndent
= 0;
321 const int thumbShortIndent
= 2;
323 // Indents for the crosshatch on a scroll grip.
324 const int gripLongIndent
= 3;
325 const int gripShortIndent
= 5;
327 // Indents for the the slider track.
328 const int sliderIndent
= 2;
330 int halfHeight
= irect
.height() / 2;
331 int halfWidth
= irect
.width() / 2;
332 int quarterHeight
= irect
.height() / 4;
333 int quarterWidth
= irect
.width() / 4;
334 int left
= irect
.fLeft
;
335 int right
= irect
.fRight
;
336 int top
= irect
.fTop
;
337 int bottom
= irect
.fBottom
;
340 case WebThemeEngine::PartScrollbarDownArrow
:
341 box(canvas
, irect
, bgColors(state
));
343 left
+ quarterWidth
, top
+ quarterHeight
,
344 right
- quarterWidth
, top
+ quarterHeight
,
345 left
+ halfWidth
, bottom
- quarterHeight
,
347 markState(canvas
, irect
, state
);
350 case WebThemeEngine::PartScrollbarLeftArrow
:
351 box(canvas
, irect
, bgColors(state
));
353 right
- quarterWidth
, top
+ quarterHeight
,
354 right
- quarterWidth
, bottom
- quarterHeight
,
355 left
+ quarterWidth
, top
+ halfHeight
,
359 case WebThemeEngine::PartScrollbarRightArrow
:
360 box(canvas
, irect
, bgColors(state
));
362 left
+ quarterWidth
, top
+ quarterHeight
,
363 right
- quarterWidth
, top
+ halfHeight
,
364 left
+ quarterWidth
, bottom
- quarterHeight
,
368 case WebThemeEngine::PartScrollbarUpArrow
:
369 box(canvas
, irect
, bgColors(state
));
371 left
+ quarterWidth
, bottom
- quarterHeight
,
372 left
+ halfWidth
, top
+ quarterHeight
,
373 right
- quarterWidth
, bottom
- quarterHeight
,
375 markState(canvas
, irect
, state
);
378 case WebThemeEngine::PartScrollbarHorizontalThumb
: {
379 // Draw a narrower box on top of the outside box.
380 nestedBoxes(canvas
, irect
, thumbLongIndent
, thumbShortIndent
,
381 thumbLongIndent
, thumbShortIndent
,
382 bgColors(state
), bgColors(state
));
383 // Draw a horizontal crosshatch for the grip.
384 int longOffset
= halfWidth
- gripLongIndent
;
386 left
+ gripLongIndent
, top
+ halfHeight
,
387 right
- gripLongIndent
, top
+ halfHeight
,
390 left
+ longOffset
, top
+ gripShortIndent
,
391 left
+ longOffset
, bottom
- gripShortIndent
,
394 right
- longOffset
, top
+ gripShortIndent
,
395 right
- longOffset
, bottom
- gripShortIndent
,
397 markState(canvas
, irect
, state
);
401 case WebThemeEngine::PartScrollbarVerticalThumb
: {
402 // Draw a shorter box on top of the outside box.
403 nestedBoxes(canvas
, irect
, thumbShortIndent
, thumbLongIndent
,
404 thumbShortIndent
, thumbLongIndent
,
405 bgColors(state
), bgColors(state
));
406 // Draw a vertical crosshatch for the grip.
407 int longOffset
= halfHeight
- gripLongIndent
;
409 left
+ halfWidth
, top
+ gripLongIndent
,
410 left
+ halfWidth
, bottom
- gripLongIndent
,
413 left
+ gripShortIndent
, top
+ longOffset
,
414 right
- gripShortIndent
, top
+ longOffset
,
417 left
+ gripShortIndent
, bottom
- longOffset
,
418 right
- gripShortIndent
, bottom
- longOffset
,
420 markState(canvas
, irect
, state
);
424 case WebThemeEngine::PartScrollbarHorizontalTrack
: {
425 int longOffset
= halfHeight
- notchLongOffset
;
426 int shortOffset
= irect
.width() - notchShortOffset
;
427 box(canvas
, irect
, bgColors(state
));
428 // back, notch on right
436 // forward, notch on right
444 markState(canvas
, irect
, state
);
448 case WebThemeEngine::PartScrollbarVerticalTrack
: {
449 int longOffset
= halfWidth
- notchLongOffset
;
450 int shortOffset
= irect
.height() - notchShortOffset
;
451 box(canvas
, irect
, bgColors(state
));
452 // back, notch at top
460 // forward, notch at bottom
468 markState(canvas
, irect
, state
);
472 case WebThemeEngine::PartScrollbarCorner
: {
473 SkIRect cornerRect
= {rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
};
474 paint
.setColor(SK_ColorWHITE
);
475 paint
.setStyle(SkPaint::kFill_Style
);
476 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
477 paint
.setAntiAlias(true);
478 canvas
->drawIRect(cornerRect
, paint
);
482 case WebThemeEngine::PartCheckbox
:
483 if (extraParams
->button
.indeterminate
) {
484 nestedBoxes(canvas
, irect
,
485 checkIndent
, halfHeight
,
486 checkIndent
, halfHeight
,
487 bgColors(state
), edgeColor
);
488 } else if (extraParams
->button
.checked
) {
489 irect
= validate(irect
, part
);
490 nestedBoxes(canvas
, irect
,
491 checkIndent
, checkIndent
,
492 checkIndent
, checkIndent
,
493 bgColors(state
), edgeColor
);
495 irect
= validate(irect
, part
);
496 box(canvas
, irect
, bgColors(state
));
500 case WebThemeEngine::PartRadio
:
501 irect
= validate(irect
, part
);
502 halfHeight
= irect
.height() / 2;
503 if (extraParams
->button
.checked
) {
504 circle(canvas
, irect
, SkIntToScalar(halfHeight
), bgColors(state
));
505 circle(canvas
, irect
, SkIntToScalar(halfHeight
- checkIndent
), edgeColor
);
507 circle(canvas
, irect
, SkIntToScalar(halfHeight
), bgColors(state
));
511 case WebThemeEngine::PartButton
:
512 roundRect(canvas
, irect
, bgColors(state
));
513 markState(canvas
, irect
, state
);
516 case WebThemeEngine::PartTextField
:
517 paint
.setColor(extraParams
->textField
.backgroundColor
);
518 paint
.setStyle(SkPaint::kFill_Style
);
519 canvas
->drawIRect(irect
, paint
);
521 paint
.setColor(edgeColor
);
522 paint
.setStyle(SkPaint::kStroke_Style
);
523 canvas
->drawIRect(irect
, paint
);
525 markState(canvas
, irect
, state
);
528 case WebThemeEngine::PartMenuList
:
529 if (extraParams
->menuList
.fillContentArea
) {
530 box(canvas
, irect
, extraParams
->menuList
.backgroundColor
);
533 paint
.setColor(edgeColor
);
534 paint
.setStyle(SkPaint::kStroke_Style
);
535 canvas
->drawIRect(irect
, paint
);
538 // clip the drop-down arrow to be inside the select box
539 if (extraParams
->menuList
.arrowX
- 4 > irect
.fLeft
)
540 irect
.fLeft
= extraParams
->menuList
.arrowX
- 4;
541 if (extraParams
->menuList
.arrowX
+ 12 < irect
.fRight
)
542 irect
.fRight
= extraParams
->menuList
.arrowX
+ 12;
544 irect
.fTop
= extraParams
->menuList
.arrowY
- (extraParams
->menuList
.arrowHeight
) / 2;
545 irect
.fBottom
= extraParams
->menuList
.arrowY
+ (extraParams
->menuList
.arrowHeight
- 1) / 2;
546 halfWidth
= irect
.width() / 2;
547 quarterWidth
= irect
.width() / 4;
549 if (state
== WebThemeEngine::StateFocused
) // FIXME: draw differenty?
550 state
= WebThemeEngine::StateNormal
;
551 box(canvas
, irect
, bgColors(state
));
553 irect
.fLeft
+ quarterWidth
, irect
.fTop
,
554 irect
.fRight
- quarterWidth
, irect
.fTop
,
555 irect
.fLeft
+ halfWidth
, irect
.fBottom
,
560 case WebThemeEngine::PartSliderTrack
: {
561 SkIRect lirect
= irect
;
563 // Draw a narrow rect for the track plus box hatches on the ends.
564 if (state
== WebThemeEngine::StateFocused
) // FIXME: draw differently?
565 state
= WebThemeEngine::StateNormal
;
566 if (extraParams
->slider
.vertical
) {
567 lirect
.inset(halfWidth
- sliderIndent
, noOffset
);
568 box(canvas
, lirect
, bgColors(state
));
569 line(canvas
, left
, top
, right
, top
, edgeColor
);
570 line(canvas
, left
, bottom
, right
, bottom
, edgeColor
);
572 lirect
.inset(noOffset
, halfHeight
- sliderIndent
);
573 box(canvas
, lirect
, bgColors(state
));
574 line(canvas
, left
, top
, left
, bottom
, edgeColor
);
575 line(canvas
, right
, top
, right
, bottom
, edgeColor
);
580 case WebThemeEngine::PartSliderThumb
:
581 if (state
== WebThemeEngine::StateFocused
) // FIXME: draw differently?
582 state
= WebThemeEngine::StateNormal
;
583 oval(canvas
, irect
, bgColors(state
));
586 case WebThemeEngine::PartInnerSpinButton
: {
587 // stack half-height up and down arrows on top of each other
589 int halfHeight
= rect
.height
/ 2;
590 if (extraParams
->innerSpin
.readOnly
)
591 state
= blink::WebThemeEngine::StateDisabled
;
593 lirect
.set(rect
.x
, rect
.y
, rect
.x
+ rect
.width
- 1, rect
.y
+ halfHeight
- 1);
594 box(canvas
, lirect
, bgColors(state
));
595 bottom
= lirect
.fBottom
;
596 quarterHeight
= lirect
.height() / 4;
598 left
+ quarterWidth
, bottom
- quarterHeight
,
599 right
- quarterWidth
, bottom
- quarterHeight
,
600 left
+ halfWidth
, top
+ quarterHeight
,
603 lirect
.set(rect
.x
, rect
.y
+ halfHeight
, rect
.x
+ rect
.width
- 1,
604 rect
.y
+ 2 * halfHeight
- 1);
606 bottom
= lirect
.fBottom
;
607 quarterHeight
= lirect
.height() / 4;
608 box(canvas
, lirect
, bgColors(state
));
610 left
+ quarterWidth
, top
+ quarterHeight
,
611 right
- quarterWidth
, top
+ quarterHeight
,
612 left
+ halfWidth
, bottom
- quarterHeight
,
614 markState(canvas
, irect
, state
);
617 case WebThemeEngine::PartProgressBar
: {
618 paint
.setColor(bgColors(state
));
619 paint
.setStyle(SkPaint::kFill_Style
);
620 canvas
->drawIRect(irect
, paint
);
623 SkIRect tofill
= irect
;
624 if (extraParams
->progressBar
.determinate
) {
625 tofill
.set(extraParams
->progressBar
.valueRectX
,
626 extraParams
->progressBar
.valueRectY
,
627 extraParams
->progressBar
.valueRectX
+
628 extraParams
->progressBar
.valueRectWidth
- 1,
629 extraParams
->progressBar
.valueRectY
+
630 extraParams
->progressBar
.valueRectHeight
);
633 tofill
.intersect(irect
, tofill
);
634 paint
.setColor(edgeColor
);
635 paint
.setStyle(SkPaint::kFill_Style
);
636 canvas
->drawIRect(tofill
, paint
);
638 markState(canvas
, irect
, state
);
642 // FIXME: Should we do something here to indicate that we got an invalid part?
643 // Unfortunately, we can't assert because we don't have access to WTF or base.
648 } // namespace WebTestRunner