Bug 1510695 - Fix URL comparisons in reftestWait r=jgraham
[gecko.git] / widget / nsNativeTheme.cpp
blob024af294a1b7ff82b622cc21bfe56de3dc068d6b
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 "nsIPresShell.h"
12 #include "nsNumberControlFrame.h"
13 #include "nsPresContext.h"
14 #include "nsString.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/ComputedStyle.h"
26 #include "mozilla/EventStates.h"
27 #include "mozilla/dom/Element.h"
28 #include "mozilla/dom/HTMLBodyElement.h"
29 #include "mozilla/dom/HTMLInputElement.h"
30 #include "mozilla/dom/HTMLProgressElement.h"
31 #include "mozilla/StaticPrefs.h"
32 #include "mozilla/dom/DocumentInlines.h"
33 #include <algorithm>
35 using namespace mozilla;
36 using namespace mozilla::dom;
38 nsNativeTheme::nsNativeTheme() : mAnimatedContentTimeout(UINT32_MAX) {}
40 NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed)
42 nsIPresShell* nsNativeTheme::GetPresShell(nsIFrame* aFrame) {
43 if (!aFrame) return nullptr;
45 nsPresContext* context = aFrame->PresContext();
46 return context ? context->GetPresShell() : nullptr;
49 EventStates nsNativeTheme::GetContentState(nsIFrame* aFrame,
50 StyleAppearance aAppearance) {
51 if (!aFrame) return EventStates();
53 bool isXULCheckboxRadio = (aAppearance == StyleAppearance::Checkbox ||
54 aAppearance == StyleAppearance::Radio) &&
55 aFrame->GetContent()->IsXULElement();
56 if (isXULCheckboxRadio) aFrame = aFrame->GetParent();
58 if (!aFrame->GetContent()) return EventStates();
60 nsIPresShell* shell = GetPresShell(aFrame);
61 if (!shell) return EventStates();
63 nsIContent* frameContent = aFrame->GetContent();
64 EventStates flags;
65 if (frameContent->IsElement()) {
66 flags = frameContent->AsElement()->State();
68 // <input type=number> needs special handling since its nested native
69 // anonymous <input type=text> takes focus for it.
70 if (aAppearance == StyleAppearance::NumberInput &&
71 frameContent->IsHTMLElement(nsGkAtoms::input)) {
72 nsNumberControlFrame* numberControlFrame = do_QueryFrame(aFrame);
73 if (numberControlFrame && numberControlFrame->IsFocused()) {
74 flags |= NS_EVENT_STATE_FOCUS;
78 nsNumberControlFrame* numberControlFrame =
79 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame);
80 if (numberControlFrame &&
81 numberControlFrame->GetContent()->AsElement()->State().HasState(
82 NS_EVENT_STATE_DISABLED)) {
83 flags |= NS_EVENT_STATE_DISABLED;
87 if (isXULCheckboxRadio && aAppearance == StyleAppearance::Radio) {
88 if (IsFocused(aFrame)) flags |= NS_EVENT_STATE_FOCUS;
91 // On Windows and Mac, only draw focus rings if they should be shown. This
92 // means that focus rings are only shown once the keyboard has been used to
93 // focus something in the window.
94 #if defined(XP_MACOSX)
95 // Mac always draws focus rings for textboxes and lists.
96 if (aAppearance == StyleAppearance::MenulistTextfield ||
97 aAppearance == StyleAppearance::NumberInput ||
98 aAppearance == StyleAppearance::Textfield ||
99 aAppearance == StyleAppearance::Textarea ||
100 aAppearance == StyleAppearance::Searchfield ||
101 aAppearance == StyleAppearance::Listbox) {
102 return flags;
104 #endif
105 #if defined(XP_WIN)
106 // On Windows, focused buttons are always drawn as such by the native theme.
107 if (aAppearance == StyleAppearance::Button) return flags;
108 #endif
109 #if defined(XP_MACOSX) || defined(XP_WIN)
110 Document* doc = aFrame->GetContent()->OwnerDoc();
111 nsPIDOMWindowOuter* window = doc->GetWindow();
112 if (window && !window->ShouldShowFocusRing()) flags &= ~NS_EVENT_STATE_FOCUS;
113 #endif
115 return flags;
118 /* static */
119 bool nsNativeTheme::CheckBooleanAttr(nsIFrame* aFrame, nsAtom* aAtom) {
120 if (!aFrame) return false;
122 nsIContent* content = aFrame->GetContent();
123 if (!content || !content->IsElement()) return false;
125 if (content->IsHTMLElement())
126 return content->AsElement()->HasAttr(kNameSpaceID_None, aAtom);
128 // For XML/XUL elements, an attribute must be equal to the literal
129 // string "true" to be counted as true. An empty string should _not_
130 // be counted as true.
131 return content->AsElement()->AttrValueIs(
132 kNameSpaceID_None, aAtom, NS_LITERAL_STRING("true"), eCaseMatters);
135 /* static */
136 int32_t nsNativeTheme::CheckIntAttr(nsIFrame* aFrame, nsAtom* aAtom,
137 int32_t defaultValue) {
138 if (!aFrame) return defaultValue;
140 nsIContent* content = aFrame->GetContent();
141 if (!content || !content->IsElement()) return defaultValue;
143 nsAutoString attr;
144 content->AsElement()->GetAttr(kNameSpaceID_None, aAtom, attr);
145 nsresult err;
146 int32_t value = attr.ToInteger(&err);
147 if (attr.IsEmpty() || NS_FAILED(err)) return defaultValue;
149 return value;
152 /* static */
153 double nsNativeTheme::GetProgressValue(nsIFrame* aFrame) {
154 if (!aFrame || !aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
155 return 0;
158 return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Value();
161 /* static */
162 double nsNativeTheme::GetProgressMaxValue(nsIFrame* aFrame) {
163 if (!aFrame || !aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
164 return 100;
167 return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Max();
170 bool nsNativeTheme::GetCheckedOrSelected(nsIFrame* aFrame,
171 bool aCheckSelected) {
172 if (!aFrame) return false;
174 nsIContent* content = aFrame->GetContent();
176 if (content->IsXULElement()) {
177 // For a XUL checkbox or radio button, the state of the parent determines
178 // the checked state
179 aFrame = aFrame->GetParent();
180 } else {
181 // Check for an HTML input element
182 HTMLInputElement* inputElt = HTMLInputElement::FromNode(content);
183 if (inputElt) {
184 return inputElt->Checked();
188 return CheckBooleanAttr(
189 aFrame, aCheckSelected ? nsGkAtoms::selected : nsGkAtoms::checked);
192 bool nsNativeTheme::IsButtonTypeMenu(nsIFrame* aFrame) {
193 if (!aFrame) return false;
195 nsIContent* content = aFrame->GetContent();
196 return content->IsXULElement(nsGkAtoms::button) &&
197 content->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
198 NS_LITERAL_STRING("menu"),
199 eCaseMatters);
202 bool nsNativeTheme::IsPressedButton(nsIFrame* aFrame) {
203 EventStates eventState =
204 GetContentState(aFrame, StyleAppearance::Toolbarbutton);
205 if (IsDisabled(aFrame, eventState)) return false;
207 return IsOpenButton(aFrame) ||
208 eventState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER);
211 bool nsNativeTheme::GetIndeterminate(nsIFrame* aFrame) {
212 if (!aFrame) return false;
214 nsIContent* content = aFrame->GetContent();
216 if (content->IsXULElement()) {
217 // For a XUL checkbox or radio button, the state of the parent determines
218 // the state
219 return CheckBooleanAttr(aFrame->GetParent(), nsGkAtoms::indeterminate);
222 // Check for an HTML input element
223 HTMLInputElement* inputElt = HTMLInputElement::FromNode(content);
224 if (inputElt) {
225 return inputElt->Indeterminate();
228 return false;
231 bool nsNativeTheme::IsWidgetStyled(nsPresContext* aPresContext,
232 nsIFrame* aFrame,
233 StyleAppearance aAppearance) {
234 // Check for specific widgets to see if HTML has overridden the style.
235 if (!aFrame) return false;
237 // Resizers have some special handling, dependent on whether in a scrollable
238 // container or not. If so, use the scrollable container's to determine
239 // whether the style is overriden instead of the resizer. This allows a
240 // non-native transparent resizer to be used instead. Otherwise, we just
241 // fall through and return false.
242 if (aAppearance == StyleAppearance::Resizer) {
243 nsIFrame* parentFrame = aFrame->GetParent();
244 if (parentFrame && parentFrame->IsScrollFrame()) {
245 // if the parent is a scrollframe, the resizer should be native themed
246 // only if the scrollable area doesn't override the widget style.
247 parentFrame = parentFrame->GetParent();
248 if (parentFrame) {
249 return IsWidgetStyled(aPresContext, parentFrame,
250 parentFrame->StyleDisplay()->mAppearance);
256 * Progress bar appearance should be the same for the bar and the container
257 * frame. nsProgressFrame owns the logic and will tell us what we should do.
259 if (aAppearance == StyleAppearance::Progresschunk ||
260 aAppearance == StyleAppearance::ProgressBar) {
261 nsProgressFrame* progressFrame = do_QueryFrame(
262 aAppearance == StyleAppearance::Progresschunk ? aFrame->GetParent()
263 : aFrame);
264 if (progressFrame) {
265 return !progressFrame->ShouldUseNativeStyle();
270 * Meter bar appearance should be the same for the bar and the container
271 * frame. nsMeterFrame owns the logic and will tell us what we should do.
273 if (aAppearance == StyleAppearance::Meterchunk ||
274 aAppearance == StyleAppearance::Meter) {
275 nsMeterFrame* meterFrame = do_QueryFrame(
276 aAppearance == StyleAppearance::Meterchunk ? aFrame->GetParent()
277 : aFrame);
278 if (meterFrame) {
279 return !meterFrame->ShouldUseNativeStyle();
284 * An nsRangeFrame and its children are treated atomically when it
285 * comes to native theming (either all parts, or no parts, are themed).
286 * nsRangeFrame owns the logic and will tell us what we should do.
288 if (aAppearance == StyleAppearance::Range ||
289 aAppearance == StyleAppearance::RangeThumb) {
290 nsRangeFrame* rangeFrame = do_QueryFrame(
291 aAppearance == StyleAppearance::RangeThumb ? aFrame->GetParent()
292 : aFrame);
293 if (rangeFrame) {
294 return !rangeFrame->ShouldUseNativeStyle();
298 if (aAppearance == StyleAppearance::SpinnerUpbutton ||
299 aAppearance == StyleAppearance::SpinnerDownbutton) {
300 nsNumberControlFrame* numberControlFrame =
301 nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame);
302 if (numberControlFrame) {
303 return !numberControlFrame->ShouldUseNativeStyleForSpinner();
307 return (aAppearance == StyleAppearance::NumberInput ||
308 aAppearance == StyleAppearance::Button ||
309 aAppearance == StyleAppearance::MenulistTextfield ||
310 aAppearance == StyleAppearance::Textfield ||
311 aAppearance == StyleAppearance::Textarea ||
312 aAppearance == StyleAppearance::Listbox ||
313 aAppearance == StyleAppearance::Menulist ||
314 (aAppearance == StyleAppearance::MenulistButton &&
315 StaticPrefs::layout_css_webkit_appearance_enabled())) &&
316 aFrame->GetContent()->IsHTMLElement() &&
317 aPresContext->HasAuthorSpecifiedRules(
318 aFrame,
319 NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND);
322 bool nsNativeTheme::IsDisabled(nsIFrame* aFrame, EventStates aEventStates) {
323 if (!aFrame) {
324 return false;
327 nsIContent* content = aFrame->GetContent();
328 if (!content || !content->IsElement()) {
329 return false;
332 if (content->IsHTMLElement()) {
333 return aEventStates.HasState(NS_EVENT_STATE_DISABLED);
336 // For XML/XUL elements, an attribute must be equal to the literal
337 // string "true" to be counted as true. An empty string should _not_
338 // be counted as true.
339 return content->AsElement()->AttrValueIs(
340 kNameSpaceID_None, nsGkAtoms::disabled, NS_LITERAL_STRING("true"),
341 eCaseMatters);
344 /* static */ bool nsNativeTheme::IsFrameRTL(nsIFrame* aFrame) {
345 if (!aFrame) {
346 return false;
348 WritingMode wm = aFrame->GetWritingMode();
349 return !(wm.IsVertical() ? wm.IsVerticalLR() : wm.IsBidiLTR());
352 bool nsNativeTheme::IsHTMLContent(nsIFrame* aFrame) {
353 if (!aFrame) {
354 return false;
356 nsIContent* content = aFrame->GetContent();
357 return content && content->IsHTMLElement();
360 // scrollbar button:
361 int32_t nsNativeTheme::GetScrollbarButtonType(nsIFrame* aFrame) {
362 if (!aFrame) return 0;
364 static Element::AttrValuesArray strings[] = {
365 nsGkAtoms::scrollbarDownBottom, nsGkAtoms::scrollbarDownTop,
366 nsGkAtoms::scrollbarUpBottom, nsGkAtoms::scrollbarUpTop, nullptr};
368 nsIContent* content = aFrame->GetContent();
369 if (!content || !content->IsElement()) {
370 return 0;
373 switch (content->AsElement()->FindAttrValueIn(
374 kNameSpaceID_None, nsGkAtoms::sbattr, strings, eCaseMatters)) {
375 case 0:
376 return eScrollbarButton_Down | eScrollbarButton_Bottom;
377 case 1:
378 return eScrollbarButton_Down;
379 case 2:
380 return eScrollbarButton_Bottom;
381 case 3:
382 return eScrollbarButton_UpTop;
385 return 0;
388 // treeheadercell:
389 nsNativeTheme::TreeSortDirection nsNativeTheme::GetTreeSortDirection(
390 nsIFrame* aFrame) {
391 if (!aFrame || !aFrame->GetContent()) return eTreeSortDirection_Natural;
393 static Element::AttrValuesArray strings[] = {nsGkAtoms::descending,
394 nsGkAtoms::ascending, nullptr};
396 nsIContent* content = aFrame->GetContent();
397 if (content->IsElement()) {
398 switch (content->AsElement()->FindAttrValueIn(
399 kNameSpaceID_None, nsGkAtoms::sortDirection, strings, eCaseMatters)) {
400 case 0:
401 return eTreeSortDirection_Descending;
402 case 1:
403 return eTreeSortDirection_Ascending;
407 return eTreeSortDirection_Natural;
410 bool nsNativeTheme::IsLastTreeHeaderCell(nsIFrame* aFrame) {
411 if (!aFrame) return false;
413 // A tree column picker is always the last header cell.
414 if (aFrame->GetContent()->IsXULElement(nsGkAtoms::treecolpicker)) return true;
416 // Find the parent tree.
417 nsIContent* parent = aFrame->GetContent()->GetParent();
418 while (parent && !parent->IsXULElement(nsGkAtoms::tree)) {
419 parent = parent->GetParent();
422 // If the column picker is visible, this can't be the last column.
423 if (parent && !parent->AsElement()->AttrValueIs(
424 kNameSpaceID_None, nsGkAtoms::hidecolumnpicker,
425 NS_LITERAL_STRING("true"), eCaseMatters))
426 return false;
428 while ((aFrame = aFrame->GetNextSibling())) {
429 if (aFrame->GetRect().Width() > 0) return false;
431 return true;
434 // tab:
435 bool nsNativeTheme::IsBottomTab(nsIFrame* aFrame) {
436 if (!aFrame) return false;
438 nsAutoString classStr;
439 if (aFrame->GetContent()->IsElement()) {
440 aFrame->GetContent()->AsElement()->GetAttr(kNameSpaceID_None,
441 nsGkAtoms::_class, classStr);
443 // FIXME: This looks bogus, shouldn't this be looking at GetClasses()?
444 return !classStr.IsEmpty() && classStr.Find("tab-bottom") != kNotFound;
447 bool nsNativeTheme::IsFirstTab(nsIFrame* aFrame) {
448 if (!aFrame) return false;
450 for (nsIFrame* first : aFrame->GetParent()->PrincipalChildList()) {
451 if (first->GetRect().Width() > 0 &&
452 first->GetContent()->IsXULElement(nsGkAtoms::tab))
453 return (first == aFrame);
455 return false;
458 bool nsNativeTheme::IsHorizontal(nsIFrame* aFrame) {
459 if (!aFrame) return false;
461 if (!aFrame->GetContent()->IsElement()) return true;
463 return !aFrame->GetContent()->AsElement()->AttrValueIs(
464 kNameSpaceID_None, nsGkAtoms::orient, nsGkAtoms::vertical, eCaseMatters);
467 bool nsNativeTheme::IsNextToSelectedTab(nsIFrame* aFrame, int32_t aOffset) {
468 if (!aFrame) return false;
470 if (aOffset == 0) return IsSelectedTab(aFrame);
472 int32_t thisTabIndex = -1, selectedTabIndex = -1;
474 nsIFrame* currentTab = aFrame->GetParent()->PrincipalChildList().FirstChild();
475 for (int32_t i = 0; currentTab; currentTab = currentTab->GetNextSibling()) {
476 if (currentTab->GetRect().Width() == 0) continue;
477 if (aFrame == currentTab) thisTabIndex = i;
478 if (IsSelectedTab(currentTab)) selectedTabIndex = i;
479 ++i;
482 if (thisTabIndex == -1 || selectedTabIndex == -1) return false;
484 return (thisTabIndex - selectedTabIndex == aOffset);
487 bool nsNativeTheme::IsIndeterminateProgress(nsIFrame* aFrame,
488 EventStates aEventStates) {
489 if (!aFrame || !aFrame->GetContent() ||
490 !aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
491 return false;
494 return aEventStates.HasState(NS_EVENT_STATE_INDETERMINATE);
497 bool nsNativeTheme::IsVerticalProgress(nsIFrame* aFrame) {
498 if (!aFrame) {
499 return false;
501 return IsVerticalMeter(aFrame);
504 bool nsNativeTheme::IsVerticalMeter(nsIFrame* aFrame) {
505 MOZ_ASSERT(aFrame, "You have to pass a non-null aFrame");
506 switch (aFrame->StyleDisplay()->mOrient) {
507 case StyleOrient::Horizontal:
508 return false;
509 case StyleOrient::Vertical:
510 return true;
511 case StyleOrient::Inline:
512 return aFrame->GetWritingMode().IsVertical();
513 case StyleOrient::Block:
514 return !aFrame->GetWritingMode().IsVertical();
516 MOZ_ASSERT_UNREACHABLE("unexpected -moz-orient value");
517 return false;
520 // menupopup:
521 bool nsNativeTheme::IsSubmenu(nsIFrame* aFrame, bool* aLeftOfParent) {
522 if (!aFrame) return false;
524 nsIContent* parentContent = aFrame->GetContent()->GetParent();
525 if (!parentContent || !parentContent->IsXULElement(nsGkAtoms::menu))
526 return false;
528 nsIFrame* parent = aFrame;
529 while ((parent = parent->GetParent())) {
530 if (parent->GetContent() == parentContent) {
531 if (aLeftOfParent) {
532 LayoutDeviceIntRect selfBounds, parentBounds;
533 selfBounds = aFrame->GetNearestWidget()->GetScreenBounds();
534 parentBounds = parent->GetNearestWidget()->GetScreenBounds();
535 *aLeftOfParent = selfBounds.X() < parentBounds.X();
537 return true;
541 return false;
544 bool nsNativeTheme::IsRegularMenuItem(nsIFrame* aFrame) {
545 nsMenuFrame* menuFrame = do_QueryFrame(aFrame);
546 return !(menuFrame &&
547 (menuFrame->IsOnMenuBar() || menuFrame->IsParentMenuList()));
550 bool nsNativeTheme::QueueAnimatedContentForRefresh(nsIContent* aContent,
551 uint32_t aMinimumFrameRate) {
552 NS_ASSERTION(aContent, "Null pointer!");
553 NS_ASSERTION(aMinimumFrameRate, "aMinimumFrameRate must be non-zero!");
554 NS_ASSERTION(aMinimumFrameRate <= 1000,
555 "aMinimumFrameRate must be less than 1000!");
557 uint32_t timeout = 1000 / aMinimumFrameRate;
558 timeout = std::min(mAnimatedContentTimeout, timeout);
560 if (!mAnimatedContentTimer) {
561 mAnimatedContentTimer = NS_NewTimer();
562 NS_ENSURE_TRUE(mAnimatedContentTimer, false);
565 if (mAnimatedContentList.IsEmpty() || timeout != mAnimatedContentTimeout) {
566 nsresult rv;
567 if (!mAnimatedContentList.IsEmpty()) {
568 rv = mAnimatedContentTimer->Cancel();
569 NS_ENSURE_SUCCESS(rv, false);
572 if (XRE_IsContentProcess() && NS_IsMainThread()) {
573 mAnimatedContentTimer->SetTarget(
574 aContent->OwnerDoc()->EventTargetFor(TaskCategory::Other));
576 rv = mAnimatedContentTimer->InitWithCallback(this, timeout,
577 nsITimer::TYPE_ONE_SHOT);
578 NS_ENSURE_SUCCESS(rv, false);
580 mAnimatedContentTimeout = timeout;
583 if (!mAnimatedContentList.AppendElement(aContent)) {
584 NS_WARNING("Out of memory!");
585 return false;
588 return true;
591 NS_IMETHODIMP
592 nsNativeTheme::Notify(nsITimer* aTimer) {
593 NS_ASSERTION(aTimer == mAnimatedContentTimer, "Wrong timer!");
595 // XXX Assumes that calling nsIFrame::Invalidate won't reenter
596 // QueueAnimatedContentForRefresh.
598 uint32_t count = mAnimatedContentList.Length();
599 for (uint32_t index = 0; index < count; index++) {
600 nsIFrame* frame = mAnimatedContentList[index]->GetPrimaryFrame();
601 if (frame) {
602 frame->InvalidateFrame();
606 mAnimatedContentList.Clear();
607 mAnimatedContentTimeout = UINT32_MAX;
608 return NS_OK;
611 NS_IMETHODIMP
612 nsNativeTheme::GetName(nsACString& aName) {
613 aName.AssignLiteral("nsNativeTheme");
614 return NS_OK;
617 nsIFrame* nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(
618 nsIFrame* aFrame, bool aNextSibling) {
619 if (!aFrame) return nullptr;
621 // Find the next visible sibling.
622 nsIFrame* sibling = aFrame;
623 do {
624 sibling =
625 aNextSibling ? sibling->GetNextSibling() : sibling->GetPrevSibling();
626 } while (sibling && sibling->GetRect().Width() == 0);
628 // Check same appearance and adjacency.
629 if (!sibling ||
630 sibling->StyleDisplay()->mAppearance !=
631 aFrame->StyleDisplay()->mAppearance ||
632 (sibling->GetRect().XMost() != aFrame->GetRect().X() &&
633 aFrame->GetRect().XMost() != sibling->GetRect().X()))
634 return nullptr;
635 return sibling;
638 bool nsNativeTheme::IsRangeHorizontal(nsIFrame* aFrame) {
639 nsIFrame* rangeFrame = aFrame;
640 if (!rangeFrame->IsRangeFrame()) {
641 // If the thumb's frame is passed in, get its range parent:
642 rangeFrame = aFrame->GetParent();
644 if (rangeFrame->IsRangeFrame()) {
645 return static_cast<nsRangeFrame*>(rangeFrame)->IsHorizontal();
647 // Not actually a range frame - just use the ratio of the frame's size to
648 // decide:
649 return aFrame->GetSize().width >= aFrame->GetSize().height;
652 static nsIFrame* GetBodyFrame(nsIFrame* aCanvasFrame) {
653 nsIContent* content = aCanvasFrame->GetContent();
654 if (!content) {
655 return nullptr;
657 nsIContent* body = content->OwnerDoc()->GetBodyElement();
658 if (!body) {
659 return nullptr;
661 return body->GetPrimaryFrame();
664 bool nsNativeTheme::IsDarkBackground(nsIFrame* aFrame) {
665 nsIScrollableFrame* scrollFrame = nullptr;
666 while (!scrollFrame && aFrame) {
667 scrollFrame = aFrame->GetScrollTargetFrame();
668 aFrame = aFrame->GetParent();
670 if (!scrollFrame) return false;
672 nsIFrame* frame = scrollFrame->GetScrolledFrame();
673 if (nsCSSRendering::IsCanvasFrame(frame)) {
674 // For canvas frames, prefer to look at the body first, because the body
675 // background color is most likely what will be visible as the background
676 // color of the page, even if the html element has a different background
677 // color which prevents that of the body frame to propagate to the viewport.
678 nsIFrame* bodyFrame = GetBodyFrame(frame);
679 if (bodyFrame) {
680 frame = bodyFrame;
683 ComputedStyle* bgSC = nullptr;
684 if (!nsCSSRendering::FindBackground(frame, &bgSC) ||
685 bgSC->StyleBackground()->IsTransparent(bgSC)) {
686 nsIFrame* backgroundFrame =
687 nsCSSRendering::FindNonTransparentBackgroundFrame(frame, true);
688 nsCSSRendering::FindBackground(backgroundFrame, &bgSC);
690 if (bgSC) {
691 nscolor bgColor = bgSC->StyleBackground()->BackgroundColor(bgSC);
692 // Consider the background color dark if the sum of the r, g and b values is
693 // less than 384 in a semi-transparent document. This heuristic matches
694 // what WebKit does, and we can improve it later if needed.
695 return NS_GET_A(bgColor) > 127 &&
696 NS_GET_R(bgColor) + NS_GET_G(bgColor) + NS_GET_B(bgColor) < 384;
698 return false;
701 bool nsNativeTheme::IsWidgetScrollbarPart(StyleAppearance aAppearance) {
702 switch (aAppearance) {
703 case StyleAppearance::Scrollbar:
704 case StyleAppearance::ScrollbarSmall:
705 case StyleAppearance::ScrollbarVertical:
706 case StyleAppearance::ScrollbarHorizontal:
707 case StyleAppearance::ScrollbarbuttonUp:
708 case StyleAppearance::ScrollbarbuttonDown:
709 case StyleAppearance::ScrollbarbuttonLeft:
710 case StyleAppearance::ScrollbarbuttonRight:
711 case StyleAppearance::ScrollbarthumbVertical:
712 case StyleAppearance::ScrollbarthumbHorizontal:
713 case StyleAppearance::Scrollcorner:
714 return true;
715 default:
716 return false;