Bug 1731994: part 7) Update documentation of `nsIContentPermissionPrompt`. r=edgar...
[gecko.git] / widget / nsNativeTheme.cpp
blob6044a0e1068b9ff7f1c766b0bde3d036994a26cb
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"
7 #include "nsIWidget.h"
8 #include "mozilla/dom/Document.h"
9 #include "nsIContent.h"
10 #include "nsIFrame.h"
11 #include "nsIScrollableFrame.h"
12 #include "nsNumberControlFrame.h"
13 #include "nsPresContext.h"
14 #include "nsString.h"
15 #include "nsNameSpaceManager.h"
16 #include "nsStyleConsts.h"
17 #include "nsPIDOMWindow.h"
18 #include "nsProgressFrame.h"
19 #include "nsMeterFrame.h"
20 #include "nsMenuFrame.h"
21 #include "nsRangeFrame.h"
22 #include "nsCSSRendering.h"
23 #include "ImageContainer.h"
24 #include "mozilla/ComputedStyle.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/PresShell.h"
31 #include "mozilla/StaticPrefs_layout.h"
32 #include "mozilla/dom/DocumentInlines.h"
33 #include "mozilla/RelativeLuminanceUtils.h"
34 #include <algorithm>
36 using namespace mozilla;
37 using namespace mozilla::dom;
39 nsNativeTheme::nsNativeTheme() : mAnimatedContentTimeout(UINT32_MAX) {}
41 NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed)
43 /* static */ EventStates nsNativeTheme::GetContentState(
44 nsIFrame* aFrame, StyleAppearance aAppearance) {
45 if (!aFrame) return EventStates();
47 bool isXULCheckboxRadio = (aAppearance == StyleAppearance::Checkbox ||
48 aAppearance == StyleAppearance::Radio) &&
49 aFrame->GetContent()->IsXULElement();
50 if (isXULCheckboxRadio) {
51 aFrame = aFrame->GetParent();
54 if (!aFrame->GetContent()) {
55 return EventStates();
58 nsIContent* frameContent = aFrame->GetContent();
59 EventStates flags;
60 if (frameContent->IsElement()) {
61 flags = frameContent->AsElement()->State();
63 nsNumberControlFrame* numberControlFrame =
64 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame);
65 if (numberControlFrame &&
66 numberControlFrame->GetContent()->AsElement()->State().HasState(
67 NS_EVENT_STATE_DISABLED)) {
68 flags |= NS_EVENT_STATE_DISABLED;
72 if (isXULCheckboxRadio && aAppearance == StyleAppearance::Radio &&
73 IsFocused(aFrame)) {
74 flags |= NS_EVENT_STATE_FOCUS;
75 nsPIDOMWindowOuter* window = aFrame->GetContent()->OwnerDoc()->GetWindow();
76 if (window && window->ShouldShowFocusRing()) {
77 flags |= NS_EVENT_STATE_FOCUSRING;
81 return flags;
84 /* static */
85 bool nsNativeTheme::CheckBooleanAttr(nsIFrame* aFrame, nsAtom* aAtom) {
86 if (!aFrame) return false;
88 nsIContent* content = aFrame->GetContent();
89 if (!content || !content->IsElement()) return false;
91 if (content->IsHTMLElement())
92 return content->AsElement()->HasAttr(kNameSpaceID_None, aAtom);
94 // For XML/XUL elements, an attribute must be equal to the literal
95 // string "true" to be counted as true. An empty string should _not_
96 // be counted as true.
97 return content->AsElement()->AttrValueIs(kNameSpaceID_None, aAtom, u"true"_ns,
98 eCaseMatters);
101 /* static */
102 int32_t nsNativeTheme::CheckIntAttr(nsIFrame* aFrame, nsAtom* aAtom,
103 int32_t defaultValue) {
104 if (!aFrame) return defaultValue;
106 nsIContent* content = aFrame->GetContent();
107 if (!content || !content->IsElement()) return defaultValue;
109 nsAutoString attr;
110 content->AsElement()->GetAttr(kNameSpaceID_None, aAtom, attr);
111 nsresult err;
112 int32_t value = attr.ToInteger(&err);
113 if (attr.IsEmpty() || NS_FAILED(err)) return defaultValue;
115 return value;
118 /* static */
119 double nsNativeTheme::GetProgressValue(nsIFrame* aFrame) {
120 if (!aFrame || !aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
121 return 0;
124 return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Value();
127 /* static */
128 double nsNativeTheme::GetProgressMaxValue(nsIFrame* aFrame) {
129 if (!aFrame || !aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
130 return 100;
133 return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Max();
136 bool nsNativeTheme::GetCheckedOrSelected(nsIFrame* aFrame,
137 bool aCheckSelected) {
138 if (!aFrame) return false;
140 nsIContent* content = aFrame->GetContent();
142 if (content->IsXULElement()) {
143 // For a XUL checkbox or radio button, the state of the parent determines
144 // the checked state
145 aFrame = aFrame->GetParent();
146 } else {
147 // Check for an HTML input element
148 HTMLInputElement* inputElt = HTMLInputElement::FromNode(content);
149 if (inputElt) {
150 return inputElt->Checked();
154 return CheckBooleanAttr(
155 aFrame, aCheckSelected ? nsGkAtoms::selected : nsGkAtoms::checked);
158 bool nsNativeTheme::IsButtonTypeMenu(nsIFrame* aFrame) {
159 if (!aFrame) return false;
161 nsIContent* content = aFrame->GetContent();
162 return content->IsXULElement(nsGkAtoms::button) &&
163 content->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
164 u"menu"_ns, eCaseMatters);
167 bool nsNativeTheme::IsPressedButton(nsIFrame* aFrame) {
168 EventStates eventState =
169 GetContentState(aFrame, StyleAppearance::Toolbarbutton);
170 if (IsDisabled(aFrame, eventState)) return false;
172 return IsOpenButton(aFrame) ||
173 eventState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER);
176 bool nsNativeTheme::GetIndeterminate(nsIFrame* aFrame) {
177 if (!aFrame) return false;
179 nsIContent* content = aFrame->GetContent();
181 if (content->IsXULElement()) {
182 // For a XUL checkbox or radio button, the state of the parent determines
183 // the state
184 return CheckBooleanAttr(aFrame->GetParent(), nsGkAtoms::indeterminate);
187 // Check for an HTML input element
188 HTMLInputElement* inputElt = HTMLInputElement::FromNode(content);
189 if (inputElt) {
190 return inputElt->Indeterminate();
193 return false;
196 bool nsNativeTheme::IsWidgetStyled(nsPresContext* aPresContext,
197 nsIFrame* aFrame,
198 StyleAppearance aAppearance) {
199 // Check for specific widgets to see if HTML has overridden the style.
200 if (!aFrame) {
201 return false;
204 // Resizers have some special handling, dependent on whether in a scrollable
205 // container or not. If so, use the scrollable container's to determine
206 // whether the style is overriden instead of the resizer. This allows a
207 // non-native transparent resizer to be used instead. Otherwise, we just
208 // fall through and return false.
209 if (aAppearance == StyleAppearance::Resizer) {
210 nsIFrame* parentFrame = aFrame->GetParent();
211 if (parentFrame && parentFrame->IsScrollFrame()) {
212 // if the parent is a scrollframe, the resizer should be native themed
213 // only if the scrollable area doesn't override the widget style.
215 // note that the condition below looks a bit suspect but it's the right
216 // one. If there's no valid appearance, then we should return true, it's
217 // effectively the same as if it had overridden the appearance.
218 parentFrame = parentFrame->GetParent();
219 if (!parentFrame) {
220 return false;
222 auto parentAppearance =
223 parentFrame->StyleDisplay()->EffectiveAppearance();
224 return parentAppearance == StyleAppearance::None ||
225 IsWidgetStyled(aPresContext, parentFrame, parentAppearance);
230 * Progress bar appearance should be the same for the bar and the container
231 * frame. nsProgressFrame owns the logic and will tell us what we should do.
233 if (aAppearance == StyleAppearance::Progresschunk ||
234 aAppearance == StyleAppearance::ProgressBar) {
235 nsProgressFrame* progressFrame = do_QueryFrame(
236 aAppearance == StyleAppearance::Progresschunk ? aFrame->GetParent()
237 : aFrame);
238 if (progressFrame) {
239 return !progressFrame->ShouldUseNativeStyle();
244 * Meter bar appearance should be the same for the bar and the container
245 * frame. nsMeterFrame owns the logic and will tell us what we should do.
247 if (aAppearance == StyleAppearance::Meterchunk ||
248 aAppearance == StyleAppearance::Meter) {
249 nsMeterFrame* meterFrame = do_QueryFrame(
250 aAppearance == StyleAppearance::Meterchunk ? aFrame->GetParent()
251 : aFrame);
252 if (meterFrame) {
253 return !meterFrame->ShouldUseNativeStyle();
258 * An nsRangeFrame and its children are treated atomically when it
259 * comes to native theming (either all parts, or no parts, are themed).
260 * nsRangeFrame owns the logic and will tell us what we should do.
262 if (aAppearance == StyleAppearance::Range ||
263 aAppearance == StyleAppearance::RangeThumb) {
264 nsRangeFrame* rangeFrame = do_QueryFrame(
265 aAppearance == StyleAppearance::RangeThumb ? aFrame->GetParent()
266 : aFrame);
267 if (rangeFrame) {
268 return !rangeFrame->ShouldUseNativeStyle();
272 return nsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming(
273 aAppearance) &&
274 aFrame->GetContent()->IsHTMLElement() &&
275 aPresContext->HasAuthorSpecifiedRules(
276 aFrame, NS_AUTHOR_SPECIFIED_BORDER_OR_BACKGROUND);
279 bool nsNativeTheme::IsDisabled(nsIFrame* aFrame, EventStates aEventStates) {
280 if (!aFrame) {
281 return false;
284 nsIContent* content = aFrame->GetContent();
285 if (!content || !content->IsElement()) {
286 return false;
289 if (content->IsHTMLElement()) {
290 return aEventStates.HasState(NS_EVENT_STATE_DISABLED);
293 // For XML/XUL elements, an attribute must be equal to the literal
294 // string "true" to be counted as true. An empty string should _not_
295 // be counted as true.
296 return content->AsElement()->AttrValueIs(
297 kNameSpaceID_None, nsGkAtoms::disabled, u"true"_ns, eCaseMatters);
300 /* static */
301 bool nsNativeTheme::IsFrameRTL(nsIFrame* aFrame) {
302 if (!aFrame) {
303 return false;
305 return aFrame->GetWritingMode().IsPhysicalRTL();
308 /* static */
309 bool nsNativeTheme::IsHTMLContent(nsIFrame* aFrame) {
310 if (!aFrame) {
311 return false;
313 nsIContent* content = aFrame->GetContent();
314 return content && content->IsHTMLElement();
317 // scrollbar button:
318 int32_t nsNativeTheme::GetScrollbarButtonType(nsIFrame* aFrame) {
319 if (!aFrame) return 0;
321 static Element::AttrValuesArray strings[] = {
322 nsGkAtoms::scrollbarDownBottom, nsGkAtoms::scrollbarDownTop,
323 nsGkAtoms::scrollbarUpBottom, nsGkAtoms::scrollbarUpTop, nullptr};
325 nsIContent* content = aFrame->GetContent();
326 if (!content || !content->IsElement()) {
327 return 0;
330 switch (content->AsElement()->FindAttrValueIn(
331 kNameSpaceID_None, nsGkAtoms::sbattr, strings, eCaseMatters)) {
332 case 0:
333 return eScrollbarButton_Down | eScrollbarButton_Bottom;
334 case 1:
335 return eScrollbarButton_Down;
336 case 2:
337 return eScrollbarButton_Bottom;
338 case 3:
339 return eScrollbarButton_UpTop;
342 return 0;
345 // treeheadercell:
346 nsNativeTheme::TreeSortDirection nsNativeTheme::GetTreeSortDirection(
347 nsIFrame* aFrame) {
348 if (!aFrame || !aFrame->GetContent()) return eTreeSortDirection_Natural;
350 static Element::AttrValuesArray strings[] = {nsGkAtoms::descending,
351 nsGkAtoms::ascending, nullptr};
353 nsIContent* content = aFrame->GetContent();
354 if (content->IsElement()) {
355 switch (content->AsElement()->FindAttrValueIn(
356 kNameSpaceID_None, nsGkAtoms::sortDirection, strings, eCaseMatters)) {
357 case 0:
358 return eTreeSortDirection_Descending;
359 case 1:
360 return eTreeSortDirection_Ascending;
364 return eTreeSortDirection_Natural;
367 bool nsNativeTheme::IsLastTreeHeaderCell(nsIFrame* aFrame) {
368 if (!aFrame) return false;
370 // A tree column picker is always the last header cell.
371 if (aFrame->GetContent()->IsXULElement(nsGkAtoms::treecolpicker)) return true;
373 // Find the parent tree.
374 nsIContent* parent = aFrame->GetContent()->GetParent();
375 while (parent && !parent->IsXULElement(nsGkAtoms::tree)) {
376 parent = parent->GetParent();
379 // If the column picker is visible, this can't be the last column.
380 if (parent && !parent->AsElement()->AttrValueIs(kNameSpaceID_None,
381 nsGkAtoms::hidecolumnpicker,
382 u"true"_ns, eCaseMatters))
383 return false;
385 while ((aFrame = aFrame->GetNextSibling())) {
386 if (aFrame->GetRect().Width() > 0) return false;
388 return true;
391 // tab:
392 bool nsNativeTheme::IsBottomTab(nsIFrame* aFrame) {
393 if (!aFrame) return false;
395 nsAutoString classStr;
396 if (aFrame->GetContent()->IsElement()) {
397 aFrame->GetContent()->AsElement()->GetAttr(kNameSpaceID_None,
398 nsGkAtoms::_class, classStr);
400 // FIXME: This looks bogus, shouldn't this be looking at GetClasses()?
401 return !classStr.IsEmpty() && classStr.Find("tab-bottom") != kNotFound;
404 bool nsNativeTheme::IsFirstTab(nsIFrame* aFrame) {
405 if (!aFrame) return false;
407 for (nsIFrame* first : aFrame->GetParent()->PrincipalChildList()) {
408 if (first->GetRect().Width() > 0 &&
409 first->GetContent()->IsXULElement(nsGkAtoms::tab))
410 return (first == aFrame);
412 return false;
415 bool nsNativeTheme::IsHorizontal(nsIFrame* aFrame) {
416 if (!aFrame) return false;
418 if (!aFrame->GetContent()->IsElement()) return true;
420 return !aFrame->GetContent()->AsElement()->AttrValueIs(
421 kNameSpaceID_None, nsGkAtoms::orient, nsGkAtoms::vertical, eCaseMatters);
424 bool nsNativeTheme::IsNextToSelectedTab(nsIFrame* aFrame, int32_t aOffset) {
425 if (!aFrame) return false;
427 if (aOffset == 0) return IsSelectedTab(aFrame);
429 int32_t thisTabIndex = -1, selectedTabIndex = -1;
431 nsIFrame* currentTab = aFrame->GetParent()->PrincipalChildList().FirstChild();
432 for (int32_t i = 0; currentTab; currentTab = currentTab->GetNextSibling()) {
433 if (currentTab->GetRect().Width() == 0) continue;
434 if (aFrame == currentTab) thisTabIndex = i;
435 if (IsSelectedTab(currentTab)) selectedTabIndex = i;
436 ++i;
439 if (thisTabIndex == -1 || selectedTabIndex == -1) return false;
441 return (thisTabIndex - selectedTabIndex == aOffset);
444 bool nsNativeTheme::IsIndeterminateProgress(nsIFrame* aFrame,
445 EventStates aEventStates) {
446 if (!aFrame || !aFrame->GetContent() ||
447 !aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
448 return false;
451 return aEventStates.HasState(NS_EVENT_STATE_INDETERMINATE);
454 bool nsNativeTheme::IsVerticalProgress(nsIFrame* aFrame) {
455 if (!aFrame) {
456 return false;
458 return IsVerticalMeter(aFrame);
461 bool nsNativeTheme::IsVerticalMeter(nsIFrame* aFrame) {
462 MOZ_ASSERT(aFrame, "You have to pass a non-null aFrame");
463 switch (aFrame->StyleDisplay()->mOrient) {
464 case StyleOrient::Horizontal:
465 return false;
466 case StyleOrient::Vertical:
467 return true;
468 case StyleOrient::Inline:
469 return aFrame->GetWritingMode().IsVertical();
470 case StyleOrient::Block:
471 return !aFrame->GetWritingMode().IsVertical();
473 MOZ_ASSERT_UNREACHABLE("unexpected -moz-orient value");
474 return false;
477 // menupopup:
478 bool nsNativeTheme::IsSubmenu(nsIFrame* aFrame, bool* aLeftOfParent) {
479 if (!aFrame) return false;
481 nsIContent* parentContent = aFrame->GetContent()->GetParent();
482 if (!parentContent || !parentContent->IsXULElement(nsGkAtoms::menu))
483 return false;
485 nsIFrame* parent = aFrame;
486 while ((parent = parent->GetParent())) {
487 if (parent->GetContent() == parentContent) {
488 if (aLeftOfParent) {
489 LayoutDeviceIntRect selfBounds, parentBounds;
490 selfBounds = aFrame->GetNearestWidget()->GetScreenBounds();
491 parentBounds = parent->GetNearestWidget()->GetScreenBounds();
492 *aLeftOfParent = selfBounds.X() < parentBounds.X();
494 return true;
498 return false;
501 bool nsNativeTheme::IsRegularMenuItem(nsIFrame* aFrame) {
502 nsMenuFrame* menuFrame = do_QueryFrame(aFrame);
503 return !(menuFrame &&
504 (menuFrame->IsOnMenuBar() || menuFrame->IsParentMenuList()));
507 bool nsNativeTheme::QueueAnimatedContentForRefresh(nsIContent* aContent,
508 uint32_t aMinimumFrameRate) {
509 NS_ASSERTION(aContent, "Null pointer!");
510 NS_ASSERTION(aMinimumFrameRate, "aMinimumFrameRate must be non-zero!");
511 NS_ASSERTION(aMinimumFrameRate <= 1000,
512 "aMinimumFrameRate must be less than 1000!");
514 uint32_t timeout = 1000 / aMinimumFrameRate;
515 timeout = std::min(mAnimatedContentTimeout, timeout);
517 if (!mAnimatedContentTimer) {
518 mAnimatedContentTimer = NS_NewTimer();
519 NS_ENSURE_TRUE(mAnimatedContentTimer, false);
522 if (mAnimatedContentList.IsEmpty() || timeout != mAnimatedContentTimeout) {
523 nsresult rv;
524 if (!mAnimatedContentList.IsEmpty()) {
525 rv = mAnimatedContentTimer->Cancel();
526 NS_ENSURE_SUCCESS(rv, false);
529 if (XRE_IsContentProcess() && NS_IsMainThread()) {
530 mAnimatedContentTimer->SetTarget(
531 aContent->OwnerDoc()->EventTargetFor(TaskCategory::Other));
533 rv = mAnimatedContentTimer->InitWithCallback(this, timeout,
534 nsITimer::TYPE_ONE_SHOT);
535 NS_ENSURE_SUCCESS(rv, false);
537 mAnimatedContentTimeout = timeout;
540 // XXX(Bug 1631371) Check if this should use a fallible operation as it
541 // pretended earlier.
542 mAnimatedContentList.AppendElement(aContent);
544 return true;
547 NS_IMETHODIMP
548 nsNativeTheme::Notify(nsITimer* aTimer) {
549 NS_ASSERTION(aTimer == mAnimatedContentTimer, "Wrong timer!");
551 // XXX Assumes that calling nsIFrame::Invalidate won't reenter
552 // QueueAnimatedContentForRefresh.
554 uint32_t count = mAnimatedContentList.Length();
555 for (uint32_t index = 0; index < count; index++) {
556 nsIFrame* frame = mAnimatedContentList[index]->GetPrimaryFrame();
557 if (frame) {
558 frame->InvalidateFrame();
562 mAnimatedContentList.Clear();
563 mAnimatedContentTimeout = UINT32_MAX;
564 return NS_OK;
567 NS_IMETHODIMP
568 nsNativeTheme::GetName(nsACString& aName) {
569 aName.AssignLiteral("nsNativeTheme");
570 return NS_OK;
573 nsIFrame* nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(
574 nsIFrame* aFrame, bool aNextSibling) {
575 if (!aFrame) return nullptr;
577 // Find the next visible sibling.
578 nsIFrame* sibling = aFrame;
579 do {
580 sibling =
581 aNextSibling ? sibling->GetNextSibling() : sibling->GetPrevSibling();
582 } while (sibling && sibling->GetRect().Width() == 0);
584 // Check same appearance and adjacency.
585 if (!sibling ||
586 sibling->StyleDisplay()->EffectiveAppearance() !=
587 aFrame->StyleDisplay()->EffectiveAppearance() ||
588 (sibling->GetRect().XMost() != aFrame->GetRect().X() &&
589 aFrame->GetRect().XMost() != sibling->GetRect().X()))
590 return nullptr;
591 return sibling;
594 bool nsNativeTheme::IsRangeHorizontal(nsIFrame* aFrame) {
595 nsIFrame* rangeFrame = aFrame;
596 if (!rangeFrame->IsRangeFrame()) {
597 // If the thumb's frame is passed in, get its range parent:
598 rangeFrame = aFrame->GetParent();
600 if (rangeFrame->IsRangeFrame()) {
601 return static_cast<nsRangeFrame*>(rangeFrame)->IsHorizontal();
603 // Not actually a range frame - just use the ratio of the frame's size to
604 // decide:
605 return aFrame->GetSize().width >= aFrame->GetSize().height;
608 static nsIFrame* GetBodyFrame(nsIFrame* aCanvasFrame) {
609 nsIContent* body = aCanvasFrame->PresContext()->Document()->GetBodyElement();
610 if (!body) {
611 return nullptr;
613 return body->GetPrimaryFrame();
616 bool nsNativeTheme::IsDarkColor(nscolor aColor) {
617 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
618 // threshold that tells us whether contrast is better against white or black.
620 // Contrast ratio against black is: (L + 0.05) / 0.05
621 // Contrast ratio against white is: 1.05 / (L + 0.05)
623 // So the intersection is:
625 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
627 // And the solution to that equation is:
629 // sqrt(1.05 * 0.05) - 0.05
631 // So we consider a color dark if the contrast is below this threshold, and
632 // it's at least half-opaque.
633 constexpr float kThreshold = 0.179129;
634 return NS_GET_A(aColor) > 127 &&
635 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
638 /* static */
639 bool nsNativeTheme::IsDarkBackground(nsIFrame* aFrame) {
640 // Try to find the scrolled frame. Note that for stuff like xul <tree> there
641 // might be none.
643 nsIFrame* frame = aFrame;
644 nsIScrollableFrame* scrollFrame = nullptr;
645 while (!scrollFrame && frame) {
646 scrollFrame = frame->GetScrollTargetFrame();
647 frame = frame->GetParent();
649 if (scrollFrame) {
650 aFrame = scrollFrame->GetScrolledFrame();
651 } else {
652 // Leave aFrame untouched.
656 auto backgroundFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(
657 aFrame, /* aStopAtThemed = */ false);
658 if (!backgroundFrame.mFrame) {
659 return false;
662 nscolor color = backgroundFrame.mFrame->StyleBackground()->BackgroundColor(
663 backgroundFrame.mFrame);
665 if (backgroundFrame.mIsForCanvas) {
666 // For canvas frames, prefer to look at the body first, because the body
667 // background color is most likely what will be visible as the background
668 // color of the page, even if the html element has a different background
669 // color which prevents that of the body frame to propagate to the viewport.
670 if (nsIFrame* bodyFrame = GetBodyFrame(aFrame)) {
671 nscolor bodyColor =
672 bodyFrame->StyleBackground()->BackgroundColor(bodyFrame);
673 if (NS_GET_A(bodyColor)) {
674 color = bodyColor;
679 return IsDarkColor(color);
682 /*static*/
683 bool nsNativeTheme::IsWidgetScrollbarPart(StyleAppearance aAppearance) {
684 switch (aAppearance) {
685 case StyleAppearance::ScrollbarVertical:
686 case StyleAppearance::ScrollbarHorizontal:
687 case StyleAppearance::ScrollbarbuttonUp:
688 case StyleAppearance::ScrollbarbuttonDown:
689 case StyleAppearance::ScrollbarbuttonLeft:
690 case StyleAppearance::ScrollbarbuttonRight:
691 case StyleAppearance::ScrollbarthumbVertical:
692 case StyleAppearance::ScrollbarthumbHorizontal:
693 case StyleAppearance::ScrollbartrackHorizontal:
694 case StyleAppearance::ScrollbartrackVertical:
695 case StyleAppearance::Scrollcorner:
696 return true;
697 default:
698 return false;