Home | History | Annotate | Download | only in runner
      1 /*
      2  * Copyright (C) 2010 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 // This file implements a simple generic version of the WebThemeEngine,
     32 // which is used to draw all the native controls on a web page. We use this
     33 // file when running in layout test mode in order to remove any
     34 // platform-specific rendering differences due to themes, colors, etc.
     35 //
     36 
     37 #include "WebTestThemeControlWin.h"
     38 
     39 #include "TestCommon.h"
     40 #include "skia/ext/skia_utils_win.h"
     41 #include "third_party/skia/include/core/SkCanvas.h"
     42 #include "third_party/skia/include/core/SkPaint.h"
     43 #include "third_party/skia/include/core/SkPath.h"
     44 #include "third_party/skia/include/core/SkRect.h"
     45 
     46 #include <algorithm>
     47 
     48 using namespace WebKit;
     49 using namespace std;
     50 
     51 namespace WebTestRunner {
     52 
     53 namespace {
     54 
     55 const SkColor edgeColor     = SK_ColorBLACK;
     56 const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6);
     57 const SkColor fgColor       = SK_ColorBLACK;
     58 const SkColor bgColors[]    = {
     59     SK_ColorBLACK, // Unknown
     60     SkColorSetRGB(0xc9, 0xc9, 0xc9), // Disabled
     61     SkColorSetRGB(0xf3, 0xe0, 0xd0), // Readonly
     62     SkColorSetRGB(0x89, 0xc4, 0xff), // Normal
     63     SkColorSetRGB(0x43, 0xf9, 0xff), // Hot
     64     SkColorSetRGB(0x20, 0xf6, 0xcc), // Focused
     65     SkColorSetRGB(0x00, 0xf3, 0xac), // Hover
     66     SkColorSetRGB(0xa9, 0xff, 0x12), // Pressed
     67     SkColorSetRGB(0xcc, 0xcc, 0xcc) // Indeterminate
     68 };
     69 
     70 SkIRect validate(const SkIRect& rect, WebTestThemeControlWin::Type ctype)
     71 {
     72     switch (ctype) {
     73     case WebTestThemeControlWin::UncheckedBoxType:
     74     case WebTestThemeControlWin::CheckedBoxType:
     75     case WebTestThemeControlWin::UncheckedRadioType:
     76     case WebTestThemeControlWin::CheckedRadioType: {
     77         SkIRect retval = rect;
     78 
     79         // The maximum width and height is 13.
     80         // Center the square in the passed rectangle.
     81         const int maxControlSize = 13;
     82         int controlSize = std::min(rect.width(), rect.height());
     83         controlSize = std::min(controlSize, maxControlSize);
     84 
     85         retval.fLeft   = rect.fLeft + (rect.width() / 2) - (controlSize / 2);
     86         retval.fRight  = retval.fLeft + controlSize - 1;
     87         retval.fTop    = rect.fTop + (rect.height() / 2) - (controlSize / 2);
     88         retval.fBottom = retval.fTop + controlSize - 1;
     89 
     90         return retval;
     91     }
     92 
     93     default:
     94         return rect;
     95     }
     96 }
     97 
     98 }
     99 
    100 WebTestThemeControlWin::WebTestThemeControlWin(SkCanvas* canvas, const SkIRect& irect, Type ctype, State cstate)
    101     : m_canvas(canvas)
    102     , m_irect(validate(irect, ctype))
    103     , m_type(ctype)
    104     , m_state(cstate)
    105     , m_left(m_irect.fLeft)
    106     , m_right(m_irect.fRight)
    107     , m_top(m_irect.fTop)
    108     , m_bottom(m_irect.fBottom)
    109     , m_height(m_irect.height())
    110     , m_width(m_irect.width())
    111     , m_edgeColor(edgeColor)
    112     , m_bgColor(bgColors[cstate])
    113     , m_fgColor(fgColor)
    114 {
    115 }
    116 
    117 WebTestThemeControlWin::~WebTestThemeControlWin()
    118 {
    119 }
    120 
    121 void WebTestThemeControlWin::box(const SkIRect& rect, SkColor fillColor)
    122 {
    123     SkPaint paint;
    124 
    125     paint.setStyle(SkPaint::kFill_Style);
    126     paint.setColor(fillColor);
    127     m_canvas->drawIRect(rect, paint);
    128 
    129     paint.setColor(m_edgeColor);
    130     paint.setStyle(SkPaint::kStroke_Style);
    131     m_canvas->drawIRect(rect, paint);
    132 }
    133 
    134 void WebTestThemeControlWin::line(int x0, int y0, int x1, int y1, SkColor color)
    135 {
    136     SkPaint paint;
    137     paint.setColor(color);
    138     m_canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0), SkIntToScalar(x1), SkIntToScalar(y1), paint);
    139 }
    140 
    141 void WebTestThemeControlWin::triangle(int x0, int y0, int x1, int y1, int x2, int y2, SkColor color)
    142 {
    143     SkPath path;
    144     SkPaint paint;
    145 
    146     paint.setColor(color);
    147     paint.setStyle(SkPaint::kFill_Style);
    148     path.incReserve(4);
    149     path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0));
    150     path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1));
    151     path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2));
    152     path.close();
    153     m_canvas->drawPath(path, paint);
    154 
    155     paint.setColor(m_edgeColor);
    156     paint.setStyle(SkPaint::kStroke_Style);
    157     m_canvas->drawPath(path, paint);
    158 }
    159 
    160 void WebTestThemeControlWin::roundRect(SkColor color)
    161 {
    162     SkRect rect;
    163     SkScalar radius = SkIntToScalar(5);
    164     SkPaint paint;
    165 
    166     rect.set(m_irect);
    167     paint.setColor(color);
    168     paint.setStyle(SkPaint::kFill_Style);
    169     m_canvas->drawRoundRect(rect, radius, radius, paint);
    170 
    171     paint.setColor(m_edgeColor);
    172     paint.setStyle(SkPaint::kStroke_Style);
    173     m_canvas->drawRoundRect(rect, radius, radius, paint);
    174 }
    175 
    176 void WebTestThemeControlWin::oval(SkColor color)
    177 {
    178     SkRect rect;
    179     SkPaint paint;
    180 
    181     rect.set(m_irect);
    182     paint.setColor(color);
    183     paint.setStyle(SkPaint::kFill_Style);
    184     m_canvas->drawOval(rect, paint);
    185 
    186     paint.setColor(m_edgeColor);
    187     paint.setStyle(SkPaint::kStroke_Style);
    188     m_canvas->drawOval(rect, paint);
    189 }
    190 
    191 void WebTestThemeControlWin::circle(SkScalar radius, SkColor color)
    192 {
    193     SkScalar cy = SkIntToScalar(m_top  + m_height / 2);
    194     SkScalar cx = SkIntToScalar(m_left + m_width / 2);
    195     SkPaint paint;
    196 
    197     paint.setColor(color);
    198     paint.setStyle(SkPaint::kFill_Style);
    199     m_canvas->drawCircle(cx, cy, radius, paint);
    200 
    201     paint.setColor(m_edgeColor);
    202     paint.setStyle(SkPaint::kStroke_Style);
    203     m_canvas->drawCircle(cx, cy, radius, paint);
    204 }
    205 
    206 void WebTestThemeControlWin::nestedBoxes(int indentLeft, int indentTop, int indentRight, int indentBottom, SkColor outerColor, SkColor innerColor)
    207 {
    208     SkIRect lirect;
    209     box(m_irect, outerColor);
    210     lirect.set(m_irect.fLeft + indentLeft, m_irect.fTop + indentTop, m_irect.fRight - indentRight, m_irect.fBottom - indentBottom);
    211     box(lirect, innerColor);
    212 }
    213 
    214 void WebTestThemeControlWin::markState()
    215 {
    216     // The horizontal lines in a read only control are spaced by this amount.
    217     const int readOnlyLineOffset = 5;
    218 
    219     // The length of a triangle side for the corner marks.
    220     const int triangleSize = 5;
    221 
    222     switch (m_state) {
    223     case UnknownState:
    224     case DisabledState:
    225     case NormalState:
    226     case IndeterminateState:
    227         // Don't visually mark these states (color is enough).
    228         break;
    229     case ReadOnlyState:
    230         // Drawing lines across the control.
    231         for (int i = m_top + readOnlyLineOffset; i < m_bottom; i += readOnlyLineOffset)
    232             line(m_left + 1, i, m_right - 1, i, readOnlyColor);
    233         break;
    234 
    235     case HotState:
    236         // Draw a triangle in the upper left corner of the control.
    237         triangle(m_left, m_top, m_left + triangleSize, m_top, m_left, m_top + triangleSize, m_edgeColor);
    238         break;
    239 
    240     case HoverState:
    241         // Draw a triangle in the upper right corner of the control.
    242         triangle(m_right, m_top, m_right, m_top + triangleSize, m_right - triangleSize, m_top, m_edgeColor);
    243         break;
    244 
    245     case FocusedState:
    246         // Draw a triangle in the bottom right corner of the control.
    247         triangle(m_right, m_bottom, m_right - triangleSize, m_bottom, m_right, m_bottom - triangleSize, m_edgeColor);
    248         break;
    249 
    250     case PressedState:
    251         // Draw a triangle in the bottom left corner of the control.
    252         triangle(m_left, m_bottom, m_left, m_bottom - triangleSize, m_left + triangleSize, m_bottom, m_edgeColor);
    253         break;
    254 
    255     default:
    256         WEBKIT_ASSERT_NOT_REACHED();
    257         break;
    258     }
    259 }
    260 
    261 void WebTestThemeControlWin::draw()
    262 {
    263     int halfWidth = m_width / 2;
    264     int halfHeight = m_height / 2;
    265     int quarterWidth = m_width / 4;
    266     int quarterHeight = m_height / 4;
    267 
    268     // Indent amounts for the check in a checkbox or radio button.
    269     const int checkIndent = 3;
    270 
    271     // Indent amounts for short and long sides of the scrollbar notches.
    272     const int notchLongOffset = 1;
    273     const int notchShortOffset = 4;
    274     const int noOffset = 0;
    275 
    276     // Indent amounts for the short and long sides of a scroll thumb box.
    277     const int thumbLongIndent = 0;
    278     const int thumbShortIndent = 2;
    279 
    280     // Indents for the crosshatch on a scroll grip.
    281     const int gripLongIndent = 3;
    282     const int gripShortIndent = 5;
    283 
    284     // Indents for the the slider track.
    285     const int sliderIndent = 2;
    286 
    287     switch (m_type) {
    288     case UnknownType:
    289         WEBKIT_ASSERT_NOT_REACHED();
    290         break;
    291 
    292     case TextFieldType:
    293         // We render this by hand outside of this function.
    294         WEBKIT_ASSERT_NOT_REACHED();
    295         break;
    296 
    297     case PushButtonType:
    298         // push buttons render as a rounded rectangle
    299         roundRect(m_bgColor);
    300         break;
    301 
    302     case UncheckedBoxType:
    303         // Unchecked boxes are simply plain boxes.
    304         box(m_irect, m_bgColor);
    305         break;
    306 
    307     case CheckedBoxType:
    308         nestedBoxes(checkIndent, checkIndent, checkIndent, checkIndent, m_bgColor, m_fgColor);
    309         break;
    310 
    311     case IndeterminateCheckboxType:
    312         // Indeterminate checkbox is a box containing '-'.
    313         nestedBoxes(checkIndent, halfHeight, checkIndent, halfHeight, m_bgColor, m_fgColor);
    314         break;
    315 
    316     case UncheckedRadioType:
    317         circle(SkIntToScalar(halfHeight), m_bgColor);
    318         break;
    319 
    320     case CheckedRadioType:
    321         circle(SkIntToScalar(halfHeight), m_bgColor);
    322         circle(SkIntToScalar(halfHeight - checkIndent), m_fgColor);
    323         break;
    324 
    325     case HorizontalScrollTrackBackType: {
    326         // Draw a box with a notch at the left.
    327         int longOffset = halfHeight - notchLongOffset;
    328         int shortOffset = m_width - notchShortOffset;
    329         nestedBoxes(noOffset, longOffset, shortOffset, longOffset, m_bgColor, m_edgeColor);
    330         break;
    331     }
    332 
    333     case HorizontalScrollTrackForwardType: {
    334         // Draw a box with a notch at the right.
    335         int longOffset  = halfHeight - notchLongOffset;
    336         int shortOffset = m_width - notchShortOffset;
    337         nestedBoxes(shortOffset, longOffset, noOffset, longOffset, m_bgColor, m_fgColor);
    338         break;
    339     }
    340 
    341     case VerticalScrollTrackBackType: {
    342         // Draw a box with a notch at the top.
    343         int longOffset  = halfWidth - notchLongOffset;
    344         int shortOffset = m_height - notchShortOffset;
    345         nestedBoxes(longOffset, noOffset, longOffset, shortOffset, m_bgColor, m_fgColor);
    346         break;
    347     }
    348 
    349     case VerticalScrollTrackForwardType: {
    350         // Draw a box with a notch at the bottom.
    351         int longOffset  = halfWidth - notchLongOffset;
    352         int shortOffset = m_height - notchShortOffset;
    353         nestedBoxes(longOffset, shortOffset, longOffset, noOffset, m_bgColor, m_fgColor);
    354         break;
    355     }
    356 
    357     case HorizontalScrollThumbType:
    358         // Draw a narrower box on top of the outside box.
    359         nestedBoxes(thumbLongIndent, thumbShortIndent, thumbLongIndent, thumbShortIndent, m_bgColor, m_bgColor);
    360         break;
    361 
    362     case VerticalScrollThumbType:
    363         // Draw a shorter box on top of the outside box.
    364         nestedBoxes(thumbShortIndent, thumbLongIndent, thumbShortIndent, thumbLongIndent, m_bgColor, m_bgColor);
    365         break;
    366 
    367     case HorizontalSliderThumbType:
    368     case VerticalSliderThumbType:
    369         // Slider thumbs are ovals.
    370         oval(m_bgColor);
    371         break;
    372 
    373     case HorizontalScrollGripType: {
    374         // Draw a horizontal crosshatch for the grip.
    375         int longOffset = halfWidth - gripLongIndent;
    376         line(m_left + gripLongIndent, m_top + halfHeight, m_right - gripLongIndent, m_top + halfHeight, m_fgColor);
    377         line(m_left + longOffset, m_top + gripShortIndent, m_left + longOffset, m_bottom - gripShortIndent, m_fgColor);
    378         line(m_right - longOffset, m_top + gripShortIndent, m_right - longOffset, m_bottom - gripShortIndent, m_fgColor);
    379         break;
    380     }
    381 
    382     case VerticalScrollGripType: {
    383         // Draw a vertical crosshatch for the grip.
    384         int longOffset = halfHeight - gripLongIndent;
    385         line(m_left + halfWidth, m_top + gripLongIndent, m_left + halfWidth, m_bottom - gripLongIndent, m_fgColor);
    386         line(m_left + gripShortIndent, m_top + longOffset, m_right - gripShortIndent, m_top + longOffset, m_fgColor);
    387         line(m_left + gripShortIndent, m_bottom - longOffset, m_right - gripShortIndent, m_bottom - longOffset, m_fgColor);
    388         break;
    389     }
    390 
    391     case LeftArrowType:
    392         // Draw a left arrow inside a box.
    393         box(m_irect, m_bgColor);
    394         triangle(m_right - quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_bottom - quarterHeight, m_left + quarterWidth, m_top + halfHeight, m_fgColor);
    395         break;
    396 
    397     case RightArrowType:
    398         // Draw a left arrow inside a box.
    399         box(m_irect, m_bgColor);
    400         triangle(m_left + quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_top + halfHeight, m_left + quarterWidth, m_bottom - quarterHeight, m_fgColor);
    401         break;
    402 
    403     case UpArrowType:
    404         // Draw an up arrow inside a box.
    405         box(m_irect, m_bgColor);
    406         triangle(m_left + quarterWidth, m_bottom - quarterHeight, m_left + halfWidth, m_top + quarterHeight, m_right - quarterWidth, m_bottom - quarterHeight, m_fgColor);
    407         break;
    408 
    409     case DownArrowType:
    410         // Draw a down arrow inside a box.
    411         box(m_irect, m_bgColor);
    412         triangle(m_left + quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_top + quarterHeight, m_left + halfWidth, m_bottom - quarterHeight, m_fgColor);
    413         break;
    414 
    415     case HorizontalSliderTrackType: {
    416         // Draw a narrow rect for the track plus box hatches on the ends.
    417         SkIRect lirect;
    418         lirect = m_irect;
    419         lirect.inset(noOffset, halfHeight - sliderIndent);
    420         box(lirect, m_bgColor);
    421         line(m_left,  m_top, m_left,  m_bottom, m_edgeColor);
    422         line(m_right, m_top, m_right, m_bottom, m_edgeColor);
    423         break;
    424     }
    425 
    426     case VerticalSliderTrackType: {
    427         // Draw a narrow rect for the track plus box hatches on the ends.
    428         SkIRect lirect;
    429         lirect = m_irect;
    430         lirect.inset(halfWidth - sliderIndent, noOffset);
    431         box(lirect, m_bgColor);
    432         line(m_left, m_top, m_right, m_top, m_edgeColor);
    433         line(m_left, m_bottom, m_right, m_bottom, m_edgeColor);
    434         break;
    435     }
    436 
    437     case DropDownButtonType:
    438         // Draw a box with a big down arrow on top.
    439         box(m_irect, m_bgColor);
    440         triangle(m_left + quarterWidth, m_top, m_right - quarterWidth, m_top, m_left + halfWidth, m_bottom, m_fgColor);
    441         break;
    442 
    443     default:
    444         WEBKIT_ASSERT_NOT_REACHED();
    445         break;
    446     }
    447 
    448     markState();
    449 }
    450 
    451 // Because rendering a text field is dependent on input
    452 // parameters the other controls don't have, we render it directly
    453 // rather than trying to overcomplicate draw() further.
    454 void WebTestThemeControlWin::drawTextField(bool drawEdges, bool fillContentArea, SkColor color)
    455 {
    456     SkPaint paint;
    457 
    458     if (fillContentArea) {
    459         paint.setColor(color);
    460         paint.setStyle(SkPaint::kFill_Style);
    461         m_canvas->drawIRect(m_irect, paint);
    462     }
    463     if (drawEdges) {
    464         paint.setColor(m_edgeColor);
    465         paint.setStyle(SkPaint::kStroke_Style);
    466         m_canvas->drawIRect(m_irect, paint);
    467     }
    468 
    469     markState();
    470 }
    471 
    472 void WebTestThemeControlWin::drawProgressBar(const SkIRect& fillRect)
    473 {
    474     SkPaint paint;
    475 
    476     paint.setColor(m_bgColor);
    477     paint.setStyle(SkPaint::kFill_Style);
    478     m_canvas->drawIRect(m_irect, paint);
    479 
    480     // Emulate clipping
    481     SkIRect tofill;
    482     tofill.intersect(m_irect, fillRect);
    483     paint.setColor(m_fgColor);
    484     paint.setStyle(SkPaint::kFill_Style);
    485     m_canvas->drawIRect(tofill, paint);
    486 
    487     markState();
    488 }
    489 
    490 }
    491