widget/xpwidgets/nsNativeTheme.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial