Coverage Report

Created: 2018-09-25 14:53

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