Bug 418833 - Move default checkbox/radio drawing to images. Allow overriding checkbox/radio styling. r=tnikkel
☠☠ backed out by 7a2381a43ae5 ☠ ☠
authorWes Johnston <we.j@live.com>
Fri, 24 Jun 2016 09:35:14 -0700
changeset 320750 c875e87e5301a75613c22c99172efd12106b8e98
parent 320749 2e502ac7212c3b537df4003e41bf9d1a98208d6c
child 320751 872ebe45f2f6a650a22097348c99c5c268343406
push id30904
push userphilringnalda@gmail.com
push dateFri, 04 Nov 2016 02:24:17 +0000
treeherdermozilla-central@38fcc30d818f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs418833
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 418833 - Move default checkbox/radio drawing to images. Allow overriding checkbox/radio styling. r=tnikkel MozReview-Commit-ID: HqyXXuYzjrE
layout/forms/nsGfxCheckboxControlFrame.cpp
layout/forms/nsGfxCheckboxControlFrame.h
layout/forms/nsGfxRadioControlFrame.cpp
layout/forms/nsGfxRadioControlFrame.h
layout/style/jar.mn
layout/style/res/checkmark.svg
layout/style/res/forms.css
layout/style/res/indeterminate-checkmark.svg
layout/style/res/radio.svg
--- a/layout/forms/nsGfxCheckboxControlFrame.cpp
+++ b/layout/forms/nsGfxCheckboxControlFrame.cpp
@@ -13,72 +13,16 @@
 #include "nsRenderingContext.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsDisplayList.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
-static void
-PaintCheckMark(nsIFrame* aFrame,
-               DrawTarget* aDrawTarget,
-               const nsRect& aDirtyRect,
-               nsPoint aPt)
-{
-  nsRect rect(aPt, aFrame->GetSize());
-  rect.Deflate(aFrame->GetUsedBorderAndPadding());
-
-  // Points come from the coordinates on a 7X7 unit box centered at 0,0
-  const int32_t checkPolygonX[] = { -3, -1,  3,  3, -1, -3 };
-  const int32_t checkPolygonY[] = { -1,  1, -3, -1,  3,  1 };
-  const int32_t checkNumPoints = sizeof(checkPolygonX) / sizeof(int32_t);
-  const int32_t checkSize      = 9; // 2 units of padding on either side
-                                    // of the 7x7 unit checkmark
-
-  // Scale the checkmark based on the smallest dimension
-  nscoord paintScale = std::min(rect.width, rect.height) / checkSize;
-  nsPoint paintCenter(rect.x + rect.width  / 2,
-                      rect.y + rect.height / 2);
-
-  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
-  nsPoint p = paintCenter + nsPoint(checkPolygonX[0] * paintScale,
-                                    checkPolygonY[0] * paintScale);
-
-  int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
-  builder->MoveTo(NSPointToPoint(p, appUnitsPerDevPixel));
-  for (int32_t polyIndex = 1; polyIndex < checkNumPoints; polyIndex++) {
-    p = paintCenter + nsPoint(checkPolygonX[polyIndex] * paintScale,
-                              checkPolygonY[polyIndex] * paintScale);
-    builder->LineTo(NSPointToPoint(p, appUnitsPerDevPixel));
-  }
-  RefPtr<Path> path = builder->Finish();
-  aDrawTarget->Fill(path,
-                    ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
-}
-
-static void
-PaintIndeterminateMark(nsIFrame* aFrame,
-                       DrawTarget* aDrawTarget,
-                       const nsRect& aDirtyRect,
-                       nsPoint aPt)
-{
-  int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
-
-  nsRect rect(aPt, aFrame->GetSize());
-  rect.Deflate(aFrame->GetUsedBorderAndPadding());
-  rect.y += (rect.height - rect.height/4) / 2;
-  rect.height /= 4;
-
-  Rect devPxRect = NSRectToSnappedRect(rect, appUnitsPerDevPixel, *aDrawTarget);
-
-  aDrawTarget->FillRect(
-    devPxRect, ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
-}
-
 //------------------------------------------------------------
 nsIFrame*
 NS_NewGfxCheckboxControlFrame(nsIPresShell* aPresShell,
                               nsStyleContext* aContext)
 {
   return new (aPresShell) nsGfxCheckboxControlFrame(aContext);
 }
 
@@ -100,39 +44,16 @@ nsGfxCheckboxControlFrame::~nsGfxCheckbo
 a11y::AccType
 nsGfxCheckboxControlFrame::AccessibleType()
 {
   return a11y::eHTMLCheckboxType;
 }
 #endif
 
 //------------------------------------------------------------
-void
-nsGfxCheckboxControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                            const nsRect&           aDirtyRect,
-                                            const nsDisplayListSet& aLists)
-{
-  nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
-  
-  // Get current checked state through content model.
-  if ((!IsChecked() && !IsIndeterminate()) || !IsVisibleForPainting(aBuilder))
-    return;   // we're not checked or not visible, nothing to paint.
-    
-  if (IsThemed())
-    return; // No need to paint the checkmark. The theme will do it.
-
-  aLists.Content()->AppendNewToTop(new (aBuilder)
-    nsDisplayGeneric(aBuilder, this,
-                     IsIndeterminate()
-                     ? PaintIndeterminateMark : PaintCheckMark,
-                     "CheckedCheckbox",
-                     nsDisplayItem::TYPE_CHECKED_CHECKBOX));
-}
-
-//------------------------------------------------------------
 bool
 nsGfxCheckboxControlFrame::IsChecked()
 {
   nsCOMPtr<nsIDOMHTMLInputElement> elem(do_QueryInterface(mContent));
   bool retval = false;
   elem->GetChecked(&retval);
   return retval;
 }
--- a/layout/forms/nsGfxCheckboxControlFrame.h
+++ b/layout/forms/nsGfxCheckboxControlFrame.h
@@ -17,20 +17,16 @@ public:
   virtual ~nsGfxCheckboxControlFrame();
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override {
     return MakeFrameName(NS_LITERAL_STRING("CheckboxControl"), aResult);
   }
 #endif
 
-  virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                const nsRect&           aDirtyRect,
-                                const nsDisplayListSet& aLists) override;
-
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
 protected:
 
   bool IsChecked();
   bool IsIndeterminate();
