Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / content / shell / renderer / test_runner / WebTestThemeEngineMock.cpp
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.
4
5 #include "content/shell/renderer/test_runner/WebTestThemeEngineMock.h"
6
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"
12
13 using blink::WebCanvas;
14 using blink::WebColor;
15 using blink::WebRect;
16 using blink::WebThemeEngine;
17
18 namespace content {
19
20 static const SkColor edgeColor     = SK_ColorBLACK;
21 static const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6);
22
23 SkColor bgColors(WebThemeEngine::State state) {
24   switch (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);
37     default:
38       NOTREACHED();
39   }
40   return SkColorSetRGB(0x00, 0x00, 0xff);
41 }
42
43 blink::WebSize WebTestThemeEngineMock::getSize(WebThemeEngine::Part part)
44 {
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);
50
51     switch (part) {
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);
75     default:
76         return invalidPartSize;
77     }
78 }
79
80 static SkIRect webRectToSkIRect(const WebRect& webRect)
81 {
82     SkIRect irect;
83     irect.set(webRect.x, webRect.y,
84         webRect.x + webRect.width - 1, webRect.y + webRect.height - 1);
85     return irect;
86 }
87
88 static SkIRect validate(const SkIRect& rect, WebThemeEngine::Part part)
89 {
90     switch (part) {
91     case WebThemeEngine::PartCheckbox:
92     case WebThemeEngine::PartRadio: {
93         SkIRect retval = rect;
94
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);
100
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;
105
106         return retval;
107     }
108     default:
109         return rect;
110     }
111 }
112
113
114 void box(SkCanvas *canvas, const SkIRect& rect, SkColor fillColor)
115 {
116     SkPaint paint;
117
118     paint.setStyle(SkPaint::kFill_Style);
119     paint.setColor(fillColor);
120     canvas->drawIRect(rect, paint);
121
122     paint.setColor(edgeColor);
123     paint.setStyle(SkPaint::kStroke_Style);
124     canvas->drawIRect(rect, paint);
125 }
126
127 void line(SkCanvas *canvas, int x0, int y0, int x1, int y1, SkColor color)
128 {
129     SkPaint paint;
130     paint.setColor(color);
131     canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0),
132         SkIntToScalar(x1), SkIntToScalar(y1), paint);
133 }
134
135 void triangle(SkCanvas *canvas,
136     int x0, int y0,
137     int x1, int y1,
138     int x2, int y2,
139     SkColor color)
140 {
141     SkPath path;
142     SkPaint paint;
143
144     paint.setColor(color);
145     paint.setStyle(SkPaint::kFill_Style);
146     path.incReserve(4);
147     path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0));
148     path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1));
149     path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2));
150     path.close();
151     canvas->drawPath(path, paint);
152
153     paint.setColor(edgeColor);
154     paint.setStyle(SkPaint::kStroke_Style);
155     canvas->drawPath(path, paint);
156 }
157
158 void roundRect(SkCanvas *canvas, SkIRect irect, SkColor color)
159 {
160     SkRect rect;
161     SkScalar radius = SkIntToScalar(5);
162     SkPaint paint;
163
164     rect.set(irect);
165     paint.setColor(color);
166     paint.setStyle(SkPaint::kFill_Style);
167     canvas->drawRoundRect(rect, radius, radius, paint);
168
169     paint.setColor(edgeColor);
170     paint.setStyle(SkPaint::kStroke_Style);
171     canvas->drawRoundRect(rect, radius, radius, paint);
172 }
173
174 void oval(SkCanvas* canvas, SkIRect irect, SkColor color)
175 {
176     SkRect rect;
177     SkPaint paint;
178
179     rect.set(irect);
180     paint.setColor(color);
181     paint.setStyle(SkPaint::kFill_Style);
182     canvas->drawOval(rect, paint);
183
184     paint.setColor(edgeColor);
185     paint.setStyle(SkPaint::kStroke_Style);
186     canvas->drawOval(rect, paint);
187 }
188
189 void circle(SkCanvas *canvas, SkIRect irect, SkScalar radius, SkColor color)
190 {
191     int left = irect.fLeft;
192     int width = irect.width();
193     int height = irect.height();
194     int top = irect.fTop;
195
196     SkScalar cy = SkIntToScalar(top  + height / 2);
197     SkScalar cx = SkIntToScalar(left + width / 2);
198     SkPaint paint;
199
200     paint.setColor(color);
201     paint.setStyle(SkPaint::kFill_Style);
202     canvas->drawCircle(cx, cy, radius, paint);
203
204     paint.setColor(edgeColor);
205     paint.setStyle(SkPaint::kStroke_Style);
206     canvas->drawCircle(cx, cy, radius, paint);
207 }
208
209 void nestedBoxes(SkCanvas *canvas,
210     SkIRect irect,
211     int indentLeft,
212     int indentTop,
213     int indentRight,
214     int indentBottom,
215     SkColor outerColor,
216     SkColor innerColor)
217 {
218     SkIRect lirect;
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);
225 }
226
227 void insetBox(SkCanvas* canvas,
228               SkIRect irect,
229               int indentLeft,
230               int indentTop,
231               int indentRight,
232               int indentBottom,
233               SkColor color) {
234   SkIRect lirect;
235   lirect.set(irect.fLeft + indentLeft,
236              irect.fTop + indentTop,
237              irect.fRight - indentRight,
238              irect.fBottom - indentBottom);
239   box(canvas, lirect, color);
240 }
241
242 void markState(SkCanvas *canvas, SkIRect irect, WebThemeEngine::State state)
243 {
244     int left = irect.fLeft;
245     int right = irect.fRight;
246     int top = irect.fTop;
247     int bottom = irect.fBottom;
248
249     // The length of a triangle side for the corner marks.
250     const int triangleSize = 5;
251
252     switch (state) {
253     case WebThemeEngine::StateDisabled:
254     case WebThemeEngine::StateNormal:
255         // Don't visually mark these states (color is enough).
256         break;
257
258     case WebThemeEngine::StateReadonly: {
259         // The horizontal lines in a read only control are spaced by this amount.
260         const int readOnlyLineOffset = 5;
261
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);
265         break;
266     }
267     case WebThemeEngine::StateHover:
268         // Draw a triangle in the upper left corner of the control. (Win's "hot")
269         triangle(canvas,
270             left,                 top,
271             left + triangleSize,  top,
272             left,                 top + triangleSize,
273             edgeColor);
274         break;
275
276     case WebThemeEngine::StateFocused:
277         // Draw a triangle in the bottom right corner of the control.
278         triangle(canvas,
279             right,                bottom,
280             right - triangleSize, bottom,
281             right,                bottom - triangleSize,
282             edgeColor);
283         break;
284
285     case WebThemeEngine::StatePressed:
286         // Draw a triangle in the bottom left corner of the control.
287         triangle(canvas,
288             left,                 bottom,
289             left,                 bottom - triangleSize,
290             left + triangleSize,  bottom,
291             edgeColor);
292         break;
293
294     default:
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.
297         break;
298     }
299 }
300
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)
307 {
308     SkIRect irect = webRectToSkIRect(rect);
309     SkPaint paint;
310
311     // Indent amounts for the check in a checkbox or radio button.
312     const int checkIndent = 3;
313
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;
318
319     // Indent amounts for the short and long sides of a scroll thumb box.
320     const int thumbLongIndent = 0;
321     const int thumbShortIndent = 2;
322
323     // Indents for the crosshatch on a scroll grip.
324     const int gripLongIndent = 3;
325     const int gripShortIndent = 5;
326
327     // Indents for the the slider track.
328     const int sliderIndent = 2;
329
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;
338
339     switch (part) {
340     case WebThemeEngine::PartScrollbarDownArrow:
341         box(canvas, irect, bgColors(state));
342         triangle(canvas,
343             left  + quarterWidth, top    + quarterHeight,
344             right - quarterWidth, top    + quarterHeight,
345             left  + halfWidth,    bottom - quarterHeight,
346             edgeColor);
347         markState(canvas, irect, state);
348         break;
349
350     case WebThemeEngine::PartScrollbarLeftArrow:
351         box(canvas, irect, bgColors(state));
352         triangle(canvas,
353             right - quarterWidth, top    + quarterHeight,
354             right - quarterWidth, bottom - quarterHeight,
355             left  + quarterWidth, top    + halfHeight,
356             edgeColor);
357         break;
358
359     case WebThemeEngine::PartScrollbarRightArrow:
360         box(canvas, irect, bgColors(state));
361         triangle(canvas,
362             left  + quarterWidth, top    + quarterHeight,
363             right - quarterWidth, top    + halfHeight,
364             left  + quarterWidth, bottom - quarterHeight,
365             edgeColor);
366         break;
367
368     case WebThemeEngine::PartScrollbarUpArrow:
369         box(canvas, irect, bgColors(state));
370         triangle(canvas,
371             left  + quarterWidth, bottom - quarterHeight,
372             left  + halfWidth,    top    + quarterHeight,
373             right - quarterWidth, bottom - quarterHeight,
374             edgeColor);
375         markState(canvas, irect, state);
376         break;
377
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;
385         line(canvas,
386             left  + gripLongIndent, top    + halfHeight,
387             right - gripLongIndent, top    + halfHeight,
388             edgeColor);
389         line(canvas,
390             left  + longOffset,     top    + gripShortIndent,
391             left  + longOffset,     bottom - gripShortIndent,
392             edgeColor);
393         line(canvas,
394             right - longOffset,     top    + gripShortIndent,
395             right - longOffset,     bottom - gripShortIndent,
396             edgeColor);
397         markState(canvas, irect, state);
398         break;
399     }
400
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;
408         line(canvas,
409             left  + halfWidth,       top    + gripLongIndent,
410             left  + halfWidth,       bottom - gripLongIndent,
411             edgeColor);
412         line(canvas,
413             left  + gripShortIndent, top    + longOffset,
414             right - gripShortIndent, top    + longOffset,
415             edgeColor);
416         line(canvas,
417             left  + gripShortIndent, bottom - longOffset,
418             right - gripShortIndent, bottom - longOffset,
419             edgeColor);
420         markState(canvas, irect, state);
421         break;
422     }
423
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
429         insetBox(canvas,
430                  irect,
431                  noOffset,
432                  longOffset,
433                  shortOffset,
434                  longOffset,
435                  edgeColor);
436         // forward, notch on right
437         insetBox(canvas,
438                  irect,
439                  shortOffset,
440                  longOffset,
441                  noOffset,
442                  longOffset,
443                  edgeColor);
444         markState(canvas, irect, state);
445         break;
446     }
447
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
453         insetBox(canvas,
454                  irect,
455                  longOffset,
456                  noOffset,
457                  longOffset,
458                  shortOffset,
459                  edgeColor);
460         // forward, notch at bottom
461         insetBox(canvas,
462                  irect,
463                  longOffset,
464                  shortOffset,
465                  longOffset,
466                  noOffset,
467                  edgeColor);
468         markState(canvas, irect, state);
469         break;
470     }
471
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);
479         break;
480     }
481
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);
494         } else {
495             irect = validate(irect, part);
496             box(canvas, irect, bgColors(state));
497         }
498         break;
499
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);
506         } else {
507             circle(canvas, irect, SkIntToScalar(halfHeight), bgColors(state));
508         }
509         break;
510
511     case WebThemeEngine::PartButton:
512         roundRect(canvas, irect, bgColors(state));
513         markState(canvas, irect, state);
514         break;
515
516     case WebThemeEngine::PartTextField:
517         paint.setColor(extraParams->textField.backgroundColor);
518         paint.setStyle(SkPaint::kFill_Style);
519         canvas->drawIRect(irect, paint);
520
521         paint.setColor(edgeColor);
522         paint.setStyle(SkPaint::kStroke_Style);
523         canvas->drawIRect(irect, paint);
524
525         markState(canvas, irect, state);
526         break;
527
528     case WebThemeEngine::PartMenuList:
529         if (extraParams->menuList.fillContentArea) {
530             box(canvas, irect, extraParams->menuList.backgroundColor);
531         } else {
532             SkPaint paint;
533             paint.setColor(edgeColor);
534             paint.setStyle(SkPaint::kStroke_Style);
535             canvas->drawIRect(irect, paint);
536         }
537
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;
543
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;
548
549         if (state == WebThemeEngine::StateFocused) // FIXME: draw differenty?
550             state = WebThemeEngine::StateNormal;
551         box(canvas, irect, bgColors(state));
552         triangle(canvas,
553             irect.fLeft  + quarterWidth, irect.fTop,
554             irect.fRight - quarterWidth, irect.fTop,
555             irect.fLeft  + halfWidth,    irect.fBottom,
556             edgeColor);
557
558         break;
559
560     case WebThemeEngine::PartSliderTrack: {
561         SkIRect lirect =  irect;
562
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);
571         } else {
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);
576         }
577         break;
578     }
579
580     case WebThemeEngine::PartSliderThumb:
581         if (state == WebThemeEngine::StateFocused) // FIXME: draw differently?
582             state = WebThemeEngine::StateNormal;
583         oval(canvas, irect, bgColors(state));
584         break;
585
586     case WebThemeEngine::PartInnerSpinButton: {
587         // stack half-height up and down arrows on top of each other
588         SkIRect lirect;
589         int halfHeight = rect.height / 2;
590         if (extraParams->innerSpin.readOnly)
591             state = blink::WebThemeEngine::StateDisabled;
592
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;
597         triangle(canvas,
598             left  + quarterWidth, bottom - quarterHeight,
599             right - quarterWidth, bottom - quarterHeight,
600             left  + halfWidth,    top    + quarterHeight,
601             edgeColor);
602
603         lirect.set(rect.x, rect.y + halfHeight, rect.x + rect.width - 1,
604             rect.y + 2 * halfHeight - 1);
605         top = lirect.fTop;
606         bottom = lirect.fBottom;
607         quarterHeight = lirect.height() / 4;
608         box(canvas, lirect, bgColors(state));
609         triangle(canvas,
610             left  + quarterWidth, top    + quarterHeight,
611             right - quarterWidth, top    + quarterHeight,
612             left  + halfWidth,    bottom - quarterHeight,
613             edgeColor);
614         markState(canvas, irect, state);
615         break;
616     }
617     case WebThemeEngine::PartProgressBar: {
618         paint.setColor(bgColors(state));
619         paint.setStyle(SkPaint::kFill_Style);
620         canvas->drawIRect(irect, paint);
621
622         // Emulate clipping
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);
631         }
632
633         tofill.intersect(irect, tofill);
634         paint.setColor(edgeColor);
635         paint.setStyle(SkPaint::kFill_Style);
636         canvas->drawIRect(tofill, paint);
637
638         markState(canvas, irect, state);
639         break;
640     }
641     default:
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.
644         break;
645     }
646 }
647
648 }  // namespace content