Line data Source code
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/. */
5 :
6 : #include "nsNativeTheme.h"
7 : #include "nsIWidget.h"
8 : #include "nsIDocument.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 "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"
31 : #include <algorithm>
32 :
33 : using namespace mozilla;
34 : using namespace mozilla::dom;
35 :
36 2 : nsNativeTheme::nsNativeTheme()
37 2 : : mAnimatedContentTimeout(UINT32_MAX)
38 : {
39 2 : }
40 :
41 16 : NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback)
42 :
43 : nsIPresShell *
44 18 : nsNativeTheme::GetPresShell(nsIFrame* aFrame)
45 : {
46 18 : if (!aFrame)
47 0 : return nullptr;
48 :
49 18 : nsPresContext* context = aFrame->PresContext();
50 18 : return context ? context->GetPresShell() : nullptr;
51 : }
52 :
53 : EventStates
54 18 : nsNativeTheme::GetContentState(nsIFrame* aFrame, uint8_t aWidgetType)
55 : {
56 18 : if (!aFrame)
57 0 : return EventStates();
58 :
59 : bool isXULCheckboxRadio =
60 18 : (aWidgetType == NS_THEME_CHECKBOX ||
61 18 : aWidgetType == NS_THEME_RADIO) &&
62 18 : aFrame->GetContent()->IsXULElement();
63 18 : if (isXULCheckboxRadio)
64 0 : aFrame = aFrame->GetParent();
65 :
66 18 : if (!aFrame->GetContent())
67 0 : return EventStates();
68 :
69 18 : nsIPresShell *shell = GetPresShell(aFrame);
70 18 : if (!shell)
71 0 : return EventStates();
72 :
73 18 : nsIContent* frameContent = aFrame->GetContent();
74 18 : EventStates flags;
75 18 : if (frameContent->IsElement()) {
76 18 : flags = frameContent->AsElement()->State();
77 :
78 : // <input type=number> needs special handling since its nested native
79 : // anonymous <input type=text> takes focus for it.
80 18 : if (aWidgetType == NS_THEME_NUMBER_INPUT &&
81 0 : frameContent->IsHTMLElement(nsGkAtoms::input)) {
82 0 : nsNumberControlFrame *numberControlFrame = do_QueryFrame(aFrame);
83 0 : if (numberControlFrame && numberControlFrame->IsFocused()) {
84 0 : flags |= NS_EVENT_STATE_FOCUS;
85 : }
86 : }
87 :
88 : nsNumberControlFrame* numberControlFrame =
89 18 : nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame);
90 54 : if (numberControlFrame &&
91 18 : numberControlFrame->GetContent()->AsElement()->State().
92 18 : HasState(NS_EVENT_STATE_DISABLED)) {
93 0 : flags |= NS_EVENT_STATE_DISABLED;
94 : }
95 : }
96 :
97 18 : if (isXULCheckboxRadio && aWidgetType == NS_THEME_RADIO) {
98 0 : if (IsFocused(aFrame))
99 0 : flags |= NS_EVENT_STATE_FOCUS;
100 : }
101 :
102 : // On Windows and Mac, only draw focus rings if they should be shown. This
103 : // means that focus rings are only shown once the keyboard has been used to
104 : // focus something in the window.
105 : #if defined(XP_MACOSX)
106 : // Mac always draws focus rings for textboxes and lists.
107 : if (aWidgetType == NS_THEME_NUMBER_INPUT ||
108 : aWidgetType == NS_THEME_TEXTFIELD ||
109 : aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
110 : aWidgetType == NS_THEME_SEARCHFIELD ||
111 : aWidgetType == NS_THEME_LISTBOX) {
112 : return flags;
113 : }
114 : #endif
115 : #if defined(XP_WIN)
116 : // On Windows, focused buttons are always drawn as such by the native theme.
117 : if (aWidgetType == NS_THEME_BUTTON)
118 : return flags;
119 : #endif
120 : #if defined(XP_MACOSX) || defined(XP_WIN)
121 : nsIDocument* doc = aFrame->GetContent()->OwnerDoc();
122 : nsPIDOMWindowOuter* window = doc->GetWindow();
123 : if (window && !window->ShouldShowFocusRing())
124 : flags &= ~NS_EVENT_STATE_FOCUS;
125 : #endif
126 :
127 18 : return flags;
128 : }
129 :
130 : /* static */
131 : bool
132 36 : nsNativeTheme::CheckBooleanAttr(nsIFrame* aFrame, nsIAtom* aAtom)
133 : {
134 36 : if (!aFrame)
135 0 : return false;
136 :
137 36 : nsIContent* content = aFrame->GetContent();
138 36 : if (!content)
139 0 : return false;
140 :
141 36 : if (content->IsHTMLElement())
142 0 : return content->HasAttr(kNameSpaceID_None, aAtom);
143 :
144 : // For XML/XUL elements, an attribute must be equal to the literal
145 : // string "true" to be counted as true. An empty string should _not_
146 : // be counted as true.
147 36 : return content->AttrValueIs(kNameSpaceID_None, aAtom,
148 72 : NS_LITERAL_STRING("true"), eCaseMatters);
149 : }
150 :
151 : /* static */
152 : int32_t
153 0 : nsNativeTheme::CheckIntAttr(nsIFrame* aFrame, nsIAtom* aAtom, int32_t defaultValue)
154 : {
155 0 : if (!aFrame)
156 0 : return defaultValue;
157 :
158 0 : nsAutoString attr;
159 0 : aFrame->GetContent()->GetAttr(kNameSpaceID_None, aAtom, attr);
160 : nsresult err;
161 0 : int32_t value = attr.ToInteger(&err);
162 0 : if (attr.IsEmpty() || NS_FAILED(err))
163 0 : return defaultValue;
164 :
165 0 : return value;
166 : }
167 :
168 : /* static */
169 : double
170 0 : nsNativeTheme::GetProgressValue(nsIFrame* aFrame)
171 : {
172 : // When we are using the HTML progress element,
173 : // we can get the value from the IDL property.
174 0 : if (aFrame && aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
175 0 : return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Value();
176 : }
177 :
178 0 : return (double)nsNativeTheme::CheckIntAttr(aFrame, nsGkAtoms::value, 0);
179 : }
180 :
181 : /* static */
182 : double
183 0 : nsNativeTheme::GetProgressMaxValue(nsIFrame* aFrame)
184 : {
185 : // When we are using the HTML progress element,
186 : // we can get the max from the IDL property.
187 0 : if (aFrame && aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
188 0 : return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Max();
189 : }
190 :
191 0 : return (double)std::max(nsNativeTheme::CheckIntAttr(aFrame, nsGkAtoms::max, 100), 1);
192 : }
193 :
194 : bool
195 0 : nsNativeTheme::GetCheckedOrSelected(nsIFrame* aFrame, bool aCheckSelected)
196 : {
197 0 : if (!aFrame)
198 0 : return false;
199 :
200 0 : nsIContent* content = aFrame->GetContent();
201 :
202 0 : if (content->IsXULElement()) {
203 : // For a XUL checkbox or radio button, the state of the parent determines
204 : // the checked state
205 0 : aFrame = aFrame->GetParent();
206 : } else {
207 : // Check for an HTML input element
208 0 : nsCOMPtr<nsIDOMHTMLInputElement> inputElt = do_QueryInterface(content);
209 0 : if (inputElt) {
210 : bool checked;
211 0 : inputElt->GetChecked(&checked);
212 0 : return checked;
213 : }
214 : }
215 :
216 0 : return CheckBooleanAttr(aFrame, aCheckSelected ? nsGkAtoms::selected
217 0 : : nsGkAtoms::checked);
218 : }
219 :
220 : bool
221 0 : nsNativeTheme::IsButtonTypeMenu(nsIFrame* aFrame)
222 : {
223 0 : if (!aFrame)
224 0 : return false;
225 :
226 0 : nsIContent* content = aFrame->GetContent();
227 0 : return content->IsXULElement(nsGkAtoms::button) &&
228 0 : content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
229 0 : NS_LITERAL_STRING("menu"), eCaseMatters);
230 : }
231 :
232 : bool
233 0 : nsNativeTheme::IsPressedButton(nsIFrame* aFrame)
234 : {
235 0 : EventStates eventState = GetContentState(aFrame, NS_THEME_TOOLBARBUTTON);
236 0 : if (IsDisabled(aFrame, eventState))
237 0 : return false;
238 :
239 0 : return IsOpenButton(aFrame) ||
240 0 : eventState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER);
241 : }
242 :
243 :
244 : bool
245 0 : nsNativeTheme::GetIndeterminate(nsIFrame* aFrame)
246 : {
247 0 : if (!aFrame)
248 0 : return false;
249 :
250 0 : nsIContent* content = aFrame->GetContent();
251 :
252 0 : if (content->IsXULElement()) {
253 : // For a XUL checkbox or radio button, the state of the parent determines
254 : // the state
255 0 : return CheckBooleanAttr(aFrame->GetParent(), nsGkAtoms::indeterminate);
256 : }
257 :
258 : // Check for an HTML input element
259 0 : nsCOMPtr<nsIDOMHTMLInputElement> inputElt = do_QueryInterface(content);
260 0 : if (inputElt) {
261 : bool indeterminate;
262 0 : inputElt->GetIndeterminate(&indeterminate);
263 0 : return indeterminate;
264 : }
265 :
266 0 : return false;
267 : }
268 :
269 : bool
270 1268 : nsNativeTheme::IsWidgetStyled(nsPresContext* aPresContext, nsIFrame* aFrame,
271 : uint8_t aWidgetType)
272 : {
273 : // Check for specific widgets to see if HTML has overridden the style.
274 1268 : if (!aFrame)
275 0 : return false;
276 :
277 : // Resizers have some special handling, dependent on whether in a scrollable
278 : // container or not. If so, use the scrollable container's to determine
279 : // whether the style is overriden instead of the resizer. This allows a
280 : // non-native transparent resizer to be used instead. Otherwise, we just
281 : // fall through and return false.
282 1268 : if (aWidgetType == NS_THEME_RESIZER) {
283 0 : nsIFrame* parentFrame = aFrame->GetParent();
284 0 : if (parentFrame && parentFrame->IsScrollFrame()) {
285 : // if the parent is a scrollframe, the resizer should be native themed
286 : // only if the scrollable area doesn't override the widget style.
287 0 : parentFrame = parentFrame->GetParent();
288 0 : if (parentFrame) {
289 0 : return IsWidgetStyled(aPresContext, parentFrame,
290 0 : parentFrame->StyleDisplay()->mAppearance);
291 : }
292 : }
293 : }
294 :
295 : /**
296 : * Progress bar appearance should be the same for the bar and the container
297 : * frame. nsProgressFrame owns the logic and will tell us what we should do.
298 : */
299 1268 : if (aWidgetType == NS_THEME_PROGRESSCHUNK ||
300 : aWidgetType == NS_THEME_PROGRESSBAR) {
301 0 : nsProgressFrame* progressFrame = do_QueryFrame(aWidgetType == NS_THEME_PROGRESSCHUNK
302 0 : ? aFrame->GetParent() : aFrame);
303 0 : if (progressFrame) {
304 0 : return !progressFrame->ShouldUseNativeStyle();
305 : }
306 : }
307 :
308 : /**
309 : * Meter bar appearance should be the same for the bar and the container
310 : * frame. nsMeterFrame owns the logic and will tell us what we should do.
311 : */
312 1268 : if (aWidgetType == NS_THEME_METERCHUNK ||
313 : aWidgetType == NS_THEME_METERBAR) {
314 0 : nsMeterFrame* meterFrame = do_QueryFrame(aWidgetType == NS_THEME_METERCHUNK
315 0 : ? aFrame->GetParent() : aFrame);
316 0 : if (meterFrame) {
317 0 : return !meterFrame->ShouldUseNativeStyle();
318 : }
319 : }
320 :
321 : /**
322 : * An nsRangeFrame and its children are treated atomically when it
323 : * comes to native theming (either all parts, or no parts, are themed).
324 : * nsRangeFrame owns the logic and will tell us what we should do.
325 : */
326 1268 : if (aWidgetType == NS_THEME_RANGE ||
327 : aWidgetType == NS_THEME_RANGE_THUMB) {
328 : nsRangeFrame* rangeFrame =
329 0 : do_QueryFrame(aWidgetType == NS_THEME_RANGE_THUMB
330 0 : ? aFrame->GetParent() : aFrame);
331 0 : if (rangeFrame) {
332 0 : return !rangeFrame->ShouldUseNativeStyle();
333 : }
334 : }
335 :
336 1268 : if (aWidgetType == NS_THEME_SPINNER_UPBUTTON ||
337 : aWidgetType == NS_THEME_SPINNER_DOWNBUTTON) {
338 : nsNumberControlFrame* numberControlFrame =
339 0 : nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame);
340 0 : if (numberControlFrame) {
341 0 : return !numberControlFrame->ShouldUseNativeStyleForSpinner();
342 : }
343 : }
344 :
345 1268 : return (aWidgetType == NS_THEME_NUMBER_INPUT ||
346 1268 : aWidgetType == NS_THEME_BUTTON ||
347 1268 : aWidgetType == NS_THEME_TEXTFIELD ||
348 1268 : aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
349 1268 : aWidgetType == NS_THEME_LISTBOX ||
350 0 : aWidgetType == NS_THEME_MENULIST) &&
351 1268 : aFrame->GetContent()->IsHTMLElement() &&
352 0 : aPresContext->HasAuthorSpecifiedRules(aFrame,
353 : NS_AUTHOR_SPECIFIED_BORDER |
354 1268 : NS_AUTHOR_SPECIFIED_BACKGROUND);
355 : }
356 :
357 : bool
358 18 : nsNativeTheme::IsDisabled(nsIFrame* aFrame, EventStates aEventStates)
359 : {
360 18 : if (!aFrame) {
361 0 : return false;
362 : }
363 :
364 18 : nsIContent* content = aFrame->GetContent();
365 18 : if (!content) {
366 0 : return false;
367 : }
368 :
369 18 : if (content->IsHTMLElement()) {
370 0 : return aEventStates.HasState(NS_EVENT_STATE_DISABLED);
371 : }
372 :
373 : // For XML/XUL elements, an attribute must be equal to the literal
374 : // string "true" to be counted as true. An empty string should _not_
375 : // be counted as true.
376 18 : return content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
377 36 : NS_LITERAL_STRING("true"), eCaseMatters);
378 : }
379 :
380 : /* static */ bool
381 404 : nsNativeTheme::IsFrameRTL(nsIFrame* aFrame)
382 : {
383 404 : if (!aFrame) {
384 0 : return false;
385 : }
386 404 : WritingMode wm = aFrame->GetWritingMode();
387 404 : return !(wm.IsVertical() ? wm.IsVerticalLR() : wm.IsBidiLTR());
388 : }
389 :
390 : bool
391 0 : nsNativeTheme::IsHTMLContent(nsIFrame *aFrame)
392 : {
393 0 : if (!aFrame) {
394 0 : return false;
395 : }
396 0 : nsIContent* content = aFrame->GetContent();
397 0 : return content && content->IsHTMLElement();
398 : }
399 :
400 :
401 : // scrollbar button:
402 : int32_t
403 0 : nsNativeTheme::GetScrollbarButtonType(nsIFrame* aFrame)
404 : {
405 0 : if (!aFrame)
406 0 : return 0;
407 :
408 : static nsIContent::AttrValuesArray strings[] =
409 : {&nsGkAtoms::scrollbarDownBottom, &nsGkAtoms::scrollbarDownTop,
410 : &nsGkAtoms::scrollbarUpBottom, &nsGkAtoms::scrollbarUpTop,
411 : nullptr};
412 :
413 0 : switch (aFrame->GetContent()->FindAttrValueIn(kNameSpaceID_None,
414 : nsGkAtoms::sbattr,
415 0 : strings, eCaseMatters)) {
416 0 : case 0: return eScrollbarButton_Down | eScrollbarButton_Bottom;
417 0 : case 1: return eScrollbarButton_Down;
418 0 : case 2: return eScrollbarButton_Bottom;
419 0 : case 3: return eScrollbarButton_UpTop;
420 : }
421 :
422 0 : return 0;
423 : }
424 :
425 : // treeheadercell:
426 : nsNativeTheme::TreeSortDirection
427 0 : nsNativeTheme::GetTreeSortDirection(nsIFrame* aFrame)
428 : {
429 0 : if (!aFrame || !aFrame->GetContent())
430 0 : return eTreeSortDirection_Natural;
431 :
432 : static nsIContent::AttrValuesArray strings[] =
433 : {&nsGkAtoms::descending, &nsGkAtoms::ascending, nullptr};
434 0 : switch (aFrame->GetContent()->FindAttrValueIn(kNameSpaceID_None,
435 : nsGkAtoms::sortDirection,
436 0 : strings, eCaseMatters)) {
437 0 : case 0: return eTreeSortDirection_Descending;
438 0 : case 1: return eTreeSortDirection_Ascending;
439 : }
440 :
441 0 : return eTreeSortDirection_Natural;
442 : }
443 :
444 : bool
445 0 : nsNativeTheme::IsLastTreeHeaderCell(nsIFrame* aFrame)
446 : {
447 0 : if (!aFrame)
448 0 : return false;
449 :
450 : // A tree column picker is always the last header cell.
451 0 : if (aFrame->GetContent()->IsXULElement(nsGkAtoms::treecolpicker))
452 0 : return true;
453 :
454 : // Find the parent tree.
455 0 : nsIContent* parent = aFrame->GetContent()->GetParent();
456 0 : while (parent && !parent->IsXULElement(nsGkAtoms::tree)) {
457 0 : parent = parent->GetParent();
458 : }
459 :
460 : // If the column picker is visible, this can't be the last column.
461 0 : if (parent && !parent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidecolumnpicker,
462 0 : NS_LITERAL_STRING("true"), eCaseMatters))
463 0 : return false;
464 :
465 0 : while ((aFrame = aFrame->GetNextSibling())) {
466 0 : if (aFrame->GetRect().width > 0)
467 0 : return false;
468 : }
469 0 : return true;
470 : }
471 :
472 : // tab:
473 : bool
474 0 : nsNativeTheme::IsBottomTab(nsIFrame* aFrame)
475 : {
476 0 : if (!aFrame)
477 0 : return false;
478 :
479 0 : nsAutoString classStr;
480 0 : aFrame->GetContent()->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, classStr);
481 0 : return !classStr.IsEmpty() && classStr.Find("tab-bottom") != kNotFound;
482 : }
483 :
484 : bool
485 0 : nsNativeTheme::IsFirstTab(nsIFrame* aFrame)
486 : {
487 0 : if (!aFrame)
488 0 : return false;
489 :
490 0 : for (nsIFrame* first : aFrame->GetParent()->PrincipalChildList()) {
491 0 : if (first->GetRect().width > 0 &&
492 0 : first->GetContent()->IsXULElement(nsGkAtoms::tab))
493 0 : return (first == aFrame);
494 : }
495 0 : return false;
496 : }
497 :
498 : bool
499 0 : nsNativeTheme::IsHorizontal(nsIFrame* aFrame)
500 : {
501 0 : if (!aFrame)
502 0 : return false;
503 :
504 0 : return !aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient,
505 : nsGkAtoms::vertical,
506 0 : eCaseMatters);
507 : }
508 :
509 : bool
510 0 : nsNativeTheme::IsNextToSelectedTab(nsIFrame* aFrame, int32_t aOffset)
511 : {
512 0 : if (!aFrame)
513 0 : return false;
514 :
515 0 : if (aOffset == 0)
516 0 : return IsSelectedTab(aFrame);
517 :
518 0 : int32_t thisTabIndex = -1, selectedTabIndex = -1;
519 :
520 0 : nsIFrame* currentTab = aFrame->GetParent()->PrincipalChildList().FirstChild();
521 0 : for (int32_t i = 0; currentTab; currentTab = currentTab->GetNextSibling()) {
522 0 : if (currentTab->GetRect().width == 0)
523 0 : continue;
524 0 : if (aFrame == currentTab)
525 0 : thisTabIndex = i;
526 0 : if (IsSelectedTab(currentTab))
527 0 : selectedTabIndex = i;
528 0 : ++i;
529 : }
530 :
531 0 : if (thisTabIndex == -1 || selectedTabIndex == -1)
532 0 : return false;
533 :
534 0 : return (thisTabIndex - selectedTabIndex == aOffset);
535 : }
536 :
537 : // progressbar:
538 : bool
539 0 : nsNativeTheme::IsIndeterminateProgress(nsIFrame* aFrame,
540 : EventStates aEventStates)
541 : {
542 0 : if (!aFrame || !aFrame->GetContent())
543 0 : return false;
544 :
545 0 : if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::progress)) {
546 0 : return aEventStates.HasState(NS_EVENT_STATE_INDETERMINATE);
547 : }
548 :
549 0 : return aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mode,
550 0 : NS_LITERAL_STRING("undetermined"),
551 0 : eCaseMatters);
552 : }
553 :
554 : bool
555 0 : nsNativeTheme::IsVerticalProgress(nsIFrame* aFrame)
556 : {
557 0 : if (!aFrame) {
558 0 : return false;
559 : }
560 0 : return IsVerticalMeter(aFrame);
561 : }
562 :
563 : bool
564 0 : nsNativeTheme::IsVerticalMeter(nsIFrame* aFrame)
565 : {
566 0 : NS_PRECONDITION(aFrame, "You have to pass a non-null aFrame");
567 0 : switch (aFrame->StyleDisplay()->mOrient) {
568 : case StyleOrient::Horizontal:
569 0 : return false;
570 : case StyleOrient::Vertical:
571 0 : return true;
572 : case StyleOrient::Inline:
573 0 : return aFrame->GetWritingMode().IsVertical();
574 : case StyleOrient::Block:
575 0 : return !aFrame->GetWritingMode().IsVertical();
576 : }
577 0 : NS_NOTREACHED("unexpected -moz-orient value");
578 0 : return false;
579 : }
580 :
581 : // menupopup:
582 : bool
583 0 : nsNativeTheme::IsSubmenu(nsIFrame* aFrame, bool* aLeftOfParent)
584 : {
585 0 : if (!aFrame)
586 0 : return false;
587 :
588 0 : nsIContent* parentContent = aFrame->GetContent()->GetParent();
589 0 : if (!parentContent || !parentContent->IsXULElement(nsGkAtoms::menu))
590 0 : return false;
591 :
592 0 : nsIFrame* parent = aFrame;
593 0 : while ((parent = parent->GetParent())) {
594 0 : if (parent->GetContent() == parentContent) {
595 0 : if (aLeftOfParent) {
596 0 : LayoutDeviceIntRect selfBounds, parentBounds;
597 0 : selfBounds = aFrame->GetNearestWidget()->GetScreenBounds();
598 0 : parentBounds = parent->GetNearestWidget()->GetScreenBounds();
599 0 : *aLeftOfParent = selfBounds.x < parentBounds.x;
600 : }
601 0 : return true;
602 : }
603 : }
604 :
605 0 : return false;
606 : }
607 :
608 : bool
609 154 : nsNativeTheme::IsRegularMenuItem(nsIFrame *aFrame)
610 : {
611 154 : nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
612 154 : return !(menuFrame && (menuFrame->IsOnMenuBar() ||
613 154 : menuFrame->GetParentMenuListType() != eNotMenuList));
614 : }
615 :
616 : bool
617 0 : nsNativeTheme::IsMenuListEditable(nsIFrame *aFrame)
618 : {
619 0 : bool isEditable = false;
620 0 : nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aFrame->GetContent());
621 0 : if (menulist)
622 0 : menulist->GetEditable(&isEditable);
623 0 : return isEditable;
624 : }
625 :
626 : bool
627 0 : nsNativeTheme::QueueAnimatedContentForRefresh(nsIContent* aContent,
628 : uint32_t aMinimumFrameRate)
629 : {
630 0 : NS_ASSERTION(aContent, "Null pointer!");
631 0 : NS_ASSERTION(aMinimumFrameRate, "aMinimumFrameRate must be non-zero!");
632 0 : NS_ASSERTION(aMinimumFrameRate <= 1000,
633 : "aMinimumFrameRate must be less than 1000!");
634 :
635 0 : uint32_t timeout = 1000 / aMinimumFrameRate;
636 0 : timeout = std::min(mAnimatedContentTimeout, timeout);
637 :
638 0 : if (!mAnimatedContentTimer) {
639 0 : mAnimatedContentTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
640 0 : NS_ENSURE_TRUE(mAnimatedContentTimer, false);
641 : }
642 :
643 0 : if (mAnimatedContentList.IsEmpty() || timeout != mAnimatedContentTimeout) {
644 : nsresult rv;
645 0 : if (!mAnimatedContentList.IsEmpty()) {
646 0 : rv = mAnimatedContentTimer->Cancel();
647 0 : NS_ENSURE_SUCCESS(rv, false);
648 : }
649 :
650 0 : if (XRE_IsContentProcess() && NS_IsMainThread()) {
651 0 : mAnimatedContentTimer->SetTarget(aContent->OwnerDoc()->EventTargetFor(TaskCategory::Other));
652 : }
653 0 : rv = mAnimatedContentTimer->InitWithCallback(this, timeout,
654 0 : nsITimer::TYPE_ONE_SHOT);
655 0 : NS_ENSURE_SUCCESS(rv, false);
656 :
657 0 : mAnimatedContentTimeout = timeout;
658 : }
659 :
660 0 : if (!mAnimatedContentList.AppendElement(aContent)) {
661 0 : NS_WARNING("Out of memory!");
662 0 : return false;
663 : }
664 :
665 0 : return true;
666 : }
667 :
668 : NS_IMETHODIMP
669 0 : nsNativeTheme::Notify(nsITimer* aTimer)
670 : {
671 0 : NS_ASSERTION(aTimer == mAnimatedContentTimer, "Wrong timer!");
672 :
673 : // XXX Assumes that calling nsIFrame::Invalidate won't reenter
674 : // QueueAnimatedContentForRefresh.
675 :
676 0 : uint32_t count = mAnimatedContentList.Length();
677 0 : for (uint32_t index = 0; index < count; index++) {
678 0 : nsIFrame* frame = mAnimatedContentList[index]->GetPrimaryFrame();
679 0 : if (frame) {
680 0 : frame->InvalidateFrame();
681 : }
682 : }
683 :
684 0 : mAnimatedContentList.Clear();
685 0 : mAnimatedContentTimeout = UINT32_MAX;
686 0 : return NS_OK;
687 : }
688 :
689 : nsIFrame*
690 0 : nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
691 : bool aNextSibling)
692 : {
693 0 : if (!aFrame)
694 0 : return nullptr;
695 :
696 : // Find the next visible sibling.
697 0 : nsIFrame* sibling = aFrame;
698 0 : do {
699 0 : sibling = aNextSibling ? sibling->GetNextSibling() : sibling->GetPrevSibling();
700 0 : } while (sibling && sibling->GetRect().width == 0);
701 :
702 : // Check same appearance and adjacency.
703 0 : if (!sibling ||
704 0 : sibling->StyleDisplay()->mAppearance != aFrame->StyleDisplay()->mAppearance ||
705 0 : (sibling->GetRect().XMost() != aFrame->GetRect().x &&
706 0 : aFrame->GetRect().XMost() != sibling->GetRect().x))
707 0 : return nullptr;
708 0 : return sibling;
709 : }
710 :
711 : bool
712 0 : nsNativeTheme::IsRangeHorizontal(nsIFrame* aFrame)
713 : {
714 0 : nsIFrame* rangeFrame = aFrame;
715 0 : if (!rangeFrame->IsRangeFrame()) {
716 : // If the thumb's frame is passed in, get its range parent:
717 0 : rangeFrame = aFrame->GetParent();
718 : }
719 0 : if (rangeFrame->IsRangeFrame()) {
720 0 : return static_cast<nsRangeFrame*>(rangeFrame)->IsHorizontal();
721 : }
722 : // Not actually a range frame - just use the ratio of the frame's size to
723 : // decide:
724 0 : return aFrame->GetSize().width >= aFrame->GetSize().height;
725 : }
726 :
727 : static nsIFrame*
728 0 : GetBodyFrame(nsIFrame* aCanvasFrame)
729 : {
730 0 : nsIContent* content = aCanvasFrame->GetContent();
731 0 : if (!content) {
732 0 : return nullptr;
733 : }
734 0 : nsIDocument* document = content->OwnerDoc();
735 0 : nsIContent* body = document->GetBodyElement();
736 0 : if (!body) {
737 0 : return nullptr;
738 : }
739 0 : return body->GetPrimaryFrame();
740 : }
741 :
742 : bool
743 0 : nsNativeTheme::IsDarkBackground(nsIFrame* aFrame)
744 : {
745 0 : nsIScrollableFrame* scrollFrame = nullptr;
746 0 : while (!scrollFrame && aFrame) {
747 0 : scrollFrame = aFrame->GetScrollTargetFrame();
748 0 : aFrame = aFrame->GetParent();
749 : }
750 0 : if (!scrollFrame)
751 0 : return false;
752 :
753 0 : nsIFrame* frame = scrollFrame->GetScrolledFrame();
754 0 : if (nsCSSRendering::IsCanvasFrame(frame)) {
755 : // For canvas frames, prefer to look at the body first, because the body
756 : // background color is most likely what will be visible as the background
757 : // color of the page, even if the html element has a different background
758 : // color which prevents that of the body frame to propagate to the viewport.
759 0 : nsIFrame* bodyFrame = GetBodyFrame(frame);
760 0 : if (bodyFrame) {
761 0 : frame = bodyFrame;
762 : }
763 : }
764 0 : nsStyleContext* bgSC = nullptr;
765 0 : if (!nsCSSRendering::FindBackground(frame, &bgSC) ||
766 0 : bgSC->StyleBackground()->IsTransparent(bgSC)) {
767 0 : nsIFrame* backgroundFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(frame, true);
768 0 : nsCSSRendering::FindBackground(backgroundFrame, &bgSC);
769 : }
770 0 : if (bgSC) {
771 0 : nscolor bgColor = bgSC->StyleBackground()->BackgroundColor(bgSC);
772 : // Consider the background color dark if the sum of the r, g and b values is
773 : // less than 384 in a semi-transparent document. This heuristic matches what
774 : // WebKit does, and we can improve it later if needed.
775 0 : return NS_GET_A(bgColor) > 127 &&
776 0 : NS_GET_R(bgColor) + NS_GET_G(bgColor) + NS_GET_B(bgColor) < 384;
777 : }
778 0 : return false;
779 : }
|