1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/xpwidgets/nsNativeTheme.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,761 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsNativeTheme.h" 1.10 +#include "nsIWidget.h" 1.11 +#include "nsIDocument.h" 1.12 +#include "nsIContent.h" 1.13 +#include "nsIFrame.h" 1.14 +#include "nsIPresShell.h" 1.15 +#include "nsNumberControlFrame.h" 1.16 +#include "nsPresContext.h" 1.17 +#include "nsString.h" 1.18 +#include "nsNameSpaceManager.h" 1.19 +#include "nsIDOMHTMLInputElement.h" 1.20 +#include "nsIDOMXULMenuListElement.h" 1.21 +#include "nsThemeConstants.h" 1.22 +#include "nsIComponentManager.h" 1.23 +#include "nsPIDOMWindow.h" 1.24 +#include "nsProgressFrame.h" 1.25 +#include "nsMeterFrame.h" 1.26 +#include "nsMenuFrame.h" 1.27 +#include "nsRangeFrame.h" 1.28 +#include "nsCSSRendering.h" 1.29 +#include "mozilla/EventStates.h" 1.30 +#include "mozilla/dom/Element.h" 1.31 +#include "mozilla/dom/HTMLBodyElement.h" 1.32 +#include "mozilla/dom/HTMLProgressElement.h" 1.33 +#include "nsIDocumentInlines.h" 1.34 +#include <algorithm> 1.35 + 1.36 +using namespace mozilla; 1.37 +using namespace mozilla::dom; 1.38 + 1.39 +nsNativeTheme::nsNativeTheme() 1.40 +: mAnimatedContentTimeout(UINT32_MAX) 1.41 +{ 1.42 +} 1.43 + 1.44 +NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback) 1.45 + 1.46 +nsIPresShell * 1.47 +nsNativeTheme::GetPresShell(nsIFrame* aFrame) 1.48 +{ 1.49 + if (!aFrame) 1.50 + return nullptr; 1.51 + 1.52 + // this is a workaround for the egcs 1.1.2 not inlining 1.53 + // aFrame->PresContext(), which causes an undefined symbol 1.54 + nsPresContext *context = aFrame->StyleContext()->RuleNode()->PresContext(); 1.55 + return context ? context->GetPresShell() : nullptr; 1.56 +} 1.57 + 1.58 +EventStates 1.59 +nsNativeTheme::GetContentState(nsIFrame* aFrame, uint8_t aWidgetType) 1.60 +{ 1.61 + if (!aFrame) 1.62 + return EventStates(); 1.63 + 1.64 + bool isXULCheckboxRadio = 1.65 + (aWidgetType == NS_THEME_CHECKBOX || 1.66 + aWidgetType == NS_THEME_RADIO) && 1.67 + aFrame->GetContent()->IsXUL(); 1.68 + if (isXULCheckboxRadio) 1.69 + aFrame = aFrame->GetParent(); 1.70 + 1.71 + if (!aFrame->GetContent()) 1.72 + return EventStates(); 1.73 + 1.74 + nsIPresShell *shell = GetPresShell(aFrame); 1.75 + if (!shell) 1.76 + return EventStates(); 1.77 + 1.78 + nsIContent* frameContent = aFrame->GetContent(); 1.79 + EventStates flags; 1.80 + if (frameContent->IsElement()) { 1.81 + flags = frameContent->AsElement()->State(); 1.82 + 1.83 + // <input type=number> needs special handling since its nested native 1.84 + // anonymous <input type=text> takes focus for it. 1.85 + if (aWidgetType == NS_THEME_NUMBER_INPUT && 1.86 + frameContent->IsHTML(nsGkAtoms::input)) { 1.87 + nsNumberControlFrame *numberControlFrame = do_QueryFrame(aFrame); 1.88 + if (numberControlFrame && numberControlFrame->IsFocused()) { 1.89 + flags |= NS_EVENT_STATE_FOCUS; 1.90 + } 1.91 + } 1.92 + 1.93 + nsNumberControlFrame* numberControlFrame = 1.94 + nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame); 1.95 + if (numberControlFrame && 1.96 + numberControlFrame->GetContent()->AsElement()->State(). 1.97 + HasState(NS_EVENT_STATE_DISABLED)) { 1.98 + flags |= NS_EVENT_STATE_DISABLED; 1.99 + } 1.100 + } 1.101 + 1.102 + if (isXULCheckboxRadio && aWidgetType == NS_THEME_RADIO) { 1.103 + if (IsFocused(aFrame)) 1.104 + flags |= NS_EVENT_STATE_FOCUS; 1.105 + } 1.106 + 1.107 + // On Windows and Mac, only draw focus rings if they should be shown. This 1.108 + // means that focus rings are only shown once the keyboard has been used to 1.109 + // focus something in the window. 1.110 +#if defined(XP_MACOSX) 1.111 + // Mac always draws focus rings for textboxes and lists. 1.112 + if (aWidgetType == NS_THEME_NUMBER_INPUT || 1.113 + aWidgetType == NS_THEME_TEXTFIELD || 1.114 + aWidgetType == NS_THEME_TEXTFIELD_MULTILINE || 1.115 + aWidgetType == NS_THEME_SEARCHFIELD || 1.116 + aWidgetType == NS_THEME_LISTBOX) { 1.117 + return flags; 1.118 + } 1.119 +#endif 1.120 +#if defined(XP_WIN) 1.121 + // On Windows, focused buttons are always drawn as such by the native theme. 1.122 + if (aWidgetType == NS_THEME_BUTTON) 1.123 + return flags; 1.124 +#endif 1.125 +#if defined(XP_MACOSX) || defined(XP_WIN) 1.126 + nsIDocument* doc = aFrame->GetContent()->OwnerDoc(); 1.127 + nsPIDOMWindow* window = doc->GetWindow(); 1.128 + if (window && !window->ShouldShowFocusRing()) 1.129 + flags &= ~NS_EVENT_STATE_FOCUS; 1.130 +#endif 1.131 + 1.132 + return flags; 1.133 +} 1.134 + 1.135 +/* static */ 1.136 +bool 1.137 +nsNativeTheme::CheckBooleanAttr(nsIFrame* aFrame, nsIAtom* aAtom) 1.138 +{ 1.139 + if (!aFrame) 1.140 + return false; 1.141 + 1.142 + nsIContent* content = aFrame->GetContent(); 1.143 + if (!content) 1.144 + return false; 1.145 + 1.146 + if (content->IsHTML()) 1.147 + return content->HasAttr(kNameSpaceID_None, aAtom); 1.148 + 1.149 + // For XML/XUL elements, an attribute must be equal to the literal 1.150 + // string "true" to be counted as true. An empty string should _not_ 1.151 + // be counted as true. 1.152 + return content->AttrValueIs(kNameSpaceID_None, aAtom, 1.153 + NS_LITERAL_STRING("true"), eCaseMatters); 1.154 +} 1.155 + 1.156 +/* static */ 1.157 +int32_t 1.158 +nsNativeTheme::CheckIntAttr(nsIFrame* aFrame, nsIAtom* aAtom, int32_t defaultValue) 1.159 +{ 1.160 + if (!aFrame) 1.161 + return defaultValue; 1.162 + 1.163 + nsAutoString attr; 1.164 + aFrame->GetContent()->GetAttr(kNameSpaceID_None, aAtom, attr); 1.165 + nsresult err; 1.166 + int32_t value = attr.ToInteger(&err); 1.167 + if (attr.IsEmpty() || NS_FAILED(err)) 1.168 + return defaultValue; 1.169 + 1.170 + return value; 1.171 +} 1.172 + 1.173 +/* static */ 1.174 +double 1.175 +nsNativeTheme::GetProgressValue(nsIFrame* aFrame) 1.176 +{ 1.177 + // When we are using the HTML progress element, 1.178 + // we can get the value from the IDL property. 1.179 + if (aFrame && aFrame->GetContent()->IsHTML(nsGkAtoms::progress)) { 1.180 + return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Value(); 1.181 + } 1.182 + 1.183 + return (double)nsNativeTheme::CheckIntAttr(aFrame, nsGkAtoms::value, 0); 1.184 +} 1.185 + 1.186 +/* static */ 1.187 +double 1.188 +nsNativeTheme::GetProgressMaxValue(nsIFrame* aFrame) 1.189 +{ 1.190 + // When we are using the HTML progress element, 1.191 + // we can get the max from the IDL property. 1.192 + if (aFrame && aFrame->GetContent()->IsHTML(nsGkAtoms::progress)) { 1.193 + return static_cast<HTMLProgressElement*>(aFrame->GetContent())->Max(); 1.194 + } 1.195 + 1.196 + return (double)std::max(nsNativeTheme::CheckIntAttr(aFrame, nsGkAtoms::max, 100), 1); 1.197 +} 1.198 + 1.199 +bool 1.200 +nsNativeTheme::GetCheckedOrSelected(nsIFrame* aFrame, bool aCheckSelected) 1.201 +{ 1.202 + if (!aFrame) 1.203 + return false; 1.204 + 1.205 + nsIContent* content = aFrame->GetContent(); 1.206 + 1.207 + if (content->IsXUL()) { 1.208 + // For a XUL checkbox or radio button, the state of the parent determines 1.209 + // the checked state 1.210 + aFrame = aFrame->GetParent(); 1.211 + } else { 1.212 + // Check for an HTML input element 1.213 + nsCOMPtr<nsIDOMHTMLInputElement> inputElt = do_QueryInterface(content); 1.214 + if (inputElt) { 1.215 + bool checked; 1.216 + inputElt->GetChecked(&checked); 1.217 + return checked; 1.218 + } 1.219 + } 1.220 + 1.221 + return CheckBooleanAttr(aFrame, aCheckSelected ? nsGkAtoms::selected 1.222 + : nsGkAtoms::checked); 1.223 +} 1.224 + 1.225 +bool 1.226 +nsNativeTheme::IsButtonTypeMenu(nsIFrame* aFrame) 1.227 +{ 1.228 + if (!aFrame) 1.229 + return false; 1.230 + 1.231 + nsIContent* content = aFrame->GetContent(); 1.232 + return content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, 1.233 + NS_LITERAL_STRING("menu"), eCaseMatters); 1.234 +} 1.235 + 1.236 +bool 1.237 +nsNativeTheme::IsPressedButton(nsIFrame* aFrame) 1.238 +{ 1.239 + EventStates eventState = GetContentState(aFrame, NS_THEME_TOOLBAR_BUTTON); 1.240 + if (IsDisabled(aFrame, eventState)) 1.241 + return false; 1.242 + 1.243 + return IsOpenButton(aFrame) || 1.244 + eventState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER); 1.245 +} 1.246 + 1.247 + 1.248 +bool 1.249 +nsNativeTheme::GetIndeterminate(nsIFrame* aFrame) 1.250 +{ 1.251 + if (!aFrame) 1.252 + return false; 1.253 + 1.254 + nsIContent* content = aFrame->GetContent(); 1.255 + 1.256 + if (content->IsXUL()) { 1.257 + // For a XUL checkbox or radio button, the state of the parent determines 1.258 + // the state 1.259 + return CheckBooleanAttr(aFrame->GetParent(), nsGkAtoms::indeterminate); 1.260 + } 1.261 + 1.262 + // Check for an HTML input element 1.263 + nsCOMPtr<nsIDOMHTMLInputElement> inputElt = do_QueryInterface(content); 1.264 + if (inputElt) { 1.265 + bool indeterminate; 1.266 + inputElt->GetIndeterminate(&indeterminate); 1.267 + return indeterminate; 1.268 + } 1.269 + 1.270 + return false; 1.271 +} 1.272 + 1.273 +bool 1.274 +nsNativeTheme::IsWidgetStyled(nsPresContext* aPresContext, nsIFrame* aFrame, 1.275 + uint8_t aWidgetType) 1.276 +{ 1.277 + // Check for specific widgets to see if HTML has overridden the style. 1.278 + if (!aFrame) 1.279 + return false; 1.280 + 1.281 + // Resizers have some special handling, dependent on whether in a scrollable 1.282 + // container or not. If so, use the scrollable container's to determine 1.283 + // whether the style is overriden instead of the resizer. This allows a 1.284 + // non-native transparent resizer to be used instead. Otherwise, we just 1.285 + // fall through and return false. 1.286 + if (aWidgetType == NS_THEME_RESIZER) { 1.287 + nsIFrame* parentFrame = aFrame->GetParent(); 1.288 + if (parentFrame && parentFrame->GetType() == nsGkAtoms::scrollFrame) { 1.289 + // if the parent is a scrollframe, the resizer should be native themed 1.290 + // only if the scrollable area doesn't override the widget style. 1.291 + parentFrame = parentFrame->GetParent(); 1.292 + if (parentFrame) { 1.293 + return IsWidgetStyled(aPresContext, parentFrame, 1.294 + parentFrame->StyleDisplay()->mAppearance); 1.295 + } 1.296 + } 1.297 + } 1.298 + 1.299 + /** 1.300 + * Progress bar appearance should be the same for the bar and the container 1.301 + * frame. nsProgressFrame owns the logic and will tell us what we should do. 1.302 + */ 1.303 + if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK || 1.304 + aWidgetType == NS_THEME_PROGRESSBAR) { 1.305 + nsProgressFrame* progressFrame = do_QueryFrame(aWidgetType == NS_THEME_PROGRESSBAR_CHUNK 1.306 + ? aFrame->GetParent() : aFrame); 1.307 + if (progressFrame) { 1.308 + return !progressFrame->ShouldUseNativeStyle(); 1.309 + } 1.310 + } 1.311 + 1.312 + /** 1.313 + * Meter bar appearance should be the same for the bar and the container 1.314 + * frame. nsMeterFrame owns the logic and will tell us what we should do. 1.315 + */ 1.316 + if (aWidgetType == NS_THEME_METERBAR_CHUNK || 1.317 + aWidgetType == NS_THEME_METERBAR) { 1.318 + nsMeterFrame* meterFrame = do_QueryFrame(aWidgetType == NS_THEME_METERBAR_CHUNK 1.319 + ? aFrame->GetParent() : aFrame); 1.320 + if (meterFrame) { 1.321 + return !meterFrame->ShouldUseNativeStyle(); 1.322 + } 1.323 + } 1.324 + 1.325 + /** 1.326 + * An nsRangeFrame and its children are treated atomically when it 1.327 + * comes to native theming (either all parts, or no parts, are themed). 1.328 + * nsRangeFrame owns the logic and will tell us what we should do. 1.329 + */ 1.330 + if (aWidgetType == NS_THEME_RANGE || 1.331 + aWidgetType == NS_THEME_RANGE_THUMB) { 1.332 + nsRangeFrame* rangeFrame = 1.333 + do_QueryFrame(aWidgetType == NS_THEME_RANGE_THUMB 1.334 + ? aFrame->GetParent() : aFrame); 1.335 + if (rangeFrame) { 1.336 + return !rangeFrame->ShouldUseNativeStyle(); 1.337 + } 1.338 + } 1.339 + 1.340 + if (aWidgetType == NS_THEME_SPINNER_UP_BUTTON || 1.341 + aWidgetType == NS_THEME_SPINNER_DOWN_BUTTON) { 1.342 + nsNumberControlFrame* numberControlFrame = 1.343 + nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame); 1.344 + if (numberControlFrame) { 1.345 + return !numberControlFrame->ShouldUseNativeStyleForSpinner(); 1.346 + } 1.347 + } 1.348 + 1.349 + return (aWidgetType == NS_THEME_NUMBER_INPUT || 1.350 + aWidgetType == NS_THEME_BUTTON || 1.351 + aWidgetType == NS_THEME_TEXTFIELD || 1.352 + aWidgetType == NS_THEME_TEXTFIELD_MULTILINE || 1.353 + aWidgetType == NS_THEME_LISTBOX || 1.354 + aWidgetType == NS_THEME_DROPDOWN) && 1.355 + aFrame->GetContent()->IsHTML() && 1.356 + aPresContext->HasAuthorSpecifiedRules(aFrame, 1.357 + NS_AUTHOR_SPECIFIED_BORDER | 1.358 + NS_AUTHOR_SPECIFIED_BACKGROUND); 1.359 +} 1.360 + 1.361 +bool 1.362 +nsNativeTheme::IsDisabled(nsIFrame* aFrame, EventStates aEventStates) 1.363 +{ 1.364 + if (!aFrame) { 1.365 + return false; 1.366 + } 1.367 + 1.368 + nsIContent* content = aFrame->GetContent(); 1.369 + if (!content) { 1.370 + return false; 1.371 + } 1.372 + 1.373 + if (content->IsHTML()) { 1.374 + return aEventStates.HasState(NS_EVENT_STATE_DISABLED); 1.375 + } 1.376 + 1.377 + // For XML/XUL elements, an attribute must be equal to the literal 1.378 + // string "true" to be counted as true. An empty string should _not_ 1.379 + // be counted as true. 1.380 + return content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, 1.381 + NS_LITERAL_STRING("true"), eCaseMatters); 1.382 +} 1.383 + 1.384 +bool 1.385 +nsNativeTheme::IsFrameRTL(nsIFrame* aFrame) 1.386 +{ 1.387 + return aFrame && aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; 1.388 +} 1.389 + 1.390 +bool 1.391 +nsNativeTheme::IsHTMLContent(nsIFrame *aFrame) 1.392 +{ 1.393 + if (!aFrame) { 1.394 + return false; 1.395 + } 1.396 + nsIContent* content = aFrame->GetContent(); 1.397 + return content && content->IsHTML(); 1.398 +} 1.399 + 1.400 + 1.401 +// scrollbar button: 1.402 +int32_t 1.403 +nsNativeTheme::GetScrollbarButtonType(nsIFrame* aFrame) 1.404 +{ 1.405 + if (!aFrame) 1.406 + return 0; 1.407 + 1.408 + static nsIContent::AttrValuesArray strings[] = 1.409 + {&nsGkAtoms::scrollbarDownBottom, &nsGkAtoms::scrollbarDownTop, 1.410 + &nsGkAtoms::scrollbarUpBottom, &nsGkAtoms::scrollbarUpTop, 1.411 + nullptr}; 1.412 + 1.413 + switch (aFrame->GetContent()->FindAttrValueIn(kNameSpaceID_None, 1.414 + nsGkAtoms::sbattr, 1.415 + strings, eCaseMatters)) { 1.416 + case 0: return eScrollbarButton_Down | eScrollbarButton_Bottom; 1.417 + case 1: return eScrollbarButton_Down; 1.418 + case 2: return eScrollbarButton_Bottom; 1.419 + case 3: return eScrollbarButton_UpTop; 1.420 + } 1.421 + 1.422 + return 0; 1.423 +} 1.424 + 1.425 +// treeheadercell: 1.426 +nsNativeTheme::TreeSortDirection 1.427 +nsNativeTheme::GetTreeSortDirection(nsIFrame* aFrame) 1.428 +{ 1.429 + if (!aFrame || !aFrame->GetContent()) 1.430 + return eTreeSortDirection_Natural; 1.431 + 1.432 + static nsIContent::AttrValuesArray strings[] = 1.433 + {&nsGkAtoms::descending, &nsGkAtoms::ascending, nullptr}; 1.434 + switch (aFrame->GetContent()->FindAttrValueIn(kNameSpaceID_None, 1.435 + nsGkAtoms::sortDirection, 1.436 + strings, eCaseMatters)) { 1.437 + case 0: return eTreeSortDirection_Descending; 1.438 + case 1: return eTreeSortDirection_Ascending; 1.439 + } 1.440 + 1.441 + return eTreeSortDirection_Natural; 1.442 +} 1.443 + 1.444 +bool 1.445 +nsNativeTheme::IsLastTreeHeaderCell(nsIFrame* aFrame) 1.446 +{ 1.447 + if (!aFrame) 1.448 + return false; 1.449 + 1.450 + // A tree column picker is always the last header cell. 1.451 + if (aFrame->GetContent()->Tag() == nsGkAtoms::treecolpicker) 1.452 + return true; 1.453 + 1.454 + // Find the parent tree. 1.455 + nsIContent* parent = aFrame->GetContent()->GetParent(); 1.456 + while (parent && parent->Tag() != nsGkAtoms::tree) { 1.457 + parent = parent->GetParent(); 1.458 + } 1.459 + 1.460 + // If the column picker is visible, this can't be the last column. 1.461 + if (parent && !parent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidecolumnpicker, 1.462 + NS_LITERAL_STRING("true"), eCaseMatters)) 1.463 + return false; 1.464 + 1.465 + while ((aFrame = aFrame->GetNextSibling())) { 1.466 + if (aFrame->GetRect().width > 0) 1.467 + return false; 1.468 + } 1.469 + return true; 1.470 +} 1.471 + 1.472 +// tab: 1.473 +bool 1.474 +nsNativeTheme::IsBottomTab(nsIFrame* aFrame) 1.475 +{ 1.476 + if (!aFrame) 1.477 + return false; 1.478 + 1.479 + nsAutoString classStr; 1.480 + aFrame->GetContent()->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, classStr); 1.481 + return !classStr.IsEmpty() && classStr.Find("tab-bottom") != kNotFound; 1.482 +} 1.483 + 1.484 +bool 1.485 +nsNativeTheme::IsFirstTab(nsIFrame* aFrame) 1.486 +{ 1.487 + if (!aFrame) 1.488 + return false; 1.489 + 1.490 + nsIFrame* first = aFrame->GetParent()->GetFirstPrincipalChild(); 1.491 + while (first) { 1.492 + if (first->GetRect().width > 0 && first->GetContent()->Tag() == nsGkAtoms::tab) 1.493 + return (first == aFrame); 1.494 + first = first->GetNextSibling(); 1.495 + } 1.496 + return false; 1.497 +} 1.498 + 1.499 +bool 1.500 +nsNativeTheme::IsHorizontal(nsIFrame* aFrame) 1.501 +{ 1.502 + if (!aFrame) 1.503 + return false; 1.504 + 1.505 + return !aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient, 1.506 + nsGkAtoms::vertical, 1.507 + eCaseMatters); 1.508 +} 1.509 + 1.510 +bool 1.511 +nsNativeTheme::IsNextToSelectedTab(nsIFrame* aFrame, int32_t aOffset) 1.512 +{ 1.513 + if (!aFrame) 1.514 + return false; 1.515 + 1.516 + if (aOffset == 0) 1.517 + return IsSelectedTab(aFrame); 1.518 + 1.519 + int32_t thisTabIndex = -1, selectedTabIndex = -1; 1.520 + 1.521 + nsIFrame* currentTab = aFrame->GetParent()->GetFirstPrincipalChild(); 1.522 + for (int32_t i = 0; currentTab; currentTab = currentTab->GetNextSibling()) { 1.523 + if (currentTab->GetRect().width == 0) 1.524 + continue; 1.525 + if (aFrame == currentTab) 1.526 + thisTabIndex = i; 1.527 + if (IsSelectedTab(currentTab)) 1.528 + selectedTabIndex = i; 1.529 + ++i; 1.530 + } 1.531 + 1.532 + if (thisTabIndex == -1 || selectedTabIndex == -1) 1.533 + return false; 1.534 + 1.535 + return (thisTabIndex - selectedTabIndex == aOffset); 1.536 +} 1.537 + 1.538 +// progressbar: 1.539 +bool 1.540 +nsNativeTheme::IsIndeterminateProgress(nsIFrame* aFrame, 1.541 + EventStates aEventStates) 1.542 +{ 1.543 + if (!aFrame || !aFrame->GetContent()) 1.544 + return false; 1.545 + 1.546 + if (aFrame->GetContent()->IsHTML(nsGkAtoms::progress)) { 1.547 + return aEventStates.HasState(NS_EVENT_STATE_INDETERMINATE); 1.548 + } 1.549 + 1.550 + return aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mode, 1.551 + NS_LITERAL_STRING("undetermined"), 1.552 + eCaseMatters); 1.553 +} 1.554 + 1.555 +bool 1.556 +nsNativeTheme::IsVerticalProgress(nsIFrame* aFrame) 1.557 +{ 1.558 + return aFrame && 1.559 + aFrame->StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL; 1.560 +} 1.561 + 1.562 +bool 1.563 +nsNativeTheme::IsVerticalMeter(nsIFrame* aFrame) 1.564 +{ 1.565 + NS_PRECONDITION(aFrame, "You have to pass a non-null aFrame"); 1.566 + return aFrame->StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL; 1.567 +} 1.568 + 1.569 +// menupopup: 1.570 +bool 1.571 +nsNativeTheme::IsSubmenu(nsIFrame* aFrame, bool* aLeftOfParent) 1.572 +{ 1.573 + if (!aFrame) 1.574 + return false; 1.575 + 1.576 + nsIContent* parentContent = aFrame->GetContent()->GetParent(); 1.577 + if (!parentContent || parentContent->Tag() != nsGkAtoms::menu) 1.578 + return false; 1.579 + 1.580 + nsIFrame* parent = aFrame; 1.581 + while ((parent = parent->GetParent())) { 1.582 + if (parent->GetContent() == parentContent) { 1.583 + if (aLeftOfParent) { 1.584 + nsIntRect selfBounds, parentBounds; 1.585 + aFrame->GetNearestWidget()->GetScreenBounds(selfBounds); 1.586 + parent->GetNearestWidget()->GetScreenBounds(parentBounds); 1.587 + *aLeftOfParent = selfBounds.x < parentBounds.x; 1.588 + } 1.589 + return true; 1.590 + } 1.591 + } 1.592 + 1.593 + return false; 1.594 +} 1.595 + 1.596 +bool 1.597 +nsNativeTheme::IsRegularMenuItem(nsIFrame *aFrame) 1.598 +{ 1.599 + nsMenuFrame *menuFrame = do_QueryFrame(aFrame); 1.600 + return !(menuFrame && (menuFrame->IsOnMenuBar() || 1.601 + menuFrame->GetParentMenuListType() != eNotMenuList)); 1.602 +} 1.603 + 1.604 +bool 1.605 +nsNativeTheme::IsMenuListEditable(nsIFrame *aFrame) 1.606 +{ 1.607 + bool isEditable = false; 1.608 + nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aFrame->GetContent()); 1.609 + if (menulist) 1.610 + menulist->GetEditable(&isEditable); 1.611 + return isEditable; 1.612 +} 1.613 + 1.614 +bool 1.615 +nsNativeTheme::QueueAnimatedContentForRefresh(nsIContent* aContent, 1.616 + uint32_t aMinimumFrameRate) 1.617 +{ 1.618 + NS_ASSERTION(aContent, "Null pointer!"); 1.619 + NS_ASSERTION(aMinimumFrameRate, "aMinimumFrameRate must be non-zero!"); 1.620 + NS_ASSERTION(aMinimumFrameRate <= 1000, 1.621 + "aMinimumFrameRate must be less than 1000!"); 1.622 + 1.623 + uint32_t timeout = 1000 / aMinimumFrameRate; 1.624 + timeout = std::min(mAnimatedContentTimeout, timeout); 1.625 + 1.626 + if (!mAnimatedContentTimer) { 1.627 + mAnimatedContentTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.628 + NS_ENSURE_TRUE(mAnimatedContentTimer, false); 1.629 + } 1.630 + 1.631 + if (mAnimatedContentList.IsEmpty() || timeout != mAnimatedContentTimeout) { 1.632 + nsresult rv; 1.633 + if (!mAnimatedContentList.IsEmpty()) { 1.634 + rv = mAnimatedContentTimer->Cancel(); 1.635 + NS_ENSURE_SUCCESS(rv, false); 1.636 + } 1.637 + 1.638 + rv = mAnimatedContentTimer->InitWithCallback(this, timeout, 1.639 + nsITimer::TYPE_ONE_SHOT); 1.640 + NS_ENSURE_SUCCESS(rv, false); 1.641 + 1.642 + mAnimatedContentTimeout = timeout; 1.643 + } 1.644 + 1.645 + if (!mAnimatedContentList.AppendElement(aContent)) { 1.646 + NS_WARNING("Out of memory!"); 1.647 + return false; 1.648 + } 1.649 + 1.650 + return true; 1.651 +} 1.652 + 1.653 +NS_IMETHODIMP 1.654 +nsNativeTheme::Notify(nsITimer* aTimer) 1.655 +{ 1.656 + NS_ASSERTION(aTimer == mAnimatedContentTimer, "Wrong timer!"); 1.657 + 1.658 + // XXX Assumes that calling nsIFrame::Invalidate won't reenter 1.659 + // QueueAnimatedContentForRefresh. 1.660 + 1.661 + uint32_t count = mAnimatedContentList.Length(); 1.662 + for (uint32_t index = 0; index < count; index++) { 1.663 + nsIFrame* frame = mAnimatedContentList[index]->GetPrimaryFrame(); 1.664 + if (frame) { 1.665 + frame->InvalidateFrame(); 1.666 + } 1.667 + } 1.668 + 1.669 + mAnimatedContentList.Clear(); 1.670 + mAnimatedContentTimeout = UINT32_MAX; 1.671 + return NS_OK; 1.672 +} 1.673 + 1.674 +nsIFrame* 1.675 +nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame, 1.676 + bool aNextSibling) 1.677 +{ 1.678 + if (!aFrame) 1.679 + return nullptr; 1.680 + 1.681 + // Find the next visible sibling. 1.682 + nsIFrame* sibling = aFrame; 1.683 + do { 1.684 + sibling = aNextSibling ? sibling->GetNextSibling() : sibling->GetPrevSibling(); 1.685 + } while (sibling && sibling->GetRect().width == 0); 1.686 + 1.687 + // Check same appearance and adjacency. 1.688 + if (!sibling || 1.689 + sibling->StyleDisplay()->mAppearance != aFrame->StyleDisplay()->mAppearance || 1.690 + (sibling->GetRect().XMost() != aFrame->GetRect().x && 1.691 + aFrame->GetRect().XMost() != sibling->GetRect().x)) 1.692 + return nullptr; 1.693 + return sibling; 1.694 +} 1.695 + 1.696 +bool 1.697 +nsNativeTheme::IsRangeHorizontal(nsIFrame* aFrame) 1.698 +{ 1.699 + nsIFrame* rangeFrame = aFrame; 1.700 + if (rangeFrame->GetType() != nsGkAtoms::rangeFrame) { 1.701 + // If the thumb's frame is passed in, get its range parent: 1.702 + rangeFrame = aFrame->GetParent(); 1.703 + } 1.704 + if (rangeFrame->GetType() == nsGkAtoms::rangeFrame) { 1.705 + return static_cast<nsRangeFrame*>(rangeFrame)->IsHorizontal(); 1.706 + } 1.707 + // Not actually a range frame - just use the ratio of the frame's size to 1.708 + // decide: 1.709 + return aFrame->GetSize().width >= aFrame->GetSize().height; 1.710 +} 1.711 + 1.712 +static nsIFrame* 1.713 +GetBodyFrame(nsIFrame* aCanvasFrame) 1.714 +{ 1.715 + nsIContent* content = aCanvasFrame->GetContent(); 1.716 + if (!content) { 1.717 + return nullptr; 1.718 + } 1.719 + nsIDocument* document = content->OwnerDoc(); 1.720 + nsIContent* body = document->GetBodyElement(); 1.721 + if (!body) { 1.722 + return nullptr; 1.723 + } 1.724 + return body->GetPrimaryFrame(); 1.725 +} 1.726 + 1.727 +bool 1.728 +nsNativeTheme::IsDarkBackground(nsIFrame* aFrame) 1.729 +{ 1.730 + nsIScrollableFrame* scrollFrame = nullptr; 1.731 + while (!scrollFrame && aFrame) { 1.732 + scrollFrame = aFrame->GetScrollTargetFrame(); 1.733 + aFrame = aFrame->GetParent(); 1.734 + } 1.735 + if (!scrollFrame) 1.736 + return false; 1.737 + 1.738 + nsIFrame* frame = scrollFrame->GetScrolledFrame(); 1.739 + if (nsCSSRendering::IsCanvasFrame(frame)) { 1.740 + // For canvas frames, prefer to look at the body first, because the body 1.741 + // background color is most likely what will be visible as the background 1.742 + // color of the page, even if the html element has a different background 1.743 + // color which prevents that of the body frame to propagate to the viewport. 1.744 + nsIFrame* bodyFrame = GetBodyFrame(frame); 1.745 + if (bodyFrame) { 1.746 + frame = bodyFrame; 1.747 + } 1.748 + } 1.749 + nsStyleContext* bgSC = nullptr; 1.750 + if (!nsCSSRendering::FindBackground(frame, &bgSC) || 1.751 + bgSC->StyleBackground()->IsTransparent()) { 1.752 + nsIFrame* backgroundFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(frame, true); 1.753 + nsCSSRendering::FindBackground(backgroundFrame, &bgSC); 1.754 + } 1.755 + if (bgSC) { 1.756 + nscolor bgColor = bgSC->StyleBackground()->mBackgroundColor; 1.757 + // Consider the background color dark if the sum of the r, g and b values is 1.758 + // less than 384 in a semi-transparent document. This heuristic matches what 1.759 + // WebKit does, and we can improve it later if needed. 1.760 + return NS_GET_A(bgColor) > 127 && 1.761 + NS_GET_R(bgColor) + NS_GET_G(bgColor) + NS_GET_B(bgColor) < 384; 1.762 + } 1.763 + return false; 1.764 +}