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 
     45 #include <algorithm>
     46 
     47 using namespace blink;
     48 using namespace std;
     49 
     50 namespace WebTestRunner {
     51 
     52 namespace {
     53 
     54 const SkColor edgeColor     = SK_ColorBLACK;
     55 const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6);
     56 const SkColor fgColor       = SK_ColorBLACK;
     57 
     58 // These are indexed by WebTestThemeControlWin::State, *not* WebThemeEngine::State.
     59 const SkColor bgColors[]    = {
     60     SK_ColorBLACK, //                   Unknown (not used)
     61     SkColorSetRGB(0xc9, 0xc9, 0xc9), // Disabled
     62     SkColorSetRGB(0xf3, 0xe0, 0xd0), // Readonly
     63     SkColorSetRGB(0x89, 0xc4, 0xff), // Normal
     64     SkColorSetRGB(0x43, 0xf9, 0xff), // Hot
     65     SkColorSetRGB(0x20, 0xf6, 0xcc), // Hover
     66     SkColorSetRGB(0x00, 0xf3, 0xac), // Focused
     67     SkColorSetRGB(0xa9, 0xff, 0x12), // Pressed
     68     SkColorSetRGB(0xcc, 0xcc, 0xcc) //  Indeterminate (not used)
     69 };
     70 
     71 SkIRect validate(const SkIRect& rect, WebTestThemeControlWin::Type ctype)
     72 {
     73     switch (ctype) {
     74     case WebTestThemeControlWin::UncheckedBoxType:
     75     case WebTestThemeControlWin::CheckedBoxType:
     76     case WebTestThemeControlWin::UncheckedRadioType:
     77     case WebTestThemeControlWin::CheckedRadioType: {
     78         SkIRect retval = rect;
     79 
     80         // The maximum width and height is 13.
     81         // Center the square in the passed rectangle.
     82         const int maxControlSize = 13;
     83         int controlSize = std::min(rect.width(), rect.height());
     84         controlSize = std::min(controlSize, maxControlSize);
     85 
     86         retval.fLeft   = rect.fLeft + (rect.width() / 2) - (controlSize / 2);
     87         retval.fRight  = retval.fLeft + controlSize - 1;
     88         retval.fTop    = rect.fTop + (rect.height() / 2) - (controlSize / 2);
     89         retval.fBottom = retval.fTop + controlSize - 1;
     90 
     91         return retval;
     92     }
     93 
     94     default:
     95         return rect;
     96     }
     97 }
     98 
     99 }
    100 
    101 WebTestThemeControlWin::WebTestThemeControlWin(SkCanvas* canvas, const SkIRect& irect, Type ctype, State cstate)
    102     : m_canvas(canvas)
    103     , m_irect(validate(irect, ctype))
    104     , m_type(ctype)
    105     , m_state(cstate)
    106     , m_left(m_irect.fLeft)
    107     , m_right(m_irect.fRight)
    108     , m_top(m_irect.fTop)
    109     , m_bottom(m_irect.fBottom)
    110     , m_height(m_irect.height())
    111     , m_width(m_irect.width())
    112     , m_edgeColor(edgeColor)
    113     , m_bgColor(bgColors[cstate])
    114     , m_fgColor(fgColor)
    115 {
    116 }
    117 
    118 WebTestThemeControlWin::~WebTestThemeControlWin()
    119 {
    120 }
    121 
    122 void WebTestThemeControlWin::box(const SkIRect& rect, SkColor fillColor)
    123 {
    124     SkPaint paint;
    125 
    126     paint.setStyle(SkPaint::kFill_Style);
    127     paint.setColor(fillColor);
    128     m_canvas->drawIRect(rect, paint);
    129 
    130     paint.setColor(m_edgeColor);
    131     paint.setStyle(SkPaint::kStroke_Style);
    132     m_canvas->drawIRect(rect, paint);
    133 }
    134 
    135 void WebTestThemeControlWin::line(int x0, int y0, int x1, int y1, SkColor color)
    136 {
    137     SkPaint paint;
    138     paint.setColor(color);
    139     m_canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0), SkIntToScalar(x1), SkIntToScalar(y1), paint);
    140 }
    141 
    142 void WebTestThemeControlWin::triangle(int x0, int y0, int x1, int y1, int x2, int y2, SkColor color)
    143 {
    144     SkPath path;
    145     SkPaint paint;
    146 
    147     paint.setColor(color);
    148     paint.setStyle(SkPaint::kFill_Style);
    149     path.incReserve(4);
    150     path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0));
    151     path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1));
    152     path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2));
    153     path.close();
    154     m_canvas->drawPath(path, paint);
    155 
    156     paint.setColor(m_edgeColor);
    157     paint.setStyle(SkPaint::kStroke_Style);
    158     m_canvas->drawPath(path, paint);
    159 }
    160 
    161 void WebTestThemeControlWin::roundRect(SkColor color)
    162 {
    163     SkRect rect;
    164     SkScalar radius = SkIntToScalar(5);
    165     SkPaint paint;
    166 
    167     rect.set(m_irect);
    168     paint.setColor(color);
    169     paint.setStyle(SkPaint::kFill_Style);
    170     m_canvas->drawRoundRect(rect, radius, radius, paint);
    171 
    172     paint.setColor(m_edgeColor);
    173     paint.setStyle(SkPaint::kStroke_Style);
    174     m_canvas->drawRoundRect(rect, radius, radius, paint);
    175 }
    176 
    177 void WebTestThemeControlWin::oval(SkColor color)
    178 {
    179     SkRect rect;
    180     SkPaint paint;
    181 
    182     rect.set(m_irect);
    183     paint.setColor(color);
    184     paint.setStyle(SkPaint::kFill_Style);
    185     m_canvas->drawOval(rect, paint);
    186 
    187     paint.setColor(m_edgeColor);
    188     paint.setStyle(SkPaint::kStroke_Style);
    189     m_canvas->drawOval(rect, paint);
    190 }
    191 
    192 void WebTestThemeControlWin::circle(SkScalar radius, SkColor color)
    193 {
    194     SkScalar cy = SkIntToScalar(m_top  + m_height / 2);
    195     SkScalar cx = SkIntToScalar(m_left + m_width / 2);
    196     SkPaint paint;
    197 
    198     paint.setColor(color);
    199     paint.setStyle(SkPaint::kFill_Style);
    200     m_canvas->drawCircle(cx, cy, radius, paint);
    201 
    202     paint.setColor(m_edgeColor);
    203     paint.setStyle(SkPaint::kStroke_Style);
    204     m_canvas->drawCircle(cx, cy, radius, paint);
    205 }
    206 
    207 void WebTestThemeControlWin::nestedBoxes(int indentLeft, int indentTop, int indentRight, int indentBottom, SkColor outerColor, SkColor innerColor)
    208 {
    209     SkIRect lirect;
    210     box(m_irect, outerColor);
    211     lirect.set(m_irect.fLeft + indentLeft, m_irect.fTop + indentTop, m_irect.fRight - indentRight, m_irect.fBottom - indentBottom);
    212     box(lirect, innerColor);
    213 }
    214 
    215 void WebTestThemeControlWin::markState()
    216 {
    217     // The horizontal lines in a read only control are spaced by this amount.
    218     const int readOnlyLineOffset = 5;
    219 
    220     // The length of a triangle side for the corner marks.
    221     const int triangleSize = 5;
    222 
    223     switch (m_state) {
    224     case UnknownState:
    225     case DisabledState:
    226     case NormalState:
    227     case IndeterminateState:
    228         // Don't visually mark these states (color is enough).
    229         break;
    230     case ReadOnlyState:
    231         // Drawing lines across the control.
    232         for (int i = m_top + readOnlyLineOffset; i < m_bottom; i += readOnlyLineOffset)
    233             line(m_left + 1, i, m_right - 1, i, readOnlyColor);
    234         break;
    235 
    236     case HotState:
    237         // Draw a triangle in the upper left corner of the control.
    238         triangle(m_left, m_top, m_left + triangleSize, m_top, m_left, m_top + triangleSize, m_edgeColor);
    239         break;
    240 
    241     case HoverState:
    242         // Draw a triangle in the upper right corner of the control.
    243         triangle(m_right, m_top, m_right, m_top + triangleSize, m_right - triangleSize, m_top, m_edgeColor);
    244         break;
    245 
    246     case FocusedState:
    247         // Draw a triangle in the bottom right corner of the control.
    248         triangle(m_right, m_bottom, m_right - triangleSize, m_bottom, m_right, m_bottom - triangleSize, m_edgeColor);
    249         break;
    250 
    251     case PressedState:
    252         // Draw a triangle in the bottom left corner of the control.
    253         triangle(m_left, m_bottom, m_left, m_bottom - triangleSize, m_left + triangleSize, m_bottom, m_edgeColor);
    254         break;
    255 
    256     default:
    257         BLINK_ASSERT_NOT_REACHED();
    258         break;
    259     }
    260 }
    261 
    262 void WebTestThemeControlWin::draw()
    263 {
    264     int halfWidth = m_width / 2;
    265     int halfHeight = m_height / 2;
    266     int quarterWidth = m_width / 4;
    267     int quarterHeight = m_height / 4;
    268 
    269     // Indent amounts for the check in a checkbox or radio button.
    270     const int checkIndent = 3;
    271 
    272     // Indent amounts for short and long sides of the scrollbar notches.
    273     const int notchLongOffset = 1;
    274     const int notchShortOffset = 4;
    275     const int noOffset = 0;
    276 
    277     // Indent amounts for the short and long sides of a scroll thumb box.
    278     const int thumbLongIndent = 0;
    279     const int thumbShortIndent = 2;
    280 
    281     // Indents for the crosshatch on a scroll grip.
    282     const int gripLongIndent = 3;
    283     const int gripShortIndent = 5;
    284 
    285     // Indents for the the slider track.
    286     const int sliderIndent = 2;
    287 
    288     switch (m_type) {
    289     case UnknownType:
    290         BLINK_ASSERT_NOT_REACHED();
    291         break;
    292 
    293     case TextFieldType:
    294         // We render this by hand outside of this function.
    295         BLINK_ASSERT_NOT_REACHED();
    296         break;
    297 
    298     case PushButtonType:
    299         // push buttons render as a rounded rectangle
    300         roundRect(m_bgColor);
    301         break;
    302 
    303     case UncheckedBoxType:
    304         // Unchecked boxes are simply plain boxes.
    305         box(m_irect, m_bgColor);
    306         break;
    307 
    308     case CheckedBoxType:
    309         nestedBoxes(checkIndent, checkIndent, checkIndent, checkIndent, m_bgColor, m_fgColor);
    310         break;
    311 
    312     case IndeterminateCheckboxType:
    313         // Indeterminate checkbox is a box containing '-'.
    314         nestedBoxes(checkIndent, halfHeight, checkIndent, halfHeight, m_bgColor, m_fgColor);
    315         break;
    316 
    317     case UncheckedRadioType:
    318         circle(SkIntToScalar(halfHeight), m_bgColor);
    319         break;
    320 
    321     case CheckedRadioType:
    322         circle(SkIntToScalar(halfHeight), m_bgColor);
    323         circle(SkIntToScalar(halfHeight - checkIndent), m_fgColor);
    324         break;
    325 
    326     case HorizontalScrollTrackBackType: {
    327         // Draw a box with a notch at the left.
    328         int longOffset = halfHeight - notchLongOffset;
    329         int shortOffset = m_width - notchShortOffset;
    330         nestedBoxes(noOffset, longOffset, shortOffset, longOffset, m_bgColor, m_edgeColor);
    331         break;
    332     }
    333 
    334     case HorizontalScrollTrackForwardType: {
    335         // Draw a box with a notch at the right.
    336         int longOffset  = halfHeight - notchLongOffset;
    337         int shortOffset = m_width - notchShortOffset;
    338         nestedBoxes(shortOffset, longOffset, noOffset, longOffset, m_bgColor, m_fgColor);
    339         break;
    340     }
    341 
    342     case VerticalScrollTrackBackType: {
    343         // Draw a box with a notch at the top.
    344         int longOffset  = halfWidth - notchLongOffset;
    345         int shortOffset = m_height - notchShortOffset;
    346         nestedBoxes(longOffset, noOffset, longOffset, shortOffset, m_bgColor, m_fgColor);
    347         break;
    348     }
    349 
    350     case VerticalScrollTrackForwardType: {
    351         // Draw a box with a notch at the bottom.
    352         int longOffset  = halfWidth - notchLongOffset;
    353         int shortOffset = m_height - notchShortOffset;
    354         nestedBoxes(longOffset, shortOffset, longOffset, noOffset, m_bgColor, m_fgColor);
    355         break;
    356     }
    357 
    358     case HorizontalScrollThumbType:
    359         // Draw a narrower box on top of the outside box.
    360         nestedBoxes(thumbLongIndent, thumbShortIndent, thumbLongIndent, thumbShortIndent, m_bgColor, m_bgColor);
    361         break;
    362 
    363     case VerticalScrollThumbType:
    364         // Draw a shorter box on top of the outside box.
    365         nestedBoxes(thumbShortIndent, thumbLongIndent, thumbShortIndent, thumbLongIndent, m_bgColor, m_bgColor);
    366         break;
    367 
    368     case HorizontalSliderThumbType:
    369     case VerticalSliderThumbType:
    370         // Slider thumbs are ovals.
    371         oval(m_bgColor);
    372         break;
    373 
    374     case HorizontalScrollGripType: {
    375         // Draw a horizontal crosshatch for the grip.
    376         int longOffset = halfWidth - gripLongIndent;
    377         line(m_left + gripLongIndent, m_top + halfHeight, m_right - gripLongIndent, m_top + halfHeight, m_fgColor);
    378         line(m_left + longOffset, m_top + gripShortIndent, m_left + longOffset, m_bottom - gripShortIndent, m_fgColor);
    379         line(m_right - longOffset, m_top + gripShortIndent, m_right - longOffset, m_bottom - gripShortIndent, m_fgColor);
    380         break;
    381     }
    382 
    383     case VerticalScrollGripType: {
    384         // Draw a vertical crosshatch for the grip.
    385         int longOffset = halfHeight - gripLongIndent;
    386         line(m_left + halfWidth, m_top + gripLongIndent, m_left + halfWidth, m_bottom - gripLongIndent, m_fgColor);
    387         line(m_left + gripShortIndent, m_top + longOffset, m_right - gripShortIndent, m_top + longOffset, m_fgColor);
    388         line(m_left + gripShortIndent, m_bottom - longOffset, m_right - gripShortIndent, m_bottom - longOffset, m_fgColor);
    389         break;
    390     }
    391 
    392     case LeftArrowType:
    393         // Draw a left arrow inside a box.
    394         box(m_irect, m_bgColor);
    395         triangle(m_right - quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_bottom - quarterHeight, m_left + quarterWidth, m_top + halfHeight, m_fgColor);
    396         break;
    397 
    398     case RightArrowType:
    399         // Draw a left arrow inside a box.
    400         box(m_irect, m_bgColor);
    401         triangle(m_left + quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_top + halfHeight, m_left + quarterWidth, m_bottom - quarterHeight, m_fgColor);
    402         break;
    403 
    404     case UpArrowType:
    405         // Draw an up arrow inside a box.
    406         box(m_irect, m_bgColor);
    407         triangle(m_left + quarterWidth, m_bottom - quarterHeight, m_left + halfWidth, m_top + quarterHeight, m_right - quarterWidth, m_bottom - quarterHeight, m_fgColor);
    408         break;
    409 
    410     case DownArrowType:
    411         // Draw a down arrow inside a box.
    412         box(m_irect, m_bgColor);
    413         triangle(m_left + quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_top + quarterHeight, m_left + halfWidth, m_bottom - quarterHeight, m_fgColor);
    414         break;
    415 
    416     case HorizontalSliderTrackType: {
    417         // Draw a narrow rect for the track plus box hatches on the ends.
    418         SkIRect lirect;
    419         lirect = m_irect;
    420         lirect.inset(noOffset, halfHeight - sliderIndent);
    421         box(lirect, m_bgColor);
    422         line(m_left,  m_top, m_left,  m_bottom, m_edgeColor);
    423         line(m_right, m_top, m_right, m_bottom, m_edgeColor);
    424         break;
    425     }
    426 
    427     case VerticalSliderTrackType: {
    428         // Draw a narrow rect for the track plus box hatches on the ends.
    429         SkIRect lirect;
    430         lirect = m_irect;
    431         lirect.inset(halfWidth - sliderIndent, noOffset);
    432         box(lirect, m_bgColor);
    433         line(m_left, m_top, m_right, m_top, m_edgeColor);
    434         line(m_left, m_bottom, m_right, m_bottom, m_edgeColor);
    435         break;
    436     }
    437 
    438     case DropDownButtonType:
    439         // Draw a box with a big down arrow on top.
    440         box(m_irect, m_bgColor);
    441         triangle(m_left + quarterWidth, m_top, m_right - quarterWidth, m_top, m_left + halfWidth, m_bottom, m_fgColor);
    442         break;
    443 
    444     default:
    445         BLINK_ASSERT_NOT_REACHED();
    446         break;
    447     }
    448 
    449     markState();
    450 }
    451 
    452 // Because rendering a text field is dependent on input
    453 // parameters the other controls don't have, we render it directly
    454 // rather than trying to overcomplicate draw() further.
    455 void WebTestThemeControlWin::drawTextField(bool drawEdges, bool fillContentArea, SkColor color)
    456 {
    457     SkPaint paint;
    458 
    459     if (fillContentArea) {
    460         paint.setColor(color);
    461         paint.setStyle(SkPaint::kFill_Style);
    462         m_canvas->drawIRect(m_irect, paint);
    463     }
    464     if (drawEdges) {
    465         paint.setColor(m_edgeColor);
    466         paint.setStyle(SkPaint::kStroke_Style);
    467         m_canvas->drawIRect(m_irect, paint);
    468     }
    469 
    470     markState();
    471 }
    472 
    473 void WebTestThemeControlWin::drawProgressBar(const SkIRect& fillRect)
    474 {
    475     SkPaint paint;
    476 
    477     paint.setColor(m_bgColor);
    478     paint.setStyle(SkPaint::kFill_Style);
    479     m_canvas->drawIRect(m_irect, paint);
    480 
    481     // Emulate clipping
    482     SkIRect tofill;
    483     tofill.intersect(m_irect, fillRect);
    484     paint.setColor(m_fgColor);
    485     paint.setStyle(SkPaint::kFill_Style);
    486     m_canvas->drawIRect(tofill, paint);
    487 
    488     markState();
    489 }
    490 
    491 }
    492