1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsNativeBasicTheme.h"
7 
8 #include "mozilla/StaticPrefs_layout.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "nsComboboxControlFrame.h"
11 #include "nsCSSRendering.h"
12 #include "nsDateTimeControlFrame.h"
13 #include "nsDeviceContext.h"
14 #include "PathHelpers.h"
15 
16 using namespace mozilla;
17 using namespace mozilla::gfx;
18 using namespace mozilla::widget;
19 
20 namespace mozilla {
21 namespace widget {
22 
23 static const sRGBColor sBackgroundColor(sRGBColor(1.0f, 1.0f, 1.0f));
24 static const sRGBColor sBackgroundActiveColor(sRGBColor(0.88f, 0.88f, 0.9f));
25 static const sRGBColor sBackgroundActiveColorDisabled(sRGBColor(0.88f, 0.88f,
26                                                                 0.9f, 0.4f));
27 static const sRGBColor sBorderColor(sRGBColor(0.62f, 0.62f, 0.68f));
28 static const sRGBColor sBorderColorDisabled(sRGBColor(0.44f, 0.44f, 0.44f,
29                                                       0.4f));
30 static const sRGBColor sBorderHoverColor(sRGBColor(0.5f, 0.5f, 0.56f));
31 static const sRGBColor sBorderHoverColorDisabled(sRGBColor(0.5f, 0.5f, 0.56f,
32                                                            0.4f));
33 static const sRGBColor sBorderFocusColor(sRGBColor(0.04f, 0.52f, 1.0f));
34 static const sRGBColor sCheckBackgroundColor(sRGBColor(0.18f, 0.39f, 0.89f));
35 static const sRGBColor sCheckBackgroundColorDisabled(sRGBColor(0.18f, 0.39f,
36                                                                0.89f, 0.4f));
37 static const sRGBColor sCheckBackgroundHoverColor(sRGBColor(0.02f, 0.24f,
38                                                             0.58f));
39 static const sRGBColor sCheckBackgroundHoverColorDisabled(
40     sRGBColor(0.02f, 0.24f, 0.58f, 0.4f));
41 static const sRGBColor sCheckBackgroundActiveColor(sRGBColor(0.03f, 0.19f,
42                                                              0.45f));
43 static const sRGBColor sCheckBackgroundActiveColorDisabled(
44     sRGBColor(0.03f, 0.19f, 0.45f, 0.4f));
45 static const sRGBColor sDisabledColor(sRGBColor(0.89f, 0.89f, 0.89f));
46 static const sRGBColor sActiveColor(sRGBColor(0.47f, 0.47f, 0.48f));
47 static const sRGBColor sInputHoverColor(sRGBColor(0.05f, 0.05f, 0.05f, 0.5f));
48 static const sRGBColor sRangeInputBackgroundColor(sRGBColor(0.89f, 0.89f,
49                                                             0.89f));
50 static const sRGBColor sScrollbarColor(sRGBColor(0.94f, 0.94f, 0.94f));
51 static const sRGBColor sScrollbarBorderColor(sRGBColor(1.0f, 1.0f, 1.0f));
52 static const sRGBColor sScrollbarThumbColor(sRGBColor(0.8f, 0.8f, 0.8f));
53 static const sRGBColor sScrollbarThumbColorActive(sRGBColor(0.375f, 0.375f,
54                                                             0.375f));
55 static const sRGBColor sScrollbarThumbColorHover(sRGBColor(0.65f, 0.65f,
56                                                            0.65f));
57 static const sRGBColor sScrollbarArrowColor(sRGBColor(0.375f, 0.375f, 0.375f));
58 static const sRGBColor sScrollbarArrowColorActive(sRGBColor(1.0f, 1.0f, 1.0f));
59 static const sRGBColor sScrollbarArrowColorHover(sRGBColor(0.0f, 0.0f, 0.0f));
60 static const sRGBColor sScrollbarButtonColor(sScrollbarColor);
61 static const sRGBColor sScrollbarButtonActiveColor(sRGBColor(0.375f, 0.375f,
62                                                              0.375f));
63 static const sRGBColor sScrollbarButtonHoverColor(sRGBColor(0.86f, 0.86f,
64                                                             0.86f));
65 static const sRGBColor sButtonColor(sRGBColor(0.98f, 0.98f, 0.98f));
66 static const sRGBColor sButtonHoverColor(sRGBColor(0.94f, 0.94f, 0.96f));
67 static const sRGBColor sButtonActiveColor(sRGBColor(0.88f, 0.88f, 0.90f));
68 static const sRGBColor sWhiteColor(sRGBColor(1.0f, 1.0f, 1.0f, 0.0f));
69 
70 static const CSSIntCoord kMinimumWidgetSize = 17;
71 static const CSSCoord kButtonBorderWidth = 1.0f;
72 static const CSSCoord kMenulistBorderWidth = 1.0f;
73 static const CSSCoord kTextFieldBorderWidth = 1.0f;
74 
75 }  // namespace widget
76 }  // namespace mozilla
77 
NS_IMPL_ISUPPORTS_INHERITED(nsNativeBasicTheme,nsNativeTheme,nsITheme)78 NS_IMPL_ISUPPORTS_INHERITED(nsNativeBasicTheme, nsNativeTheme, nsITheme)
79 
80 static uint32_t GetDPIRatio(nsIFrame* aFrame) {
81   return AppUnitsPerCSSPixel() / aFrame->PresContext()
82                                      ->DeviceContext()
83                                      ->AppUnitsPerDevPixelAtUnitFullZoom();
84 }
85 
IsDateTimeResetButton(nsIFrame * aFrame)86 static bool IsDateTimeResetButton(nsIFrame* aFrame) {
87   nsIFrame* parent = aFrame->GetParent();
88   if (parent && (parent = parent->GetParent()) &&
89       (parent = parent->GetParent())) {
90     nsDateTimeControlFrame* dateTimeFrame = do_QueryFrame(parent);
91     if (dateTimeFrame) {
92       return true;
93     }
94   }
95   return false;
96 }
97 
IsDateTimeTextField(nsIFrame * aFrame)98 static bool IsDateTimeTextField(nsIFrame* aFrame) {
99   nsDateTimeControlFrame* dateTimeFrame = do_QueryFrame(aFrame);
100   return dateTimeFrame;
101 }
102 
ComputeCheckColors(const EventStates & aState,sRGBColor & aBackgroundColor,sRGBColor & aBorderColor)103 static void ComputeCheckColors(const EventStates& aState,
104                                sRGBColor& aBackgroundColor,
105                                sRGBColor& aBorderColor) {
106   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
107   bool isPressed = !isDisabled && aState.HasAllStates(NS_EVENT_STATE_HOVER |
108                                                       NS_EVENT_STATE_ACTIVE);
109   bool isHovered = !isDisabled && aState.HasState(NS_EVENT_STATE_HOVER);
110   bool isFocused = aState.HasState(NS_EVENT_STATE_FOCUS);
111   bool isChecked = aState.HasState(NS_EVENT_STATE_CHECKED);
112 
113   sRGBColor fillColor = sBackgroundColor;
114   sRGBColor borderColor = sBorderColor;
115   if (isDisabled) {
116     if (isChecked) {
117       fillColor = borderColor = sCheckBackgroundColorDisabled;
118     } else {
119       fillColor = sBackgroundColor;
120       borderColor = sBorderColorDisabled;
121     }
122   } else {
123     if (isChecked) {
124       if (isPressed) {
125         fillColor = borderColor = sCheckBackgroundActiveColor;
126       } else if (isHovered) {
127         fillColor = borderColor = sCheckBackgroundHoverColor;
128       } else {
129         fillColor = borderColor = sCheckBackgroundColor;
130       }
131     } else if (isPressed) {
132       fillColor = sBackgroundActiveColor;
133       borderColor = sBorderHoverColor;
134     } else if (isFocused) {
135       fillColor = sBackgroundActiveColor;
136       borderColor = sBorderFocusColor;
137     } else if (isHovered) {
138       fillColor = sBackgroundColor;
139       borderColor = sBorderHoverColor;
140     } else {
141       fillColor = sBackgroundColor;
142       borderColor = sBorderColor;
143     }
144   }
145 
146   aBackgroundColor = fillColor;
147   aBorderColor = borderColor;
148 }
149 
150 // Checkbox and radio need to preserve aspect-ratio for compat.
FixAspectRatio(const Rect & aRect)151 static Rect FixAspectRatio(const Rect& aRect) {
152   Rect rect(aRect);
153   if (rect.width == rect.height) {
154     return rect;
155   }
156 
157   if (rect.width > rect.height) {
158     auto diff = rect.width - rect.height;
159     rect.width = rect.height;
160     rect.x += diff / 2;
161   } else {
162     auto diff = rect.height - rect.width;
163     rect.height = rect.width;
164     rect.y += diff / 2;
165   }
166 
167   return rect;
168 }
169 
170 // This pushes and pops a clip rect to the draw target.
171 //
172 // This is done to reduce fuzz in places where we may have antialiasing, because
173 // skia is not clip-invariant: given different clips, it does not guarantee the
174 // same result, even if the painted content doesn't intersect the clips.
175 //
176 // This is a bit sad, overall, but...
177 struct MOZ_RAII AutoClipRect {
AutoClipRectAutoClipRect178   AutoClipRect(DrawTarget& aDt, const Rect& aRect) : mDt(aDt) {
179     mDt.PushClipRect(aRect);
180   }
181 
~AutoClipRectAutoClipRect182   ~AutoClipRect() { mDt.PopClip(); }
183 
184  private:
185   DrawTarget& mDt;
186 };
187 
PaintRoundedRectWithBorder(DrawTarget * aDrawTarget,const Rect & aRect,const sRGBColor & aBackgroundColor,const sRGBColor & aBorderColor,CSSCoord aBorderWidth,CSSCoord aRadius,uint32_t aDpi)188 static void PaintRoundedRectWithBorder(DrawTarget* aDrawTarget,
189                                        const Rect& aRect,
190                                        const sRGBColor& aBackgroundColor,
191                                        const sRGBColor& aBorderColor,
192                                        CSSCoord aBorderWidth, CSSCoord aRadius,
193                                        uint32_t aDpi) {
194   const LayoutDeviceCoord borderWidth(aBorderWidth * aDpi);
195   const LayoutDeviceCoord radius(aRadius * aDpi);
196 
197   Rect rect(aRect);
198   // Deflate the rect by half the border width, so that the middle of the stroke
199   // fills exactly the area we want to fill and not more.
200   rect.Deflate(borderWidth * 0.5f);
201 
202   RectCornerRadii radii(radius, radius, radius, radius);
203   RefPtr<Path> roundedRect = MakePathForRoundedRect(*aDrawTarget, rect, radii);
204 
205   aDrawTarget->Fill(roundedRect, ColorPattern(ToDeviceColor(aBackgroundColor)));
206   aDrawTarget->Stroke(roundedRect, ColorPattern(ToDeviceColor(aBorderColor)),
207                       StrokeOptions(borderWidth));
208 }
209 
PaintCheckboxControl(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)210 static void PaintCheckboxControl(DrawTarget* aDrawTarget, const Rect& aRect,
211                                  const EventStates& aState, uint32_t aDpi) {
212   const CSSCoord kBorderWidth = 2.0f;
213   const CSSCoord kRadius = 4.0f;
214 
215   sRGBColor backgroundColor;
216   sRGBColor borderColor;
217   ComputeCheckColors(aState, backgroundColor, borderColor);
218   PaintRoundedRectWithBorder(aDrawTarget, aRect, backgroundColor, borderColor,
219                              kBorderWidth, kRadius, aDpi);
220 }
221 
PaintCheckMark(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)222 static void PaintCheckMark(DrawTarget* aDrawTarget, const Rect& aRect,
223                            const EventStates& aState, uint32_t aDpi) {
224   // Points come from the coordinates on a 7X7 unit box centered at 0,0
225   const float checkPolygonX[] = {-2.5, -0.7, 2.5};
226   const float checkPolygonY[] = {-0.3, 1.7, -1.5};
227   const int32_t checkNumPoints = sizeof(checkPolygonX) / sizeof(float);
228   const int32_t checkSize = 8;
229 
230   auto center = aRect.Center();
231 
232   // Scale the checkmark based on the smallest dimension
233   nscoord paintScale = std::min(aRect.width, aRect.height) / checkSize;
234   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
235   Point p = center +
236             Point(checkPolygonX[0] * paintScale, checkPolygonY[0] * paintScale);
237   builder->MoveTo(p);
238   for (int32_t polyIndex = 1; polyIndex < checkNumPoints; polyIndex++) {
239     p = center + Point(checkPolygonX[polyIndex] * paintScale,
240                        checkPolygonY[polyIndex] * paintScale);
241     builder->LineTo(p);
242   }
243   RefPtr<Path> path = builder->Finish();
244   aDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(sBackgroundColor)),
245                       StrokeOptions(2.0f * aDpi));
246 }
247 
PaintIndeterminateMark(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState)248 static void PaintIndeterminateMark(DrawTarget* aDrawTarget, const Rect& aRect,
249                                    const EventStates& aState) {
250   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
251 
252   Rect rect(aRect);
253   rect.y += (rect.height - rect.height / 4) / 2;
254   rect.height /= 4;
255 
256   aDrawTarget->FillRect(
257       rect, ColorPattern(
258                 ToDeviceColor(isDisabled ? sDisabledColor : sBackgroundColor)));
259 }
260 
PaintStrokedEllipse(DrawTarget * aDrawTarget,const Rect & aRect,const sRGBColor & aBackgroundColor,const sRGBColor & aBorderColor,const CSSCoord aBorderWidth,uint32_t aDpi)261 static void PaintStrokedEllipse(DrawTarget* aDrawTarget, const Rect& aRect,
262                                 const sRGBColor& aBackgroundColor,
263                                 const sRGBColor& aBorderColor,
264                                 const CSSCoord aBorderWidth, uint32_t aDpi) {
265   const LayoutDeviceCoord borderWidth(aBorderWidth * aDpi);
266   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
267 
268   // Deflate for the same reason as PaintRoundedRectWithBorder. Note that the
269   // size is the diameter, so we just shrink by the border width once.
270   Size size(aRect.Size() - Size(borderWidth, borderWidth));
271   AppendEllipseToPath(builder, aRect.Center(), size);
272   RefPtr<Path> ellipse = builder->Finish();
273 
274   aDrawTarget->Fill(ellipse, ColorPattern(ToDeviceColor(aBackgroundColor)));
275   aDrawTarget->Stroke(ellipse, ColorPattern(ToDeviceColor(aBorderColor)),
276                       StrokeOptions(borderWidth));
277 }
278 
PaintRadioControl(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)279 static void PaintRadioControl(DrawTarget* aDrawTarget, const Rect& aRect,
280                               const EventStates& aState, uint32_t aDpi) {
281   const CSSCoord kBorderWidth = 2.0f;
282 
283   sRGBColor backgroundColor;
284   sRGBColor borderColor;
285   ComputeCheckColors(aState, backgroundColor, borderColor);
286 
287   PaintStrokedEllipse(aDrawTarget, aRect, backgroundColor, borderColor,
288                       kBorderWidth, aDpi);
289 }
290 
PaintCheckedRadioButton(DrawTarget * aDrawTarget,const Rect & aRect,uint32_t aDpi)291 static void PaintCheckedRadioButton(DrawTarget* aDrawTarget, const Rect& aRect,
292                                     uint32_t aDpi) {
293   Rect rect(aRect);
294   rect.x += 4.5f * aDpi;
295   rect.width -= 9.0f * aDpi;
296   rect.y += 4.5f * aDpi;
297   rect.height -= 9.0f * aDpi;
298 
299   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
300   AppendEllipseToPath(builder, rect.Center(), rect.Size());
301   RefPtr<Path> ellipse = builder->Finish();
302   aDrawTarget->Fill(ellipse, ColorPattern(ToDeviceColor(sBackgroundColor)));
303 }
304 
ComputeBorderColor(const EventStates & aState)305 static sRGBColor ComputeBorderColor(const EventStates& aState) {
306   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
307   bool isHovered = !isDisabled && aState.HasState(NS_EVENT_STATE_HOVER);
308   bool isFocused = aState.HasState(NS_EVENT_STATE_FOCUS);
309   if (isFocused) {
310     return sBorderFocusColor;
311   }
312   if (isHovered) {
313     return sBorderHoverColor;
314   }
315   return sBorderColor;
316 }
317 
PaintTextField(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)318 static void PaintTextField(DrawTarget* aDrawTarget, const Rect& aRect,
319                            const EventStates& aState, uint32_t aDpi) {
320   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
321   const sRGBColor& backgroundColor =
322       isDisabled ? sDisabledColor : sBackgroundColor;
323   const sRGBColor borderColor = ComputeBorderColor(aState);
324 
325   const CSSCoord kRadius = 4.0f;
326 
327   PaintRoundedRectWithBorder(aDrawTarget, aRect, backgroundColor, borderColor,
328                              kTextFieldBorderWidth, kRadius, aDpi);
329 }
330 
ComputeButtonColors(const EventStates & aState,bool aIsDatetimeResetButton=false)331 std::pair<sRGBColor, sRGBColor> ComputeButtonColors(
332     const EventStates& aState, bool aIsDatetimeResetButton = false) {
333   bool isActive =
334       aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
335   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
336   bool isHovered = !isDisabled && aState.HasState(NS_EVENT_STATE_HOVER);
337 
338   const sRGBColor& backgroundColor = [&] {
339     if (isDisabled) {
340       return sDisabledColor;
341     }
342     if (aIsDatetimeResetButton) {
343       return sWhiteColor;
344     }
345     if (isActive) {
346       return sButtonActiveColor;
347     }
348     if (isHovered) {
349       return sButtonHoverColor;
350     }
351     return sButtonColor;
352   }();
353 
354   const sRGBColor borderColor = ComputeBorderColor(aState);
355 
356   return std::make_pair(backgroundColor, borderColor);
357 }
358 
PaintMenulist(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)359 static void PaintMenulist(DrawTarget* aDrawTarget, const Rect& aRect,
360                           const EventStates& aState, uint32_t aDpi) {
361   const CSSCoord kRadius = 4.0f;
362 
363   sRGBColor backgroundColor, borderColor;
364   std::tie(backgroundColor, borderColor) = ComputeButtonColors(aState);
365 
366   PaintRoundedRectWithBorder(aDrawTarget, aRect, backgroundColor, borderColor,
367                              kMenulistBorderWidth, kRadius, aDpi);
368 }
369 
PaintArrow(DrawTarget * aDrawTarget,const Rect & aRect,const int32_t aArrowPolygonX[],const int32_t aArrowPolygonY[],const int32_t aArrowNumPoints,const int32_t aArrowSize,const sRGBColor aFillColor,uint32_t aDpi)370 static void PaintArrow(DrawTarget* aDrawTarget, const Rect& aRect,
371                        const int32_t aArrowPolygonX[],
372                        const int32_t aArrowPolygonY[],
373                        const int32_t aArrowNumPoints, const int32_t aArrowSize,
374                        const sRGBColor aFillColor, uint32_t aDpi) {
375   nscoord paintScale = std::min(aRect.width, aRect.height) / aArrowSize;
376   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
377   Point p = aRect.Center() + Point(aArrowPolygonX[0] * paintScale,
378                                    aArrowPolygonY[0] * paintScale);
379 
380   builder->MoveTo(p);
381   for (int32_t polyIndex = 1; polyIndex < aArrowNumPoints; polyIndex++) {
382     p = aRect.Center() + Point(aArrowPolygonX[polyIndex] * paintScale,
383                                aArrowPolygonY[polyIndex] * paintScale);
384     builder->LineTo(p);
385   }
386   RefPtr<Path> path = builder->Finish();
387 
388   aDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(aFillColor)),
389                       StrokeOptions(2.0f * aDpi));
390 }
391 
PaintMenulistArrowButton(nsIFrame * aFrame,DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)392 static void PaintMenulistArrowButton(nsIFrame* aFrame, DrawTarget* aDrawTarget,
393                                      const Rect& aRect,
394                                      const EventStates& aState, uint32_t aDpi) {
395   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
396   bool isPressed = !isDisabled && aState.HasAllStates(NS_EVENT_STATE_HOVER |
397                                                       NS_EVENT_STATE_ACTIVE);
398   bool isHovered = !isDisabled && aState.HasState(NS_EVENT_STATE_HOVER);
399   bool isHTML = nsNativeTheme::IsHTMLContent(aFrame);
400 
401   if (!isHTML && nsNativeTheme::CheckBooleanAttr(aFrame, nsGkAtoms::open)) {
402     isHovered = false;
403   }
404 
405   const int32_t arrowSize = 8;
406   int32_t arrowPolygonX[] = {-4, -2, 0};
407   int32_t arrowPolygonY[] = {-1, 1, -1};
408   const int32_t arrowNumPoints = sizeof(arrowPolygonX) / sizeof(int32_t);
409 
410   PaintArrow(
411       aDrawTarget, aRect, arrowPolygonX, arrowPolygonY, arrowNumPoints,
412       arrowSize,
413       isPressed ? sActiveColor : isHovered ? sBorderHoverColor : sBorderColor,
414       aDpi);
415 }
416 
PaintSpinnerButton(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,StyleAppearance aAppearance,uint32_t aDpi)417 static void PaintSpinnerButton(DrawTarget* aDrawTarget, const Rect& aRect,
418                                const EventStates& aState,
419                                StyleAppearance aAppearance, uint32_t aDpi) {
420   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
421   bool isPressed = !isDisabled && aState.HasAllStates(NS_EVENT_STATE_HOVER |
422                                                       NS_EVENT_STATE_ACTIVE);
423   bool isHovered = !isDisabled && aState.HasState(NS_EVENT_STATE_HOVER);
424 
425   const int32_t arrowSize = 8;
426   int32_t arrowPolygonX[] = {0, 2, 4};
427   int32_t arrowPolygonY[] = {-3, -1, -3};
428   const int32_t arrowNumPoints = sizeof(arrowPolygonX) / sizeof(int32_t);
429 
430   if (aAppearance == StyleAppearance::SpinnerUpbutton) {
431     for (int32_t i = 0; i < arrowNumPoints; i++) {
432       arrowPolygonY[i] *= -1;
433     }
434   }
435 
436   PaintArrow(
437       aDrawTarget, aRect, arrowPolygonX, arrowPolygonY, arrowNumPoints,
438       arrowSize,
439       isPressed ? sActiveColor : isHovered ? sBorderHoverColor : sBorderColor,
440       aDpi);
441 }
442 
PaintRangeInputBackground(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi,bool aHorizontal)443 static void PaintRangeInputBackground(DrawTarget* aDrawTarget,
444                                       const Rect& aRect,
445                                       const EventStates& aState, uint32_t aDpi,
446                                       bool aHorizontal) {
447   Rect rect(aRect);
448   const LayoutDeviceCoord kVerticalSize = kMinimumWidgetSize * 0.25f * aDpi;
449 
450   if (aHorizontal) {
451     rect.y += (rect.height - kVerticalSize) / 2;
452     rect.height = kVerticalSize;
453   } else {
454     rect.x += (rect.width - kVerticalSize) / 2;
455     rect.width = kVerticalSize;
456   }
457 
458   aDrawTarget->FillRect(
459       rect, ColorPattern(ToDeviceColor(sRangeInputBackgroundColor)));
460   aDrawTarget->StrokeRect(rect,
461                           ColorPattern(ToDeviceColor(sButtonActiveColor)));
462 }
463 
PaintScrollbarthumbHorizontal(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState)464 static void PaintScrollbarthumbHorizontal(DrawTarget* aDrawTarget,
465                                           const Rect& aRect,
466                                           const EventStates& aState) {
467   sRGBColor thumbColor = sScrollbarThumbColor;
468   if (aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE)) {
469     thumbColor = sScrollbarThumbColorActive;
470   } else if (aState.HasState(NS_EVENT_STATE_HOVER)) {
471     thumbColor = sScrollbarThumbColorHover;
472   }
473   aDrawTarget->FillRect(aRect, ColorPattern(ToDeviceColor(thumbColor)));
474 }
475 
PaintScrollbarthumbVertical(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState)476 static void PaintScrollbarthumbVertical(DrawTarget* aDrawTarget,
477                                         const Rect& aRect,
478                                         const EventStates& aState) {
479   sRGBColor thumbColor = sScrollbarThumbColor;
480   if (aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE)) {
481     thumbColor = sScrollbarThumbColorActive;
482   } else if (aState.HasState(NS_EVENT_STATE_HOVER)) {
483     thumbColor = sScrollbarThumbColorHover;
484   }
485   aDrawTarget->FillRect(aRect, ColorPattern(ToDeviceColor(thumbColor)));
486 }
487 
PaintScrollbarHorizontal(DrawTarget * aDrawTarget,const Rect & aRect)488 static void PaintScrollbarHorizontal(DrawTarget* aDrawTarget,
489                                      const Rect& aRect) {
490   aDrawTarget->FillRect(aRect, ColorPattern(ToDeviceColor(sScrollbarColor)));
491   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
492   builder->MoveTo(Point(aRect.x, aRect.y));
493   builder->LineTo(Point(aRect.x + aRect.width, aRect.y));
494   RefPtr<Path> path = builder->Finish();
495   aDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(sScrollbarBorderColor)));
496 }
497 
PaintScrollbarVerticalAndCorner(DrawTarget * aDrawTarget,const Rect & aRect,uint32_t aDpi)498 static void PaintScrollbarVerticalAndCorner(DrawTarget* aDrawTarget,
499                                             const Rect& aRect, uint32_t aDpi) {
500   aDrawTarget->FillRect(aRect, ColorPattern(ToDeviceColor(sScrollbarColor)));
501   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
502   builder->MoveTo(Point(aRect.x, aRect.y));
503   builder->LineTo(Point(aRect.x, aRect.y + aRect.height));
504   RefPtr<Path> path = builder->Finish();
505   aDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(sScrollbarBorderColor)),
506                       StrokeOptions(1.0f * aDpi));
507 }
508 
PaintScrollbarbutton(DrawTarget * aDrawTarget,StyleAppearance aAppearance,const Rect & aRect,const EventStates & aState,uint32_t aDpi)509 static void PaintScrollbarbutton(DrawTarget* aDrawTarget,
510                                  StyleAppearance aAppearance, const Rect& aRect,
511                                  const EventStates& aState, uint32_t aDpi) {
512   bool isActive =
513       aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
514   bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
515 
516   aDrawTarget->FillRect(
517       aRect, ColorPattern(
518                  ToDeviceColor(isActive ? sScrollbarButtonActiveColor
519                                         : isHovered ? sScrollbarButtonHoverColor
520                                                     : sScrollbarColor)));
521 
522   // Start with Up arrow.
523   int32_t arrowPolygonX[] = {3, 0, -3};
524   int32_t arrowPolygonY[] = {2, -1, 2};
525   const int32_t arrowNumPoints = sizeof(arrowPolygonX) / sizeof(int32_t);
526   const int32_t arrowSize = 14;
527 
528   switch (aAppearance) {
529     case StyleAppearance::ScrollbarbuttonUp:
530       break;
531     case StyleAppearance::ScrollbarbuttonDown:
532       for (int32_t i = 0; i < arrowNumPoints; i++) {
533         arrowPolygonY[i] *= -1;
534       }
535       break;
536     case StyleAppearance::ScrollbarbuttonLeft:
537       for (int32_t i = 0; i < arrowNumPoints; i++) {
538         int32_t temp = arrowPolygonX[i];
539         arrowPolygonX[i] = arrowPolygonY[i];
540         arrowPolygonY[i] = temp;
541       }
542       break;
543     case StyleAppearance::ScrollbarbuttonRight:
544       for (int32_t i = 0; i < arrowNumPoints; i++) {
545         int32_t temp = arrowPolygonX[i];
546         arrowPolygonX[i] = arrowPolygonY[i] * -1;
547         arrowPolygonY[i] = temp;
548       }
549       break;
550     default:
551       return;
552   }
553 
554   PaintArrow(aDrawTarget, aRect, arrowPolygonX, arrowPolygonY, arrowNumPoints,
555              arrowSize,
556              isActive
557                  ? sScrollbarArrowColorActive
558                  : isHovered ? sScrollbarArrowColorHover : sScrollbarArrowColor,
559              aDpi);
560 
561   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
562   builder->MoveTo(Point(aRect.x, aRect.y));
563   if (aAppearance == StyleAppearance::ScrollbarbuttonUp ||
564       aAppearance == StyleAppearance::ScrollbarbuttonDown) {
565     builder->LineTo(Point(aRect.x, aRect.y + aRect.height));
566   } else {
567     builder->LineTo(Point(aRect.x + aRect.width, aRect.y));
568   }
569 
570   RefPtr<Path> path = builder->Finish();
571   aDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(sScrollbarBorderColor)),
572                       StrokeOptions(1.0f * aDpi));
573 }
574 
PaintButton(nsIFrame * aFrame,DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)575 static void PaintButton(nsIFrame* aFrame, DrawTarget* aDrawTarget,
576                         const Rect& aRect, const EventStates& aState,
577                         uint32_t aDpi) {
578   const CSSCoord kRadius = 4.0f;
579 
580   // FIXME: The DateTimeResetButton bit feels like a bit of a hack.
581   sRGBColor backgroundColor, borderColor;
582   std::tie(backgroundColor, borderColor) =
583       ComputeButtonColors(aState, IsDateTimeResetButton(aFrame));
584 
585   PaintRoundedRectWithBorder(aDrawTarget, aRect, backgroundColor, borderColor,
586                              kButtonBorderWidth, kRadius, aDpi);
587 }
588 
PaintRangeThumb(DrawTarget * aDrawTarget,const Rect & aRect,const EventStates & aState,uint32_t aDpi)589 static void PaintRangeThumb(DrawTarget* aDrawTarget, const Rect& aRect,
590                             const EventStates& aState, uint32_t aDpi) {
591   const CSSCoord kBorderWidth = 2.0f;
592 
593   sRGBColor backgroundColor, borderColor;
594   std::tie(backgroundColor, borderColor) = ComputeButtonColors(aState);
595 
596   PaintStrokedEllipse(aDrawTarget, aRect, backgroundColor, borderColor,
597                       kBorderWidth, aDpi);
598 }
599 
600 NS_IMETHODIMP
DrawWidgetBackground(gfxContext * aContext,nsIFrame * aFrame,StyleAppearance aAppearance,const nsRect & aRect,const nsRect &)601 nsNativeBasicTheme::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
602                                          StyleAppearance aAppearance,
603                                          const nsRect& aRect,
604                                          const nsRect& /* aDirtyRect */) {
605   DrawTarget* dt = aContext->GetDrawTarget();
606   const nscoord twipsPerPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
607   EventStates eventState = GetContentState(aFrame, aAppearance);
608 
609   Rect devPxRect = NSRectToSnappedRect(aRect, twipsPerPixel, *dt);
610   AutoClipRect clip(*dt, devPxRect);
611 
612   if (aAppearance == StyleAppearance::MozMenulistArrowButton) {
613     bool isHTML = IsHTMLContent(aFrame);
614     nsIFrame* parentFrame = aFrame->GetParent();
615     bool isMenulist = !isHTML && parentFrame->IsMenuFrame();
616     // HTML select and XUL menulist dropdown buttons get state from the
617     // parent.
618     if (isHTML || isMenulist) {
619       aFrame = parentFrame;
620       eventState = GetContentState(parentFrame, aAppearance);
621     }
622   }
623 
624   uint32_t dpi = GetDPIRatio(aFrame);
625 
626   switch (aAppearance) {
627     case StyleAppearance::Radio: {
628       auto rect = FixAspectRatio(devPxRect);
629       PaintRadioControl(dt, rect, eventState, dpi);
630       if (IsSelected(aFrame)) {
631         PaintCheckedRadioButton(dt, rect, dpi);
632       }
633       break;
634     }
635     case StyleAppearance::Checkbox: {
636       auto rect = FixAspectRatio(devPxRect);
637       PaintCheckboxControl(dt, rect, eventState, dpi);
638       if (IsChecked(aFrame)) {
639         PaintCheckMark(dt, rect, eventState, dpi);
640       }
641       if (GetIndeterminate(aFrame)) {
642         PaintIndeterminateMark(dt, rect, eventState);
643       }
644       break;
645     }
646     case StyleAppearance::Textarea:
647     case StyleAppearance::Textfield:
648     case StyleAppearance::NumberInput:
649       PaintTextField(dt, devPxRect, eventState, dpi);
650       break;
651     case StyleAppearance::Listbox:
652     case StyleAppearance::Menulist:
653     case StyleAppearance::MenulistButton:
654     case StyleAppearance::MenulistTextfield:
655       PaintMenulist(dt, devPxRect, eventState, dpi);
656       break;
657     case StyleAppearance::MozMenulistArrowButton:
658       PaintMenulistArrowButton(aFrame, dt, devPxRect, eventState, dpi);
659       break;
660     case StyleAppearance::SpinnerUpbutton:
661     case StyleAppearance::SpinnerDownbutton:
662       PaintSpinnerButton(dt, devPxRect, eventState, aAppearance, dpi);
663       break;
664     case StyleAppearance::Range:
665       PaintRangeInputBackground(dt, devPxRect, eventState, dpi,
666                                 IsRangeHorizontal(aFrame));
667       break;
668     case StyleAppearance::RangeThumb:
669       // TODO(emilio): Do we want to enforce it being a circle using
670       // FixAspectRatio here? For now let authors tweak, it's a custom pseudo so
671       // it doesn't probably have much compat impact if at all.
672       PaintRangeThumb(dt, devPxRect, eventState, dpi);
673       break;
674     case StyleAppearance::ScrollbarthumbHorizontal:
675       PaintScrollbarthumbHorizontal(dt, devPxRect, eventState);
676       break;
677     case StyleAppearance::ScrollbarthumbVertical:
678       PaintScrollbarthumbVertical(dt, devPxRect, eventState);
679       break;
680     case StyleAppearance::ScrollbarHorizontal:
681       PaintScrollbarHorizontal(dt, devPxRect);
682       break;
683     case StyleAppearance::ScrollbarVertical:
684     case StyleAppearance::Scrollcorner:
685       PaintScrollbarVerticalAndCorner(dt, devPxRect, dpi);
686       break;
687     case StyleAppearance::ScrollbarbuttonUp:
688     case StyleAppearance::ScrollbarbuttonDown:
689     case StyleAppearance::ScrollbarbuttonLeft:
690     case StyleAppearance::ScrollbarbuttonRight:
691       PaintScrollbarbutton(dt, aAppearance, devPxRect, eventState, dpi);
692       break;
693     case StyleAppearance::Button:
694       PaintButton(aFrame, dt, devPxRect, eventState, dpi);
695       break;
696     default:
697       MOZ_ASSERT_UNREACHABLE(
698           "Should not get here with a widget type we don't support.");
699       return NS_ERROR_NOT_IMPLEMENTED;
700   }
701 
702   return NS_OK;
703 }
704 
705 /*bool
706 nsNativeBasicTheme::CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder&
707 aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const
708 mozilla::layers::StackingContextHelper& aSc,
709                                       mozilla::layers::RenderRootStateManager*
710 aManager, nsIFrame* aFrame, StyleAppearance aAppearance, const nsRect& aRect) {
711 }*/
712 
GetWidgetBorder(nsDeviceContext * aContext,nsIFrame * aFrame,StyleAppearance aAppearance)713 LayoutDeviceIntMargin nsNativeBasicTheme::GetWidgetBorder(
714     nsDeviceContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance) {
715   uint32_t dpi = GetDPIRatio(aFrame);
716   switch (aAppearance) {
717     case StyleAppearance::Textfield:
718     case StyleAppearance::Textarea:
719     case StyleAppearance::NumberInput: {
720       const LayoutDeviceIntCoord w = kTextFieldBorderWidth * dpi;
721       return LayoutDeviceIntMargin(w, w, w, w);
722     }
723     case StyleAppearance::Listbox:
724     case StyleAppearance::Menulist:
725     case StyleAppearance::MenulistButton:
726     case StyleAppearance::MenulistTextfield: {
727       const LayoutDeviceIntCoord w = kMenulistBorderWidth * dpi;
728       return LayoutDeviceIntMargin(w, w, w, w);
729     }
730     case StyleAppearance::Button: {
731       const LayoutDeviceIntCoord w = kButtonBorderWidth * dpi;
732       return LayoutDeviceIntMargin(w, w, w, w);
733     }
734     default:
735       return LayoutDeviceIntMargin();
736   }
737 }
738 
GetWidgetPadding(nsDeviceContext * aContext,nsIFrame * aFrame,StyleAppearance aAppearance,LayoutDeviceIntMargin * aResult)739 bool nsNativeBasicTheme::GetWidgetPadding(nsDeviceContext* aContext,
740                                           nsIFrame* aFrame,
741                                           StyleAppearance aAppearance,
742                                           LayoutDeviceIntMargin* aResult) {
743   switch (aAppearance) {
744     // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
745     // and have a meaningful baseline, so they can't have
746     // author-specified padding.
747     case StyleAppearance::Radio:
748     case StyleAppearance::Checkbox:
749     case StyleAppearance::MozMenulistArrowButton:
750       aResult->SizeTo(0, 0, 0, 0);
751       return true;
752     default:
753       break;
754   }
755 
756   // Respect author padding.
757   //
758   // TODO(emilio): Consider just unconditionally returning false, so that the
759   // default size of all elements matches other platforms and the UA stylesheet.
760   if (aFrame->PresContext()->HasAuthorSpecifiedRules(
761           aFrame, NS_AUTHOR_SPECIFIED_PADDING)) {
762     return false;
763   }
764 
765   uint32_t dpi = GetDPIRatio(aFrame);
766   switch (aAppearance) {
767     case StyleAppearance::Textarea:
768     case StyleAppearance::Listbox:
769     case StyleAppearance::Menulist:
770     case StyleAppearance::MenulistButton:
771     case StyleAppearance::MenulistTextfield:
772     case StyleAppearance::NumberInput:
773       aResult->SizeTo(6 * dpi, 7 * dpi, 6 * dpi, 7 * dpi);
774       return true;
775     case StyleAppearance::Button:
776       aResult->SizeTo(6 * dpi, 21 * dpi, 6 * dpi, 21 * dpi);
777       return true;
778     case StyleAppearance::Textfield:
779       if (IsDateTimeTextField(aFrame)) {
780         aResult->SizeTo(7 * dpi, 7 * dpi, 5 * dpi, 7 * dpi);
781         return true;
782       }
783       aResult->SizeTo(6 * dpi, 7 * dpi, 6 * dpi, 7 * dpi);
784       return true;
785     default:
786       return false;
787   }
788 }
789 
GetWidgetOverflow(nsDeviceContext * aContext,nsIFrame * aFrame,StyleAppearance aAppearance,nsRect * aOverflowRect)790 bool nsNativeBasicTheme::GetWidgetOverflow(nsDeviceContext* aContext,
791                                            nsIFrame* aFrame,
792                                            StyleAppearance aAppearance,
793                                            nsRect* aOverflowRect) {
794   // TODO(bug 1620360): This should return non-zero for
795   // StyleAppearance::FocusOutline, if we implement outline-style: auto.
796   return false;
797 }
798 
799 NS_IMETHODIMP
GetMinimumWidgetSize(nsPresContext * aPresContext,nsIFrame * aFrame,StyleAppearance aAppearance,LayoutDeviceIntSize * aResult,bool * aIsOverridable)800 nsNativeBasicTheme::GetMinimumWidgetSize(nsPresContext* aPresContext,
801                                          nsIFrame* aFrame,
802                                          StyleAppearance aAppearance,
803                                          LayoutDeviceIntSize* aResult,
804                                          bool* aIsOverridable) {
805   aResult->width = aResult->height =
806       static_cast<uint32_t>(kMinimumWidgetSize) * GetDPIRatio(aFrame);
807   *aIsOverridable = true;
808   return NS_OK;
809 }
810 
GetWidgetTransparency(nsIFrame * aFrame,StyleAppearance aAppearance)811 nsITheme::Transparency nsNativeBasicTheme::GetWidgetTransparency(
812     nsIFrame* aFrame, StyleAppearance aAppearance) {
813   return eUnknownTransparency;
814 }
815 
816 NS_IMETHODIMP
WidgetStateChanged(nsIFrame * aFrame,StyleAppearance aAppearance,nsAtom * aAttribute,bool * aShouldRepaint,const nsAttrValue * aOldValue)817 nsNativeBasicTheme::WidgetStateChanged(nsIFrame* aFrame,
818                                        StyleAppearance aAppearance,
819                                        nsAtom* aAttribute, bool* aShouldRepaint,
820                                        const nsAttrValue* aOldValue) {
821   if (!aAttribute) {
822     // Hover/focus/active changed.  Always repaint.
823     *aShouldRepaint = true;
824   } else {
825     // Check the attribute to see if it's relevant.
826     // disabled, checked, dlgtype, default, etc.
827     *aShouldRepaint = false;
828     if ((aAttribute == nsGkAtoms::disabled) ||
829         (aAttribute == nsGkAtoms::checked) ||
830         (aAttribute == nsGkAtoms::selected) ||
831         (aAttribute == nsGkAtoms::visuallyselected) ||
832         (aAttribute == nsGkAtoms::menuactive) ||
833         (aAttribute == nsGkAtoms::sortDirection) ||
834         (aAttribute == nsGkAtoms::focused) ||
835         (aAttribute == nsGkAtoms::_default) ||
836         (aAttribute == nsGkAtoms::open) || (aAttribute == nsGkAtoms::hover)) {
837       *aShouldRepaint = true;
838     }
839   }
840 
841   return NS_OK;
842 }
843 
844 NS_IMETHODIMP
ThemeChanged()845 nsNativeBasicTheme::ThemeChanged() { return NS_OK; }
846 
WidgetAppearanceDependsOnWindowFocus(StyleAppearance)847 bool nsNativeBasicTheme::WidgetAppearanceDependsOnWindowFocus(StyleAppearance) {
848   return false;
849 }
850 
ThemeGeometryTypeForWidget(nsIFrame * aFrame,StyleAppearance aAppearance)851 nsITheme::ThemeGeometryType nsNativeBasicTheme::ThemeGeometryTypeForWidget(
852     nsIFrame* aFrame, StyleAppearance aAppearance) {
853   return eThemeGeometryTypeUnknown;
854 }
855 
ThemeSupportsWidget(nsPresContext * aPresContext,nsIFrame * aFrame,StyleAppearance aAppearance)856 bool nsNativeBasicTheme::ThemeSupportsWidget(nsPresContext* aPresContext,
857                                              nsIFrame* aFrame,
858                                              StyleAppearance aAppearance) {
859   if (IsWidgetScrollbarPart(aAppearance)) {
860     const auto* style = nsLayoutUtils::StyleForScrollbar(aFrame);
861     // We don't currently handle custom scrollbars on nsNativeBasicTheme. We
862     // could, potentially.
863     if (style->StyleUI()->HasCustomScrollbars() ||
864         style->StyleUIReset()->mScrollbarWidth == StyleScrollbarWidth::Thin) {
865       return false;
866     }
867   }
868 
869   switch (aAppearance) {
870     case StyleAppearance::Radio:
871     case StyleAppearance::Checkbox:
872     case StyleAppearance::Textarea:
873     case StyleAppearance::Textfield:
874     case StyleAppearance::Range:
875     case StyleAppearance::RangeThumb:
876     case StyleAppearance::ScrollbarbuttonUp:
877     case StyleAppearance::ScrollbarbuttonDown:
878     case StyleAppearance::ScrollbarbuttonLeft:
879     case StyleAppearance::ScrollbarbuttonRight:
880     case StyleAppearance::ScrollbarthumbHorizontal:
881     case StyleAppearance::ScrollbarthumbVertical:
882     case StyleAppearance::ScrollbarHorizontal:
883     case StyleAppearance::ScrollbarNonDisappearing:
884     case StyleAppearance::ScrollbarVertical:
885     case StyleAppearance::Scrollcorner:
886     case StyleAppearance::Button:
887     case StyleAppearance::Listbox:
888     case StyleAppearance::Menulist:
889     case StyleAppearance::MenulistButton:
890     case StyleAppearance::MenulistTextfield:
891     case StyleAppearance::NumberInput:
892     case StyleAppearance::MozMenulistArrowButton:
893     case StyleAppearance::SpinnerUpbutton:
894     case StyleAppearance::SpinnerDownbutton:
895       return !IsWidgetStyled(aPresContext, aFrame, aAppearance);
896     default:
897       return false;
898   }
899 }
900 
WidgetIsContainer(StyleAppearance aAppearance)901 bool nsNativeBasicTheme::WidgetIsContainer(StyleAppearance aAppearance) {
902   switch (aAppearance) {
903     case StyleAppearance::MozMenulistArrowButton:
904     case StyleAppearance::Radio:
905     case StyleAppearance::Checkbox:
906       return false;
907     default:
908       return true;
909   }
910 }
911 
ThemeDrawsFocusForWidget(StyleAppearance aAppearance)912 bool nsNativeBasicTheme::ThemeDrawsFocusForWidget(StyleAppearance aAppearance) {
913   switch (aAppearance) {
914     case StyleAppearance::Range:
915     // TODO(emilio): Checkbox / Radio don't have focus indicators when checked.
916     // If they did, we could just return true here unconditionally.
917     case StyleAppearance::Checkbox:
918     case StyleAppearance::Radio:
919       return false;
920     default:
921       return true;
922   }
923 }
924 
ThemeNeedsComboboxDropmarker()925 bool nsNativeBasicTheme::ThemeNeedsComboboxDropmarker() { return true; }
926 
do_GetBasicNativeThemeDoNotUseDirectly()927 already_AddRefed<nsITheme> do_GetBasicNativeThemeDoNotUseDirectly() {
928   static StaticRefPtr<nsITheme> gInstance;
929   if (MOZ_UNLIKELY(!gInstance)) {
930     gInstance = new nsNativeBasicTheme();
931     ClearOnShutdown(&gInstance);
932   }
933   return do_AddRef(gInstance);
934 }
935