1 /* -*- Mode: C++; tab-width: 2; 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/. */
6 #include "nsNativeTheme.h"
8 #include "nsIDocument.h"
9 #include "nsIContent.h"
11 #include "nsIPresShell.h"
12 #include "nsNumberControlFrame.h"
13 #include "nsPresContext.h"
15 #include "nsNameSpaceManager.h"
16 #include "nsIDOMXULMenuListElement.h"
17 #include "nsStyleConsts.h"
18 #include "nsIComponentManager.h"
19 #include "nsPIDOMWindow.h"
20 #include "nsProgressFrame.h"
21 #include "nsMeterFrame.h"
22 #include "nsMenuFrame.h"
23 #include "nsRangeFrame.h"
24 #include "nsCSSRendering.h"
25 #include "mozilla/EventStates.h"
26 #include "mozilla/dom/Element.h"
27 #include "mozilla/dom/HTMLBodyElement.h"
28 #include "mozilla/dom/HTMLInputElement.h"
29 #include "mozilla/dom/HTMLProgressElement.h"
30 #include "mozilla/StaticPrefs.h"
31 #include "nsIDocumentInlines.h"
34 using namespace mozilla
;
35 using namespace mozilla::dom
;
37 nsNativeTheme::nsNativeTheme()
38 : mAnimatedContentTimeout(UINT32_MAX
)
42 NS_IMPL_ISUPPORTS(nsNativeTheme
, nsITimerCallback
, nsINamed
)
45 nsNativeTheme::GetPresShell(nsIFrame
* aFrame
)
50 nsPresContext
* context
= aFrame
->PresContext();
51 return context
? context
->GetPresShell() : nullptr;
55 nsNativeTheme::GetContentState(nsIFrame
* aFrame
, StyleAppearance aWidgetType
)
60 bool isXULCheckboxRadio
=
61 (aWidgetType
== StyleAppearance::Checkbox
||
62 aWidgetType
== StyleAppearance::Radio
) &&
63 aFrame
->GetContent()->IsXULElement();
64 if (isXULCheckboxRadio
)
65 aFrame
= aFrame
->GetParent();
67 if (!aFrame
->GetContent())
70 nsIPresShell
*shell
= GetPresShell(aFrame
);
74 nsIContent
* frameContent
= aFrame
->GetContent();
76 if (frameContent
->IsElement()) {
77 flags
= frameContent
->AsElement()->State();
79 // <input type=number> needs special handling since its nested native
80 // anonymous <input type=text> takes focus for it.
81 if (aWidgetType
== StyleAppearance::NumberInput
&&
82 frameContent
->IsHTMLElement(nsGkAtoms::input
)) {
83 nsNumberControlFrame
*numberControlFrame
= do_QueryFrame(aFrame
);
84 if (numberControlFrame
&& numberControlFrame
->IsFocused()) {
85 flags
|= NS_EVENT_STATE_FOCUS
;
89 nsNumberControlFrame
* numberControlFrame
=
90 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame
);
91 if (numberControlFrame
&&
92 numberControlFrame
->GetContent()->AsElement()->State().
93 HasState(NS_EVENT_STATE_DISABLED
)) {
94 flags
|= NS_EVENT_STATE_DISABLED
;
98 if (isXULCheckboxRadio
&& aWidgetType
== StyleAppearance::Radio
) {
99 if (IsFocused(aFrame
))
100 flags
|= NS_EVENT_STATE_FOCUS
;
103 // On Windows and Mac, only draw focus rings if they should be shown. This
104 // means that focus rings are only shown once the keyboard has been used to
105 // focus something in the window.
106 #if defined(XP_MACOSX)
107 // Mac always draws focus rings for textboxes and lists.
108 if (aWidgetType
== StyleAppearance::NumberInput
||
109 aWidgetType
== StyleAppearance::Textfield
||
110 aWidgetType
== StyleAppearance::TextfieldMultiline
||
111 aWidgetType
== StyleAppearance::Searchfield
||
112 aWidgetType
== StyleAppearance::Listbox
) {
117 // On Windows, focused buttons are always drawn as such by the native theme.
118 if (aWidgetType
== StyleAppearance::Button
)
121 #if defined(XP_MACOSX) || defined(XP_WIN)
122 nsIDocument
* doc
= aFrame
->GetContent()->OwnerDoc();
123 nsPIDOMWindowOuter
* window
= doc
->GetWindow();
124 if (window
&& !window
->ShouldShowFocusRing())
125 flags
&= ~NS_EVENT_STATE_FOCUS
;
133 nsNativeTheme::CheckBooleanAttr(nsIFrame
* aFrame
, nsAtom
* aAtom
)
138 nsIContent
* content
= aFrame
->GetContent();
139 if (!content
|| !content
->IsElement())
142 if (content
->IsHTMLElement())
143 return content
->AsElement()->HasAttr(kNameSpaceID_None
, aAtom
);
145 // For XML/XUL elements, an attribute must be equal to the literal
146 // string "true" to be counted as true. An empty string should _not_
147 // be counted as true.
148 return content
->AsElement()->AttrValueIs(kNameSpaceID_None
, aAtom
,
149 NS_LITERAL_STRING("true"),
155 nsNativeTheme::CheckIntAttr(nsIFrame
* aFrame
, nsAtom
* aAtom
, int32_t defaultValue
)
160 nsIContent
* content
= aFrame
->GetContent();
161 if (!content
|| !content
->IsElement())
165 content
->AsElement()->GetAttr(kNameSpaceID_None
, aAtom
, attr
);
167 int32_t value
= attr
.ToInteger(&err
);
168 if (attr
.IsEmpty() || NS_FAILED(err
))
176 nsNativeTheme::GetProgressValue(nsIFrame
* aFrame
)
178 // When we are using the HTML progress element,
179 // we can get the value from the IDL property.
180 if (aFrame
&& aFrame
->GetContent()->IsHTMLElement(nsGkAtoms::progress
)) {
181 return static_cast<HTMLProgressElement
*>(aFrame
->GetContent())->Value();
184 return (double)nsNativeTheme::CheckIntAttr(aFrame
, nsGkAtoms::value
, 0);
189 nsNativeTheme::GetProgressMaxValue(nsIFrame
* aFrame
)
191 // When we are using the HTML progress element,
192 // we can get the max from the IDL property.
193 if (aFrame
&& aFrame
->GetContent()->IsHTMLElement(nsGkAtoms::progress
)) {
194 return static_cast<HTMLProgressElement
*>(aFrame
->GetContent())->Max();
197 return (double)std::max(nsNativeTheme::CheckIntAttr(aFrame
, nsGkAtoms::max
, 100), 1);
201 nsNativeTheme::GetCheckedOrSelected(nsIFrame
* aFrame
, bool aCheckSelected
)
206 nsIContent
* content
= aFrame
->GetContent();
208 if (content
->IsXULElement()) {
209 // For a XUL checkbox or radio button, the state of the parent determines
211 aFrame
= aFrame
->GetParent();
213 // Check for an HTML input element
214 HTMLInputElement
* inputElt
= HTMLInputElement::FromNode(content
);
216 return inputElt
->Checked();
220 return CheckBooleanAttr(aFrame
, aCheckSelected
? nsGkAtoms::selected
221 : nsGkAtoms::checked
);
225 nsNativeTheme::IsButtonTypeMenu(nsIFrame
* aFrame
)
230 nsIContent
* content
= aFrame
->GetContent();
231 return content
->IsXULElement(nsGkAtoms::button
) &&
232 content
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
233 NS_LITERAL_STRING("menu"),
238 nsNativeTheme::IsPressedButton(nsIFrame
* aFrame
)
240 EventStates eventState
= GetContentState(aFrame
, StyleAppearance::Toolbarbutton
);
241 if (IsDisabled(aFrame
, eventState
))
244 return IsOpenButton(aFrame
) ||
245 eventState
.HasAllStates(NS_EVENT_STATE_ACTIVE
| NS_EVENT_STATE_HOVER
);
250 nsNativeTheme::GetIndeterminate(nsIFrame
* aFrame
)
255 nsIContent
* content
= aFrame
->GetContent();
257 if (content
->IsXULElement()) {
258 // For a XUL checkbox or radio button, the state of the parent determines
260 return CheckBooleanAttr(aFrame
->GetParent(), nsGkAtoms::indeterminate
);
263 // Check for an HTML input element
264 HTMLInputElement
* inputElt
= HTMLInputElement::FromNode(content
);
266 return inputElt
->Indeterminate();
273 nsNativeTheme::IsWidgetStyled(nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
274 StyleAppearance aWidgetType
)
276 // Check for specific widgets to see if HTML has overridden the style.
280 // Resizers have some special handling, dependent on whether in a scrollable
281 // container or not. If so, use the scrollable container's to determine
282 // whether the style is overriden instead of the resizer. This allows a
283 // non-native transparent resizer to be used instead. Otherwise, we just
284 // fall through and return false.
285 if (aWidgetType
== StyleAppearance::Resizer
) {
286 nsIFrame
* parentFrame
= aFrame
->GetParent();
287 if (parentFrame
&& parentFrame
->IsScrollFrame()) {
288 // if the parent is a scrollframe, the resizer should be native themed
289 // only if the scrollable area doesn't override the widget style.
290 parentFrame
= parentFrame
->GetParent();
292 return IsWidgetStyled(aPresContext
, parentFrame
,
293 parentFrame
->StyleDisplay()->mAppearance
);
299 * Progress bar appearance should be the same for the bar and the container
300 * frame. nsProgressFrame owns the logic and will tell us what we should do.
302 if (aWidgetType
== StyleAppearance::Progresschunk
||
303 aWidgetType
== StyleAppearance::Progressbar
) {
304 nsProgressFrame
* progressFrame
= do_QueryFrame(aWidgetType
== StyleAppearance::Progresschunk
305 ? aFrame
->GetParent() : aFrame
);
307 return !progressFrame
->ShouldUseNativeStyle();
312 * Meter bar appearance should be the same for the bar and the container
313 * frame. nsMeterFrame owns the logic and will tell us what we should do.
315 if (aWidgetType
== StyleAppearance::Meterchunk
||
316 aWidgetType
== StyleAppearance::Meterbar
) {
317 nsMeterFrame
* meterFrame
= do_QueryFrame(aWidgetType
== StyleAppearance::Meterchunk
318 ? aFrame
->GetParent() : aFrame
);
320 return !meterFrame
->ShouldUseNativeStyle();
325 * An nsRangeFrame and its children are treated atomically when it
326 * comes to native theming (either all parts, or no parts, are themed).
327 * nsRangeFrame owns the logic and will tell us what we should do.
329 if (aWidgetType
== StyleAppearance::Range
||
330 aWidgetType
== StyleAppearance::RangeThumb
) {
331 nsRangeFrame
* rangeFrame
=
332 do_QueryFrame(aWidgetType
== StyleAppearance::RangeThumb
333 ? aFrame
->GetParent() : aFrame
);
335 return !rangeFrame
->ShouldUseNativeStyle();
339 if (aWidgetType
== StyleAppearance::SpinnerUpbutton
||
340 aWidgetType
== StyleAppearance::SpinnerDownbutton
) {
341 nsNumberControlFrame
* numberControlFrame
=
342 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame
);
343 if (numberControlFrame
) {
344 return !numberControlFrame
->ShouldUseNativeStyleForSpinner();
348 return (aWidgetType
== StyleAppearance::NumberInput
||
349 aWidgetType
== StyleAppearance::Button
||
350 aWidgetType
== StyleAppearance::Textfield
||
351 aWidgetType
== StyleAppearance::TextfieldMultiline
||
352 aWidgetType
== StyleAppearance::Listbox
||
353 aWidgetType
== StyleAppearance::Menulist
||
354 (aWidgetType
== StyleAppearance::MenulistButton
&&
355 StaticPrefs::layout_css_webkit_appearance_enabled())) &&
356 aFrame
->GetContent()->IsHTMLElement() &&
357 aPresContext
->HasAuthorSpecifiedRules(aFrame
,
358 NS_AUTHOR_SPECIFIED_BORDER
|
359 NS_AUTHOR_SPECIFIED_BACKGROUND
);
363 nsNativeTheme::IsDisabled(nsIFrame
* aFrame
, EventStates aEventStates
)
369 nsIContent
* content
= aFrame
->GetContent();
370 if (!content
|| !content
->IsElement()) {
374 if (content
->IsHTMLElement()) {
375 return aEventStates
.HasState(NS_EVENT_STATE_DISABLED
);
378 // For XML/XUL elements, an attribute must be equal to the literal
379 // string "true" to be counted as true. An empty string should _not_
380 // be counted as true.
381 return content
->AsElement()->AttrValueIs(kNameSpaceID_None
,
383 NS_LITERAL_STRING("true"),
388 nsNativeTheme::IsFrameRTL(nsIFrame
* aFrame
)
393 WritingMode wm
= aFrame
->GetWritingMode();
394 return !(wm
.IsVertical() ? wm
.IsVerticalLR() : wm
.IsBidiLTR());
398 nsNativeTheme::IsHTMLContent(nsIFrame
*aFrame
)
403 nsIContent
* content
= aFrame
->GetContent();
404 return content
&& content
->IsHTMLElement();
410 nsNativeTheme::GetScrollbarButtonType(nsIFrame
* aFrame
)
415 static Element::AttrValuesArray strings
[] =
416 {&nsGkAtoms::scrollbarDownBottom
, &nsGkAtoms::scrollbarDownTop
,
417 &nsGkAtoms::scrollbarUpBottom
, &nsGkAtoms::scrollbarUpTop
,
420 nsIContent
* content
= aFrame
->GetContent();
421 if (!content
|| !content
->IsElement()) {
425 switch (content
->AsElement()->FindAttrValueIn(kNameSpaceID_None
,
427 strings
, eCaseMatters
)) {
428 case 0: return eScrollbarButton_Down
| eScrollbarButton_Bottom
;
429 case 1: return eScrollbarButton_Down
;
430 case 2: return eScrollbarButton_Bottom
;
431 case 3: return eScrollbarButton_UpTop
;
438 nsNativeTheme::TreeSortDirection
439 nsNativeTheme::GetTreeSortDirection(nsIFrame
* aFrame
)
441 if (!aFrame
|| !aFrame
->GetContent())
442 return eTreeSortDirection_Natural
;
444 static Element::AttrValuesArray strings
[] =
445 {&nsGkAtoms::descending
, &nsGkAtoms::ascending
, nullptr};
447 nsIContent
* content
= aFrame
->GetContent();
448 if (content
->IsElement()) {
449 switch (content
->AsElement()->FindAttrValueIn(kNameSpaceID_None
,
450 nsGkAtoms::sortDirection
,
451 strings
, eCaseMatters
)) {
452 case 0: return eTreeSortDirection_Descending
;
453 case 1: return eTreeSortDirection_Ascending
;
457 return eTreeSortDirection_Natural
;
461 nsNativeTheme::IsLastTreeHeaderCell(nsIFrame
* aFrame
)
466 // A tree column picker is always the last header cell.
467 if (aFrame
->GetContent()->IsXULElement(nsGkAtoms::treecolpicker
))
470 // Find the parent tree.
471 nsIContent
* parent
= aFrame
->GetContent()->GetParent();
472 while (parent
&& !parent
->IsXULElement(nsGkAtoms::tree
)) {
473 parent
= parent
->GetParent();
476 // If the column picker is visible, this can't be the last column.
477 if (parent
&& !parent
->AsElement()->AttrValueIs(kNameSpaceID_None
,
478 nsGkAtoms::hidecolumnpicker
,
479 NS_LITERAL_STRING("true"),
483 while ((aFrame
= aFrame
->GetNextSibling())) {
484 if (aFrame
->GetRect().Width() > 0)
492 nsNativeTheme::IsBottomTab(nsIFrame
* aFrame
)
497 nsAutoString classStr
;
498 if (aFrame
->GetContent()->IsElement()) {
499 aFrame
->GetContent()->AsElement()->GetAttr(kNameSpaceID_None
,
503 // FIXME: This looks bogus, shouldn't this be looking at GetClasses()?
504 return !classStr
.IsEmpty() && classStr
.Find("tab-bottom") != kNotFound
;
508 nsNativeTheme::IsFirstTab(nsIFrame
* aFrame
)
513 for (nsIFrame
* first
: aFrame
->GetParent()->PrincipalChildList()) {
514 if (first
->GetRect().Width() > 0 &&
515 first
->GetContent()->IsXULElement(nsGkAtoms::tab
))
516 return (first
== aFrame
);
522 nsNativeTheme::IsHorizontal(nsIFrame
* aFrame
)
527 if (!aFrame
->GetContent()->IsElement())
530 return !aFrame
->GetContent()->AsElement()->AttrValueIs(kNameSpaceID_None
,
537 nsNativeTheme::IsNextToSelectedTab(nsIFrame
* aFrame
, int32_t aOffset
)
543 return IsSelectedTab(aFrame
);
545 int32_t thisTabIndex
= -1, selectedTabIndex
= -1;
547 nsIFrame
* currentTab
= aFrame
->GetParent()->PrincipalChildList().FirstChild();
548 for (int32_t i
= 0; currentTab
; currentTab
= currentTab
->GetNextSibling()) {
549 if (currentTab
->GetRect().Width() == 0)
551 if (aFrame
== currentTab
)
553 if (IsSelectedTab(currentTab
))
554 selectedTabIndex
= i
;
558 if (thisTabIndex
== -1 || selectedTabIndex
== -1)
561 return (thisTabIndex
- selectedTabIndex
== aOffset
);
566 nsNativeTheme::IsIndeterminateProgress(nsIFrame
* aFrame
,
567 EventStates aEventStates
)
569 if (!aFrame
|| !aFrame
->GetContent()|| !aFrame
->GetContent()->IsElement())
572 if (aFrame
->GetContent()->IsHTMLElement(nsGkAtoms::progress
)) {
573 return aEventStates
.HasState(NS_EVENT_STATE_INDETERMINATE
);
576 return aFrame
->GetContent()->AsElement()->AttrValueIs(kNameSpaceID_None
,
578 NS_LITERAL_STRING("undetermined"),
583 nsNativeTheme::IsVerticalProgress(nsIFrame
* aFrame
)
588 return IsVerticalMeter(aFrame
);
592 nsNativeTheme::IsVerticalMeter(nsIFrame
* aFrame
)
594 MOZ_ASSERT(aFrame
, "You have to pass a non-null aFrame");
595 switch (aFrame
->StyleDisplay()->mOrient
) {
596 case StyleOrient::Horizontal
:
598 case StyleOrient::Vertical
:
600 case StyleOrient::Inline
:
601 return aFrame
->GetWritingMode().IsVertical();
602 case StyleOrient::Block
:
603 return !aFrame
->GetWritingMode().IsVertical();
605 MOZ_ASSERT_UNREACHABLE("unexpected -moz-orient value");
611 nsNativeTheme::IsSubmenu(nsIFrame
* aFrame
, bool* aLeftOfParent
)
616 nsIContent
* parentContent
= aFrame
->GetContent()->GetParent();
617 if (!parentContent
|| !parentContent
->IsXULElement(nsGkAtoms::menu
))
620 nsIFrame
* parent
= aFrame
;
621 while ((parent
= parent
->GetParent())) {
622 if (parent
->GetContent() == parentContent
) {
624 LayoutDeviceIntRect selfBounds
, parentBounds
;
625 selfBounds
= aFrame
->GetNearestWidget()->GetScreenBounds();
626 parentBounds
= parent
->GetNearestWidget()->GetScreenBounds();
627 *aLeftOfParent
= selfBounds
.X() < parentBounds
.X();
637 nsNativeTheme::IsRegularMenuItem(nsIFrame
*aFrame
)
639 nsMenuFrame
*menuFrame
= do_QueryFrame(aFrame
);
640 return !(menuFrame
&& (menuFrame
->IsOnMenuBar() ||
641 menuFrame
->GetParentMenuListType() != eNotMenuList
));
645 nsNativeTheme::QueueAnimatedContentForRefresh(nsIContent
* aContent
,
646 uint32_t aMinimumFrameRate
)
648 NS_ASSERTION(aContent
, "Null pointer!");
649 NS_ASSERTION(aMinimumFrameRate
, "aMinimumFrameRate must be non-zero!");
650 NS_ASSERTION(aMinimumFrameRate
<= 1000,
651 "aMinimumFrameRate must be less than 1000!");
653 uint32_t timeout
= 1000 / aMinimumFrameRate
;
654 timeout
= std::min(mAnimatedContentTimeout
, timeout
);
656 if (!mAnimatedContentTimer
) {
657 mAnimatedContentTimer
= NS_NewTimer();
658 NS_ENSURE_TRUE(mAnimatedContentTimer
, false);
661 if (mAnimatedContentList
.IsEmpty() || timeout
!= mAnimatedContentTimeout
) {
663 if (!mAnimatedContentList
.IsEmpty()) {
664 rv
= mAnimatedContentTimer
->Cancel();
665 NS_ENSURE_SUCCESS(rv
, false);
668 if (XRE_IsContentProcess() && NS_IsMainThread()) {
669 mAnimatedContentTimer
->SetTarget(aContent
->OwnerDoc()->EventTargetFor(TaskCategory::Other
));
671 rv
= mAnimatedContentTimer
->InitWithCallback(this, timeout
,
672 nsITimer::TYPE_ONE_SHOT
);
673 NS_ENSURE_SUCCESS(rv
, false);
675 mAnimatedContentTimeout
= timeout
;
678 if (!mAnimatedContentList
.AppendElement(aContent
)) {
679 NS_WARNING("Out of memory!");
687 nsNativeTheme::Notify(nsITimer
* aTimer
)
689 NS_ASSERTION(aTimer
== mAnimatedContentTimer
, "Wrong timer!");
691 // XXX Assumes that calling nsIFrame::Invalidate won't reenter
692 // QueueAnimatedContentForRefresh.
694 uint32_t count
= mAnimatedContentList
.Length();
695 for (uint32_t index
= 0; index
< count
; index
++) {
696 nsIFrame
* frame
= mAnimatedContentList
[index
]->GetPrimaryFrame();
698 frame
->InvalidateFrame();
702 mAnimatedContentList
.Clear();
703 mAnimatedContentTimeout
= UINT32_MAX
;
708 nsNativeTheme::GetName(nsACString
& aName
)
710 aName
.AssignLiteral("nsNativeTheme");
715 nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(nsIFrame
* aFrame
,
721 // Find the next visible sibling.
722 nsIFrame
* sibling
= aFrame
;
724 sibling
= aNextSibling
? sibling
->GetNextSibling() : sibling
->GetPrevSibling();
725 } while (sibling
&& sibling
->GetRect().Width() == 0);
727 // Check same appearance and adjacency.
729 sibling
->StyleDisplay()->mAppearance
!= aFrame
->StyleDisplay()->mAppearance
||
730 (sibling
->GetRect().XMost() != aFrame
->GetRect().X() &&
731 aFrame
->GetRect().XMost() != sibling
->GetRect().X()))
737 nsNativeTheme::IsRangeHorizontal(nsIFrame
* aFrame
)
739 nsIFrame
* rangeFrame
= aFrame
;
740 if (!rangeFrame
->IsRangeFrame()) {
741 // If the thumb's frame is passed in, get its range parent:
742 rangeFrame
= aFrame
->GetParent();
744 if (rangeFrame
->IsRangeFrame()) {
745 return static_cast<nsRangeFrame
*>(rangeFrame
)->IsHorizontal();
747 // Not actually a range frame - just use the ratio of the frame's size to
749 return aFrame
->GetSize().width
>= aFrame
->GetSize().height
;
753 GetBodyFrame(nsIFrame
* aCanvasFrame
)
755 nsIContent
* content
= aCanvasFrame
->GetContent();
759 nsIDocument
* document
= content
->OwnerDoc();
760 nsIContent
* body
= document
->GetBodyElement();
764 return body
->GetPrimaryFrame();
768 nsNativeTheme::IsDarkBackground(nsIFrame
* aFrame
)
770 nsIScrollableFrame
* scrollFrame
= nullptr;
771 while (!scrollFrame
&& aFrame
) {
772 scrollFrame
= aFrame
->GetScrollTargetFrame();
773 aFrame
= aFrame
->GetParent();
778 nsIFrame
* frame
= scrollFrame
->GetScrolledFrame();
779 if (nsCSSRendering::IsCanvasFrame(frame
)) {
780 // For canvas frames, prefer to look at the body first, because the body
781 // background color is most likely what will be visible as the background
782 // color of the page, even if the html element has a different background
783 // color which prevents that of the body frame to propagate to the viewport.
784 nsIFrame
* bodyFrame
= GetBodyFrame(frame
);
789 ComputedStyle
* bgSC
= nullptr;
790 if (!nsCSSRendering::FindBackground(frame
, &bgSC
) ||
791 bgSC
->StyleBackground()->IsTransparent(bgSC
)) {
792 nsIFrame
* backgroundFrame
= nsCSSRendering::FindNonTransparentBackgroundFrame(frame
, true);
793 nsCSSRendering::FindBackground(backgroundFrame
, &bgSC
);
796 nscolor bgColor
= bgSC
->StyleBackground()->BackgroundColor(bgSC
);
797 // Consider the background color dark if the sum of the r, g and b values is
798 // less than 384 in a semi-transparent document. This heuristic matches what
799 // WebKit does, and we can improve it later if needed.
800 return NS_GET_A(bgColor
) > 127 &&
801 NS_GET_R(bgColor
) + NS_GET_G(bgColor
) + NS_GET_B(bgColor
) < 384;
807 nsNativeTheme::IsWidgetScrollbarPart(StyleAppearance aWidgetType
)
809 switch (aWidgetType
) {
810 case StyleAppearance::Scrollbar
:
811 case StyleAppearance::ScrollbarSmall
:
812 case StyleAppearance::ScrollbarVertical
:
813 case StyleAppearance::ScrollbarHorizontal
:
814 case StyleAppearance::ScrollbarbuttonUp
:
815 case StyleAppearance::ScrollbarbuttonDown
:
816 case StyleAppearance::ScrollbarbuttonLeft
:
817 case StyleAppearance::ScrollbarbuttonRight
:
818 case StyleAppearance::ScrollbarthumbVertical
:
819 case StyleAppearance::ScrollbarthumbHorizontal
:
820 case StyleAppearance::Scrollcorner
:
828 GetOpaqueBackgroundColor(ComputedStyle
* aStyle
)
830 nscolor color
= aStyle
->StyleBackground()->BackgroundColor(aStyle
);
831 if (NS_GET_A(color
) == 255) {
834 // Compose white background with the background color.
835 return NS_ComposeColors(NS_RGB(255, 255, 255), color
);
839 nsNativeTheme::GetScrollbarFaceColor(ComputedStyle
* aStyle
,
840 AutoColorGetter aAutoGetter
)
842 StyleComplexColor complexColor
= aStyle
->StyleUI()->mScrollbarFaceColor
;
843 if (complexColor
.IsAuto()) {
844 return aAutoGetter(aStyle
);
846 nscolor color
= complexColor
.CalcColor(aStyle
);
847 if (NS_GET_A(color
) == 255) {
850 nscolor bgColor
= GetOpaqueBackgroundColor(aStyle
);
851 return NS_ComposeColors(bgColor
, color
);
855 nsNativeTheme::GetScrollbarTrackColor(ComputedStyle
* aStyle
,
856 AutoColorGetter aAutoGetter
)
858 StyleComplexColor complexColor
= aStyle
->StyleUI()->mScrollbarTrackColor
;
860 if (complexColor
.IsAuto()) {
861 color
= aAutoGetter(aStyle
);
863 color
= complexColor
.CalcColor(aStyle
);
865 if (NS_GET_A(color
) == 255) {
868 nscolor bgColor
= GetOpaqueBackgroundColor(aStyle
);
869 return NS_ComposeColors(bgColor
, color
);