--- a/layout/forms/nsGfxRadioControlFrame.cpp
+++ b/layout/forms/nsGfxRadioControlFrame.cpp
@@ -35,59 +35,8 @@ nsGfxRadioControlFrame::~nsGfxRadioContr
 
 #ifdef ACCESSIBILITY
 a11y::AccType
 nsGfxRadioControlFrame::AccessibleType()
 {
   return a11y::eHTMLRadioButtonType;
 }
 #endif
-
-//--------------------------------------------------------------
-// Draw the dot for a non-native radio button in the checked state.
-static void
-PaintCheckedRadioButton(nsIFrame* aFrame,
-                        DrawTarget* aDrawTarget,
-                        const nsRect& aDirtyRect,
-                        nsPoint aPt)
-{
-  // The dot is an ellipse 2px on all sides smaller than the content-box,
-  // drawn in the foreground color.
-  nsRect rect(aPt, aFrame->GetSize());
-  rect.Deflate(aFrame->GetUsedBorderAndPadding());
-  rect.Deflate(nsPresContext::CSSPixelsToAppUnits(2),
-               nsPresContext::CSSPixelsToAppUnits(2));
-
-  Rect devPxRect =
-    ToRect(nsLayoutUtils::RectToGfxRect(rect,
-                                        aFrame->PresContext()->AppUnitsPerDevPixel()));
-
-  ColorPattern color(ToDeviceColor(aFrame->StyleColor()->mColor));
-
-  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
-  AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
-  RefPtr<Path> ellipse = builder->Finish();
-  aDrawTarget->Fill(ellipse, color);
-}
-
-void
-nsGfxRadioControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                         const nsRect&           aDirtyRect,
-                                         const nsDisplayListSet& aLists)
-{
-  nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
-
-  if (!IsVisibleForPainting(aBuilder))
-    return;
-  
-  if (IsThemed())
-    return; // The theme will paint the check, if any.
-
-  bool checked = true;
-  GetCurrentCheckState(&checked); // Get check state from the content model
-  if (!checked)
-    return;
-    
-  aLists.Content()->AppendNewToTop(new (aBuilder)
-    nsDisplayGeneric(aBuilder, this, PaintCheckedRadioButton,
-                     "CheckedRadioButton",
-                     nsDisplayItem::TYPE_CHECKED_RADIOBUTTON));
-}
--- a/layout/forms/nsGfxRadioControlFrame.h
+++ b/layout/forms/nsGfxRadioControlFrame.h
@@ -18,15 +18,11 @@ public:
   explicit nsGfxRadioControlFrame(nsStyleContext* aContext);
   ~nsGfxRadioControlFrame();
 
   NS_DECL_FRAMEARENA_HELPERS
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
-
-  virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                const nsRect&           aDirtyRect,
-                                const nsDisplayListSet& aLists) override;
 };
 
 #endif
--- a/layout/style/jar.mn
+++ b/layout/style/jar.mn
@@ -26,10 +26,13 @@ toolkit.jar:
    res/accessiblecaret-tilt-left@1x.png      (res/accessiblecaret-tilt-left@1x.png)
    res/accessiblecaret-tilt-left@1.5x.png    (res/accessiblecaret-tilt-left@1.5x.png)
    res/accessiblecaret-tilt-left@2x.png      (res/accessiblecaret-tilt-left@2x.png)
    res/accessiblecaret-tilt-left@2.25x.png   (res/accessiblecaret-tilt-left@2.25x.png)
    res/accessiblecaret-tilt-right@1x.png     (res/accessiblecaret-tilt-right@1x.png)
    res/accessiblecaret-tilt-right@1.5x.png   (res/accessiblecaret-tilt-right@1.5x.png)
    res/accessiblecaret-tilt-right@2x.png     (res/accessiblecaret-tilt-right@2x.png)
    res/accessiblecaret-tilt-right@2.25x.png  (res/accessiblecaret-tilt-right@2.25x.png)
+   res/checkmark.svg                         (res/checkmark.svg)
+   res/indeterminate-checkmark.svg           (res/indeterminate-checkmark.svg)
+   res/radio.svg                             (res/radio.svg)
 
 % resource gre-resources %res/
new file mode 100644
--- /dev/null
+++ b/layout/style/res/checkmark.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 10 10" width="10" height="10">
+    <style type="text/css">
+        path { fill: -moz-FieldText; }
+        #disabled:target { fill: GrayText; }
+    </style>
+    <path id="disabled" d="M 2 4 L 4 6 L 8 2 L 8 4 L 4 8 L 2 6 Z"/>
+</svg>
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -566,52 +566,80 @@ input[type="checkbox"] {
 input[type="radio"],
 input[type="checkbox"] {
   box-sizing: border-box;
   inline-size: 13px;
   block-size: 13px;
   cursor: default;
   padding: 0 !important;
   -moz-binding: none;
-  /* same colors as |input| rule, but |!important| this time. */
-  background-color: -moz-Field ! important;
-  color: -moz-FieldText ! important;
-  border: 2px inset ThreeDLightShadow ! important;
+  border: 2px inset ThreeDLightShadow;
+  background-repeat: no-repeat;
+  background-position: center;
 }
 
 input[type="radio"]:disabled,
 input[type="radio"]:disabled:active,
 input[type="radio"]:disabled:hover,
 input[type="radio"]:disabled:hover:active,
 input[type="checkbox"]:disabled,
 input[type="checkbox"]:disabled:active,
 input[type="checkbox"]:disabled:hover,
 input[type="checkbox"]:disabled:hover:active {
   padding: 1px;
-  border: 1px inset ThreeDShadow ! important;
-  /* same as above, but !important */
-  color: GrayText ! important;
-  background-color: ThreeDFace ! important;
+  border: 1px inset ThreeDShadow;
   cursor: inherit;
 }
 
 % On Mac, the native theme takes care of this.
 % See nsNativeThemeCocoa::ThemeDrawsFocusForWidget.
 %ifndef XP_MACOSX
 input[type="checkbox"]:-moz-focusring,
 input[type="radio"]:-moz-focusring {
   /* Don't specify the outline-color, we should always use initial value. */
   outline: 1px dotted;
 }
 %endif
 
 input[type="checkbox"]:hover:active,
 input[type="radio"]:hover:active {
-  background-color: ThreeDFace ! important;
-  border-style: inset !important;
+  background-color: ThreeDFace;
+  border-style: inset;
+}
+
+input[type="radio"] {
+  background-size: calc(100% - 4px) calc(100% - 4px);
+}
+
+input[type="radio"]:checked {
+  background-image: url(radio.svg);
+}
+
+input[type="radio"]:disabled:checked {
+  background-image: url(radio.svg#disabled);
+}
+
+input[type="checkbox"] {
+  background-size: 100% 100%;
+}
+
+input[type="checkbox"]:checked {
+  background-image: url(checkmark.svg);
+}
+
+input[type="checkbox"]:disabled:checked {
+  background-image: url(checkmark.svg#disabled);
+}
+
+input[type="checkbox"]:indeterminate {
+  background-image: url(indeterminate-checkmark.svg);
+}
+
+input[type="checkbox"]:indeterminate:disabled {
+  background-image: url(indeterminate-checkmark.svg#disabled);
 }
 
 input[type="search"] {
   box-sizing: border-box;
 }
 
 /* buttons */
 
new file mode 100644
--- /dev/null
+++ b/layout/style/res/indeterminate-checkmark.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 5 5" width="5" height="5">
+    <style type="text/css">
+        rect { fill: -moz-FieldText; }
+        #disabled:target { fill: GrayText; }
+    </style>
+    <rect id="disabled" x="0" y="2" width="5" height="1"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/style/res/radio.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" width="20" height="20">
+    <style type="text/css">
+        circle { fill: -moz-FieldText; }
+        #disabled:target { fill: GrayText; }
+    </style>
+    <circle id="disabled" cx="10" cy="10" r="10"/>
+</svg>