Add more padding round select boxes, use different assets at
different resolutions, use new assets and don't deform the arrows
when stretching the select box.
http://b/issue?id=2506670

DO NOT MERGE, cherry pick to GingerBread
Bug: 3101402

Change-Id: I21224922db45c1cf4585667d8fa323c6a59dd240
diff --git a/WebCore/platform/android/RenderThemeAndroid.cpp b/WebCore/platform/android/RenderThemeAndroid.cpp
index b1d0431..ab93167 100644
--- a/WebCore/platform/android/RenderThemeAndroid.cpp
+++ b/WebCore/platform/android/RenderThemeAndroid.cpp
@@ -428,7 +428,10 @@
 
 static void adjustMenuListStyleCommon(RenderStyle* style, Element* e)
 {
-    // Added to make room for our arrow.
+    // Added to make room for our arrow and make the touch target less cramped.
+    style->setPaddingLeft(Length(RenderSkinCombo::padding(), Fixed));
+    style->setPaddingTop(Length(RenderSkinCombo::padding(), Fixed));
+    style->setPaddingBottom(Length(RenderSkinCombo::padding(), Fixed));
     style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed));
 }
 
diff --git a/WebKit/android/RenderSkinCombo.cpp b/WebKit/android/RenderSkinCombo.cpp
index 6f88ee3..4378371 100644
--- a/WebKit/android/RenderSkinCombo.cpp
+++ b/WebKit/android/RenderSkinCombo.cpp
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "RenderSkinCombo.h"
 
+#include "CString.h"
 #include "Document.h"
 #include "Element.h"
 #include "Node.h"
@@ -36,43 +37,85 @@
 
 namespace WebCore {
 
-static SkBitmap         s_bitmap[2];    // Collection of assets for a combo box
-static bool             s_decoded;      // True if all assets were decoded
-static const int        s_margin = 2;
-static const SkIRect    s_mar = { s_margin, s_margin,
-                                RenderSkinCombo::extraWidth(), s_margin };
-static SkIRect          s_subset;
+// Indicates if the entire asset is being drawn, or if the border is being
+// excluded and just the arrow drawn.
+enum BorderStyle {
+    FullAsset,
+    NoBorder
+};
 
+// There are 2.5 different concepts of a 'border' here, which results
+// in rather a lot of magic constants. In each case, there are 2
+// numbers, one for medium res and one for high-res. All sizes are in pixels.
 
-RenderSkinCombo::RenderSkinCombo()
+// Firstly, we have the extra padding that webkit needs to know about,
+// which defines how much bigger this element is made by the
+// asset. This is actually a bit broader than the actual border on the
+// asset, to make things look less cramped. The border is the same
+// width on all sides, except on the right when it's significantly
+// wider to allow for the arrow.
+const int RenderSkinCombo::arrowMargin[2] = {22, 34};
+const int RenderSkinCombo::padMargin[2] = {2, 5};
+
+// Then we have the borders used for the 9-patch stretch. The
+// rectangle at the centre of these borders is entirely below and to
+// the left of the arrow in the asset. Hence the border widths are the
+// same for the bottom and left, but are different for the top. The
+// right hand border width happens to be the same as arrowMargin
+// defined above.
+static const int        stretchMargin[2] = {3, 5};   // border width for the bottom and left of the 9-patch
+static const int        stretchTop[2] = {15, 23};     // border width for the top of the 9-patch
+
+// Finally, if the border is defined by the CSS, we only draw the
+// arrow and not the border. We do this by drawing the relevant subset
+// of the bitmap, which must now be precisely determined by what's in
+// the asset with no extra padding to make things look properly
+// spaced. The border to remove at the top, right and bottom of the
+// image is the same as stretchMargin above, but we need to know the width
+// of the arrow.
+static const int arrowWidth[2] = {22, 31};
+
+RenderSkinCombo::Resolution RenderSkinCombo::resolution = MedRes;
+
+const SkIRect RenderSkinCombo::margin[2][2] = {{{ stretchMargin[MedRes], stretchTop[MedRes],
+                                          RenderSkinCombo::arrowMargin[MedRes] + stretchMargin[MedRes], stretchMargin[MedRes] },
+                                        {0, stretchTop[MedRes], 0, stretchMargin[MedRes]}},
+                                       {{ stretchMargin[HighRes], stretchTop[HighRes],
+                                          RenderSkinCombo::arrowMargin[HighRes] + stretchMargin[HighRes], stretchMargin[HighRes] },
+                                        {0, stretchTop[HighRes], 0, stretchMargin[HighRes]}}};
+static SkBitmap         bitmaps[2][2]; // Collection of assets for a combo box
+static bool             isDecoded;      // True if all assets were decoded
+
+void RenderSkinCombo::Init(android::AssetManager* am, String drawableDirectory)
 {
-}
-
-void RenderSkinCombo::Init(android::AssetManager* am)
-{
-    if (s_decoded)
+    if (isDecoded)
         return;
-    // Maybe short circuiting is fine, since I don't even draw if one state is not decoded properly
-    // but is that necessary in the final version?
-    s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-noHighlight.png", &s_bitmap[kNormal]);
-    s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-disabled.png", &s_bitmap[kDisabled]) && s_decoded;
 
-    int width = s_bitmap[kNormal].width();
-    int height = s_bitmap[kNormal].height();
-    s_subset.set(width - RenderSkinCombo::extraWidth()  + s_margin, 0, width, height);
+    if (drawableDirectory[drawableDirectory.length() - 5] == 'h')
+        resolution = HighRes;
+
+    isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]);
+    isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]);
+
+    int width = bitmaps[kNormal][FullAsset].width();
+    int height = bitmaps[kNormal][FullAsset].height();
+    SkIRect  subset;
+    subset.set(width - arrowWidth[resolution], 0, width, height);
+    bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset);
+    bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset);
 }
 
 
 bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
 {
-    if (!s_decoded)
+    if (!isDecoded)
         return true;
 
     State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled;
-    if (height < (s_margin<<1) + 1) {
-        height = (s_margin<<1) + 1;
-    }
+    height = std::max(height, (stretchMargin[resolution]<<1) + 1);
+
     SkRect bounds;
+    BorderStyle drawBorder = FullAsset;
 
     bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1));
     RenderStyle* style = element->renderStyle();
