Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / layout / style / nsComputedDOMStyle.cpp
blob6a467b679b92fbeade8cf60b0477727fe80007b5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* DOM object returned from element.getComputedStyle() */
9 #include "nsComputedDOMStyle.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/FloatingPoint.h"
13 #include "mozilla/FontPropertyTypes.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/PresShell.h"
16 #include "mozilla/PresShellInlines.h"
17 #include "mozilla/ScopeExit.h"
18 #include "mozilla/StaticPtr.h"
19 #include "mozilla/StaticPrefs_layout.h"
21 #include "nsError.h"
22 #include "nsIFrame.h"
23 #include "nsIFrameInlines.h"
24 #include "mozilla/ComputedStyle.h"
25 #include "nsIScrollableFrame.h"
26 #include "nsContentUtils.h"
27 #include "nsDocShell.h"
28 #include "nsIContent.h"
29 #include "nsStyleConsts.h"
31 #include "nsDOMCSSValueList.h"
32 #include "nsFlexContainerFrame.h"
33 #include "nsGridContainerFrame.h"
34 #include "nsGkAtoms.h"
35 #include "mozilla/ReflowInput.h"
36 #include "nsStyleUtil.h"
37 #include "nsStyleStructInlines.h"
38 #include "nsROCSSPrimitiveValue.h"
40 #include "nsPresContext.h"
41 #include "mozilla/dom/Document.h"
43 #include "nsCSSProps.h"
44 #include "nsCSSPseudoElements.h"
45 #include "mozilla/EffectSet.h"
46 #include "mozilla/IntegerRange.h"
47 #include "mozilla/ServoStyleSet.h"
48 #include "mozilla/RestyleManager.h"
49 #include "mozilla/ViewportFrame.h"
50 #include "nsLayoutUtils.h"
51 #include "nsDisplayList.h"
52 #include "nsDOMCSSDeclaration.h"
53 #include "nsStyleTransformMatrix.h"
54 #include "mozilla/dom/Element.h"
55 #include "mozilla/dom/ElementInlines.h"
56 #include "prtime.h"
57 #include "nsWrapperCacheInlines.h"
58 #include "mozilla/AppUnits.h"
59 #include <algorithm>
60 #include "mozilla/ComputedStyleInlines.h"
61 #include "nsPrintfCString.h"
63 using namespace mozilla;
64 using namespace mozilla::dom;
67 * This is the implementation of the readonly CSSStyleDeclaration that is
68 * returned by the getComputedStyle() function.
71 already_AddRefed<nsComputedDOMStyle> NS_NewComputedDOMStyle(
72 dom::Element* aElement, const nsAString& aPseudoElt, Document* aDocument,
73 nsComputedDOMStyle::StyleType aStyleType, mozilla::ErrorResult&) {
74 auto [pseudo, functionalPseudoParameter] =
75 nsCSSPseudoElements::ParsePseudoElement(aPseudoElt,
76 CSSEnabledState::ForAllContent);
77 auto returnEmpty = nsComputedDOMStyle::AlwaysReturnEmptyStyle::No;
78 if (!pseudo) {
79 if (!aPseudoElt.IsEmpty() && aPseudoElt.First() == u':') {
80 returnEmpty = nsComputedDOMStyle::AlwaysReturnEmptyStyle::Yes;
82 pseudo.emplace(PseudoStyleType::NotPseudo);
84 RefPtr<nsComputedDOMStyle> computedStyle =
85 new nsComputedDOMStyle(aElement, *pseudo, functionalPseudoParameter,
86 aDocument, aStyleType, returnEmpty);
87 return computedStyle.forget();
90 static nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited) {
91 return new nsDOMCSSValueList(aCommaDelimited);
94 static const Element* GetRenderedElement(const Element* aElement,
95 PseudoStyleType aPseudo) {
96 if (aPseudo == PseudoStyleType::NotPseudo) {
97 return aElement;
99 if (aPseudo == PseudoStyleType::before) {
100 return nsLayoutUtils::GetBeforePseudo(aElement);
102 if (aPseudo == PseudoStyleType::after) {
103 return nsLayoutUtils::GetAfterPseudo(aElement);
105 if (aPseudo == PseudoStyleType::marker) {
106 return nsLayoutUtils::GetMarkerPseudo(aElement);
108 return nullptr;
111 // Whether aDocument needs to restyle for aElement
112 static bool ElementNeedsRestyle(Element* aElement, PseudoStyleType aPseudo,
113 bool aMayNeedToFlushLayout) {
114 const Document* doc = aElement->GetComposedDoc();
115 if (!doc) {
116 // If the element is out of the document we don't return styles for it, so
117 // nothing to do.
118 return false;
121 PresShell* presShell = doc->GetPresShell();
122 if (!presShell) {
123 // If there's no pres-shell we'll just either mint a new style from our
124 // caller document, or return no styles, so nothing to do (unless our owner
125 // element needs to get restyled, which could cause us to gain a pres shell,
126 // but the caller checks that).
127 return false;
130 // Unfortunately we don't know if the sheet change affects mElement or not, so
131 // just assume it will and that we need to flush normally.
132 ServoStyleSet* styleSet = presShell->StyleSet();
133 if (styleSet->StyleSheetsHaveChanged()) {
134 return true;
137 nsPresContext* presContext = presShell->GetPresContext();
138 MOZ_ASSERT(presContext);
140 // Pending media query updates can definitely change style on the element. For
141 // example, if you change the zoom factor and then call getComputedStyle, you
142 // should be able to observe the style with the new media queries.
144 // TODO(emilio): Does this need to also check the user font set? (it affects
145 // ch / ex units).
146 if (presContext->HasPendingMediaQueryUpdates()) {
147 // So gotta flush.
148 return true;
151 // If the pseudo-element is animating, make sure to flush.
152 if (aElement->MayHaveAnimations() && aPseudo != PseudoStyleType::NotPseudo &&
153 AnimationUtils::IsSupportedPseudoForAnimations(aPseudo)) {
154 if (EffectSet::Get(aElement, aPseudo)) {
155 return true;
159 // For Servo, we need to process the restyle-hint-invalidations first, to
160 // expand LaterSiblings hint, so that we can look whether ancestors need
161 // restyling.
162 RestyleManager* restyleManager = presContext->RestyleManager();
163 restyleManager->ProcessAllPendingAttributeAndStateInvalidations();
165 if (!presContext->EffectCompositor()->HasPendingStyleUpdates() &&
166 !doc->GetServoRestyleRoot()) {
167 return false;
170 // If there's a pseudo, we need to prefer that element, as the pseudo itself
171 // may have explicit restyles.
172 const Element* styledElement = GetRenderedElement(aElement, aPseudo);
173 // Try to skip the restyle otherwise.
174 return Servo_HasPendingRestyleAncestor(
175 styledElement ? styledElement : aElement, aMayNeedToFlushLayout);
179 * An object that represents the ordered set of properties that are exposed on
180 * an nsComputedDOMStyle object and how their computed values can be obtained.
182 struct ComputedStyleMap {
183 friend class nsComputedDOMStyle;
185 struct Entry {
186 // Create a pointer-to-member-function type.
187 using ComputeMethod = already_AddRefed<CSSValue> (nsComputedDOMStyle::*)();
189 nsCSSPropertyID mProperty;
191 // Whether the property can ever be exposed in getComputedStyle(). For
192 // example, @page descriptors implemented as CSS properties or other
193 // internal properties, would have this flag set to `false`.
194 bool mCanBeExposed = false;
196 ComputeMethod mGetter = nullptr;
198 bool IsEnumerable() const {
199 return IsEnabled() && !nsCSSProps::IsShorthand(mProperty);
202 bool IsEnabled() const {
203 if (!mCanBeExposed ||
204 !nsCSSProps::IsEnabled(mProperty, CSSEnabledState::ForAllContent)) {
205 return false;
207 if (nsCSSProps::IsShorthand(mProperty) &&
208 !StaticPrefs::layout_css_computed_style_shorthands()) {
209 return nsCSSProps::PropHasFlags(
210 mProperty, CSSPropFlags::ShorthandUnconditionallyExposedOnGetCS);
212 return true;
216 // This generated file includes definition of kEntries which is typed
217 // Entry[] and used below, so this #include has to be put here.
218 #include "nsComputedDOMStyleGenerated.inc"
221 * Returns the number of properties that should be exposed on an
222 * nsComputedDOMStyle, ecxluding any disabled properties.
224 uint32_t Length() {
225 Update();
226 return mEnumerablePropertyCount;
230 * Returns the property at the given index in the list of properties
231 * that should be exposed on an nsComputedDOMStyle, excluding any
232 * disabled properties.
234 nsCSSPropertyID PropertyAt(uint32_t aIndex) {
235 Update();
236 return kEntries[EntryIndex(aIndex)].mProperty;
240 * Searches for and returns the computed style map entry for the given
241 * property, or nullptr if the property is not exposed on nsComputedDOMStyle
242 * or is currently disabled.
244 const Entry* FindEntryForProperty(nsCSSPropertyID aPropID) {
245 if (size_t(aPropID) >= ArrayLength(kEntryIndices)) {
246 MOZ_ASSERT(aPropID == eCSSProperty_UNKNOWN);
247 return nullptr;
249 MOZ_ASSERT(kEntryIndices[aPropID] < ArrayLength(kEntries));
250 const auto& entry = kEntries[kEntryIndices[aPropID]];
251 if (!entry.IsEnabled()) {
252 return nullptr;
254 return &entry;
258 * Records that mIndexMap needs updating, due to prefs changing that could
259 * affect the set of properties exposed on an nsComputedDOMStyle.
261 void MarkDirty() { mEnumerablePropertyCount = 0; }
263 // The member variables are public so that we can use an initializer in
264 // nsComputedDOMStyle::GetComputedStyleMap. Use the member functions
265 // above to get information from this object.
268 * The number of properties that should be exposed on an nsComputedDOMStyle.
269 * This will be less than eComputedStyleProperty_COUNT if some property
270 * prefs are disabled. A value of 0 indicates that it and mIndexMap are out
271 * of date.
273 uint32_t mEnumerablePropertyCount = 0;
276 * A map of indexes on the nsComputedDOMStyle object to indexes into kEntries.
278 uint32_t mIndexMap[ArrayLength(kEntries)];
280 private:
282 * Returns whether mEnumerablePropertyCount and mIndexMap are out of date.
284 bool IsDirty() { return mEnumerablePropertyCount == 0; }
287 * Updates mEnumerablePropertyCount and mIndexMap to take into account
288 * properties whose prefs are currently disabled.
290 void Update();
293 * Maps an nsComputedDOMStyle indexed getter index to an index into kEntries.
295 uint32_t EntryIndex(uint32_t aIndex) const {
296 MOZ_ASSERT(aIndex < mEnumerablePropertyCount);
297 return mIndexMap[aIndex];
301 constexpr ComputedStyleMap::Entry
302 ComputedStyleMap::kEntries[ArrayLength(kEntries)];
304 constexpr size_t ComputedStyleMap::kEntryIndices[ArrayLength(kEntries)];
306 void ComputedStyleMap::Update() {
307 if (!IsDirty()) {
308 return;
311 uint32_t index = 0;
312 for (uint32_t i = 0; i < ArrayLength(kEntries); i++) {
313 if (kEntries[i].IsEnumerable()) {
314 mIndexMap[index++] = i;
317 mEnumerablePropertyCount = index;
320 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement,
321 PseudoStyleType aPseudo,
322 nsAtom* aFunctionalPseudoParameter,
323 Document* aDocument,
324 StyleType aStyleType,
325 AlwaysReturnEmptyStyle aAlwaysEmpty)
326 : mDocumentWeak(nullptr),
327 mOuterFrame(nullptr),
328 mInnerFrame(nullptr),
329 mPresShell(nullptr),
330 mPseudo(aPseudo),
331 mFunctionalPseudoParameter(aFunctionalPseudoParameter),
332 mStyleType(aStyleType),
333 mAlwaysReturnEmpty(aAlwaysEmpty) {
334 MOZ_ASSERT(aElement);
335 MOZ_ASSERT(aDocument);
336 // TODO(emilio, bug 548397, https://github.com/w3c/csswg-drafts/issues/2403):
337 // Should use aElement->OwnerDoc() instead.
338 mDocumentWeak = do_GetWeakReference(aDocument);
339 mElement = aElement;
342 nsComputedDOMStyle::~nsComputedDOMStyle() {
343 MOZ_ASSERT(!mResolvedComputedStyle,
344 "Should have called ClearComputedStyle() during last release.");
347 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsComputedDOMStyle)
349 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle)
350 tmp->ClearComputedStyle(); // remove observer before clearing mElement
351 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
352 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
353 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
355 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsComputedDOMStyle)
356 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
357 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
359 // We can skip the nsComputedDOMStyle if it has no wrapper and its
360 // element is skippable, because it will have no outgoing edges, so
361 // it can't be part of a cycle.
363 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle)
364 if (!tmp->GetWrapperPreserveColor()) {
365 return !tmp->mElement ||
366 mozilla::dom::FragmentOrElement::CanSkip(tmp->mElement, true);
368 return tmp->HasKnownLiveWrapper();
369 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
371 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle)
372 if (!tmp->GetWrapperPreserveColor()) {
373 return !tmp->mElement ||
374 mozilla::dom::FragmentOrElement::CanSkipInCC(tmp->mElement);
376 return tmp->HasKnownLiveWrapper();
377 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
379 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsComputedDOMStyle)
380 if (!tmp->GetWrapperPreserveColor()) {
381 return !tmp->mElement ||
382 mozilla::dom::FragmentOrElement::CanSkipThis(tmp->mElement);
384 return tmp->HasKnownLiveWrapper();
385 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
387 // QueryInterface implementation for nsComputedDOMStyle
388 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle)
389 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
390 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
391 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
393 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle)
394 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsComputedDOMStyle,
395 ClearComputedStyle())
397 void nsComputedDOMStyle::GetPropertyValue(const nsCSSPropertyID aPropID,
398 nsACString& aValue) {
399 return GetPropertyValue(aPropID, EmptyCString(), aValue);
402 void nsComputedDOMStyle::SetPropertyValue(const nsCSSPropertyID aPropID,
403 const nsACString& aValue,
404 nsIPrincipal* aSubjectPrincipal,
405 ErrorResult& aRv) {
406 aRv.ThrowNoModificationAllowedError(nsPrintfCString(
407 "Can't set value for property '%s' in computed style",
408 PromiseFlatCString(nsCSSProps::GetStringValue(aPropID)).get()));
411 void nsComputedDOMStyle::GetCssText(nsACString& aCssText) {
412 aCssText.Truncate();
415 void nsComputedDOMStyle::SetCssText(const nsACString& aCssText,
416 nsIPrincipal* aSubjectPrincipal,
417 ErrorResult& aRv) {
418 aRv.ThrowNoModificationAllowedError("Can't set cssText on computed style");
421 uint32_t nsComputedDOMStyle::Length() {
422 // Make sure we have up to date style so that we can include custom
423 // properties.
424 UpdateCurrentStyleSources(eCSSPropertyExtra_variable);
425 if (!mComputedStyle) {
426 return 0;
429 uint32_t length = GetComputedStyleMap()->Length() +
430 Servo_GetCustomPropertiesCount(mComputedStyle);
432 ClearCurrentStyleSources();
434 return length;
437 css::Rule* nsComputedDOMStyle::GetParentRule() { return nullptr; }
439 void nsComputedDOMStyle::GetPropertyValue(const nsACString& aPropertyName,
440 nsACString& aReturn) {
441 nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
442 GetPropertyValue(prop, aPropertyName, aReturn);
445 void nsComputedDOMStyle::GetPropertyValue(
446 nsCSSPropertyID aPropID, const nsACString& aMaybeCustomPropertyName,
447 nsACString& aReturn) {
448 MOZ_ASSERT(aReturn.IsEmpty());
450 const ComputedStyleMap::Entry* entry = nullptr;
451 if (aPropID != eCSSPropertyExtra_variable) {
452 entry = GetComputedStyleMap()->FindEntryForProperty(aPropID);
453 if (!entry) {
454 return;
458 UpdateCurrentStyleSources(aPropID);
459 if (!mComputedStyle) {
460 return;
463 auto cleanup = mozilla::MakeScopeExit([&] { ClearCurrentStyleSources(); });
465 if (!entry) {
466 MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aMaybeCustomPropertyName));
467 const nsACString& name =
468 Substring(aMaybeCustomPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
469 Servo_GetCustomPropertyValue(
470 mComputedStyle, mPresShell->StyleSet()->RawData(), &name, &aReturn);
471 return;
474 if (nsCSSProps::PropHasFlags(aPropID, CSSPropFlags::IsLogical)) {
475 MOZ_ASSERT(entry);
476 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
478 DebugOnly<nsCSSPropertyID> logicalProp = aPropID;
480 aPropID = Servo_ResolveLogicalProperty(aPropID, mComputedStyle);
481 entry = GetComputedStyleMap()->FindEntryForProperty(aPropID);
483 MOZ_ASSERT(NeedsToFlushLayout(logicalProp) == NeedsToFlushLayout(aPropID),
484 "Logical and physical property don't agree on whether layout is "
485 "needed");
488 if (!nsCSSProps::PropHasFlags(aPropID, CSSPropFlags::SerializedByServo)) {
489 if (RefPtr<CSSValue> value = (this->*entry->mGetter)()) {
490 nsAutoString text;
491 value->GetCssText(text);
492 CopyUTF16toUTF8(text, aReturn);
494 return;
497 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
498 Servo_GetResolvedValue(mComputedStyle, aPropID,
499 mPresShell->StyleSet()->RawData(), mElement, &aReturn);
502 /* static */
503 already_AddRefed<const ComputedStyle> nsComputedDOMStyle::GetComputedStyle(
504 Element* aElement, PseudoStyleType aPseudo,
505 nsAtom* aFunctionalPseudoParameter, StyleType aStyleType) {
506 if (Document* doc = aElement->GetComposedDoc()) {
507 doc->FlushPendingNotifications(FlushType::Style);
509 return GetComputedStyleNoFlush(aElement, aPseudo, aFunctionalPseudoParameter,
510 aStyleType);
514 * The following function checks whether we need to explicitly resolve the style
515 * again, even though we have a style coming from the frame.
517 * This basically checks whether the style is or may be under a ::first-line or
518 * ::first-letter frame, in which case we can't return the frame style, and we
519 * need to resolve it. See bug 505515.
521 static bool MustReresolveStyle(const ComputedStyle* aStyle) {
522 MOZ_ASSERT(aStyle);
524 // TODO(emilio): We may want to avoid re-resolving pseudo-element styles
525 // more often.
526 return aStyle->HasPseudoElementData() && !aStyle->IsPseudoElement();
529 static bool IsInFlatTree(const Element& aElement) {
530 const auto* topmost = &aElement;
531 while (true) {
532 if (topmost->HasServoData()) {
533 // If we have styled this element then we know it's in the flat tree.
534 return true;
536 const Element* parent = topmost->GetFlattenedTreeParentElement();
537 if (!parent) {
538 break;
540 topmost = parent;
542 auto* root = topmost->GetFlattenedTreeParentNode();
543 return root && root->IsDocument();
546 already_AddRefed<const ComputedStyle>
547 nsComputedDOMStyle::DoGetComputedStyleNoFlush(
548 const Element* aElement, PseudoStyleType aPseudo,
549 nsAtom* aFunctionalPseudoParameter, PresShell* aPresShell,
550 StyleType aStyleType) {
551 MOZ_ASSERT(aElement, "NULL element");
553 // If the content has a pres shell, we must use it. Otherwise we'd
554 // potentially mix rule trees by using the wrong pres shell's style
555 // set. Using the pres shell from the content also means that any
556 // content that's actually *in* a document will get the style from the
557 // correct document.
558 PresShell* presShell = nsContentUtils::GetPresShellForContent(aElement);
559 bool inDocWithShell = true;
560 if (!presShell) {
561 inDocWithShell = false;
562 presShell = aPresShell;
563 if (!presShell) {
564 return nullptr;
568 MOZ_ASSERT(aPseudo == PseudoStyleType::NotPseudo ||
569 PseudoStyle::IsPseudoElement(aPseudo));
570 if (!aElement->IsInComposedDoc()) {
571 // Don't return styles for disconnected elements, that makes no sense. This
572 // can only happen with a non-null presShell for cross-document calls.
573 return nullptr;
576 if (!IsInFlatTree(*aElement)) {
577 return nullptr;
580 // XXX the !aElement->IsHTMLElement(nsGkAtoms::area)
581 // check is needed due to bug 135040 (to avoid using
582 // mPrimaryFrame). Remove it once that's fixed.
583 if (inDocWithShell && aStyleType == StyleType::All &&
584 !aElement->IsHTMLElement(nsGkAtoms::area)) {
585 if (const Element* element = GetRenderedElement(aElement, aPseudo)) {
586 if (element->HasServoData()) {
587 const ComputedStyle* result =
588 Servo_Element_GetMaybeOutOfDateStyle(element);
589 return do_AddRef(result);
594 // No frame has been created, or we have a pseudo, or we're looking
595 // for the default style, so resolve the style ourselves.
596 ServoStyleSet* styleSet = presShell->StyleSet();
598 StyleRuleInclusion rules = aStyleType == StyleType::DefaultOnly
599 ? StyleRuleInclusion::DefaultOnly
600 : StyleRuleInclusion::All;
601 RefPtr<ComputedStyle> result = styleSet->ResolveStyleLazily(
602 *aElement, aPseudo, aFunctionalPseudoParameter, rules);
603 return result.forget();
606 already_AddRefed<const ComputedStyle>
607 nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(
608 Element* aElement, PseudoStyleType aPseudo,
609 nsAtom* aFunctionalPseudoParameter) {
610 RefPtr<const ComputedStyle> style =
611 GetComputedStyleNoFlush(aElement, aPseudo, aFunctionalPseudoParameter);
612 if (!style) {
613 return nullptr;
616 PresShell* presShell = aElement->OwnerDoc()->GetPresShell();
617 MOZ_ASSERT(presShell,
618 "How in the world did we get a style a few lines above?");
620 Element* elementOrPseudoElement =
621 AnimationUtils::GetElementForRestyle(aElement, aPseudo);
622 if (!elementOrPseudoElement) {
623 return nullptr;
626 return presShell->StyleSet()->GetBaseContextForElement(elementOrPseudoElement,
627 style);
630 nsMargin nsComputedDOMStyle::GetAdjustedValuesForBoxSizing() {
631 // We want the width/height of whatever parts 'width' or 'height' controls,
632 // which can be different depending on the value of the 'box-sizing' property.
633 const nsStylePosition* stylePos = StylePosition();
635 nsMargin adjustment;
636 if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
637 adjustment = mInnerFrame->GetUsedBorderAndPadding();
640 return adjustment;
643 static void AddImageURL(nsIURI& aURI, nsTArray<nsCString>& aURLs) {
644 nsCString spec;
645 nsresult rv = aURI.GetSpec(spec);
646 if (NS_FAILED(rv)) {
647 return;
650 aURLs.AppendElement(std::move(spec));
653 static void AddImageURL(const StyleComputedUrl& aURL,
654 nsTArray<nsCString>& aURLs) {
655 if (aURL.IsLocalRef()) {
656 return;
659 if (nsIURI* uri = aURL.GetURI()) {
660 AddImageURL(*uri, aURLs);
664 static void AddImageURL(const StyleImage& aImage, nsTArray<nsCString>& aURLs) {
665 if (auto* urlValue = aImage.GetImageRequestURLValue()) {
666 AddImageURL(*urlValue, aURLs);
670 static void AddImageURL(const StyleShapeOutside& aShapeOutside,
671 nsTArray<nsCString>& aURLs) {
672 if (aShapeOutside.IsImage()) {
673 AddImageURL(aShapeOutside.AsImage(), aURLs);
677 static void AddImageURL(const StyleClipPath& aClipPath,
678 nsTArray<nsCString>& aURLs) {
679 if (aClipPath.IsUrl()) {
680 AddImageURL(aClipPath.AsUrl(), aURLs);
684 static void AddImageURLs(const nsStyleImageLayers& aLayers,
685 nsTArray<nsCString>& aURLs) {
686 for (auto i : IntegerRange(aLayers.mLayers.Length())) {
687 AddImageURL(aLayers.mLayers[i].mImage, aURLs);
691 static void CollectImageURLsForProperty(nsCSSPropertyID aProp,
692 const ComputedStyle& aStyle,
693 nsTArray<nsCString>& aURLs) {
694 if (nsCSSProps::IsShorthand(aProp)) {
695 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProp,
696 CSSEnabledState::ForAllContent) {
697 CollectImageURLsForProperty(*p, aStyle, aURLs);
699 return;
702 switch (aProp) {
703 case eCSSProperty_cursor:
704 for (auto& image : aStyle.StyleUI()->Cursor().images.AsSpan()) {
705 AddImageURL(image.image, aURLs);
707 break;
708 case eCSSProperty_background_image:
709 AddImageURLs(aStyle.StyleBackground()->mImage, aURLs);
710 break;
711 case eCSSProperty_mask_clip:
712 AddImageURLs(aStyle.StyleSVGReset()->mMask, aURLs);
713 break;
714 case eCSSProperty_list_style_image: {
715 const auto& image = aStyle.StyleList()->mListStyleImage;
716 if (image.IsUrl()) {
717 AddImageURL(image.AsUrl(), aURLs);
719 break;
721 case eCSSProperty_border_image_source:
722 AddImageURL(aStyle.StyleBorder()->mBorderImageSource, aURLs);
723 break;
724 case eCSSProperty_clip_path:
725 AddImageURL(aStyle.StyleSVGReset()->mClipPath, aURLs);
726 break;
727 case eCSSProperty_shape_outside:
728 AddImageURL(aStyle.StyleDisplay()->mShapeOutside, aURLs);
729 break;
730 default:
731 break;
735 void nsComputedDOMStyle::GetCSSImageURLs(const nsACString& aPropertyName,
736 nsTArray<nsCString>& aImageURLs,
737 mozilla::ErrorResult& aRv) {
738 nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
739 if (prop == eCSSProperty_UNKNOWN) {
740 // Note: not using nsPrintfCString here in case aPropertyName contains
741 // nulls.
742 aRv.ThrowSyntaxError("Invalid property name '"_ns + aPropertyName + "'"_ns);
743 return;
746 UpdateCurrentStyleSources(prop);
748 if (!mComputedStyle) {
749 return;
752 CollectImageURLsForProperty(prop, *mComputedStyle, aImageURLs);
753 ClearCurrentStyleSources();
756 // nsDOMCSSDeclaration abstract methods which should never be called
757 // on a nsComputedDOMStyle object, but must be defined to avoid
758 // compile errors.
759 DeclarationBlock* nsComputedDOMStyle::GetOrCreateCSSDeclaration(
760 Operation aOperation, DeclarationBlock** aCreated) {
761 MOZ_CRASH("called nsComputedDOMStyle::GetCSSDeclaration");
764 nsresult nsComputedDOMStyle::SetCSSDeclaration(DeclarationBlock*,
765 MutationClosureData*) {
766 MOZ_CRASH("called nsComputedDOMStyle::SetCSSDeclaration");
769 Document* nsComputedDOMStyle::DocToUpdate() {
770 MOZ_CRASH("called nsComputedDOMStyle::DocToUpdate");
773 nsDOMCSSDeclaration::ParsingEnvironment
774 nsComputedDOMStyle::GetParsingEnvironment(
775 nsIPrincipal* aSubjectPrincipal) const {
776 MOZ_CRASH("called nsComputedDOMStyle::GetParsingEnvironment");
779 void nsComputedDOMStyle::ClearComputedStyle() {
780 if (mResolvedComputedStyle) {
781 mResolvedComputedStyle = false;
782 mElement->RemoveMutationObserver(this);
784 mComputedStyle = nullptr;
787 void nsComputedDOMStyle::SetResolvedComputedStyle(
788 RefPtr<const ComputedStyle>&& aContext, uint64_t aGeneration) {
789 if (!mResolvedComputedStyle) {
790 mResolvedComputedStyle = true;
791 mElement->AddMutationObserver(this);
793 mComputedStyle = aContext;
794 mComputedStyleGeneration = aGeneration;
795 mPresShellId = mPresShell->GetPresShellId();
798 void nsComputedDOMStyle::SetFrameComputedStyle(mozilla::ComputedStyle* aStyle,
799 uint64_t aGeneration) {
800 ClearComputedStyle();
801 mComputedStyle = aStyle;
802 mComputedStyleGeneration = aGeneration;
803 mPresShellId = mPresShell->GetPresShellId();
806 static bool MayNeedToFlushLayout(nsCSSPropertyID aPropID) {
807 switch (aPropID) {
808 case eCSSProperty_width:
809 case eCSSProperty_height:
810 case eCSSProperty_block_size:
811 case eCSSProperty_inline_size:
812 case eCSSProperty_line_height:
813 case eCSSProperty_grid_template_rows:
814 case eCSSProperty_grid_template_columns:
815 case eCSSProperty_perspective_origin:
816 case eCSSProperty_transform_origin:
817 case eCSSProperty_transform:
818 case eCSSProperty_border_top_width:
819 case eCSSProperty_border_bottom_width:
820 case eCSSProperty_border_left_width:
821 case eCSSProperty_border_right_width:
822 case eCSSProperty_border_block_start_width:
823 case eCSSProperty_border_block_end_width:
824 case eCSSProperty_border_inline_start_width:
825 case eCSSProperty_border_inline_end_width:
826 case eCSSProperty_top:
827 case eCSSProperty_right:
828 case eCSSProperty_bottom:
829 case eCSSProperty_left:
830 case eCSSProperty_inset_block_start:
831 case eCSSProperty_inset_block_end:
832 case eCSSProperty_inset_inline_start:
833 case eCSSProperty_inset_inline_end:
834 case eCSSProperty_padding_top:
835 case eCSSProperty_padding_right:
836 case eCSSProperty_padding_bottom:
837 case eCSSProperty_padding_left:
838 case eCSSProperty_padding_block_start:
839 case eCSSProperty_padding_block_end:
840 case eCSSProperty_padding_inline_start:
841 case eCSSProperty_padding_inline_end:
842 case eCSSProperty_margin_top:
843 case eCSSProperty_margin_right:
844 case eCSSProperty_margin_bottom:
845 case eCSSProperty_margin_left:
846 case eCSSProperty_margin_block_start:
847 case eCSSProperty_margin_block_end:
848 case eCSSProperty_margin_inline_start:
849 case eCSSProperty_margin_inline_end:
850 return true;
851 default:
852 return false;
856 bool nsComputedDOMStyle::NeedsToFlushStyle(nsCSSPropertyID aPropID) const {
857 bool mayNeedToFlushLayout = MayNeedToFlushLayout(aPropID);
859 // We always compute styles from the element's owner document.
860 if (ElementNeedsRestyle(mElement, mPseudo, mayNeedToFlushLayout)) {
861 return true;
864 Document* doc = mElement->OwnerDoc();
865 // If parent document is there, also needs to check if there is some change
866 // that needs to flush this document (e.g. size change for iframe).
867 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
868 if (Element* element = doc->GetEmbedderElement()) {
869 if (ElementNeedsRestyle(element, PseudoStyleType::NotPseudo,
870 mayNeedToFlushLayout)) {
871 return true;
875 doc = doc->GetInProcessParentDocument();
878 return false;
881 static bool IsNonReplacedInline(nsIFrame* aFrame) {
882 // FIXME: this should be IsInlineInsideStyle() since width/height
883 // doesn't apply to ruby boxes.
884 return aFrame->StyleDisplay()->IsInlineFlow() &&
885 !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
886 !aFrame->IsFieldSetFrame() && !aFrame->IsBlockFrame() &&
887 !aFrame->IsScrollFrame() && !aFrame->IsColumnSetWrapperFrame();
890 static Side SideForPaddingOrMarginOrInsetProperty(nsCSSPropertyID aPropID) {
891 switch (aPropID) {
892 case eCSSProperty_top:
893 case eCSSProperty_margin_top:
894 case eCSSProperty_padding_top:
895 return eSideTop;
896 case eCSSProperty_right:
897 case eCSSProperty_margin_right:
898 case eCSSProperty_padding_right:
899 return eSideRight;
900 case eCSSProperty_bottom:
901 case eCSSProperty_margin_bottom:
902 case eCSSProperty_padding_bottom:
903 return eSideBottom;
904 case eCSSProperty_left:
905 case eCSSProperty_margin_left:
906 case eCSSProperty_padding_left:
907 return eSideLeft;
908 default:
909 MOZ_ASSERT_UNREACHABLE("Unexpected property");
910 return eSideTop;
914 static bool PaddingNeedsUsedValue(const LengthPercentage& aValue,
915 const ComputedStyle& aStyle) {
916 return !aValue.ConvertsToLength() || aStyle.StyleDisplay()->HasAppearance();
919 bool nsComputedDOMStyle::NeedsToFlushLayout(nsCSSPropertyID aPropID) const {
920 MOZ_ASSERT(aPropID != eCSSProperty_UNKNOWN);
921 if (aPropID == eCSSPropertyExtra_variable) {
922 return false;
924 nsIFrame* outerFrame = GetOuterFrame();
925 if (!outerFrame) {
926 return false;
928 nsIFrame* frame = nsLayoutUtils::GetStyleFrame(outerFrame);
929 auto* style = frame->Style();
930 if (nsCSSProps::PropHasFlags(aPropID, CSSPropFlags::IsLogical)) {
931 aPropID = Servo_ResolveLogicalProperty(aPropID, style);
934 switch (aPropID) {
935 case eCSSProperty_width:
936 case eCSSProperty_height:
937 return !IsNonReplacedInline(frame);
938 case eCSSProperty_line_height:
939 return frame->StyleFont()->mLineHeight.IsMozBlockHeight();
940 case eCSSProperty_grid_template_rows:
941 case eCSSProperty_grid_template_columns:
942 return !!nsGridContainerFrame::GetGridContainerFrame(frame);
943 case eCSSProperty_perspective_origin:
944 return style->StyleDisplay()->mPerspectiveOrigin.HasPercent();
945 case eCSSProperty_transform_origin:
946 return style->StyleDisplay()->mTransformOrigin.HasPercent();
947 case eCSSProperty_transform:
948 return style->StyleDisplay()->mTransform.HasPercent();
949 case eCSSProperty_border_top_width:
950 case eCSSProperty_border_bottom_width:
951 case eCSSProperty_border_left_width:
952 case eCSSProperty_border_right_width:
953 // FIXME(emilio): This should return false per spec (bug 1551000), but
954 // themed borders don't make that easy, so for now flush for that case.
956 // TODO(emilio): If we make GetUsedBorder() stop returning 0 for an
957 // unreflowed frame, or something of that sort, then we can stop flushing
958 // layout for themed frames.
959 return style->StyleDisplay()->HasAppearance();
960 case eCSSProperty_top:
961 case eCSSProperty_right:
962 case eCSSProperty_bottom:
963 case eCSSProperty_left:
964 // Doing better than this is actually hard.
965 return style->StyleDisplay()->mPosition != StylePositionProperty::Static;
966 case eCSSProperty_padding_top:
967 case eCSSProperty_padding_right:
968 case eCSSProperty_padding_bottom:
969 case eCSSProperty_padding_left: {
970 Side side = SideForPaddingOrMarginOrInsetProperty(aPropID);
971 // Theming can override used padding, sigh.
973 // TODO(emilio): If we make GetUsedPadding() stop returning 0 for an
974 // unreflowed frame, or something of that sort, then we can stop flushing
975 // layout for themed frames.
976 return PaddingNeedsUsedValue(style->StylePadding()->mPadding.Get(side),
977 *style);
979 case eCSSProperty_margin_top:
980 case eCSSProperty_margin_right:
981 case eCSSProperty_margin_bottom:
982 case eCSSProperty_margin_left: {
983 // NOTE(emilio): This is dubious, but matches other browsers.
984 // See https://github.com/w3c/csswg-drafts/issues/2328
985 Side side = SideForPaddingOrMarginOrInsetProperty(aPropID);
986 return !style->StyleMargin()->mMargin.Get(side).ConvertsToLength();
988 default:
989 return false;
993 void nsComputedDOMStyle::Flush(Document& aDocument, FlushType aFlushType) {
994 MOZ_ASSERT(mElement->IsInComposedDoc());
996 #ifdef DEBUG
998 nsCOMPtr<Document> document = do_QueryReferent(mDocumentWeak);
999 MOZ_ASSERT(document == &aDocument);
1001 #endif
1003 aDocument.FlushPendingNotifications(aFlushType);
1004 if (MOZ_UNLIKELY(&aDocument != mElement->OwnerDoc())) {
1005 mElement->OwnerDoc()->FlushPendingNotifications(aFlushType);
1009 nsIFrame* nsComputedDOMStyle::GetOuterFrame() const {
1010 if (mPseudo == PseudoStyleType::NotPseudo) {
1011 return mElement->GetPrimaryFrame();
1013 nsAtom* property = nullptr;
1014 if (mPseudo == PseudoStyleType::before) {
1015 property = nsGkAtoms::beforePseudoProperty;
1016 } else if (mPseudo == PseudoStyleType::after) {
1017 property = nsGkAtoms::afterPseudoProperty;
1018 } else if (mPseudo == PseudoStyleType::marker) {
1019 property = nsGkAtoms::markerPseudoProperty;
1021 if (!property) {
1022 return nullptr;
1024 auto* pseudo = static_cast<Element*>(mElement->GetProperty(property));
1025 return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
1028 void nsComputedDOMStyle::UpdateCurrentStyleSources(nsCSSPropertyID aPropID) {
1029 nsCOMPtr<Document> document = do_QueryReferent(mDocumentWeak);
1030 if (!document) {
1031 ClearComputedStyle();
1032 return;
1035 // We don't return styles for disconnected elements anymore, so don't go
1036 // through the trouble of flushing or what not.
1038 // TODO(emilio): We may want to return earlier for elements outside of the
1039 // flat tree too: https://github.com/w3c/csswg-drafts/issues/1964
1040 if (!mElement->IsInComposedDoc()) {
1041 ClearComputedStyle();
1042 return;
1045 if (mAlwaysReturnEmpty == AlwaysReturnEmptyStyle::Yes) {
1046 ClearComputedStyle();
1047 return;
1050 DebugOnly<bool> didFlush = false;
1051 if (NeedsToFlushStyle(aPropID)) {
1052 didFlush = true;
1053 // We look at the frame in NeedsToFlushLayout, so flush frames, not only
1054 // styles.
1055 Flush(*document, FlushType::Frames);
1058 if (NeedsToFlushLayout(aPropID)) {
1059 MOZ_ASSERT(MayNeedToFlushLayout(aPropID));
1060 didFlush = true;
1061 Flush(*document, FlushType::Layout);
1062 #ifdef DEBUG
1063 mFlushedPendingReflows = true;
1064 #endif
1065 } else {
1066 #ifdef DEBUG
1067 mFlushedPendingReflows = false;
1068 #endif
1071 mPresShell = document->GetPresShell();
1072 if (!mPresShell || !mPresShell->GetPresContext()) {
1073 ClearComputedStyle();
1074 return;
1077 // We need to use GetUndisplayedRestyleGeneration instead of
1078 // GetRestyleGeneration, because the caching of mComputedStyle is an
1079 // optimization that is useful only for displayed elements.
1080 // For undisplayed elements we need to take into account any DOM changes that
1081 // might cause a restyle, because Servo will not increase the generation for
1082 // undisplayed elements.
1083 uint64_t currentGeneration =
1084 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration();
1086 if (mComputedStyle && mComputedStyleGeneration == currentGeneration &&
1087 mPresShellId == mPresShell->GetPresShellId()) {
1088 // Our cached style is still valid.
1089 return;
1092 mComputedStyle = nullptr;
1094 // XXX the !mElement->IsHTMLElement(nsGkAtoms::area) check is needed due to
1095 // bug 135040 (to avoid using mPrimaryFrame). Remove it once that's fixed.
1096 if (mStyleType == StyleType::All &&
1097 !mElement->IsHTMLElement(nsGkAtoms::area)) {
1098 mOuterFrame = GetOuterFrame();
1099 mInnerFrame = mOuterFrame;
1100 if (mOuterFrame) {
1101 mInnerFrame = nsLayoutUtils::GetStyleFrame(mOuterFrame);
1102 SetFrameComputedStyle(mInnerFrame->Style(), currentGeneration);
1103 NS_ASSERTION(mComputedStyle, "Frame without style?");
1107 if (!mComputedStyle || MustReresolveStyle(mComputedStyle)) {
1108 PresShell* presShellForContent = mElement->OwnerDoc()->GetPresShell();
1109 // Need to resolve a style.
1110 RefPtr<const ComputedStyle> resolvedComputedStyle =
1111 DoGetComputedStyleNoFlush(
1112 mElement, mPseudo, mFunctionalPseudoParameter,
1113 presShellForContent ? presShellForContent : mPresShell, mStyleType);
1114 if (!resolvedComputedStyle) {
1115 ClearComputedStyle();
1116 return;
1119 // No need to re-get the generation, even though GetComputedStyle
1120 // will flush, since we flushed style at the top of this function.
1121 // We don't need to check this if we only flushed the parent.
1122 NS_ASSERTION(
1123 !didFlush ||
1124 currentGeneration ==
1125 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration(),
1126 "why should we have flushed style again?");
1128 SetResolvedComputedStyle(std::move(resolvedComputedStyle),
1129 currentGeneration);
1130 NS_ASSERTION(mPseudo != PseudoStyleType::NotPseudo ||
1131 !mComputedStyle->HasPseudoElementData(),
1132 "should not have pseudo-element data");
1135 // mExposeVisitedStyle is set to true only by testing APIs that
1136 // require chrome privilege.
1137 MOZ_ASSERT(!mExposeVisitedStyle || nsContentUtils::IsCallerChrome(),
1138 "mExposeVisitedStyle set incorrectly");
1139 if (mExposeVisitedStyle && mComputedStyle->RelevantLinkVisited()) {
1140 if (const auto* styleIfVisited = mComputedStyle->GetStyleIfVisited()) {
1141 mComputedStyle = styleIfVisited;
1146 void nsComputedDOMStyle::ClearCurrentStyleSources() {
1147 // Release the current style if we got it off the frame.
1149 // For a style we resolved, keep it around so that we can re-use it next time
1150 // this object is queried, but not if it-s a re-resolved style because we were
1151 // inside a pseudo-element.
1152 if (!mResolvedComputedStyle || mOuterFrame) {
1153 ClearComputedStyle();
1156 mOuterFrame = nullptr;
1157 mInnerFrame = nullptr;
1158 mPresShell = nullptr;
1161 void nsComputedDOMStyle::RemoveProperty(const nsACString& aPropertyName,
1162 nsACString& aReturn, ErrorResult& aRv) {
1163 // Note: not using nsPrintfCString here in case aPropertyName contains
1164 // nulls.
1165 aRv.ThrowNoModificationAllowedError("Can't remove property '"_ns +
1166 aPropertyName +
1167 "' from computed style"_ns);
1170 void nsComputedDOMStyle::GetPropertyPriority(const nsACString& aPropertyName,
1171 nsACString& aReturn) {
1172 aReturn.Truncate();
1175 void nsComputedDOMStyle::SetProperty(const nsACString& aPropertyName,
1176 const nsACString& aValue,
1177 const nsACString& aPriority,
1178 nsIPrincipal* aSubjectPrincipal,
1179 ErrorResult& aRv) {
1180 // Note: not using nsPrintfCString here in case aPropertyName contains
1181 // nulls.
1182 aRv.ThrowNoModificationAllowedError("Can't set value for property '"_ns +
1183 aPropertyName + "' in computed style"_ns);
1186 void nsComputedDOMStyle::IndexedGetter(uint32_t aIndex, bool& aFound,
1187 nsACString& aPropName) {
1188 ComputedStyleMap* map = GetComputedStyleMap();
1189 uint32_t length = map->Length();
1191 if (aIndex < length) {
1192 aFound = true;
1193 aPropName.Assign(nsCSSProps::GetStringValue(map->PropertyAt(aIndex)));
1194 return;
1197 // Custom properties are exposed with indexed properties just after all
1198 // of the built-in properties.
1199 UpdateCurrentStyleSources(eCSSPropertyExtra_variable);
1200 if (!mComputedStyle) {
1201 aFound = false;
1202 return;
1205 uint32_t count = Servo_GetCustomPropertiesCount(mComputedStyle);
1207 const uint32_t index = aIndex - length;
1208 if (index < count) {
1209 aFound = true;
1210 aPropName.AssignLiteral("--");
1211 if (nsAtom* atom = Servo_GetCustomPropertyNameAt(mComputedStyle, index)) {
1212 aPropName.Append(nsAtomCString(atom));
1214 } else {
1215 aFound = false;
1218 ClearCurrentStyleSources();
1221 // Property getters...
1223 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBottom() {
1224 return GetOffsetWidthFor(eSideBottom);
1227 static Position MaybeResolvePositionForTransform(const LengthPercentage& aX,
1228 const LengthPercentage& aY,
1229 nsIFrame* aInnerFrame) {
1230 if (!aInnerFrame) {
1231 return {aX, aY};
1233 nsStyleTransformMatrix::TransformReferenceBox refBox(aInnerFrame);
1234 CSSPoint p = nsStyleTransformMatrix::Convert2DPosition(aX, aY, refBox);
1235 return {LengthPercentage::FromPixels(p.x), LengthPercentage::FromPixels(p.y)};
1238 /* Convert the stored representation into a list of two values and then hand
1239 * it back.
1241 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransformOrigin() {
1242 /* We need to build up a list of two values. We'll call them
1243 * width and height.
1246 /* Store things as a value list */
1247 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1249 /* Now, get the values. */
1250 const auto& origin = StyleDisplay()->mTransformOrigin;
1252 RefPtr<nsROCSSPrimitiveValue> width = new nsROCSSPrimitiveValue;
1253 auto position = MaybeResolvePositionForTransform(
1254 origin.horizontal, origin.vertical, mInnerFrame);
1255 SetValueToPosition(position, valueList);
1256 if (!origin.depth.IsZero()) {
1257 RefPtr<nsROCSSPrimitiveValue> depth = new nsROCSSPrimitiveValue;
1258 depth->SetPixels(origin.depth.ToCSSPixels());
1259 valueList->AppendCSSValue(depth.forget());
1261 return valueList.forget();
1264 /* Convert the stored representation into a list of two values and then hand
1265 * it back.
1267 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPerspectiveOrigin() {
1268 /* We need to build up a list of two values. We'll call them
1269 * width and height.
1272 /* Store things as a value list */
1273 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1275 /* Now, get the values. */
1276 const auto& origin = StyleDisplay()->mPerspectiveOrigin;
1278 auto position = MaybeResolvePositionForTransform(
1279 origin.horizontal, origin.vertical, mInnerFrame);
1280 SetValueToPosition(position, valueList);
1281 return valueList.forget();
1284 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransform() {
1285 const nsStyleDisplay* display = StyleDisplay();
1286 return GetTransformValue(display->mTransform);
1289 /* static */
1290 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::MatrixToCSSValue(
1291 const mozilla::gfx::Matrix4x4& matrix) {
1292 bool is3D = !matrix.Is2D();
1294 nsAutoString resultString(u"matrix"_ns);
1295 if (is3D) {
1296 resultString.AppendLiteral("3d");
1299 resultString.Append('(');
1300 resultString.AppendFloat(matrix._11);
1301 resultString.AppendLiteral(", ");
1302 resultString.AppendFloat(matrix._12);
1303 resultString.AppendLiteral(", ");
1304 if (is3D) {
1305 resultString.AppendFloat(matrix._13);
1306 resultString.AppendLiteral(", ");
1307 resultString.AppendFloat(matrix._14);
1308 resultString.AppendLiteral(", ");
1310 resultString.AppendFloat(matrix._21);
1311 resultString.AppendLiteral(", ");
1312 resultString.AppendFloat(matrix._22);
1313 resultString.AppendLiteral(", ");
1314 if (is3D) {
1315 resultString.AppendFloat(matrix._23);
1316 resultString.AppendLiteral(", ");
1317 resultString.AppendFloat(matrix._24);
1318 resultString.AppendLiteral(", ");
1319 resultString.AppendFloat(matrix._31);
1320 resultString.AppendLiteral(", ");
1321 resultString.AppendFloat(matrix._32);
1322 resultString.AppendLiteral(", ");
1323 resultString.AppendFloat(matrix._33);
1324 resultString.AppendLiteral(", ");
1325 resultString.AppendFloat(matrix._34);
1326 resultString.AppendLiteral(", ");
1328 resultString.AppendFloat(matrix._41);
1329 resultString.AppendLiteral(", ");
1330 resultString.AppendFloat(matrix._42);
1331 if (is3D) {
1332 resultString.AppendLiteral(", ");
1333 resultString.AppendFloat(matrix._43);
1334 resultString.AppendLiteral(", ");
1335 resultString.AppendFloat(matrix._44);
1337 resultString.Append(')');
1339 /* Create a value to hold our result. */
1340 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1342 val->SetString(resultString);
1343 return val.forget();
1346 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMozOsxFontSmoothing() {
1347 if (nsContentUtils::ShouldResistFingerprinting(
1348 mPresShell->GetPresContext()->GetDocShell(),
1349 RFPTarget::DOMStyleOsxFontSmoothing)) {
1350 return nullptr;
1353 nsAutoCString result;
1354 mComputedStyle->GetComputedPropertyValue(eCSSProperty__moz_osx_font_smoothing,
1355 result);
1356 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1357 val->SetString(result);
1358 return val.forget();
1361 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetImageLayerPosition(
1362 const nsStyleImageLayers& aLayers) {
1363 if (aLayers.mPositionXCount != aLayers.mPositionYCount) {
1364 // No value to return. We can't express this combination of
1365 // values as a shorthand.
1366 return nullptr;
1369 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
1370 for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) {
1371 RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
1373 SetValueToPosition(aLayers.mLayers[i].mPosition, itemList);
1374 valueList->AppendCSSValue(itemList.forget());
1377 return valueList.forget();
1380 void nsComputedDOMStyle::SetValueToPosition(const Position& aPosition,
1381 nsDOMCSSValueList* aValueList) {
1382 RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
1383 SetValueToLengthPercentage(valX, aPosition.horizontal, false);
1384 aValueList->AppendCSSValue(valX.forget());
1386 RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
1387 SetValueToLengthPercentage(valY, aPosition.vertical, false);
1388 aValueList->AppendCSSValue(valY.forget());
1391 enum class Brackets { No, Yes };
1393 static void AppendGridLineNames(nsACString& aResult,
1394 Span<const StyleCustomIdent> aLineNames,
1395 Brackets aBrackets) {
1396 if (aLineNames.IsEmpty()) {
1397 if (aBrackets == Brackets::Yes) {
1398 aResult.AppendLiteral("[]");
1400 return;
1402 uint32_t numLines = aLineNames.Length();
1403 if (aBrackets == Brackets::Yes) {
1404 aResult.Append('[');
1406 for (uint32_t i = 0;;) {
1407 // TODO: Maybe use servo to do this and avoid the silly utf16->utf8 dance?
1408 nsAutoString name;
1409 nsStyleUtil::AppendEscapedCSSIdent(
1410 nsDependentAtomString(aLineNames[i].AsAtom()), name);
1411 AppendUTF16toUTF8(name, aResult);
1413 if (++i == numLines) {
1414 break;
1416 aResult.Append(' ');
1418 if (aBrackets == Brackets::Yes) {
1419 aResult.Append(']');
1423 static void AppendGridLineNames(nsDOMCSSValueList* aValueList,
1424 Span<const StyleCustomIdent> aLineNames,
1425 bool aSuppressEmptyList = true) {
1426 if (aLineNames.IsEmpty() && aSuppressEmptyList) {
1427 return;
1429 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1430 nsAutoCString lineNamesString;
1431 AppendGridLineNames(lineNamesString, aLineNames, Brackets::Yes);
1432 val->SetString(lineNamesString);
1433 aValueList->AppendCSSValue(val.forget());
1436 static void AppendGridLineNames(nsDOMCSSValueList* aValueList,
1437 Span<const StyleCustomIdent> aLineNames1,
1438 Span<const StyleCustomIdent> aLineNames2) {
1439 if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) {
1440 return;
1442 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1443 nsAutoCString lineNamesString;
1444 lineNamesString.Assign('[');
1445 if (!aLineNames1.IsEmpty()) {
1446 AppendGridLineNames(lineNamesString, aLineNames1, Brackets::No);
1448 if (!aLineNames2.IsEmpty()) {
1449 if (!aLineNames1.IsEmpty()) {
1450 lineNamesString.Append(' ');
1452 AppendGridLineNames(lineNamesString, aLineNames2, Brackets::No);
1454 lineNamesString.Append(']');
1455 val->SetString(lineNamesString);
1456 aValueList->AppendCSSValue(val.forget());
1459 void nsComputedDOMStyle::SetValueToTrackBreadth(
1460 nsROCSSPrimitiveValue* aValue, const StyleTrackBreadth& aBreadth) {
1461 using Tag = StyleTrackBreadth::Tag;
1462 switch (aBreadth.tag) {
1463 case Tag::MinContent:
1464 return aValue->SetString("min-content");
1465 case Tag::MaxContent:
1466 return aValue->SetString("max-content");
1467 case Tag::Auto:
1468 return aValue->SetString("auto");
1469 case Tag::Breadth:
1470 return SetValueToLengthPercentage(aValue, aBreadth.AsBreadth(), true);
1471 case Tag::Fr: {
1472 nsAutoString tmpStr;
1473 nsStyleUtil::AppendCSSNumber(aBreadth.AsFr(), tmpStr);
1474 tmpStr.AppendLiteral("fr");
1475 return aValue->SetString(tmpStr);
1477 default:
1478 MOZ_ASSERT_UNREACHABLE("Unknown breadth value");
1479 return;
1483 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackBreadth(
1484 const StyleTrackBreadth& aBreadth) {
1485 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1486 SetValueToTrackBreadth(val, aBreadth);
1487 return val.forget();
1490 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackSize(
1491 const StyleTrackSize& aTrackSize) {
1492 if (aTrackSize.IsFitContent()) {
1493 // A fit-content() function.
1494 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1495 MOZ_ASSERT(aTrackSize.AsFitContent().IsBreadth(),
1496 "unexpected unit for fit-content() argument value");
1497 SetValueFromFitContentFunction(val, aTrackSize.AsFitContent().AsBreadth());
1498 return val.forget();
1501 if (aTrackSize.IsBreadth()) {
1502 return GetGridTrackBreadth(aTrackSize.AsBreadth());
1505 MOZ_ASSERT(aTrackSize.IsMinmax());
1506 const auto& min = aTrackSize.AsMinmax()._0;
1507 const auto& max = aTrackSize.AsMinmax()._1;
1508 if (min == max) {
1509 return GetGridTrackBreadth(min);
1512 // minmax(auto, <flex>) is equivalent to (and is our internal representation
1513 // of) <flex>, and both compute to <flex>
1514 if (min.IsAuto() && max.IsFr()) {
1515 return GetGridTrackBreadth(max);
1518 nsAutoString argumentStr, minmaxStr;
1519 minmaxStr.AppendLiteral("minmax(");
1522 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(min);
1523 argValue->GetCssText(argumentStr);
1524 minmaxStr.Append(argumentStr);
1525 argumentStr.Truncate();
1528 minmaxStr.AppendLiteral(", ");
1531 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(max);
1532 argValue->GetCssText(argumentStr);
1533 minmaxStr.Append(argumentStr);
1536 minmaxStr.Append(char16_t(')'));
1537 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1538 val->SetString(minmaxStr);
1539 return val.forget();
1542 already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows(
1543 const StyleGridTemplateComponent& aTrackList,
1544 const ComputedGridTrackInfo& aTrackInfo) {
1545 if (aTrackInfo.mIsMasonry) {
1546 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1547 val->SetString("masonry");
1548 return val.forget();
1551 if (aTrackInfo.mIsSubgrid) {
1552 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1553 RefPtr<nsROCSSPrimitiveValue> subgridKeyword = new nsROCSSPrimitiveValue;
1554 subgridKeyword->SetString("subgrid");
1555 valueList->AppendCSSValue(subgridKeyword.forget());
1556 for (const auto& lineNames : aTrackInfo.mResolvedLineNames) {
1557 AppendGridLineNames(valueList, lineNames, /*aSuppressEmptyList*/ false);
1559 uint32_t line = aTrackInfo.mResolvedLineNames.Length();
1560 uint32_t lastLine = aTrackInfo.mNumExplicitTracks + 1;
1561 const Span<const StyleCustomIdent> empty;
1562 for (; line < lastLine; ++line) {
1563 AppendGridLineNames(valueList, empty, /*aSuppressEmptyList*/ false);
1565 return valueList.forget();
1568 const bool serializeImplicit =
1569 StaticPrefs::layout_css_serialize_grid_implicit_tracks();
1571 const nsTArray<nscoord>& trackSizes = aTrackInfo.mSizes;
1572 const uint32_t numExplicitTracks = aTrackInfo.mNumExplicitTracks;
1573 const uint32_t numLeadingImplicitTracks =
1574 aTrackInfo.mNumLeadingImplicitTracks;
1575 uint32_t numSizes = trackSizes.Length();
1576 MOZ_ASSERT(numSizes >= numLeadingImplicitTracks + numExplicitTracks);
1578 const bool hasTracksToSerialize =
1579 serializeImplicit ? !!numSizes : !!numExplicitTracks;
1580 const bool hasRepeatAuto = aTrackList.HasRepeatAuto();
1581 if (!hasTracksToSerialize && !hasRepeatAuto) {
1582 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1583 val->SetString("none");
1584 return val.forget();
1587 // We've done layout on the grid and have resolved the sizes of its tracks,
1588 // so we'll return those sizes here. The grid spec says we MAY use
1589 // repeat(<positive-integer>, Npx) here for consecutive tracks with the same
1590 // size, but that doesn't seem worth doing since even for repeat(auto-*)
1591 // the resolved size might differ for the repeated tracks.
1592 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1594 // Add any leading implicit tracks.
1595 if (serializeImplicit) {
1596 for (uint32_t i = 0; i < numLeadingImplicitTracks; ++i) {
1597 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1598 val->SetAppUnits(trackSizes[i]);
1599 valueList->AppendCSSValue(val.forget());
1603 if (hasRepeatAuto) {
1604 const auto* const autoRepeatValue = aTrackList.GetRepeatAutoValue();
1605 const auto repeatLineNames = autoRepeatValue->line_names.AsSpan();
1606 MOZ_ASSERT(repeatLineNames.Length() >= 2);
1607 // Number of tracks inside the repeat, not including any repetitions.
1608 // Check that if we have truncated the number of tracks due to overflowing
1609 // the maximum track limit then we also truncate this repeat count.
1610 MOZ_ASSERT(repeatLineNames.Length() ==
1611 autoRepeatValue->track_sizes.len + 1);
1612 // If we have truncated the first repetition of repeat tracks, then we
1613 // can't index using autoRepeatValue->track_sizes.len, and
1614 // aTrackInfo.mRemovedRepeatTracks.Length() will account for all repeat
1615 // tracks that haven't been truncated.
1616 const uint32_t numRepeatTracks =
1617 std::min(aTrackInfo.mRemovedRepeatTracks.Length(),
1618 autoRepeatValue->track_sizes.len);
1619 MOZ_ASSERT(repeatLineNames.Length() >= numRepeatTracks + 1);
1620 // The total of all tracks in all repetitions of the repeat.
1621 const uint32_t totalNumRepeatTracks =
1622 aTrackInfo.mRemovedRepeatTracks.Length();
1623 const uint32_t repeatStart = aTrackInfo.mRepeatFirstTrack;
1624 // We need to skip over any track sizes which were resolved to 0 by
1625 // collapsed tracks. Keep track of the iteration separately.
1626 const auto explicitTrackSizeBegin =
1627 trackSizes.cbegin() + numLeadingImplicitTracks;
1628 const auto explicitTrackSizeEnd =
1629 explicitTrackSizeBegin + numExplicitTracks;
1630 auto trackSizeIter = explicitTrackSizeBegin;
1631 // Write any leading explicit tracks before the repeat.
1632 for (uint32_t i = 0; i < repeatStart; i++) {
1633 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]);
1634 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1635 val->SetAppUnits(*trackSizeIter++);
1636 valueList->AppendCSSValue(val.forget());
1638 auto lineNameIter = aTrackInfo.mResolvedLineNames.cbegin() + repeatStart;
1639 // Write the track names at the start of the repeat, including the names
1640 // at the end of the last non-repeat track. Unlike all later repeat line
1641 // name lists, this one needs the resolved line name which includes both
1642 // the last non-repeat line names and the leading repeat line names.
1643 AppendGridLineNames(valueList, *lineNameIter++);
1645 // Write out the first repeat value, checking for size zero (removed
1646 // track).
1647 const nscoord firstRepeatTrackSize =
1648 (!aTrackInfo.mRemovedRepeatTracks[0]) ? *trackSizeIter++ : 0;
1649 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1650 val->SetAppUnits(firstRepeatTrackSize);
1651 valueList->AppendCSSValue(val.forget());
1653 // Write the line names and track sizes inside the repeat, checking for
1654 // removed tracks (size 0).
1655 for (uint32_t i = 1; i < totalNumRepeatTracks; i++) {
1656 const uint32_t repeatIndex = i % numRepeatTracks;
1657 // If we are rolling over from one repetition to the next, include track
1658 // names from both the end of the previous repeat and the start of the
1659 // next.
1660 if (repeatIndex == 0) {
1661 AppendGridLineNames(valueList,
1662 repeatLineNames[numRepeatTracks].AsSpan(),
1663 repeatLineNames[0].AsSpan());
1664 } else {
1665 AppendGridLineNames(valueList, repeatLineNames[repeatIndex].AsSpan());
1667 MOZ_ASSERT(aTrackInfo.mRemovedRepeatTracks[i] ||
1668 trackSizeIter != explicitTrackSizeEnd);
1669 const nscoord repeatTrackSize =
1670 (!aTrackInfo.mRemovedRepeatTracks[i]) ? *trackSizeIter++ : 0;
1671 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1672 val->SetAppUnits(repeatTrackSize);
1673 valueList->AppendCSSValue(val.forget());
1675 // The resolved line names include a single repetition of the auto-repeat
1676 // line names. Skip over those.
1677 lineNameIter += numRepeatTracks - 1;
1678 // Write out any more tracks after the repeat.
1679 while (trackSizeIter != explicitTrackSizeEnd) {
1680 AppendGridLineNames(valueList, *lineNameIter++);
1681 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1682 val->SetAppUnits(*trackSizeIter++);
1683 valueList->AppendCSSValue(val.forget());
1685 // Write the final trailing line name.
1686 AppendGridLineNames(valueList, *lineNameIter++);
1687 } else if (numExplicitTracks > 0) {
1688 // If there are explicit tracks but no repeat tracks, just serialize those.
1689 for (uint32_t i = 0;; i++) {
1690 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]);
1691 if (i == numExplicitTracks) {
1692 break;
1694 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1695 val->SetAppUnits(trackSizes[i + numLeadingImplicitTracks]);
1696 valueList->AppendCSSValue(val.forget());
1699 // Add any trailing implicit tracks.
1700 if (serializeImplicit) {
1701 for (uint32_t i = numLeadingImplicitTracks + numExplicitTracks;
1702 i < numSizes; ++i) {
1703 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1704 val->SetAppUnits(trackSizes[i]);
1705 valueList->AppendCSSValue(val.forget());
1709 return valueList.forget();
1712 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateColumns() {
1713 nsGridContainerFrame* gridFrame =
1714 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
1715 if (!gridFrame) {
1716 // The element doesn't have a box - return the computed value.
1717 // https://drafts.csswg.org/css-grid/#resolved-track-list
1718 nsAutoCString string;
1719 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_columns,
1720 string);
1721 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1722 value->SetString(string);
1723 return value.forget();
1726 // GetGridFrameWithComputedInfo() above ensures that this returns non-null:
1727 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateColumns();
1728 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns,
1729 *info);
1732 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateRows() {
1733 nsGridContainerFrame* gridFrame =
1734 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
1735 if (!gridFrame) {
1736 // The element doesn't have a box - return the computed value.
1737 // https://drafts.csswg.org/css-grid/#resolved-track-list
1738 nsAutoCString string;
1739 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_rows,
1740 string);
1741 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1742 value->SetString(string);
1743 return value.forget();
1746 // GetGridFrameWithComputedInfo() above ensures that this returns non-null:
1747 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateRows();
1748 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, *info);
1751 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingTop() {
1752 return GetPaddingWidthFor(eSideTop);
1755 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingBottom() {
1756 return GetPaddingWidthFor(eSideBottom);
1759 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingLeft() {
1760 return GetPaddingWidthFor(eSideLeft);
1763 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingRight() {
1764 return GetPaddingWidthFor(eSideRight);
1767 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderSpacing() {
1768 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1770 RefPtr<nsROCSSPrimitiveValue> xSpacing = new nsROCSSPrimitiveValue;
1771 RefPtr<nsROCSSPrimitiveValue> ySpacing = new nsROCSSPrimitiveValue;
1773 const nsStyleTableBorder* border = StyleTableBorder();
1774 xSpacing->SetAppUnits(border->mBorderSpacingCol);
1775 ySpacing->SetAppUnits(border->mBorderSpacingRow);
1777 valueList->AppendCSSValue(xSpacing.forget());
1778 valueList->AppendCSSValue(ySpacing.forget());
1780 return valueList.forget();
1783 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderTopWidth() {
1784 return GetBorderWidthFor(eSideTop);
1787 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderBottomWidth() {
1788 return GetBorderWidthFor(eSideBottom);
1791 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderLeftWidth() {
1792 return GetBorderWidthFor(eSideLeft);
1795 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderRightWidth() {
1796 return GetBorderWidthFor(eSideRight);
1799 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginTop() {
1800 return GetMarginFor(eSideTop);
1803 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginBottom() {
1804 return GetMarginFor(eSideBottom);
1807 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginLeft() {
1808 return GetMarginFor(eSideLeft);
1811 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginRight() {
1812 return GetMarginFor(eSideRight);
1815 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetHeight() {
1816 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1818 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) {
1819 AssertFlushedPendingReflows();
1820 nsMargin adjustedValues = GetAdjustedValuesForBoxSizing();
1821 val->SetAppUnits(mInnerFrame->GetContentRect().height +
1822 adjustedValues.TopBottom());
1823 } else {
1824 SetValueToSize(val, StylePosition()->mHeight);
1827 return val.forget();
1830 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetWidth() {
1831 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1833 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) {
1834 AssertFlushedPendingReflows();
1835 nsMargin adjustedValues = GetAdjustedValuesForBoxSizing();
1836 val->SetAppUnits(mInnerFrame->GetContentRect().width +
1837 adjustedValues.LeftRight());
1838 } else {
1839 SetValueToSize(val, StylePosition()->mWidth);
1842 return val.forget();
1845 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxHeight() {
1846 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1847 SetValueToMaxSize(val, StylePosition()->mMaxHeight);
1848 return val.forget();
1851 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxWidth() {
1852 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1853 SetValueToMaxSize(val, StylePosition()->mMaxWidth);
1854 return val.forget();
1858 * This function indicates whether we should return "auto" as the
1859 * getComputedStyle() result for the (default) "min-width: auto" and
1860 * "min-height: auto" CSS values.
1862 * As of this writing, the CSS Sizing draft spec says this "auto" value
1863 * *always* computes to itself. However, for now, we only make it compute to
1864 * itself for grid and flex items (the containers where "auto" has special
1865 * significance), because those are the only areas where the CSSWG has actually
1866 * resolved on this "computes-to-itself" behavior. For elements in other sorts
1867 * of containers, this function returns false, which will make us resolve
1868 * "auto" to 0.
1870 bool nsComputedDOMStyle::ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis) {
1871 return mOuterFrame && mOuterFrame->IsFlexOrGridItem();
1874 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinHeight() {
1875 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1876 StyleSize minHeight = StylePosition()->mMinHeight;
1878 if (minHeight.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisVertical)) {
1879 minHeight = StyleSize::LengthPercentage(LengthPercentage::Zero());
1882 SetValueToSize(val, minHeight);
1883 return val.forget();
1886 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinWidth() {
1887 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1889 StyleSize minWidth = StylePosition()->mMinWidth;
1891 if (minWidth.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisHorizontal)) {
1892 minWidth = StyleSize::LengthPercentage(LengthPercentage::Zero());
1895 SetValueToSize(val, minWidth);
1896 return val.forget();
1899 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetLeft() {
1900 return GetOffsetWidthFor(eSideLeft);
1903 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetRight() {
1904 return GetOffsetWidthFor(eSideRight);
1907 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTop() {
1908 return GetOffsetWidthFor(eSideTop);
1911 already_AddRefed<CSSValue> nsComputedDOMStyle::GetOffsetWidthFor(
1912 mozilla::Side aSide) {
1913 const nsStyleDisplay* display = StyleDisplay();
1915 mozilla::StylePositionProperty position = display->mPosition;
1916 if (!mOuterFrame) {
1917 // GetNonStaticPositionOffset or GetAbsoluteOffset don't handle elements
1918 // without frames in any sensible way. GetStaticOffset, however, is perfect
1919 // for that case.
1920 position = StylePositionProperty::Static;
1923 switch (position) {
1924 case StylePositionProperty::Static:
1925 return GetStaticOffset(aSide);
1926 case StylePositionProperty::Sticky:
1927 return GetNonStaticPositionOffset(
1928 aSide, false, &nsComputedDOMStyle::GetScrollFrameContentWidth,
1929 &nsComputedDOMStyle::GetScrollFrameContentHeight);
1930 case StylePositionProperty::Absolute:
1931 case StylePositionProperty::Fixed:
1932 return GetAbsoluteOffset(aSide);
1933 case StylePositionProperty::Relative:
1934 return GetNonStaticPositionOffset(
1935 aSide, true, &nsComputedDOMStyle::GetCBContentWidth,
1936 &nsComputedDOMStyle::GetCBContentHeight);
1937 default:
1938 MOZ_ASSERT_UNREACHABLE("Invalid position");
1939 return nullptr;
1943 static_assert(eSideTop == 0 && eSideRight == 1 && eSideBottom == 2 &&
1944 eSideLeft == 3,
1945 "box side constants not as expected for NS_OPPOSITE_SIDE");
1946 #define NS_OPPOSITE_SIDE(s_) mozilla::Side(((s_) + 2) & 3)
1948 already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset(
1949 mozilla::Side aSide, bool aResolveAuto, PercentageBaseGetter aWidthGetter,
1950 PercentageBaseGetter aHeightGetter) {
1951 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1953 const nsStylePosition* positionData = StylePosition();
1954 int32_t sign = 1;
1955 LengthPercentageOrAuto coord = positionData->mOffset.Get(aSide);
1957 if (coord.IsAuto()) {
1958 if (!aResolveAuto) {
1959 val->SetString("auto");
1960 return val.forget();
1962 coord = positionData->mOffset.Get(NS_OPPOSITE_SIDE(aSide));
1963 sign = -1;
1965 if (!coord.IsLengthPercentage()) {
1966 val->SetPixels(0.0f);
1967 return val.forget();
1970 auto& lp = coord.AsLengthPercentage();
1971 if (lp.ConvertsToLength()) {
1972 val->SetPixels(sign * lp.ToLengthInCSSPixels());
1973 return val.forget();
1976 PercentageBaseGetter baseGetter = (aSide == eSideLeft || aSide == eSideRight)
1977 ? aWidthGetter
1978 : aHeightGetter;
1979 nscoord percentageBase;
1980 if (!(this->*baseGetter)(percentageBase)) {
1981 val->SetPixels(0.0f);
1982 return val.forget();
1984 nscoord result = lp.Resolve(percentageBase);
1985 val->SetAppUnits(sign * result);
1986 return val.forget();
1989 already_AddRefed<CSSValue> nsComputedDOMStyle::GetAbsoluteOffset(
1990 mozilla::Side aSide) {
1991 const auto& offset = StylePosition()->mOffset;
1992 const auto& coord = offset.Get(aSide);
1993 const auto& oppositeCoord = offset.Get(NS_OPPOSITE_SIDE(aSide));
1995 if (coord.IsAuto() || oppositeCoord.IsAuto()) {
1996 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1997 val->SetAppUnits(GetUsedAbsoluteOffset(aSide));
1998 return val.forget();
2001 return GetNonStaticPositionOffset(
2002 aSide, false, &nsComputedDOMStyle::GetCBPaddingRectWidth,
2003 &nsComputedDOMStyle::GetCBPaddingRectHeight);
2006 nscoord nsComputedDOMStyle::GetUsedAbsoluteOffset(mozilla::Side aSide) {
2007 MOZ_ASSERT(mOuterFrame, "need a frame, so we can call GetContainingBlock()");
2009 nsIFrame* container = mOuterFrame->GetContainingBlock();
2010 nsMargin margin = mOuterFrame->GetUsedMargin();
2011 nsMargin border = container->GetUsedBorder();
2012 nsMargin scrollbarSizes(0, 0, 0, 0);
2013 nsRect rect = mOuterFrame->GetRect();
2014 nsRect containerRect = container->GetRect();
2016 if (container->IsViewportFrame()) {
2017 // For absolutely positioned frames scrollbars are taken into
2018 // account by virtue of getting a containing block that does
2019 // _not_ include the scrollbars. For fixed positioned frames,
2020 // the containing block is the viewport, which _does_ include
2021 // scrollbars. We have to do some extra work.
2022 // the first child in the default frame list is what we want
2023 nsIFrame* scrollingChild = container->PrincipalChildList().FirstChild();
2024 nsIScrollableFrame* scrollFrame = do_QueryFrame(scrollingChild);
2025 if (scrollFrame) {
2026 scrollbarSizes = scrollFrame->GetActualScrollbarSizes();
2029 // The viewport size might have been expanded by the visual viewport or
2030 // the minimum-scale size.
2031 const ViewportFrame* viewportFrame = do_QueryFrame(container);
2032 MOZ_ASSERT(viewportFrame);
2033 containerRect.SizeTo(
2034 viewportFrame->AdjustViewportSizeForFixedPosition(containerRect));
2035 } else if (container->IsGridContainerFrame() &&
2036 mOuterFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
2037 containerRect = nsGridContainerFrame::GridItemCB(mOuterFrame);
2038 rect.MoveBy(-containerRect.x, -containerRect.y);
2041 nscoord offset = 0;
2042 switch (aSide) {
2043 case eSideTop:
2044 offset = rect.y - margin.top - border.top - scrollbarSizes.top;
2046 break;
2047 case eSideRight:
2048 offset = containerRect.width - rect.width - rect.x - margin.right -
2049 border.right - scrollbarSizes.right;
2051 break;
2052 case eSideBottom:
2053 offset = containerRect.height - rect.height - rect.y - margin.bottom -
2054 border.bottom - scrollbarSizes.bottom;
2056 break;
2057 case eSideLeft:
2058 offset = rect.x - margin.left - border.left - scrollbarSizes.left;
2060 break;
2061 default:
2062 NS_ERROR("Invalid side");
2063 break;
2066 return offset;
2069 already_AddRefed<CSSValue> nsComputedDOMStyle::GetStaticOffset(
2070 mozilla::Side aSide) {
2071 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2072 SetValueToLengthPercentageOrAuto(val, StylePosition()->mOffset.Get(aSide),
2073 false);
2074 return val.forget();
2077 already_AddRefed<CSSValue> nsComputedDOMStyle::GetPaddingWidthFor(
2078 mozilla::Side aSide) {
2079 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2081 auto& padding = StylePadding()->mPadding.Get(aSide);
2082 if (!mInnerFrame || !PaddingNeedsUsedValue(padding, *mComputedStyle)) {
2083 SetValueToLengthPercentage(val, padding, true);
2084 } else {
2085 AssertFlushedPendingReflows();
2086 val->SetAppUnits(mInnerFrame->GetUsedPadding().Side(aSide));
2089 return val.forget();
2092 already_AddRefed<CSSValue> nsComputedDOMStyle::GetBorderWidthFor(
2093 mozilla::Side aSide) {
2094 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2096 nscoord width;
2097 if (mInnerFrame && mComputedStyle->StyleDisplay()->HasAppearance()) {
2098 AssertFlushedPendingReflows();
2099 width = mInnerFrame->GetUsedBorder().Side(aSide);
2100 } else {
2101 width = StyleBorder()->GetComputedBorderWidth(aSide);
2103 val->SetAppUnits(width);
2105 return val.forget();
2108 already_AddRefed<CSSValue> nsComputedDOMStyle::GetMarginFor(Side aSide) {
2109 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2111 auto& margin = StyleMargin()->mMargin.Get(aSide);
2112 if (!mInnerFrame || margin.ConvertsToLength()) {
2113 SetValueToLengthPercentageOrAuto(val, margin, false);
2114 } else {
2115 AssertFlushedPendingReflows();
2117 // For tables, GetUsedMargin always returns an empty margin, so we
2118 // should read the margin from the table wrapper frame instead.
2119 val->SetAppUnits(mOuterFrame->GetUsedMargin().Side(aSide));
2120 NS_ASSERTION(mOuterFrame == mInnerFrame ||
2121 mInnerFrame->GetUsedMargin() == nsMargin(0, 0, 0, 0),
2122 "Inner tables must have zero margins");
2125 return val.forget();
2128 static void SetValueToExtremumLength(nsROCSSPrimitiveValue* aValue,
2129 nsIFrame::ExtremumLength aSize) {
2130 switch (aSize) {
2131 case nsIFrame::ExtremumLength::MaxContent:
2132 return aValue->SetString("max-content");
2133 case nsIFrame::ExtremumLength::MinContent:
2134 return aValue->SetString("min-content");
2135 case nsIFrame::ExtremumLength::MozAvailable:
2136 return aValue->SetString("-moz-available");
2137 case nsIFrame::ExtremumLength::FitContent:
2138 return aValue->SetString("fit-content");
2139 case nsIFrame::ExtremumLength::FitContentFunction:
2140 MOZ_ASSERT_UNREACHABLE("fit-content() should be handled separately");
2142 MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
2145 void nsComputedDOMStyle::SetValueFromFitContentFunction(
2146 nsROCSSPrimitiveValue* aValue, const LengthPercentage& aLength) {
2147 nsAutoString argumentStr;
2148 SetValueToLengthPercentage(aValue, aLength, true);
2149 aValue->GetCssText(argumentStr);
2151 nsAutoString fitContentStr;
2152 fitContentStr.AppendLiteral("fit-content(");
2153 fitContentStr.Append(argumentStr);
2154 fitContentStr.Append(u')');
2155 aValue->SetString(fitContentStr);
2158 void nsComputedDOMStyle::SetValueToSize(nsROCSSPrimitiveValue* aValue,
2159 const StyleSize& aSize) {
2160 if (aSize.IsAuto()) {
2161 return aValue->SetString("auto");
2163 if (aSize.IsFitContentFunction()) {
2164 return SetValueFromFitContentFunction(aValue, aSize.AsFitContentFunction());
2166 if (auto length = nsIFrame::ToExtremumLength(aSize)) {
2167 return SetValueToExtremumLength(aValue, *length);
2169 MOZ_ASSERT(aSize.IsLengthPercentage());
2170 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true);
2173 void nsComputedDOMStyle::SetValueToMaxSize(nsROCSSPrimitiveValue* aValue,
2174 const StyleMaxSize& aSize) {
2175 if (aSize.IsNone()) {
2176 return aValue->SetString("none");
2178 if (aSize.IsFitContentFunction()) {
2179 return SetValueFromFitContentFunction(aValue, aSize.AsFitContentFunction());
2181 if (auto length = nsIFrame::ToExtremumLength(aSize)) {
2182 return SetValueToExtremumLength(aValue, *length);
2184 MOZ_ASSERT(aSize.IsLengthPercentage());
2185 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true);
2188 void nsComputedDOMStyle::SetValueToLengthPercentageOrAuto(
2189 nsROCSSPrimitiveValue* aValue, const LengthPercentageOrAuto& aSize,
2190 bool aClampNegativeCalc) {
2191 if (aSize.IsAuto()) {
2192 return aValue->SetString("auto");
2194 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(),
2195 aClampNegativeCalc);
2198 void nsComputedDOMStyle::SetValueToLengthPercentage(
2199 nsROCSSPrimitiveValue* aValue, const mozilla::LengthPercentage& aLength,
2200 bool aClampNegativeCalc) {
2201 if (aLength.ConvertsToLength()) {
2202 CSSCoord length = aLength.ToLengthInCSSPixels();
2203 if (aClampNegativeCalc) {
2204 length = std::max(float(length), 0.0f);
2206 return aValue->SetPixels(length);
2208 if (aLength.ConvertsToPercentage()) {
2209 float result = aLength.ToPercentage();
2210 if (aClampNegativeCalc) {
2211 result = std::max(result, 0.0f);
2213 return aValue->SetPercent(result);
2216 nsAutoCString result;
2217 Servo_LengthPercentage_ToCss(&aLength, &result);
2218 aValue->SetString(result);
2221 bool nsComputedDOMStyle::GetCBContentWidth(nscoord& aWidth) {
2222 if (!mOuterFrame) {
2223 return false;
2226 AssertFlushedPendingReflows();
2228 aWidth = mOuterFrame->GetContainingBlock()->GetContentRect().width;
2229 return true;
2232 bool nsComputedDOMStyle::GetCBContentHeight(nscoord& aHeight) {
2233 if (!mOuterFrame) {
2234 return false;
2237 AssertFlushedPendingReflows();
2239 aHeight = mOuterFrame->GetContainingBlock()->GetContentRect().height;
2240 return true;
2243 bool nsComputedDOMStyle::GetCBPaddingRectWidth(nscoord& aWidth) {
2244 if (!mOuterFrame) {
2245 return false;
2248 AssertFlushedPendingReflows();
2250 aWidth = mOuterFrame->GetContainingBlock()->GetPaddingRect().width;
2251 return true;
2254 bool nsComputedDOMStyle::GetCBPaddingRectHeight(nscoord& aHeight) {
2255 if (!mOuterFrame) {
2256 return false;
2259 AssertFlushedPendingReflows();
2261 aHeight = mOuterFrame->GetContainingBlock()->GetPaddingRect().height;
2262 return true;
2265 bool nsComputedDOMStyle::GetScrollFrameContentWidth(nscoord& aWidth) {
2266 if (!mOuterFrame) {
2267 return false;
2270 AssertFlushedPendingReflows();
2272 nsIScrollableFrame* scrollableFrame =
2273 nsLayoutUtils::GetNearestScrollableFrame(
2274 mOuterFrame->GetParent(),
2275 nsLayoutUtils::SCROLLABLE_SAME_DOC |
2276 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
2278 if (!scrollableFrame) {
2279 return false;
2281 aWidth =
2282 scrollableFrame->GetScrolledFrame()->GetContentRectRelativeToSelf().width;
2283 return true;
2286 bool nsComputedDOMStyle::GetScrollFrameContentHeight(nscoord& aHeight) {
2287 if (!mOuterFrame) {
2288 return false;
2291 AssertFlushedPendingReflows();
2293 nsIScrollableFrame* scrollableFrame =
2294 nsLayoutUtils::GetNearestScrollableFrame(
2295 mOuterFrame->GetParent(),
2296 nsLayoutUtils::SCROLLABLE_SAME_DOC |
2297 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
2299 if (!scrollableFrame) {
2300 return false;
2302 aHeight = scrollableFrame->GetScrolledFrame()
2303 ->GetContentRectRelativeToSelf()
2304 .height;
2305 return true;
2308 bool nsComputedDOMStyle::GetFrameBorderRectWidth(nscoord& aWidth) {
2309 if (!mInnerFrame) {
2310 return false;
2313 AssertFlushedPendingReflows();
2315 aWidth = mInnerFrame->GetSize().width;
2316 return true;
2319 bool nsComputedDOMStyle::GetFrameBorderRectHeight(nscoord& aHeight) {
2320 if (!mInnerFrame) {
2321 return false;
2324 AssertFlushedPendingReflows();
2326 aHeight = mInnerFrame->GetSize().height;
2327 return true;
2330 /* If the property is "none", hand back "none" wrapped in a value.
2331 * Otherwise, compute the aggregate transform matrix and hands it back in a
2332 * "matrix" wrapper.
2334 already_AddRefed<CSSValue> nsComputedDOMStyle::GetTransformValue(
2335 const StyleTransform& aTransform) {
2336 /* If there are no transforms, then we should construct a single-element
2337 * entry and hand it back.
2339 if (aTransform.IsNone()) {
2340 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2341 val->SetString("none");
2342 return val.forget();
2345 /* Otherwise, we need to compute the current value of the transform matrix,
2346 * store it in a string, and hand it back to the caller.
2349 /* Use the inner frame for the reference box. If we don't have an inner
2350 * frame we use empty dimensions to allow us to continue (and percentage
2351 * values in the transform will simply give broken results).
2352 * TODO: There is no good way for us to represent the case where there's no
2353 * frame, which is problematic. The reason is that when we have percentage
2354 * transforms, there are a total of four stored matrix entries that influence
2355 * the transform based on the size of the element. However, this poses a
2356 * problem, because only two of these values can be explicitly referenced
2357 * using the named transforms. Until a real solution is found, we'll just
2358 * use this approach.
2360 nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame, nsRect());
2361 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
2362 aTransform, refBox, float(mozilla::AppUnitsPerCSSPixel()));
2364 return MatrixToCSSValue(matrix);
2367 already_AddRefed<CSSValue> nsComputedDOMStyle::DummyGetter() {
2368 MOZ_CRASH("DummyGetter is not supposed to be invoked");
2371 static void MarkComputedStyleMapDirty(const char* aPref, void* aMap) {
2372 static_cast<ComputedStyleMap*>(aMap)->MarkDirty();
2375 void nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent) {
2376 NS_ASSERTION(mElement == aContent, "didn't we register mElement?");
2377 NS_ASSERTION(mResolvedComputedStyle,
2378 "should have only registered an observer when "
2379 "mResolvedComputedStyle is true");
2381 ClearComputedStyle();
2384 /* static */
2385 ComputedStyleMap* nsComputedDOMStyle::GetComputedStyleMap() {
2386 static ComputedStyleMap map{};
2387 return &map;
2390 static StaticAutoPtr<nsTArray<const char*>> gCallbackPrefs;
2392 /* static */
2393 void nsComputedDOMStyle::RegisterPrefChangeCallbacks() {
2394 // Note that this will register callbacks for all properties with prefs, not
2395 // just those that are implemented on computed style objects, as it's not
2396 // easy to grab specific property data from ServoCSSPropList.h based on the
2397 // entries iterated in nsComputedDOMStylePropertyList.h.
2399 AutoTArray<const char*, 64> prefs;
2400 for (const auto* p = nsCSSProps::kPropertyPrefTable;
2401 p->mPropID != eCSSProperty_UNKNOWN; p++) {
2402 // Many properties are controlled by the same preference, so de-duplicate
2403 // them before adding observers.
2405 // Note: This is done by pointer comparison, which works because the mPref
2406 // members are string literals from the same same translation unit, and are
2407 // therefore de-duplicated by the compiler. On the off chance that we wind
2408 // up with some duplicates with different pointers, though, it's not a bit
2409 // deal.
2410 if (!prefs.ContainsSorted(p->mPref)) {
2411 prefs.InsertElementSorted(p->mPref);
2415 prefs.AppendElement(
2416 StaticPrefs::GetPrefName_layout_css_computed_style_shorthands());
2418 prefs.AppendElement(nullptr);
2420 MOZ_ASSERT(!gCallbackPrefs);
2421 gCallbackPrefs = new nsTArray<const char*>(std::move(prefs));
2423 Preferences::RegisterCallbacks(MarkComputedStyleMapDirty,
2424 gCallbackPrefs->Elements(),
2425 GetComputedStyleMap());
2428 /* static */
2429 void nsComputedDOMStyle::UnregisterPrefChangeCallbacks() {
2430 if (!gCallbackPrefs) {
2431 return;
2434 Preferences::UnregisterCallbacks(MarkComputedStyleMapDirty,
2435 gCallbackPrefs->Elements(),
2436 GetComputedStyleMap());
2437 gCallbackPrefs = nullptr;