nsNativeTheme.cpp 22 KB

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