@@ -90,10 +133,9 @@
         bounds.fRight -= SkIntToScalar(style->borderRightWidth());
         bounds.fTop += SkIntToScalar(style->borderTopWidth());
         bounds.fBottom -= SkIntToScalar(style->borderBottomWidth());
-        canvas->drawBitmapRect(s_bitmap[state], &s_subset, bounds);
-    } else {
-        SkNinePatch::DrawNine(canvas, bounds, s_bitmap[state], s_mar);
+        drawBorder = NoBorder;
     }
+    SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[resolution][drawBorder]);
     return false;
 }
 
diff --git a/WebKit/android/RenderSkinCombo.h b/WebKit/android/RenderSkinCombo.h
index 91c9367..38cd048 100644
--- a/WebKit/android/RenderSkinCombo.h
+++ b/WebKit/android/RenderSkinCombo.h
@@ -37,13 +37,10 @@
 class RenderSkinCombo : public RenderSkinAndroid
 {
 public:
-    RenderSkinCombo();
-    virtual ~RenderSkinCombo() {}
-
     /**
      * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use.
      */
-    static void Init(android::AssetManager*);
+    static void Init(android::AssetManager*, String drawableDirectory);
 
     /**
      * Draw the provided Node on the SkCanvas, using the dimensions provided by
@@ -53,11 +50,18 @@
     static bool Draw(SkCanvas* , Node* , int x, int y, int w, int h);
 
     // The image is wider than the RenderObject, so this accounts for that.
-    static int extraWidth() { return arrowMargin; }
-    
+    static int extraWidth() { return arrowMargin[resolution]; }
+    static int padding() { return padMargin[resolution]; }
+
+    enum Resolution {
+        MedRes,
+        HighRes
+    };
 private:
-    
-    static const int arrowMargin = 22;
+    static Resolution resolution;
+    const static int arrowMargin[2];
+    const static int padMargin[2];
+    const static SkIRect margin[2][2];
 }; 
 
 } // WebCore