summaryrefslogtreecommitdiffstats
path: root/layout/style/ServoStyleSet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/ServoStyleSet.cpp')
-rw-r--r--layout/style/ServoStyleSet.cpp479
1 files changed, 479 insertions, 0 deletions
diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp
new file mode 100644
index 000000000..519d17aa8
--- /dev/null
+++ b/layout/style/ServoStyleSet.cpp
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ServoStyleSet.h"
+
+#include "mozilla/ServoRestyleManager.h"
+#include "mozilla/dom/ChildIterator.h"
+#include "nsCSSAnonBoxes.h"
+#include "nsCSSPseudoElements.h"
+#include "nsIDocumentInlines.h"
+#include "nsPrintfCString.h"
+#include "nsStyleContext.h"
+#include "nsStyleSet.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+ServoStyleSet::ServoStyleSet()
+ : mPresContext(nullptr)
+ , mRawSet(Servo_StyleSet_Init())
+ , mBatching(0)
+{
+}
+
+void
+ServoStyleSet::Init(nsPresContext* aPresContext)
+{
+ mPresContext = aPresContext;
+}
+
+void
+ServoStyleSet::BeginShutdown()
+{
+}
+
+void
+ServoStyleSet::Shutdown()
+{
+ mRawSet = nullptr;
+}
+
+bool
+ServoStyleSet::GetAuthorStyleDisabled() const
+{
+ return false;
+}
+
+nsresult
+ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
+{
+ MOZ_CRASH("stylo: not implemented");
+}
+
+void
+ServoStyleSet::BeginUpdate()
+{
+ ++mBatching;
+}
+
+nsresult
+ServoStyleSet::EndUpdate()
+{
+ MOZ_ASSERT(mBatching > 0);
+ if (--mBatching > 0) {
+ return NS_OK;
+ }
+
+ // ... do something ...
+ return NS_OK;
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ResolveStyleFor(Element* aElement,
+ nsStyleContext* aParentContext)
+{
+ return GetContext(aElement, aParentContext, nullptr,
+ CSSPseudoElementType::NotPseudo);
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::GetContext(nsIContent* aContent,
+ nsStyleContext* aParentContext,
+ nsIAtom* aPseudoTag,
+ CSSPseudoElementType aPseudoType)
+{
+ RefPtr<ServoComputedValues> computedValues =
+ Servo_ComputedValues_Get(aContent).Consume();
+ MOZ_ASSERT(computedValues);
+ return GetContext(computedValues.forget(), aParentContext, aPseudoTag, aPseudoType);
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::GetContext(already_AddRefed<ServoComputedValues> aComputedValues,
+ nsStyleContext* aParentContext,
+ nsIAtom* aPseudoTag,
+ CSSPseudoElementType aPseudoType)
+{
+ // XXXbholley: nsStyleSet does visited handling here.
+
+ // XXXbholley: Figure out the correct thing to pass here. Does this fixup
+ // duplicate something that servo already does?
+ bool skipFixup = false;
+
+ return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
+ aPseudoType, Move(aComputedValues), skipFixup);
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ResolveStyleFor(Element* aElement,
+ nsStyleContext* aParentContext,
+ TreeMatchContext& aTreeMatchContext)
+{
+ // aTreeMatchContext is used to speed up selector matching,
+ // but if the element already has a ServoComputedValues computed in
+ // advance, then we shouldn't need to use it.
+ return ResolveStyleFor(aElement, aParentContext);
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ResolveStyleForText(nsIContent* aTextNode,
+ nsStyleContext* aParentContext)
+{
+ MOZ_ASSERT(aTextNode && aTextNode->IsNodeOfType(nsINode::eTEXT));
+ MOZ_ASSERT(aTextNode->GetParent());
+ MOZ_ASSERT(aParentContext);
+
+ // Gecko expects text node style contexts to be like elements that match no
+ // rules: inherit the inherit structs, reset the reset structs. This is cheap
+ // enough to do on the main thread, which means that the parallel style system
+ // can avoid worrying about text nodes.
+ const ServoComputedValues* parentComputedValues =
+ aParentContext->StyleSource().AsServoComputedValues();
+ RefPtr<ServoComputedValues> computedValues =
+ Servo_ComputedValues_Inherit(parentComputedValues).Consume();
+
+ return GetContext(computedValues.forget(), aParentContext,
+ nsCSSAnonBoxes::mozText, CSSPseudoElementType::AnonBox);
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ResolveStyleForOtherNonElement(nsStyleContext* aParentContext)
+{
+ // The parent context can be null if the non-element share a style context
+ // with the root of an anonymous subtree.
+ const ServoComputedValues* parent =
+ aParentContext ? aParentContext->StyleSource().AsServoComputedValues() : nullptr;
+ RefPtr<ServoComputedValues> computedValues =
+ Servo_ComputedValues_Inherit(parent).Consume();
+ MOZ_ASSERT(computedValues);
+
+ return GetContext(computedValues.forget(), aParentContext,
+ nsCSSAnonBoxes::mozOtherNonElement,
+ CSSPseudoElementType::AnonBox);
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ResolvePseudoElementStyle(Element* aParentElement,
+ CSSPseudoElementType aType,
+ nsStyleContext* aParentContext,
+ Element* aPseudoElement)
+{
+ if (aPseudoElement) {
+ NS_ERROR("stylo: We don't support CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE yet");
+ }
+ MOZ_ASSERT(aParentContext);
+ MOZ_ASSERT(aType < CSSPseudoElementType::Count);
+ nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
+
+ RefPtr<ServoComputedValues> computedValues =
+ Servo_ComputedValues_GetForPseudoElement(
+ aParentContext->StyleSource().AsServoComputedValues(),
+ aParentElement, pseudoTag, mRawSet.get(), /* is_probe = */ false).Consume();
+ MOZ_ASSERT(computedValues);
+
+ return GetContext(computedValues.forget(), aParentContext, pseudoTag, aType);
+}
+
+// aFlags is an nsStyleSet flags bitfield
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag,
+ nsStyleContext* aParentContext,
+ uint32_t aFlags)
+{
+ MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudoTag));
+
+ MOZ_ASSERT(aFlags == 0 ||
+ aFlags == nsStyleSet::eSkipParentDisplayBasedStyleFixup);
+ bool skipFixup = aFlags & nsStyleSet::eSkipParentDisplayBasedStyleFixup;
+
+ const ServoComputedValues* parentStyle =
+ aParentContext ? aParentContext->StyleSource().AsServoComputedValues()
+ : nullptr;
+ RefPtr<ServoComputedValues> computedValues =
+ Servo_ComputedValues_GetForAnonymousBox(parentStyle, aPseudoTag,
+ mRawSet.get()).Consume();
+#ifdef DEBUG
+ if (!computedValues) {
+ nsString pseudo;
+ aPseudoTag->ToString(pseudo);
+ NS_ERROR(nsPrintfCString("stylo: could not get anon-box: %s",
+ NS_ConvertUTF16toUTF8(pseudo).get()).get());
+ MOZ_CRASH();
+ }
+#endif
+
+ return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
+ CSSPseudoElementType::AnonBox,
+ computedValues.forget(), skipFixup);
+}
+
+// manage the set of style sheets in the style set
+nsresult
+ServoStyleSet::AppendStyleSheet(SheetType aType,
+ ServoStyleSheet* aSheet)
+{
+ MOZ_ASSERT(aSheet);
+ MOZ_ASSERT(aSheet->IsApplicable());
+ MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
+
+ mSheets[aType].RemoveElement(aSheet);
+ mSheets[aType].AppendElement(aSheet);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet->RawSheet());
+
+ return NS_OK;
+}
+
+nsresult
+ServoStyleSet::PrependStyleSheet(SheetType aType,
+ ServoStyleSheet* aSheet)
+{
+ MOZ_ASSERT(aSheet);
+ MOZ_ASSERT(aSheet->IsApplicable());
+ MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
+
+ mSheets[aType].RemoveElement(aSheet);
+ mSheets[aType].InsertElementAt(0, aSheet);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_PrependStyleSheet(mRawSet.get(), aSheet->RawSheet());
+
+ return NS_OK;
+}
+
+nsresult
+ServoStyleSet::RemoveStyleSheet(SheetType aType,
+ ServoStyleSheet* aSheet)
+{
+ MOZ_ASSERT(aSheet);
+ MOZ_ASSERT(aSheet->IsApplicable());
+ MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
+
+ mSheets[aType].RemoveElement(aSheet);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), aSheet->RawSheet());
+
+ return NS_OK;
+}
+
+nsresult
+ServoStyleSet::ReplaceSheets(SheetType aType,
+ const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets)
+{
+ // Gecko uses a two-dimensional array keyed by sheet type, whereas Servo
+ // stores a flattened list. This makes ReplaceSheets a pretty clunky thing
+ // to express. If the need ever arises, we can easily make this more efficent,
+ // probably by aligning the representations better between engines.
+
+ for (ServoStyleSheet* sheet : mSheets[aType]) {
+ Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), sheet->RawSheet());
+ }
+
+ mSheets[aType].Clear();
+ mSheets[aType].AppendElements(aNewSheets);
+
+ for (ServoStyleSheet* sheet : mSheets[aType]) {
+ Servo_StyleSet_AppendStyleSheet(mRawSet.get(), sheet->RawSheet());
+ }
+
+ return NS_OK;
+}
+
+nsresult
+ServoStyleSet::InsertStyleSheetBefore(SheetType aType,
+ ServoStyleSheet* aNewSheet,
+ ServoStyleSheet* aReferenceSheet)
+{
+ MOZ_ASSERT(aNewSheet);
+ MOZ_ASSERT(aReferenceSheet);
+ MOZ_ASSERT(aNewSheet->IsApplicable());
+
+ mSheets[aType].RemoveElement(aNewSheet);
+ size_t idx = mSheets[aType].IndexOf(aReferenceSheet);
+ if (idx == mSheets[aType].NoIndex) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mSheets[aType].InsertElementAt(idx, aNewSheet);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_InsertStyleSheetBefore(mRawSet.get(), aNewSheet->RawSheet(),
+ aReferenceSheet->RawSheet());
+
+ return NS_OK;
+}
+
+int32_t
+ServoStyleSet::SheetCount(SheetType aType) const
+{
+ MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
+ return mSheets[aType].Length();
+}
+
+ServoStyleSheet*
+ServoStyleSet::StyleSheetAt(SheetType aType,
+ int32_t aIndex) const
+{
+ MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
+ return mSheets[aType][aIndex];
+}
+
+nsresult
+ServoStyleSet::RemoveDocStyleSheet(ServoStyleSheet* aSheet)
+{
+ return RemoveStyleSheet(SheetType::Doc, aSheet);
+}
+
+nsresult
+ServoStyleSet::AddDocStyleSheet(ServoStyleSheet* aSheet,
+ nsIDocument* aDocument)
+{
+ RefPtr<StyleSheet> strong(aSheet);
+
+ mSheets[SheetType::Doc].RemoveElement(aSheet);
+
+ size_t index =
+ aDocument->FindDocStyleSheetInsertionPoint(mSheets[SheetType::Doc], aSheet);
+ mSheets[SheetType::Doc].InsertElementAt(index, aSheet);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ ServoStyleSheet* followingSheet =
+ mSheets[SheetType::Doc].SafeElementAt(index + 1);
+ if (followingSheet) {
+ Servo_StyleSet_InsertStyleSheetBefore(mRawSet.get(), aSheet->RawSheet(),
+ followingSheet->RawSheet());
+ } else {
+ Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet->RawSheet());
+ }
+
+ return NS_OK;
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ProbePseudoElementStyle(Element* aParentElement,
+ CSSPseudoElementType aType,
+ nsStyleContext* aParentContext)
+{
+ MOZ_ASSERT(aParentContext);
+ MOZ_ASSERT(aType < CSSPseudoElementType::Count);
+ nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
+
+ RefPtr<ServoComputedValues> computedValues =
+ Servo_ComputedValues_GetForPseudoElement(
+ aParentContext->StyleSource().AsServoComputedValues(),
+ aParentElement, pseudoTag, mRawSet.get(), /* is_probe = */ true).Consume();
+
+ if (!computedValues) {
+ return nullptr;
+ }
+
+ // For :before and :after pseudo-elements, having display: none or no
+ // 'content' property is equivalent to not having the pseudo-element
+ // at all.
+ if (computedValues &&
+ (pseudoTag == nsCSSPseudoElements::before ||
+ pseudoTag == nsCSSPseudoElements::after)) {
+ const nsStyleDisplay *display = Servo_GetStyleDisplay(computedValues);
+ const nsStyleContent *content = Servo_GetStyleContent(computedValues);
+ // XXXldb What is contentCount for |content: ""|?
+ if (display->mDisplay == StyleDisplay::None ||
+ content->ContentCount() == 0) {
+ return nullptr;
+ }
+ }
+
+ return GetContext(computedValues.forget(), aParentContext, pseudoTag, aType);
+}
+
+already_AddRefed<nsStyleContext>
+ServoStyleSet::ProbePseudoElementStyle(Element* aParentElement,
+ CSSPseudoElementType aType,
+ nsStyleContext* aParentContext,
+ TreeMatchContext& aTreeMatchContext,
+ Element* aPseudoElement)
+{
+ if (aPseudoElement) {
+ NS_ERROR("stylo: We don't support CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE yet");
+ }
+ return ProbePseudoElementStyle(aParentElement, aType, aParentContext);
+}
+
+nsRestyleHint
+ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
+ EventStates aStateMask)
+{
+ NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
+ return nsRestyleHint(0);
+}
+
+nsRestyleHint
+ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
+ CSSPseudoElementType aPseudoType,
+ dom::Element* aPseudoElement,
+ EventStates aStateMask)
+{
+ NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
+ return nsRestyleHint(0);
+}
+
+nsRestyleHint
+ServoStyleSet::ComputeRestyleHint(dom::Element* aElement,
+ ServoElementSnapshot* aSnapshot)
+{
+ return Servo_ComputeRestyleHint(aElement, aSnapshot, mRawSet.get());
+}
+
+static void
+ClearDirtyBits(nsIContent* aContent)
+{
+ bool traverseDescendants = aContent->HasDirtyDescendantsForServo();
+ aContent->UnsetIsDirtyAndHasDirtyDescendantsForServo();
+ if (!traverseDescendants) {
+ return;
+ }
+
+ StyleChildrenIterator it(aContent);
+ for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
+ ClearDirtyBits(n);
+ }
+}
+
+void
+ServoStyleSet::StyleDocument(bool aLeaveDirtyBits)
+{
+ // Grab the root.
+ nsIDocument* doc = mPresContext->Document();
+ nsIContent* root = doc->GetRootElement();
+ MOZ_ASSERT(root);
+
+ // Restyle the document, clearing the dirty bits if requested.
+ Servo_RestyleSubtree(root, mRawSet.get());
+ if (!aLeaveDirtyBits) {
+ ClearDirtyBits(root);
+ doc->UnsetHasDirtyDescendantsForServo();
+ }
+}
+
+void
+ServoStyleSet::StyleNewSubtree(nsIContent* aContent)
+{
+ MOZ_ASSERT(aContent->IsDirtyForServo());
+ if (aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)) {
+ Servo_RestyleSubtree(aContent, mRawSet.get());
+ }
+ ClearDirtyBits(aContent);
+}
+
+void
+ServoStyleSet::StyleNewChildren(nsIContent* aParent)
+{
+ MOZ_ASSERT(aParent->HasDirtyDescendantsForServo());
+ Servo_RestyleSubtree(aParent, mRawSet.get());
+ ClearDirtyBits(aParent);
+}