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 "nsIDOMHTMLInputElement.h"
17 #include "nsIDOMXULMenuListElement.h"
18 #include "nsThemeConstants.h"
19 #include "nsIComponentManager.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsProgressFrame.h"
22 #include "nsMeterFrame.h"
23 #include "nsMenuFrame.h"
24 #include "nsRangeFrame.h"
25 #include "nsCSSRendering.h"
26 #include "mozilla/EventStates.h"
27 #include "mozilla/dom/Element.h"
28 #include "mozilla/dom/HTMLBodyElement.h"
29 #include "mozilla/dom/HTMLProgressElement.h"
30 #include "nsIDocumentInlines.h"
33 using namespace mozilla
;
34 using namespace mozilla::dom
;
36 nsNativeTheme::nsNativeTheme()
37 : mAnimatedContentTimeout(UINT32_MAX
)
41 NS_IMPL_ISUPPORTS(nsNativeTheme
, nsITimerCallback
)
44 nsNativeTheme::GetPresShell(nsIFrame
* aFrame
)
49 // this is a workaround for the egcs 1.1.2 not inlining
50 // aFrame->PresContext(), which causes an undefined symbol
51 nsPresContext
*context
= aFrame
->StyleContext()->RuleNode()->PresContext();
52 return context
? context
->GetPresShell() : nullptr;
56 nsNativeTheme::GetContentState(nsIFrame
* aFrame
, uint8_t aWidgetType
)
61 bool isXULCheckboxRadio
=
62 (aWidgetType
== NS_THEME_CHECKBOX
||
63 aWidgetType
== NS_THEME_RADIO
) &&
64 aFrame
->GetContent()->IsXUL();
65 if (isXULCheckboxRadio
)
66 aFrame
= aFrame
->GetParent();
68 if (!aFrame
->GetContent())
71 nsIPresShell
*shell
= GetPresShell(aFrame
);
75 nsIContent
* frameContent
= aFrame
->GetContent();
77 if (frameContent
->IsElement()) {
78 flags
= frameContent
->AsElement()->State();
80 // <input type=number> needs special handling since its nested native
81 // anonymous <input type=text> takes focus for it.
82 if (aWidgetType
== NS_THEME_NUMBER_INPUT
&&
83 frameContent
->IsHTML(nsGkAtoms::input
)) {
84 nsNumberControlFrame
*numberControlFrame
= do_QueryFrame(aFrame
);
85 if (numberControlFrame
&& numberControlFrame
->IsFocused()) {
86 flags
|= NS_EVENT_STATE_FOCUS
;
90 nsNumberControlFrame
* numberControlFrame
=
91 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame
);
92 if (numberControlFrame
&&
93 numberControlFrame
->GetContent()->AsElement()->State().
94 HasState(NS_EVENT_STATE_DISABLED
)) {
95 flags
|= NS_EVENT_STATE_DISABLED
;
99 if (isXULCheckboxRadio
&& aWidgetType
== NS_THEME_RADIO
) {
100 if (IsFocused(aFrame
))
101 flags
|= NS_EVENT_STATE_FOCUS
;
104 // On Windows and Mac, only draw focus rings if they should be shown. This
105 // means that focus rings are only shown once the keyboard has been used to
106 // focus something in the window.
107 #if defined(XP_MACOSX)
108 // Mac always draws focus rings for textboxes and lists.
109 if (aWidgetType
== NS_THEME_NUMBER_INPUT
||
110 aWidgetType
== NS_THEME_TEXTFIELD
||
111 aWidgetType
== NS_THEME_TEXTFIELD_MULTILINE
||
112 aWidgetType
== NS_THEME_SEARCHFIELD
||
113 aWidgetType
== NS_THEME_LISTBOX
) {
118 // On Windows, focused buttons are always drawn as such by the native theme.
119 if (aWidgetType
== NS_THEME_BUTTON
)
122 #if defined(XP_MACOSX) || defined(XP_WIN)
123 nsIDocument
* doc
= aFrame
->GetContent()->OwnerDoc();
124 nsPIDOMWindow
* window
= doc
->GetWindow();
125 if (window
&& !window
->ShouldShowFocusRing())
126 flags
&= ~NS_EVENT_STATE_FOCUS
;
134 nsNativeTheme::CheckBooleanAttr(nsIFrame
* aFrame
, nsIAtom
* aAtom
)
139 nsIContent
* content
= aFrame
->GetContent();
143 if (content
->IsHTML())
144 return content
->HasAttr(kNameSpaceID_None
, aAtom
);
146 // For XML/XUL elements, an attribute must be equal to the literal
147 // string "true" to be counted as true. An empty string should _not_
148 // be counted as true.
149 return content
->AttrValueIs(kNameSpaceID_None
, aAtom
,
150 NS_LITERAL_STRING("true"), eCaseMatters
);
155 nsNativeTheme::CheckIntAttr(nsIFrame
* aFrame
, nsIAtom
* aAtom
, int32_t defaultValue
)
161 aFrame
->GetContent()->GetAttr(kNameSpaceID_None
, aAtom
, attr
);
163 int32_t value
= attr
.ToInteger(&err
);
164 if (attr
.IsEmpty() || NS_FAILED(err
))
172 nsNativeTheme::GetProgressValue(nsIFrame
* aFrame
)
174 // When we are using the HTML progress element,
175 // we can get the value from the IDL property.
176 if (aFrame
&& aFrame
->GetContent()->IsHTML(nsGkAtoms::progress
)) {
177 return static_cast<HTMLProgressElement
*>(aFrame
->GetContent())->Value();
180 return (double)nsNativeTheme::CheckIntAttr(aFrame
, nsGkAtoms::value
, 0);
185 nsNativeTheme::GetProgressMaxValue(nsIFrame
* aFrame
)
187 // When we are using the HTML progress element,
188 // we can get the max from the IDL property.
189 if (aFrame
&& aFrame
->GetContent()->IsHTML(nsGkAtoms::progress
)) {
190 return static_cast<HTMLProgressElement
*>(aFrame
->GetContent())->Max();
193 return (double)std::max(nsNativeTheme::CheckIntAttr(aFrame
, nsGkAtoms::max
, 100), 1);
197 nsNativeTheme::GetCheckedOrSelected(nsIFrame
* aFrame
, bool aCheckSelected
)
202 nsIContent
* content
= aFrame
->GetContent();
204 if (content
->IsXUL()) {
205 // For a XUL checkbox or radio button, the state of the parent determines
207 aFrame
= aFrame
->GetParent();
209 // Check for an HTML input element
210 nsCOMPtr
<nsIDOMHTMLInputElement
> inputElt
= do_QueryInterface(content
);
213 inputElt
->GetChecked(&checked
);
218 return CheckBooleanAttr(aFrame
, aCheckSelected
? nsGkAtoms::selected
219 : nsGkAtoms::checked
);
223 nsNativeTheme::IsButtonTypeMenu(nsIFrame
* aFrame
)
228 nsIContent
* content
= aFrame
->GetContent();
229 return content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
230 NS_LITERAL_STRING("menu"), eCaseMatters
);
234 nsNativeTheme::IsPressedButton(nsIFrame
* aFrame
)
236 EventStates eventState
= GetContentState(aFrame
, NS_THEME_TOOLBAR_BUTTON
);
237 if (IsDisabled(aFrame
, eventState
))
240 return IsOpenButton(aFrame
) ||
241 eventState
.HasAllStates(NS_EVENT_STATE_ACTIVE
| NS_EVENT_STATE_HOVER
);
246 nsNativeTheme::GetIndeterminate(nsIFrame
* aFrame
)
251 nsIContent
* content
= aFrame
->GetContent();
253 if (content
->IsXUL()) {
254 // For a XUL checkbox or radio button, the state of the parent determines
256 return CheckBooleanAttr(aFrame
->GetParent(), nsGkAtoms::indeterminate
);
259 // Check for an HTML input element
260 nsCOMPtr
<nsIDOMHTMLInputElement
> inputElt
= do_QueryInterface(content
);
263 inputElt
->GetIndeterminate(&indeterminate
);
264 return indeterminate
;
271 nsNativeTheme::IsWidgetStyled(nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
274 // Check for specific widgets to see if HTML has overridden the style.
278 // Resizers have some special handling, dependent on whether in a scrollable
279 // container or not. If so, use the scrollable container's to determine
280 // whether the style is overriden instead of the resizer. This allows a
281 // non-native transparent resizer to be used instead. Otherwise, we just
282 // fall through and return false.
283 if (aWidgetType
== NS_THEME_RESIZER
) {
284 nsIFrame
* parentFrame
= aFrame
->GetParent();
285 if (parentFrame
&& parentFrame
->GetType() == nsGkAtoms::scrollFrame
) {
286 // if the parent is a scrollframe, the resizer should be native themed
287 // only if the scrollable area doesn't override the widget style.
288 parentFrame
= parentFrame
->GetParent();
290 return IsWidgetStyled(aPresContext
, parentFrame
,
291 parentFrame
->StyleDisplay()->mAppearance
);
297 * Progress bar appearance should be the same for the bar and the container
298 * frame. nsProgressFrame owns the logic and will tell us what we should do.
300 if (aWidgetType
== NS_THEME_PROGRESSBAR_CHUNK
||
301 aWidgetType
== NS_THEME_PROGRESSBAR
) {
302 nsProgressFrame
* progressFrame
= do_QueryFrame(aWidgetType
== NS_THEME_PROGRESSBAR_CHUNK
303 ? aFrame
->GetParent() : aFrame
);
305 return !progressFrame
->ShouldUseNativeStyle();
310 * Meter bar appearance should be the same for the bar and the container
311 * frame. nsMeterFrame owns the logic and will tell us what we should do.
313 if (aWidgetType
== NS_THEME_METERBAR_CHUNK
||
314 aWidgetType
== NS_THEME_METERBAR
) {
315 nsMeterFrame
* meterFrame
= do_QueryFrame(aWidgetType
== NS_THEME_METERBAR_CHUNK
316 ? aFrame
->GetParent() : aFrame
);
318 return !meterFrame
->ShouldUseNativeStyle();
323 * An nsRangeFrame and its children are treated atomically when it
324 * comes to native theming (either all parts, or no parts, are themed).
325 * nsRangeFrame owns the logic and will tell us what we should do.
327 if (aWidgetType
== NS_THEME_RANGE
||
328 aWidgetType
== NS_THEME_RANGE_THUMB
) {
329 nsRangeFrame
* rangeFrame
=
330 do_QueryFrame(aWidgetType
== NS_THEME_RANGE_THUMB
331 ? aFrame
->GetParent() : aFrame
);
333 return !rangeFrame
->ShouldUseNativeStyle();
337 if (aWidgetType
== NS_THEME_SPINNER_UP_BUTTON
||
338 aWidgetType
== NS_THEME_SPINNER_DOWN_BUTTON
) {
339 nsNumberControlFrame
* numberControlFrame
=
340 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame
);
341 if (numberControlFrame
) {
342 return !numberControlFrame
->ShouldUseNativeStyleForSpinner();
346 return (aWidgetType
== NS_THEME_NUMBER_INPUT
||
347 aWidgetType
== NS_THEME_BUTTON
||
348 aWidgetType
== NS_THEME_TEXTFIELD
||
349 aWidgetType
== NS_THEME_TEXTFIELD_MULTILINE
||
350 aWidgetType
== NS_THEME_LISTBOX
||
351 aWidgetType
== NS_THEME_DROPDOWN
) &&
352 aFrame
->GetContent()->IsHTML() &&
353 aPresContext
->HasAuthorSpecifiedRules(aFrame
,
354 NS_AUTHOR_SPECIFIED_BORDER
|
355 NS_AUTHOR_SPECIFIED_BACKGROUND
);
359 nsNativeTheme::IsDisabled(nsIFrame
* aFrame
, EventStates aEventStates
)
365 nsIContent
* content
= aFrame
->GetContent();
370 if (content
->IsHTML()) {
371 return aEventStates
.HasState(NS_EVENT_STATE_DISABLED
);
374 // For XML/XUL elements, an attribute must be equal to the literal
375 // string "true" to be counted as true. An empty string should _not_
376 // be counted as true.
377 return content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
378 NS_LITERAL_STRING("true"), eCaseMatters
);
382 nsNativeTheme::IsFrameRTL(nsIFrame
* aFrame
)
384 return aFrame
&& aFrame
->StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
;
388 nsNativeTheme::IsHTMLContent(nsIFrame
*aFrame
)
393 nsIContent
* content
= aFrame
->GetContent();
394 return content
&& content
->IsHTML();
400 nsNativeTheme::GetScrollbarButtonType(nsIFrame
* aFrame
)
405 static nsIContent::AttrValuesArray strings
[] =
406 {&nsGkAtoms::scrollbarDownBottom
, &nsGkAtoms::scrollbarDownTop
,
407 &nsGkAtoms::scrollbarUpBottom
, &nsGkAtoms::scrollbarUpTop
,
410 switch (aFrame
->GetContent()->FindAttrValueIn(kNameSpaceID_None
,
412 strings
, eCaseMatters
)) {
413 case 0: return eScrollbarButton_Down
| eScrollbarButton_Bottom
;
414 case 1: return eScrollbarButton_Down
;
415 case 2: return eScrollbarButton_Bottom
;
416 case 3: return eScrollbarButton_UpTop
;
423 nsNativeTheme::TreeSortDirection
424 nsNativeTheme::GetTreeSortDirection(nsIFrame
* aFrame
)
426 if (!aFrame
|| !aFrame
->GetContent())
427 return eTreeSortDirection_Natural
;
429 static nsIContent::AttrValuesArray strings
[] =
430 {&nsGkAtoms::descending
, &nsGkAtoms::ascending
, nullptr};
431 switch (aFrame
->GetContent()->FindAttrValueIn(kNameSpaceID_None
,
432 nsGkAtoms::sortDirection
,
433 strings
, eCaseMatters
)) {
434 case 0: return eTreeSortDirection_Descending
;
435 case 1: return eTreeSortDirection_Ascending
;
438 return eTreeSortDirection_Natural
;
442 nsNativeTheme::IsLastTreeHeaderCell(nsIFrame
* aFrame
)
447 // A tree column picker is always the last header cell.
448 if (aFrame
->GetContent()->Tag() == nsGkAtoms::treecolpicker
)
451 // Find the parent tree.
452 nsIContent
* parent
= aFrame
->GetContent()->GetParent();
453 while (parent
&& parent
->Tag() != nsGkAtoms::tree
) {
454 parent
= parent
->GetParent();
457 // If the column picker is visible, this can't be the last column.
458 if (parent
&& !parent
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::hidecolumnpicker
,
459 NS_LITERAL_STRING("true"), eCaseMatters
))
462 while ((aFrame
= aFrame
->GetNextSibling())) {
463 if (aFrame
->GetRect().width
> 0)
471 nsNativeTheme::IsBottomTab(nsIFrame
* aFrame
)
476 nsAutoString classStr
;
477 aFrame
->GetContent()->GetAttr(kNameSpaceID_None
, nsGkAtoms::_class
, classStr
);
478 return !classStr
.IsEmpty() && classStr
.Find("tab-bottom") != kNotFound
;
482 nsNativeTheme::IsFirstTab(nsIFrame
* aFrame
)
487 nsIFrame
* first
= aFrame
->GetParent()->GetFirstPrincipalChild();
489 if (first
->GetRect().width
> 0 && first
->GetContent()->Tag() == nsGkAtoms::tab
)
490 return (first
== aFrame
);
491 first
= first
->GetNextSibling();
497 nsNativeTheme::IsHorizontal(nsIFrame
* aFrame
)
502 return !aFrame
->GetContent()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::orient
,
508 nsNativeTheme::IsNextToSelectedTab(nsIFrame
* aFrame
, int32_t aOffset
)
514 return IsSelectedTab(aFrame
);
516 int32_t thisTabIndex
= -1, selectedTabIndex
= -1;
518 nsIFrame
* currentTab
= aFrame
->GetParent()->GetFirstPrincipalChild();
519 for (int32_t i
= 0; currentTab
; currentTab
= currentTab
->GetNextSibling()) {
520 if (currentTab
->GetRect().width
== 0)
522 if (aFrame
== currentTab
)
524 if (IsSelectedTab(currentTab
))
525 selectedTabIndex
= i
;
529 if (thisTabIndex
== -1 || selectedTabIndex
== -1)
532 return (thisTabIndex
- selectedTabIndex
== aOffset
);
537 nsNativeTheme::IsIndeterminateProgress(nsIFrame
* aFrame
,
538 EventStates aEventStates
)
540 if (!aFrame
|| !aFrame
->GetContent())
543 if (aFrame
->GetContent()->IsHTML(nsGkAtoms::progress
)) {
544 return aEventStates
.HasState(NS_EVENT_STATE_INDETERMINATE
);
547 return aFrame
->GetContent()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::mode
,
548 NS_LITERAL_STRING("undetermined"),
553 nsNativeTheme::IsVerticalProgress(nsIFrame
* aFrame
)
556 aFrame
->StyleDisplay()->mOrient
== NS_STYLE_ORIENT_VERTICAL
;
560 nsNativeTheme::IsVerticalMeter(nsIFrame
* aFrame
)
562 NS_PRECONDITION(aFrame
, "You have to pass a non-null aFrame");
563 return aFrame
->StyleDisplay()->mOrient
== NS_STYLE_ORIENT_VERTICAL
;
568 nsNativeTheme::IsSubmenu(nsIFrame
* aFrame
, bool* aLeftOfParent
)
573 nsIContent
* parentContent
= aFrame
->GetContent()->GetParent();
574 if (!parentContent
|| parentContent
->Tag() != nsGkAtoms::menu
)
577 nsIFrame
* parent
= aFrame
;
578 while ((parent
= parent
->GetParent())) {
579 if (parent
->GetContent() == parentContent
) {
581 nsIntRect selfBounds
, parentBounds
;
582 aFrame
->GetNearestWidget()->GetScreenBounds(selfBounds
);
583 parent
->GetNearestWidget()->GetScreenBounds(parentBounds
);
584 *aLeftOfParent
= selfBounds
.x
< parentBounds
.x
;
594 nsNativeTheme::IsRegularMenuItem(nsIFrame
*aFrame
)
596 nsMenuFrame
*menuFrame
= do_QueryFrame(aFrame
);
597 return !(menuFrame
&& (menuFrame
->IsOnMenuBar() ||
598 menuFrame
->GetParentMenuListType() != eNotMenuList
));
602 nsNativeTheme::IsMenuListEditable(nsIFrame
*aFrame
)
604 bool isEditable
= false;
605 nsCOMPtr
<nsIDOMXULMenuListElement
> menulist
= do_QueryInterface(aFrame
->GetContent());
607 menulist
->GetEditable(&isEditable
);
612 nsNativeTheme::QueueAnimatedContentForRefresh(nsIContent
* aContent
,
613 uint32_t aMinimumFrameRate
)
615 NS_ASSERTION(aContent
, "Null pointer!");
616 NS_ASSERTION(aMinimumFrameRate
, "aMinimumFrameRate must be non-zero!");
617 NS_ASSERTION(aMinimumFrameRate
<= 1000,
618 "aMinimumFrameRate must be less than 1000!");
620 uint32_t timeout
= 1000 / aMinimumFrameRate
;
621 timeout
= std::min(mAnimatedContentTimeout
, timeout
);
623 if (!mAnimatedContentTimer
) {
624 mAnimatedContentTimer
= do_CreateInstance(NS_TIMER_CONTRACTID
);
625 NS_ENSURE_TRUE(mAnimatedContentTimer
, false);
628 if (mAnimatedContentList
.IsEmpty() || timeout
!= mAnimatedContentTimeout
) {
630 if (!mAnimatedContentList
.IsEmpty()) {
631 rv
= mAnimatedContentTimer
->Cancel();
632 NS_ENSURE_SUCCESS(rv
, false);
635 rv
= mAnimatedContentTimer
->InitWithCallback(this, timeout
,
636 nsITimer::TYPE_ONE_SHOT
);
637 NS_ENSURE_SUCCESS(rv
, false);
639 mAnimatedContentTimeout
= timeout
;
642 if (!mAnimatedContentList
.AppendElement(aContent
)) {
643 NS_WARNING("Out of memory!");
651 nsNativeTheme::Notify(nsITimer
* aTimer
)
653 NS_ASSERTION(aTimer
== mAnimatedContentTimer
, "Wrong timer!");
655 // XXX Assumes that calling nsIFrame::Invalidate won't reenter
656 // QueueAnimatedContentForRefresh.
658 uint32_t count
= mAnimatedContentList
.Length();
659 for (uint32_t index
= 0; index
< count
; index
++) {
660 nsIFrame
* frame
= mAnimatedContentList
[index
]->GetPrimaryFrame();
662 frame
->InvalidateFrame();
666 mAnimatedContentList
.Clear();
667 mAnimatedContentTimeout
= UINT32_MAX
;
672 nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(nsIFrame
* aFrame
,
678 // Find the next visible sibling.
679 nsIFrame
* sibling
= aFrame
;
681 sibling
= aNextSibling
? sibling
->GetNextSibling() : sibling
->GetPrevSibling();
682 } while (sibling
&& sibling
->GetRect().width
== 0);
684 // Check same appearance and adjacency.
686 sibling
->StyleDisplay()->mAppearance
!= aFrame
->StyleDisplay()->mAppearance
||
687 (sibling
->GetRect().XMost() != aFrame
->GetRect().x
&&
688 aFrame
->GetRect().XMost() != sibling
->GetRect().x
))
694 nsNativeTheme::IsRangeHorizontal(nsIFrame
* aFrame
)
696 nsIFrame
* rangeFrame
= aFrame
;
697 if (rangeFrame
->GetType() != nsGkAtoms::rangeFrame
) {
698 // If the thumb's frame is passed in, get its range parent:
699 rangeFrame
= aFrame
->GetParent();
701 if (rangeFrame
->GetType() == nsGkAtoms::rangeFrame
) {
702 return static_cast<nsRangeFrame
*>(rangeFrame
)->IsHorizontal();
704 // Not actually a range frame - just use the ratio of the frame's size to
706 return aFrame
->GetSize().width
>= aFrame
->GetSize().height
;
710 GetBodyFrame(nsIFrame
* aCanvasFrame
)
712 nsIContent
* content
= aCanvasFrame
->GetContent();
716 nsIDocument
* document
= content
->OwnerDoc();
717 nsIContent
* body
= document
->GetBodyElement();
721 return body
->GetPrimaryFrame();
725 nsNativeTheme::IsDarkBackground(nsIFrame
* aFrame
)
727 nsIScrollableFrame
* scrollFrame
= nullptr;
728 while (!scrollFrame
&& aFrame
) {
729 scrollFrame
= aFrame
->GetScrollTargetFrame();
730 aFrame
= aFrame
->GetParent();
735 nsIFrame
* frame
= scrollFrame
->GetScrolledFrame();
736 if (nsCSSRendering::IsCanvasFrame(frame
)) {
737 // For canvas frames, prefer to look at the body first, because the body
738 // background color is most likely what will be visible as the background
739 // color of the page, even if the html element has a different background
740 // color which prevents that of the body frame to propagate to the viewport.
741 nsIFrame
* bodyFrame
= GetBodyFrame(frame
);
746 nsStyleContext
* bgSC
= nullptr;
747 if (!nsCSSRendering::FindBackground(frame
, &bgSC
) ||
748 bgSC
->StyleBackground()->IsTransparent()) {
749 nsIFrame
* backgroundFrame
= nsCSSRendering::FindNonTransparentBackgroundFrame(frame
, true);
750 nsCSSRendering::FindBackground(backgroundFrame
, &bgSC
);
753 nscolor bgColor
= bgSC
->StyleBackground()->mBackgroundColor
;
754 // Consider the background color dark if the sum of the r, g and b values is
755 // less than 384 in a semi-transparent document. This heuristic matches what
756 // WebKit does, and we can improve it later if needed.
757 return NS_GET_A(bgColor
) > 127 &&
758 NS_GET_R(bgColor
) + NS_GET_G(bgColor
) + NS_GET_B(bgColor
) < 384;