ServoStyleSet.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /* -*- Mode: C++; tab-width: 8; 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 "mozilla/ServoStyleSet.h"
  6. #include "ServoBindings.h"
  7. #include "mozilla/ServoRestyleManager.h"
  8. #include "mozilla/dom/ChildIterator.h"
  9. #include "nsCSSAnonBoxes.h"
  10. #include "nsCSSPseudoElements.h"
  11. #include "nsIDocumentInlines.h"
  12. #include "nsPrintfCString.h"
  13. #include "nsStyleContext.h"
  14. #include "nsStyleSet.h"
  15. using namespace mozilla;
  16. using namespace mozilla::dom;
  17. ServoStyleSet::ServoStyleSet()
  18. : mPresContext(nullptr)
  19. , mRawSet(Servo_StyleSet_Init())
  20. , mBatching(0)
  21. {
  22. }
  23. void
  24. ServoStyleSet::Init(nsPresContext* aPresContext)
  25. {
  26. mPresContext = aPresContext;
  27. }
  28. void
  29. ServoStyleSet::BeginShutdown()
  30. {
  31. }
  32. void
  33. ServoStyleSet::Shutdown()
  34. {
  35. mRawSet = nullptr;
  36. }
  37. bool
  38. ServoStyleSet::GetAuthorStyleDisabled() const
  39. {
  40. return false;
  41. }
  42. nsresult
  43. ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
  44. {
  45. MOZ_CRASH("stylo: not implemented");
  46. }
  47. void
  48. ServoStyleSet::BeginUpdate()
  49. {
  50. ++mBatching;
  51. }
  52. nsresult
  53. ServoStyleSet::EndUpdate()
  54. {
  55. MOZ_ASSERT(mBatching > 0);
  56. if (--mBatching > 0) {
  57. return NS_OK;
  58. }
  59. // ... do something ...
  60. return NS_OK;
  61. }
  62. already_AddRefed<nsStyleContext>
  63. ServoStyleSet::ResolveStyleFor(Element* aElement,
  64. nsStyleContext* aParentContext)
  65. {
  66. return GetContext(aElement, aParentContext, nullptr,
  67. CSSPseudoElementType::NotPseudo);
  68. }
  69. already_AddRefed<nsStyleContext>
  70. ServoStyleSet::GetContext(nsIContent* aContent,
  71. nsStyleContext* aParentContext,
  72. nsIAtom* aPseudoTag,
  73. CSSPseudoElementType aPseudoType)
  74. {
  75. RefPtr<ServoComputedValues> computedValues =
  76. Servo_ComputedValues_Get(aContent).Consume();
  77. MOZ_ASSERT(computedValues);
  78. return GetContext(computedValues.forget(), aParentContext, aPseudoTag, aPseudoType);
  79. }
  80. already_AddRefed<nsStyleContext>
  81. ServoStyleSet::GetContext(already_AddRefed<ServoComputedValues> aComputedValues,
  82. nsStyleContext* aParentContext,
  83. nsIAtom* aPseudoTag,
  84. CSSPseudoElementType aPseudoType)
  85. {
  86. // XXXbholley: nsStyleSet does visited handling here.
  87. // XXXbholley: Figure out the correct thing to pass here. Does this fixup
  88. // duplicate something that servo already does?
  89. bool skipFixup = false;
  90. return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
  91. aPseudoType, Move(aComputedValues), skipFixup);
  92. }
  93. already_AddRefed<nsStyleContext>
  94. ServoStyleSet::ResolveStyleFor(Element* aElement,
  95. nsStyleContext* aParentContext,
  96. TreeMatchContext& aTreeMatchContext)
  97. {
  98. // aTreeMatchContext is used to speed up selector matching,
  99. // but if the element already has a ServoComputedValues computed in
  100. // advance, then we shouldn't need to use it.
  101. return ResolveStyleFor(aElement, aParentContext);
  102. }
  103. already_AddRefed<nsStyleContext>
  104. ServoStyleSet::ResolveStyleForText(nsIContent* aTextNode,
  105. nsStyleContext* aParentContext)
  106. {
  107. MOZ_ASSERT(aTextNode && aTextNode->IsNodeOfType(nsINode::eTEXT));
  108. MOZ_ASSERT(aTextNode->GetParent());
  109. MOZ_ASSERT(aParentContext);
  110. // Gecko expects text node style contexts to be like elements that match no
  111. // rules: inherit the inherit structs, reset the reset structs. This is cheap
  112. // enough to do on the main thread, which means that the parallel style system
  113. // can avoid worrying about text nodes.
  114. const ServoComputedValues* parentComputedValues =
  115. aParentContext->StyleSource().AsServoComputedValues();
  116. RefPtr<ServoComputedValues> computedValues =
  117. Servo_ComputedValues_Inherit(parentComputedValues).Consume();
  118. return GetContext(computedValues.forget(), aParentContext,
  119. nsCSSAnonBoxes::mozText, CSSPseudoElementType::AnonBox);
  120. }
  121. already_AddRefed<nsStyleContext>
  122. ServoStyleSet::ResolveStyleForOtherNonElement(nsStyleContext* aParentContext)
  123. {
  124. // The parent context can be null if the non-element share a style context
  125. // with the root of an anonymous subtree.
  126. const ServoComputedValues* parent =
  127. aParentContext ? aParentContext->StyleSource().AsServoComputedValues() : nullptr;
  128. RefPtr<ServoComputedValues> computedValues =
  129. Servo_ComputedValues_Inherit(parent).Consume();
  130. MOZ_ASSERT(computedValues);
  131. return GetContext(computedValues.forget(), aParentContext,
  132. nsCSSAnonBoxes::mozOtherNonElement,
  133. CSSPseudoElementType::AnonBox);
  134. }
  135. already_AddRefed<nsStyleContext>
  136. ServoStyleSet::ResolvePseudoElementStyle(Element* aParentElement,
  137. CSSPseudoElementType aType,
  138. nsStyleContext* aParentContext,
  139. Element* aPseudoElement)
  140. {
  141. if (aPseudoElement) {
  142. NS_ERROR("stylo: We don't support CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE yet");
  143. }
  144. MOZ_ASSERT(aParentContext);
  145. MOZ_ASSERT(aType < CSSPseudoElementType::Count);
  146. nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
  147. RefPtr<ServoComputedValues> computedValues =
  148. Servo_ComputedValues_GetForPseudoElement(
  149. aParentContext->StyleSource().AsServoComputedValues(),
  150. aParentElement, pseudoTag, mRawSet.get(), /* is_probe = */ false).Consume();
  151. MOZ_ASSERT(computedValues);
  152. return GetContext(computedValues.forget(), aParentContext, pseudoTag, aType);
  153. }
  154. // aFlags is an nsStyleSet flags bitfield
  155. already_AddRefed<nsStyleContext>
  156. ServoStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag,
  157. nsStyleContext* aParentContext,
  158. uint32_t aFlags)
  159. {
  160. MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudoTag));
  161. MOZ_ASSERT(aFlags == 0 ||
  162. aFlags == nsStyleSet::eSkipParentDisplayBasedStyleFixup);
  163. bool skipFixup = aFlags & nsStyleSet::eSkipParentDisplayBasedStyleFixup;
  164. const ServoComputedValues* parentStyle =
  165. aParentContext ? aParentContext->StyleSource().AsServoComputedValues()
  166. : nullptr;
  167. RefPtr<ServoComputedValues> computedValues =
  168. Servo_ComputedValues_GetForAnonymousBox(parentStyle, aPseudoTag,
  169. mRawSet.get()).Consume();
  170. #ifdef DEBUG
  171. if (!computedValues) {
  172. nsString pseudo;
  173. aPseudoTag->ToString(pseudo);
  174. NS_ERROR(nsPrintfCString("stylo: could not get anon-box: %s",
  175. NS_ConvertUTF16toUTF8(pseudo).get()).get());
  176. MOZ_CRASH();
  177. }
  178. #endif
  179. return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
  180. CSSPseudoElementType::AnonBox,
  181. computedValues.forget(), skipFixup);
  182. }
  183. // manage the set of style sheets in the style set
  184. nsresult
  185. ServoStyleSet::AppendStyleSheet(SheetType aType,
  186. ServoStyleSheet* aSheet)
  187. {
  188. MOZ_ASSERT(aSheet);
  189. MOZ_ASSERT(aSheet->IsApplicable());
  190. MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
  191. mSheets[aType].RemoveElement(aSheet);
  192. mSheets[aType].AppendElement(aSheet);
  193. // Maintain a mirrored list of sheets on the servo side.
  194. Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet->RawSheet());
  195. return NS_OK;
  196. }
  197. nsresult
  198. ServoStyleSet::PrependStyleSheet(SheetType aType,
  199. ServoStyleSheet* aSheet)
  200. {
  201. MOZ_ASSERT(aSheet);
  202. MOZ_ASSERT(aSheet->IsApplicable());
  203. MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
  204. mSheets[aType].RemoveElement(aSheet);
  205. mSheets[aType].InsertElementAt(0, aSheet);
  206. // Maintain a mirrored list of sheets on the servo side.
  207. Servo_StyleSet_PrependStyleSheet(mRawSet.get(), aSheet->RawSheet());
  208. return NS_OK;
  209. }
  210. nsresult
  211. ServoStyleSet::RemoveStyleSheet(SheetType aType,
  212. ServoStyleSheet* aSheet)
  213. {
  214. MOZ_ASSERT(aSheet);
  215. MOZ_ASSERT(aSheet->IsApplicable());
  216. MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
  217. mSheets[aType].RemoveElement(aSheet);
  218. // Maintain a mirrored list of sheets on the servo side.
  219. Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), aSheet->RawSheet());
  220. return NS_OK;
  221. }
  222. nsresult
  223. ServoStyleSet::ReplaceSheets(SheetType aType,
  224. const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets)
  225. {
  226. // Gecko uses a two-dimensional array keyed by sheet type, whereas Servo
  227. // stores a flattened list. This makes ReplaceSheets a pretty clunky thing
  228. // to express. If the need ever arises, we can easily make this more efficent,
  229. // probably by aligning the representations better between engines.
  230. for (ServoStyleSheet* sheet : mSheets[aType]) {
  231. Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), sheet->RawSheet());
  232. }
  233. mSheets[aType].Clear();
  234. mSheets[aType].AppendElements(aNewSheets);
  235. for (ServoStyleSheet* sheet : mSheets[aType]) {
  236. Servo_StyleSet_AppendStyleSheet(mRawSet.get(), sheet->RawSheet());
  237. }
  238. return NS_OK;
  239. }
  240. nsresult
  241. ServoStyleSet::InsertStyleSheetBefore(SheetType aType,
  242. ServoStyleSheet* aNewSheet,
  243. ServoStyleSheet* aReferenceSheet)
  244. {
  245. MOZ_ASSERT(aNewSheet);
  246. MOZ_ASSERT(aReferenceSheet);
  247. MOZ_ASSERT(aNewSheet->IsApplicable());
  248. mSheets[aType].RemoveElement(aNewSheet);
  249. size_t idx = mSheets[aType].IndexOf(aReferenceSheet);
  250. if (idx == mSheets[aType].NoIndex) {
  251. return NS_ERROR_INVALID_ARG;
  252. }
  253. mSheets[aType].InsertElementAt(idx, aNewSheet);
  254. // Maintain a mirrored list of sheets on the servo side.
  255. Servo_StyleSet_InsertStyleSheetBefore(mRawSet.get(), aNewSheet->RawSheet(),
  256. aReferenceSheet->RawSheet());
  257. return NS_OK;
  258. }
  259. int32_t
  260. ServoStyleSet::SheetCount(SheetType aType) const
  261. {
  262. MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
  263. return mSheets[aType].Length();
  264. }
  265. ServoStyleSheet*
  266. ServoStyleSet::StyleSheetAt(SheetType aType,
  267. int32_t aIndex) const
  268. {
  269. MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
  270. return mSheets[aType][aIndex];
  271. }
  272. nsresult
  273. ServoStyleSet::RemoveDocStyleSheet(ServoStyleSheet* aSheet)
  274. {
  275. return RemoveStyleSheet(SheetType::Doc, aSheet);
  276. }
  277. nsresult
  278. ServoStyleSet::AddDocStyleSheet(ServoStyleSheet* aSheet,
  279. nsIDocument* aDocument)
  280. {
  281. RefPtr<StyleSheet> strong(aSheet);
  282. mSheets[SheetType::Doc].RemoveElement(aSheet);
  283. size_t index =
  284. aDocument->FindDocStyleSheetInsertionPoint(mSheets[SheetType::Doc], *aSheet);
  285. mSheets[SheetType::Doc].InsertElementAt(index, aSheet);
  286. // Maintain a mirrored list of sheets on the servo side.
  287. ServoStyleSheet* followingSheet =
  288. mSheets[SheetType::Doc].SafeElementAt(index + 1);
  289. if (followingSheet) {
  290. Servo_StyleSet_InsertStyleSheetBefore(mRawSet.get(), aSheet->RawSheet(),
  291. followingSheet->RawSheet());
  292. } else {
  293. Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet->RawSheet());
  294. }
  295. return NS_OK;
  296. }
  297. already_AddRefed<nsStyleContext>
  298. ServoStyleSet::ProbePseudoElementStyle(Element* aParentElement,
  299. CSSPseudoElementType aType,
  300. nsStyleContext* aParentContext)
  301. {
  302. MOZ_ASSERT(aParentContext);
  303. MOZ_ASSERT(aType < CSSPseudoElementType::Count);
  304. nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
  305. RefPtr<ServoComputedValues> computedValues =
  306. Servo_ComputedValues_GetForPseudoElement(
  307. aParentContext->StyleSource().AsServoComputedValues(),
  308. aParentElement, pseudoTag, mRawSet.get(), /* is_probe = */ true).Consume();
  309. if (!computedValues) {
  310. return nullptr;
  311. }
  312. // For :before and :after pseudo-elements, having display: none or no
  313. // 'content' property is equivalent to not having the pseudo-element
  314. // at all.
  315. if (computedValues &&
  316. (pseudoTag == nsCSSPseudoElements::before ||
  317. pseudoTag == nsCSSPseudoElements::after)) {
  318. const nsStyleDisplay *display = Servo_GetStyleDisplay(computedValues);
  319. const nsStyleContent *content = Servo_GetStyleContent(computedValues);
  320. // XXXldb What is contentCount for |content: ""|?
  321. if (display->mDisplay == StyleDisplay::None ||
  322. content->ContentCount() == 0) {
  323. return nullptr;
  324. }
  325. }
  326. return GetContext(computedValues.forget(), aParentContext, pseudoTag, aType);
  327. }
  328. already_AddRefed<nsStyleContext>
  329. ServoStyleSet::ProbePseudoElementStyle(Element* aParentElement,
  330. CSSPseudoElementType aType,
  331. nsStyleContext* aParentContext,
  332. TreeMatchContext& aTreeMatchContext,
  333. Element* aPseudoElement)
  334. {
  335. if (aPseudoElement) {
  336. NS_ERROR("stylo: We don't support CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE yet");
  337. }
  338. return ProbePseudoElementStyle(aParentElement, aType, aParentContext);
  339. }
  340. nsRestyleHint
  341. ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
  342. EventStates aStateMask)
  343. {
  344. NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
  345. return nsRestyleHint(0);
  346. }
  347. nsRestyleHint
  348. ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
  349. CSSPseudoElementType aPseudoType,
  350. dom::Element* aPseudoElement,
  351. EventStates aStateMask)
  352. {
  353. NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
  354. return nsRestyleHint(0);
  355. }
  356. nsRestyleHint
  357. ServoStyleSet::ComputeRestyleHint(dom::Element* aElement,
  358. ServoElementSnapshot* aSnapshot)
  359. {
  360. return Servo_ComputeRestyleHint(aElement, aSnapshot, mRawSet.get());
  361. }
  362. static void
  363. ClearDirtyBits(nsIContent* aContent)
  364. {
  365. bool traverseDescendants = aContent->HasDirtyDescendantsForServo();
  366. aContent->UnsetIsDirtyAndHasDirtyDescendantsForServo();
  367. if (!traverseDescendants) {
  368. return;
  369. }
  370. StyleChildrenIterator it(aContent);
  371. for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
  372. ClearDirtyBits(n);
  373. }
  374. }
  375. void
  376. ServoStyleSet::StyleDocument(bool aLeaveDirtyBits)
  377. {
  378. // Grab the root.
  379. nsIDocument* doc = mPresContext->Document();
  380. nsIContent* root = doc->GetRootElement();
  381. MOZ_ASSERT(root);
  382. // Restyle the document, clearing the dirty bits if requested.
  383. Servo_RestyleSubtree(root, mRawSet.get());
  384. if (!aLeaveDirtyBits) {
  385. ClearDirtyBits(root);
  386. doc->UnsetHasDirtyDescendantsForServo();
  387. }
  388. }
  389. void
  390. ServoStyleSet::StyleNewSubtree(nsIContent* aContent)
  391. {
  392. MOZ_ASSERT(aContent->IsDirtyForServo());
  393. if (aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)) {
  394. Servo_RestyleSubtree(aContent, mRawSet.get());
  395. }
  396. ClearDirtyBits(aContent);
  397. }
  398. void
  399. ServoStyleSet::StyleNewChildren(nsIContent* aParent)
  400. {
  401. MOZ_ASSERT(aParent->HasDirtyDescendantsForServo());
  402. Servo_RestyleSubtree(aParent, mRawSet.get());
  403. ClearDirtyBits(aParent);
  404. }