widget/xpwidgets/nsNativeTheme.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial