Bug 1642579 [wpt PR 23909] - [COOP access reporting] Preliminary WPT tests., a=testonly
[gecko.git] / layout / style / nsComputedDOMStyle.cpp
blob33b18f94d4e801fce3e67d973465e7d868f598a8
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/StaticPtr.h"
18 #include "mozilla/StaticPrefs_layout.h"
20 #include "nsError.h"
21 #include "nsIFrame.h"
22 #include "nsIFrameInlines.h"
23 #include "mozilla/ComputedStyle.h"
24 #include "nsIScrollableFrame.h"
25 #include "nsContentUtils.h"
26 #include "nsDocShell.h"
27 #include "nsIContent.h"
28 #include "nsStyleConsts.h"
30 #include "nsDOMCSSValueList.h"
31 #include "nsFlexContainerFrame.h"
32 #include "nsGridContainerFrame.h"
33 #include "nsGkAtoms.h"
34 #include "mozilla/ReflowInput.h"
35 #include "nsStyleUtil.h"
36 #include "nsStyleStructInlines.h"
37 #include "nsROCSSPrimitiveValue.h"
39 #include "nsPresContext.h"
40 #include "mozilla/dom/Document.h"
42 #include "nsCSSProps.h"
43 #include "nsCSSPseudoElements.h"
44 #include "mozilla/EffectSet.h"
45 #include "mozilla/IntegerRange.h"
46 #include "mozilla/ServoStyleSet.h"
47 #include "mozilla/RestyleManager.h"
48 #include "mozilla/ViewportFrame.h"
49 #include "nsLayoutUtils.h"
50 #include "nsDisplayList.h"
51 #include "nsDOMCSSDeclaration.h"
52 #include "nsStyleTransformMatrix.h"
53 #include "mozilla/dom/Element.h"
54 #include "mozilla/dom/ElementInlines.h"
55 #include "prtime.h"
56 #include "nsWrapperCacheInlines.h"
57 #include "mozilla/AppUnits.h"
58 #include <algorithm>
59 #include "mozilla/ComputedStyleInlines.h"
60 #include "nsPrintfCString.h"
62 using namespace mozilla;
63 using namespace mozilla::dom;
65 #if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon)
66 # define DEBUG_ComputedDOMStyle
67 #endif
70 * This is the implementation of the readonly CSSStyleDeclaration that is
71 * returned by the getComputedStyle() function.
74 already_AddRefed<nsComputedDOMStyle> NS_NewComputedDOMStyle(
75 dom::Element* aElement, const nsAString& aPseudoElt, Document* aDocument,
76 nsComputedDOMStyle::StyleType aStyleType) {
77 RefPtr<nsComputedDOMStyle> computedStyle =
78 new nsComputedDOMStyle(aElement, aPseudoElt, aDocument, aStyleType);
79 return computedStyle.forget();
82 static nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited) {
83 return new nsDOMCSSValueList(aCommaDelimited);
86 // Whether aDocument needs to restyle for aElement
87 static bool ElementNeedsRestyle(Element* aElement, nsAtom* aPseudo,
88 bool aMayNeedToFlushLayout) {
89 const Document* doc = aElement->GetComposedDoc();
90 if (!doc) {
91 // If the element is out of the document we don't return styles for it, so
92 // nothing to do.
93 return false;
96 PresShell* presShell = doc->GetPresShell();
97 if (!presShell) {
98 // If there's no pres-shell we'll just either mint a new style from our
99 // caller document, or return no styles, so nothing to do (unless our owner
100 // element needs to get restyled, which could cause us to gain a pres shell,
101 // but the caller checks that).
102 return false;
105 // Unfortunately we don't know if the sheet change affects mElement or not, so
106 // just assume it will and that we need to flush normally.
107 ServoStyleSet* styleSet = presShell->StyleSet();
108 if (styleSet->StyleSheetsHaveChanged()) {
109 return true;
112 nsPresContext* presContext = presShell->GetPresContext();
113 MOZ_ASSERT(presContext);
115 // Pending media query updates can definitely change style on the element. For
116 // example, if you change the zoom factor and then call getComputedStyle, you
117 // should be able to observe the style with the new media queries.
119 // TODO(emilio): Does this need to also check the user font set? (it affects
120 // ch / ex units).
121 if (presContext->HasPendingMediaQueryUpdates()) {
122 // So gotta flush.
123 return true;
126 // If the pseudo-element is animating, make sure to flush.
127 if (aElement->MayHaveAnimations() && aPseudo) {
128 if (aPseudo == nsCSSPseudoElements::before()) {
129 if (EffectSet::GetEffectSet(aElement, PseudoStyleType::before)) {
130 return true;
132 } else if (aPseudo == nsCSSPseudoElements::after()) {
133 if (EffectSet::GetEffectSet(aElement, PseudoStyleType::after)) {
134 return true;
136 } else if (aPseudo == nsCSSPseudoElements::marker()) {
137 if (EffectSet::GetEffectSet(aElement, PseudoStyleType::marker)) {
138 return true;
143 // For Servo, we need to process the restyle-hint-invalidations first, to
144 // expand LaterSiblings hint, so that we can look whether ancestors need
145 // restyling.
146 RestyleManager* restyleManager = presContext->RestyleManager();
147 restyleManager->ProcessAllPendingAttributeAndStateInvalidations();
149 if (!presContext->EffectCompositor()->HasPendingStyleUpdates() &&
150 !doc->GetServoRestyleRoot()) {
151 return false;
154 // Then if there is a restyle root, we check if the root is an ancestor of
155 // this content. If it is not, then we don't need to restyle immediately.
156 // Note this is different from Gecko: we only check if any ancestor needs
157 // to restyle _itself_, not descendants, since dirty descendants can be
158 // another subtree.
159 return Servo_HasPendingRestyleAncestor(aElement, aMayNeedToFlushLayout);
163 * An object that represents the ordered set of properties that are exposed on
164 * an nsComputedDOMStyle object and how their computed values can be obtained.
166 struct ComputedStyleMap {
167 friend class nsComputedDOMStyle;
169 struct Entry {
170 // Create a pointer-to-member-function type.
171 typedef already_AddRefed<CSSValue> (nsComputedDOMStyle::*ComputeMethod)();
173 nsCSSPropertyID mProperty;
174 ComputeMethod mGetter;
176 bool IsEnabled() const {
177 return nsCSSProps::IsEnabled(mProperty, CSSEnabledState::ForAllContent);
181 // This generated file includes definition of kEntries which is typed
182 // Entry[] and used below, so this #include has to be put here.
183 #include "nsComputedDOMStyleGenerated.inc"
186 * Returns the number of properties that should be exposed on an
187 * nsComputedDOMStyle, ecxluding any disabled properties.
189 uint32_t Length() {
190 Update();
191 return mExposedPropertyCount;
195 * Returns the property at the given index in the list of properties
196 * that should be exposed on an nsComputedDOMStyle, excluding any
197 * disabled properties.
199 nsCSSPropertyID PropertyAt(uint32_t aIndex) {
200 Update();
201 return kEntries[EntryIndex(aIndex)].mProperty;
205 * Searches for and returns the computed style map entry for the given
206 * property, or nullptr if the property is not exposed on nsComputedDOMStyle
207 * or is currently disabled.
209 const Entry* FindEntryForProperty(nsCSSPropertyID aPropID) {
210 Update();
211 for (uint32_t i = 0; i < mExposedPropertyCount; i++) {
212 const Entry* entry = &kEntries[EntryIndex(i)];
213 if (entry->mProperty == aPropID) {
214 return entry;
217 return nullptr;
221 * Records that mIndexMap needs updating, due to prefs changing that could
222 * affect the set of properties exposed on an nsComputedDOMStyle.
224 void MarkDirty() { mExposedPropertyCount = 0; }
226 // The member variables are public so that we can use an initializer in
227 // nsComputedDOMStyle::GetComputedStyleMap. Use the member functions
228 // above to get information from this object.
231 * The number of properties that should be exposed on an nsComputedDOMStyle.
232 * This will be less than eComputedStyleProperty_COUNT if some property
233 * prefs are disabled. A value of 0 indicates that it and mIndexMap are out
234 * of date.
236 uint32_t mExposedPropertyCount;
239 * A map of indexes on the nsComputedDOMStyle object to indexes into kEntries.
241 uint32_t mIndexMap[ArrayLength(kEntries)];
243 private:
245 * Returns whether mExposedPropertyCount and mIndexMap are out of date.
247 bool IsDirty() { return mExposedPropertyCount == 0; }
250 * Updates mExposedPropertyCount and mIndexMap to take into account properties
251 * whose prefs are currently disabled.
253 void Update();
256 * Maps an nsComputedDOMStyle indexed getter index to an index into kEntries.
258 uint32_t EntryIndex(uint32_t aIndex) const {
259 MOZ_ASSERT(aIndex < mExposedPropertyCount);
260 return mIndexMap[aIndex];
264 constexpr ComputedStyleMap::Entry
265 ComputedStyleMap::kEntries[ArrayLength(kEntries)];
267 void ComputedStyleMap::Update() {
268 if (!IsDirty()) {
269 return;
272 uint32_t index = 0;
273 for (uint32_t i = 0; i < ArrayLength(kEntries); i++) {
274 if (kEntries[i].IsEnabled()) {
275 mIndexMap[index++] = i;
278 mExposedPropertyCount = index;
281 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement,
282 const nsAString& aPseudoElt,
283 Document* aDocument,
284 StyleType aStyleType)
285 : mDocumentWeak(nullptr),
286 mOuterFrame(nullptr),
287 mInnerFrame(nullptr),
288 mPresShell(nullptr),
289 mStyleType(aStyleType),
290 mExposeVisitedStyle(false),
291 mResolvedComputedStyle(false) {
292 MOZ_ASSERT(aElement);
293 MOZ_ASSERT(aDocument);
294 // TODO(emilio, bug 548397, https://github.com/w3c/csswg-drafts/issues/2403):
295 // Should use aElement->OwnerDoc() instead.
296 mDocumentWeak = do_GetWeakReference(aDocument);
297 mElement = aElement;
298 mPseudo = nsCSSPseudoElements::GetPseudoAtom(aPseudoElt);
301 nsComputedDOMStyle::~nsComputedDOMStyle() {
302 MOZ_ASSERT(!mResolvedComputedStyle,
303 "Should have called ClearComputedStyle() during last release.");
306 NS_IMPL_CYCLE_COLLECTION_CLASS(nsComputedDOMStyle)
308 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle)
309 tmp->ClearComputedStyle(); // remove observer before clearing mElement
310 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
311 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
312 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
314 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsComputedDOMStyle)
315 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
316 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
318 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsComputedDOMStyle)
320 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle)
321 return tmp->HasKnownLiveWrapper();
322 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
324 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle)
325 return tmp->HasKnownLiveWrapper();
326 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
328 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsComputedDOMStyle)
329 return tmp->HasKnownLiveWrapper();
330 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
332 // QueryInterface implementation for nsComputedDOMStyle
333 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle)
334 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
335 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
336 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
338 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle)
339 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
340 nsComputedDOMStyle, ClearComputedStyle())
342 nsresult nsComputedDOMStyle::GetPropertyValue(const nsCSSPropertyID aPropID,
343 nsAString& aValue) {
344 // This is mostly to avoid code duplication with GetPropertyCSSValue(); if
345 // perf ever becomes an issue here (doubtful), we can look into changing
346 // this.
347 return GetPropertyValue(nsCSSProps::GetStringValue(aPropID), aValue);
350 void nsComputedDOMStyle::SetPropertyValue(const nsCSSPropertyID aPropID,
351 const nsACString& aValue,
352 nsIPrincipal* aSubjectPrincipal,
353 ErrorResult& aRv) {
354 aRv.ThrowNoModificationAllowedError(nsPrintfCString(
355 "Can't set value for property '%s' in computed style",
356 PromiseFlatCString(nsCSSProps::GetStringValue(aPropID)).get()));
359 void nsComputedDOMStyle::GetCssText(nsAString& aCssText) {
360 aCssText.Truncate();
363 void nsComputedDOMStyle::SetCssText(const nsAString& aCssText,
364 nsIPrincipal* aSubjectPrincipal,
365 ErrorResult& aRv) {
366 aRv.ThrowNoModificationAllowedError("Can't set cssText on computed style");
369 uint32_t nsComputedDOMStyle::Length() {
370 // Make sure we have up to date style so that we can include custom
371 // properties.
372 UpdateCurrentStyleSources(eCSSPropertyExtra_variable);
373 if (!mComputedStyle) {
374 return 0;
377 uint32_t length = GetComputedStyleMap()->Length() +
378 Servo_GetCustomPropertiesCount(mComputedStyle);
380 ClearCurrentStyleSources();
382 return length;
385 css::Rule* nsComputedDOMStyle::GetParentRule() { return nullptr; }
387 NS_IMETHODIMP
388 nsComputedDOMStyle::GetPropertyValue(const nsACString& aPropertyName,
389 nsAString& aReturn) {
390 aReturn.Truncate();
392 nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
394 const ComputedStyleMap::Entry* entry = nullptr;
395 if (prop != eCSSPropertyExtra_variable) {
396 entry = GetComputedStyleMap()->FindEntryForProperty(prop);
397 if (!entry) {
398 return NS_OK;
402 UpdateCurrentStyleSources(prop);
403 if (!mComputedStyle) {
404 return NS_OK;
407 auto cleanup = mozilla::MakeScopeExit([&] { ClearCurrentStyleSources(); });
409 if (!entry) {
410 MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
411 const nsACString& name =
412 Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
413 Servo_GetCustomPropertyValue(mComputedStyle, &name, &aReturn);
414 return NS_OK;
417 if (nsCSSProps::PropHasFlags(prop, CSSPropFlags::IsLogical)) {
418 MOZ_ASSERT(entry);
419 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
421 DebugOnly<nsCSSPropertyID> logicalProp = prop;
423 prop = Servo_ResolveLogicalProperty(prop, mComputedStyle);
424 entry = GetComputedStyleMap()->FindEntryForProperty(prop);
426 MOZ_ASSERT(NeedsToFlushLayout(logicalProp) == NeedsToFlushLayout(prop),
427 "Logical and physical property don't agree on whether layout is "
428 "needed");
431 if (!nsCSSProps::PropHasFlags(prop, CSSPropFlags::SerializedByServo)) {
432 if (RefPtr<CSSValue> value = (this->*entry->mGetter)()) {
433 ErrorResult rv;
434 nsString text;
435 value->GetCssText(text, rv);
436 aReturn.Assign(text);
437 return rv.StealNSResult();
439 return NS_OK;
442 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
443 mComputedStyle->GetComputedPropertyValue(prop, aReturn);
444 return NS_OK;
447 /* static */
448 already_AddRefed<ComputedStyle> nsComputedDOMStyle::GetComputedStyle(
449 Element* aElement, nsAtom* aPseudo, StyleType aStyleType) {
450 if (Document* doc = aElement->GetComposedDoc()) {
451 doc->FlushPendingNotifications(FlushType::Style);
453 return GetComputedStyleNoFlush(aElement, aPseudo, aStyleType);
457 * The following function checks whether we need to explicitly resolve the style
458 * again, even though we have a style coming from the frame.
460 * This basically checks whether the style is or may be under a ::first-line or
461 * ::first-letter frame, in which case we can't return the frame style, and we
462 * need to resolve it. See bug 505515.
464 static bool MustReresolveStyle(const mozilla::ComputedStyle* aStyle) {
465 MOZ_ASSERT(aStyle);
467 // TODO(emilio): We may want to avoid re-resolving pseudo-element styles
468 // more often.
469 return aStyle->HasPseudoElementData() && !aStyle->IsPseudoElement();
472 static inline PseudoStyleType GetPseudoType(nsAtom* aPseudo) {
473 if (!aPseudo) {
474 return PseudoStyleType::NotPseudo;
476 return nsCSSPseudoElements::GetPseudoType(aPseudo,
477 CSSEnabledState::ForAllContent);
480 already_AddRefed<ComputedStyle> nsComputedDOMStyle::DoGetComputedStyleNoFlush(
481 Element* aElement, nsAtom* aPseudo, PresShell* aPresShell,
482 StyleType aStyleType) {
483 MOZ_ASSERT(aElement, "NULL element");
485 // If the content has a pres shell, we must use it. Otherwise we'd
486 // potentially mix rule trees by using the wrong pres shell's style
487 // set. Using the pres shell from the content also means that any
488 // content that's actually *in* a document will get the style from the
489 // correct document.
490 PresShell* presShell = nsContentUtils::GetPresShellForContent(aElement);
491 bool inDocWithShell = true;
492 if (!presShell) {
493 inDocWithShell = false;
494 presShell = aPresShell;
495 if (!presShell) {
496 return nullptr;
500 PseudoStyleType pseudoType = GetPseudoType(aPseudo);
501 if (aPseudo && !PseudoStyle::IsPseudoElement(pseudoType)) {
502 return nullptr;
505 if (!aElement->IsInComposedDoc()) {
506 // Don't return styles for disconnected elements, that makes no sense. This
507 // can only happen with a non-null presShell for cross-document calls.
509 // FIXME(emilio, bug 1483798): This should also not return styles for
510 // elements outside of the flat tree, not just outside of the document.
511 return nullptr;
514 // XXX the !aElement->IsHTMLElement(nsGkAtoms::area)
515 // check is needed due to bug 135040 (to avoid using
516 // mPrimaryFrame). Remove it once that's fixed.
517 if (inDocWithShell && aStyleType == eAll &&
518 !aElement->IsHTMLElement(nsGkAtoms::area)) {
519 Element* element = nullptr;
520 if (aPseudo == nsCSSPseudoElements::before()) {
521 element = nsLayoutUtils::GetBeforePseudo(aElement);
522 } else if (aPseudo == nsCSSPseudoElements::after()) {
523 element = nsLayoutUtils::GetAfterPseudo(aElement);
524 } else if (aPseudo == nsCSSPseudoElements::marker()) {
525 element = nsLayoutUtils::GetMarkerPseudo(aElement);
526 } else if (!aPseudo) {
527 element = aElement;
530 if (element) {
531 if (nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(element)) {
532 ComputedStyle* result = styleFrame->Style();
533 // Don't use the style if it was influenced by pseudo-elements,
534 // since then it's not the primary style for this element / pseudo.
535 if (!MustReresolveStyle(result)) {
536 RefPtr<ComputedStyle> ret = result;
537 return ret.forget();
543 // No frame has been created, or we have a pseudo, or we're looking
544 // for the default style, so resolve the style ourselves.
545 ServoStyleSet* styleSet = presShell->StyleSet();
547 StyleRuleInclusion rules = aStyleType == eDefaultOnly
548 ? StyleRuleInclusion::DefaultOnly
549 : StyleRuleInclusion::All;
550 RefPtr<ComputedStyle> result =
551 styleSet->ResolveStyleLazily(*aElement, pseudoType, rules);
552 return result.forget();
555 already_AddRefed<ComputedStyle>
556 nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(Element* aElement,
557 nsAtom* aPseudo) {
558 RefPtr<ComputedStyle> style = GetComputedStyleNoFlush(aElement, aPseudo);
559 if (!style) {
560 return nullptr;
563 PseudoStyleType pseudoType = GetPseudoType(aPseudo);
564 PresShell* presShell = aElement->OwnerDoc()->GetPresShell();
565 MOZ_ASSERT(presShell,
566 "How in the world did we get a style a few lines above?");
568 Element* elementOrPseudoElement =
569 EffectCompositor::GetElementToRestyle(aElement, pseudoType);
570 if (!elementOrPseudoElement) {
571 return nullptr;
574 return presShell->StyleSet()->GetBaseContextForElement(elementOrPseudoElement,
575 style);
578 nsMargin nsComputedDOMStyle::GetAdjustedValuesForBoxSizing() {
579 // We want the width/height of whatever parts 'width' or 'height' controls,
580 // which can be different depending on the value of the 'box-sizing' property.
581 const nsStylePosition* stylePos = StylePosition();
583 nsMargin adjustment;
584 if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
585 adjustment = mInnerFrame->GetUsedBorderAndPadding();
588 return adjustment;
591 static void AddImageURL(nsIURI& aURI, nsTArray<nsCString>& aURLs) {
592 nsCString spec;
593 nsresult rv = aURI.GetSpec(spec);
594 if (NS_FAILED(rv)) {
595 return;
598 aURLs.AppendElement(std::move(spec));
601 static void AddImageURL(const StyleComputedUrl& aURL,
602 nsTArray<nsCString>& aURLs) {
603 if (aURL.IsLocalRef()) {
604 return;
607 if (nsIURI* uri = aURL.GetURI()) {
608 AddImageURL(*uri, aURLs);
612 static void AddImageURL(const StyleImage& aImage, nsTArray<nsCString>& aURLs) {
613 if (auto* urlValue = aImage.GetImageRequestURLValue()) {
614 AddImageURL(*urlValue, aURLs);
618 static void AddImageURL(const StyleShapeOutside& aShapeOutside,
619 nsTArray<nsCString>& aURLs) {
620 if (aShapeOutside.IsImage()) {
621 AddImageURL(aShapeOutside.AsImage(), aURLs);
625 static void AddImageURL(const StyleClipPath& aClipPath,
626 nsTArray<nsCString>& aURLs) {
627 if (aClipPath.IsUrl()) {
628 AddImageURL(aClipPath.AsUrl(), aURLs);
632 static void AddImageURLs(const nsStyleImageLayers& aLayers,
633 nsTArray<nsCString>& aURLs) {
634 for (auto i : IntegerRange(aLayers.mLayers.Length())) {
635 AddImageURL(aLayers.mLayers[i].mImage, aURLs);
639 static void CollectImageURLsForProperty(nsCSSPropertyID aProp,
640 const ComputedStyle& aStyle,
641 nsTArray<nsCString>& aURLs) {
642 if (nsCSSProps::IsShorthand(aProp)) {
643 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProp,
644 CSSEnabledState::ForAllContent) {
645 CollectImageURLsForProperty(*p, aStyle, aURLs);
647 return;
650 switch (aProp) {
651 case eCSSProperty_cursor:
652 for (auto& image : aStyle.StyleUI()->mCursor.images.AsSpan()) {
653 AddImageURL(image.url, aURLs);
655 break;
656 case eCSSProperty_background_image:
657 AddImageURLs(aStyle.StyleBackground()->mImage, aURLs);
658 break;
659 case eCSSProperty_mask_clip:
660 AddImageURLs(aStyle.StyleSVGReset()->mMask, aURLs);
661 break;
662 case eCSSProperty_list_style_image: {
663 const auto& image = aStyle.StyleList()->mListStyleImage;
664 if (image.IsUrl()) {
665 AddImageURL(image.AsUrl(), aURLs);
667 break;
669 case eCSSProperty_border_image_source:
670 AddImageURL(aStyle.StyleBorder()->mBorderImageSource, aURLs);
671 break;
672 case eCSSProperty_clip_path:
673 AddImageURL(aStyle.StyleSVGReset()->mClipPath, aURLs);
674 break;
675 case eCSSProperty_shape_outside:
676 AddImageURL(aStyle.StyleDisplay()->mShapeOutside, aURLs);
677 break;
678 default:
679 break;
683 void nsComputedDOMStyle::GetCSSImageURLs(const nsACString& aPropertyName,
684 nsTArray<nsCString>& aImageURLs,
685 mozilla::ErrorResult& aRv) {
686 nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
687 if (prop == eCSSProperty_UNKNOWN) {
688 // Note: not using nsPrintfCString here in case aPropertyName contains
689 // nulls.
690 aRv.ThrowSyntaxError(NS_LITERAL_CSTRING("Invalid property name '") +
691 aPropertyName + NS_LITERAL_CSTRING("'"));
692 return;
695 UpdateCurrentStyleSources(prop);
697 if (!mComputedStyle) {
698 return;
701 CollectImageURLsForProperty(prop, *mComputedStyle, aImageURLs);
702 ClearCurrentStyleSources();
705 // nsDOMCSSDeclaration abstract methods which should never be called
706 // on a nsComputedDOMStyle object, but must be defined to avoid
707 // compile errors.
708 DeclarationBlock* nsComputedDOMStyle::GetOrCreateCSSDeclaration(
709 Operation aOperation, DeclarationBlock** aCreated) {
710 MOZ_CRASH("called nsComputedDOMStyle::GetCSSDeclaration");
713 nsresult nsComputedDOMStyle::SetCSSDeclaration(DeclarationBlock*,
714 MutationClosureData*) {
715 MOZ_CRASH("called nsComputedDOMStyle::SetCSSDeclaration");
718 Document* nsComputedDOMStyle::DocToUpdate() {
719 MOZ_CRASH("called nsComputedDOMStyle::DocToUpdate");
722 nsDOMCSSDeclaration::ParsingEnvironment
723 nsComputedDOMStyle::GetParsingEnvironment(
724 nsIPrincipal* aSubjectPrincipal) const {
725 MOZ_CRASH("called nsComputedDOMStyle::GetParsingEnvironment");
728 void nsComputedDOMStyle::ClearComputedStyle() {
729 if (mResolvedComputedStyle) {
730 mResolvedComputedStyle = false;
731 mElement->RemoveMutationObserver(this);
733 mComputedStyle = nullptr;
736 void nsComputedDOMStyle::SetResolvedComputedStyle(
737 RefPtr<ComputedStyle>&& aContext, uint64_t aGeneration) {
738 if (!mResolvedComputedStyle) {
739 mResolvedComputedStyle = true;
740 mElement->AddMutationObserver(this);
742 mComputedStyle = aContext;
743 mComputedStyleGeneration = aGeneration;
744 mPresShellId = mPresShell->GetPresShellId();
747 void nsComputedDOMStyle::SetFrameComputedStyle(mozilla::ComputedStyle* aStyle,
748 uint64_t aGeneration) {
749 ClearComputedStyle();
750 mComputedStyle = aStyle;
751 mComputedStyleGeneration = aGeneration;
752 mPresShellId = mPresShell->GetPresShellId();
755 static bool MayNeedToFlushLayout(nsCSSPropertyID aPropID) {
756 switch (aPropID) {
757 case eCSSProperty_width:
758 case eCSSProperty_height:
759 case eCSSProperty_block_size:
760 case eCSSProperty_inline_size:
761 case eCSSProperty_line_height:
762 case eCSSProperty_grid_template_rows:
763 case eCSSProperty_grid_template_columns:
764 case eCSSProperty_perspective_origin:
765 case eCSSProperty_transform_origin:
766 case eCSSProperty_transform:
767 case eCSSProperty_border_top_width:
768 case eCSSProperty_border_bottom_width:
769 case eCSSProperty_border_left_width:
770 case eCSSProperty_border_right_width:
771 case eCSSProperty_border_block_start_width:
772 case eCSSProperty_border_block_end_width:
773 case eCSSProperty_border_inline_start_width:
774 case eCSSProperty_border_inline_end_width:
775 case eCSSProperty_top:
776 case eCSSProperty_right:
777 case eCSSProperty_bottom:
778 case eCSSProperty_left:
779 case eCSSProperty_inset_block_start:
780 case eCSSProperty_inset_block_end:
781 case eCSSProperty_inset_inline_start:
782 case eCSSProperty_inset_inline_end:
783 case eCSSProperty_padding_top:
784 case eCSSProperty_padding_right:
785 case eCSSProperty_padding_bottom:
786 case eCSSProperty_padding_left:
787 case eCSSProperty_padding_block_start:
788 case eCSSProperty_padding_block_end:
789 case eCSSProperty_padding_inline_start:
790 case eCSSProperty_padding_inline_end:
791 case eCSSProperty_margin_top:
792 case eCSSProperty_margin_right:
793 case eCSSProperty_margin_bottom:
794 case eCSSProperty_margin_left:
795 case eCSSProperty_margin_block_start:
796 case eCSSProperty_margin_block_end:
797 case eCSSProperty_margin_inline_start:
798 case eCSSProperty_margin_inline_end:
799 return true;
800 default:
801 return false;
805 bool nsComputedDOMStyle::NeedsToFlushStyle(nsCSSPropertyID aPropID) const {
806 bool mayNeedToFlushLayout = MayNeedToFlushLayout(aPropID);
808 // We always compute styles from the element's owner document.
809 if (ElementNeedsRestyle(mElement, mPseudo, mayNeedToFlushLayout)) {
810 return true;
813 Document* doc = mElement->OwnerDoc();
814 // If parent document is there, also needs to check if there is some change
815 // that needs to flush this document (e.g. size change for iframe).
816 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
817 Document* parentDocument = doc->GetInProcessParentDocument();
818 Element* element = parentDocument->FindContentForSubDocument(doc);
819 if (ElementNeedsRestyle(element, nullptr, mayNeedToFlushLayout)) {
820 return true;
822 doc = parentDocument;
825 return false;
828 static nsIFrame* StyleFrame(nsIFrame* aOuterFrame) {
829 MOZ_ASSERT(aOuterFrame);
830 if (!aOuterFrame->IsTableWrapperFrame()) {
831 return aOuterFrame;
833 // If the frame is a table wrapper frame then we should get the style from the
834 // inner table frame.
835 nsIFrame* inner = aOuterFrame->PrincipalChildList().FirstChild();
836 NS_ASSERTION(inner, "table wrapper must have an inner");
837 NS_ASSERTION(!inner->GetNextSibling(),
838 "table wrapper frames should have just one child, the inner "
839 "table");
840 return inner;
843 static bool IsNonReplacedInline(nsIFrame* aFrame) {
844 // FIXME: this should be IsInlineInsideStyle() since width/height
845 // doesn't apply to ruby boxes.
846 return aFrame->StyleDisplay()->IsInlineFlow() &&
847 !aFrame->IsFrameOfType(nsIFrame::eReplaced);
850 static Side SideForPaddingOrMarginOrInsetProperty(nsCSSPropertyID aPropID) {
851 switch (aPropID) {
852 case eCSSProperty_top:
853 case eCSSProperty_margin_top:
854 case eCSSProperty_padding_top:
855 return eSideTop;
856 case eCSSProperty_right:
857 case eCSSProperty_margin_right:
858 case eCSSProperty_padding_right:
859 return eSideRight;
860 case eCSSProperty_bottom:
861 case eCSSProperty_margin_bottom:
862 case eCSSProperty_padding_bottom:
863 return eSideBottom;
864 case eCSSProperty_left:
865 case eCSSProperty_margin_left:
866 case eCSSProperty_padding_left:
867 return eSideLeft;
868 default:
869 MOZ_ASSERT_UNREACHABLE("Unexpected property");
870 return eSideTop;
874 static bool PaddingNeedsUsedValue(const LengthPercentage& aValue,
875 const ComputedStyle& aStyle) {
876 return !aValue.ConvertsToLength() || aStyle.StyleDisplay()->HasAppearance();
879 bool nsComputedDOMStyle::NeedsToFlushLayout(nsCSSPropertyID aPropID) const {
880 MOZ_ASSERT(aPropID != eCSSProperty_UNKNOWN);
881 if (aPropID == eCSSPropertyExtra_variable) {
882 return false;
884 nsIFrame* outerFrame = GetOuterFrame();
885 if (!outerFrame) {
886 return false;
888 nsIFrame* frame = StyleFrame(outerFrame);
889 auto* style = frame->Style();
890 if (nsCSSProps::PropHasFlags(aPropID, CSSPropFlags::IsLogical)) {
891 aPropID = Servo_ResolveLogicalProperty(aPropID, style);
894 switch (aPropID) {
895 case eCSSProperty_width:
896 case eCSSProperty_height:
897 return !IsNonReplacedInline(frame);
898 case eCSSProperty_line_height:
899 return frame->StyleText()->mLineHeight.IsMozBlockHeight();
900 case eCSSProperty_grid_template_rows:
901 case eCSSProperty_grid_template_columns:
902 return !!nsGridContainerFrame::GetGridContainerFrame(frame);
903 case eCSSProperty_perspective_origin:
904 return style->StyleDisplay()->mPerspectiveOrigin.HasPercent();
905 case eCSSProperty_transform_origin:
906 return style->StyleDisplay()->mTransformOrigin.HasPercent();
907 case eCSSProperty_transform:
908 return style->StyleDisplay()->mTransform.HasPercent();
909 case eCSSProperty_border_top_width:
910 case eCSSProperty_border_bottom_width:
911 case eCSSProperty_border_left_width:
912 case eCSSProperty_border_right_width:
913 // FIXME(emilio): This should return false per spec (bug 1551000), but
914 // themed borders don't make that easy, so for now flush for that case.
916 // TODO(emilio): If we make GetUsedBorder() stop returning 0 for an
917 // unreflowed frame, or something of that sort, then we can stop flushing
918 // layout for themed frames.
919 return style->StyleDisplay()->HasAppearance();
920 case eCSSProperty_top:
921 case eCSSProperty_right:
922 case eCSSProperty_bottom:
923 case eCSSProperty_left:
924 // Doing better than this is actually hard.
925 return style->StyleDisplay()->mPosition != StylePositionProperty::Static;
926 case eCSSProperty_padding_top:
927 case eCSSProperty_padding_right:
928 case eCSSProperty_padding_bottom:
929 case eCSSProperty_padding_left: {
930 Side side = SideForPaddingOrMarginOrInsetProperty(aPropID);
931 // Theming can override used padding, sigh.
933 // TODO(emilio): If we make GetUsedPadding() stop returning 0 for an
934 // unreflowed frame, or something of that sort, then we can stop flushing
935 // layout for themed frames.
936 return PaddingNeedsUsedValue(style->StylePadding()->mPadding.Get(side),
937 *style);
939 case eCSSProperty_margin_top:
940 case eCSSProperty_margin_right:
941 case eCSSProperty_margin_bottom:
942 case eCSSProperty_margin_left: {
943 // NOTE(emilio): This is dubious, but matches other browsers.
944 // See https://github.com/w3c/csswg-drafts/issues/2328
945 Side side = SideForPaddingOrMarginOrInsetProperty(aPropID);
946 return !style->StyleMargin()->mMargin.Get(side).ConvertsToLength();
948 default:
949 return false;
953 void nsComputedDOMStyle::Flush(Document& aDocument, FlushType aFlushType) {
954 MOZ_ASSERT(mElement->IsInComposedDoc());
956 #ifdef DEBUG
958 nsCOMPtr<Document> document = do_QueryReferent(mDocumentWeak);
959 MOZ_ASSERT(document == &aDocument);
961 #endif
963 aDocument.FlushPendingNotifications(aFlushType);
964 if (MOZ_UNLIKELY(&aDocument != mElement->OwnerDoc())) {
965 mElement->OwnerDoc()->FlushPendingNotifications(aFlushType);
969 nsIFrame* nsComputedDOMStyle::GetOuterFrame() const {
970 if (!mPseudo) {
971 return mElement->GetPrimaryFrame();
973 nsAtom* property = nullptr;
974 if (mPseudo == nsCSSPseudoElements::before()) {
975 property = nsGkAtoms::beforePseudoProperty;
976 } else if (mPseudo == nsCSSPseudoElements::after()) {
977 property = nsGkAtoms::afterPseudoProperty;
978 } else if (mPseudo == nsCSSPseudoElements::marker()) {
979 property = nsGkAtoms::markerPseudoProperty;
981 if (!property) {
982 return nullptr;
984 auto* pseudo = static_cast<Element*>(mElement->GetProperty(property));
985 return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
988 void nsComputedDOMStyle::UpdateCurrentStyleSources(nsCSSPropertyID aPropID) {
989 nsCOMPtr<Document> document = do_QueryReferent(mDocumentWeak);
990 if (!document) {
991 ClearComputedStyle();
992 return;
995 // We don't return styles for disconnected elements anymore, so don't go
996 // through the trouble of flushing or what not.
998 // TODO(emilio): We may want to return earlier for elements outside of the
999 // flat tree too: https://github.com/w3c/csswg-drafts/issues/1964
1000 if (!mElement->IsInComposedDoc()) {
1001 ClearComputedStyle();
1002 return;
1005 DebugOnly<bool> didFlush = false;
1006 if (NeedsToFlushStyle(aPropID)) {
1007 didFlush = true;
1008 // We look at the frame in NeedsToFlushLayout, so flush frames, not only
1009 // styles.
1010 Flush(*document, FlushType::Frames);
1013 if (NeedsToFlushLayout(aPropID)) {
1014 MOZ_ASSERT(MayNeedToFlushLayout(aPropID));
1015 didFlush = true;
1016 Flush(*document, FlushType::Layout);
1017 #ifdef DEBUG
1018 mFlushedPendingReflows = true;
1019 #endif
1020 } else {
1021 #ifdef DEBUG
1022 mFlushedPendingReflows = false;
1023 #endif
1026 mPresShell = document->GetPresShell();
1027 if (!mPresShell || !mPresShell->GetPresContext()) {
1028 ClearComputedStyle();
1029 return;
1032 // We need to use GetUndisplayedRestyleGeneration instead of
1033 // GetRestyleGeneration, because the caching of mComputedStyle is an
1034 // optimization that is useful only for displayed elements.
1035 // For undisplayed elements we need to take into account any DOM changes that
1036 // might cause a restyle, because Servo will not increase the generation for
1037 // undisplayed elements.
1038 // As for Gecko, GetUndisplayedRestyleGeneration is effectively equal to
1039 // GetRestyleGeneration, since the generation is incremented whenever we
1040 // process restyles.
1041 uint64_t currentGeneration =
1042 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration();
1044 if (mComputedStyle && mComputedStyleGeneration == currentGeneration &&
1045 mPresShellId == mPresShell->GetPresShellId()) {
1046 // Our cached style is still valid.
1047 return;
1050 mComputedStyle = nullptr;
1052 // XXX the !mElement->IsHTMLElement(nsGkAtoms::area)
1053 // check is needed due to bug 135040 (to avoid using
1054 // mPrimaryFrame). Remove it once that's fixed.
1055 if (mStyleType == eAll && !mElement->IsHTMLElement(nsGkAtoms::area)) {
1056 mOuterFrame = GetOuterFrame();
1057 mInnerFrame = mOuterFrame;
1058 if (mOuterFrame) {
1059 mInnerFrame = StyleFrame(mOuterFrame);
1060 SetFrameComputedStyle(mInnerFrame->Style(), currentGeneration);
1061 NS_ASSERTION(mComputedStyle, "Frame without style?");
1065 if (!mComputedStyle || MustReresolveStyle(mComputedStyle)) {
1066 PresShell* presShellForContent = mElement->OwnerDoc()->GetPresShell();
1067 // Need to resolve a style.
1068 RefPtr<ComputedStyle> resolvedComputedStyle = DoGetComputedStyleNoFlush(
1069 mElement, mPseudo,
1070 presShellForContent ? presShellForContent : mPresShell, mStyleType);
1071 if (!resolvedComputedStyle) {
1072 ClearComputedStyle();
1073 return;
1076 // No need to re-get the generation, even though GetComputedStyle
1077 // will flush, since we flushed style at the top of this function.
1078 // We don't need to check this if we only flushed the parent.
1079 NS_ASSERTION(
1080 !didFlush ||
1081 currentGeneration ==
1082 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration(),
1083 "why should we have flushed style again?");
1085 SetResolvedComputedStyle(std::move(resolvedComputedStyle),
1086 currentGeneration);
1087 NS_ASSERTION(mPseudo || !mComputedStyle->HasPseudoElementData(),
1088 "should not have pseudo-element data");
1091 // mExposeVisitedStyle is set to true only by testing APIs that
1092 // require chrome privilege.
1093 MOZ_ASSERT(!mExposeVisitedStyle || nsContentUtils::IsCallerChrome(),
1094 "mExposeVisitedStyle set incorrectly");
1095 if (mExposeVisitedStyle && mComputedStyle->RelevantLinkVisited()) {
1096 if (ComputedStyle* styleIfVisited = mComputedStyle->GetStyleIfVisited()) {
1097 mComputedStyle = styleIfVisited;
1102 void nsComputedDOMStyle::ClearCurrentStyleSources() {
1103 // Release the current style if we got it off the frame.
1105 // For a style we resolved, keep it around so that we can re-use it next time
1106 // this object is queried, but not if it-s a re-resolved style because we were
1107 // inside a pseudo-element.
1108 if (!mResolvedComputedStyle || mOuterFrame) {
1109 ClearComputedStyle();
1112 mOuterFrame = nullptr;
1113 mInnerFrame = nullptr;
1114 mPresShell = nullptr;
1117 void nsComputedDOMStyle::RemoveProperty(const nsACString& aPropertyName,
1118 nsAString& aReturn, ErrorResult& aRv) {
1119 // Note: not using nsPrintfCString here in case aPropertyName contains
1120 // nulls.
1121 aRv.ThrowNoModificationAllowedError(
1122 NS_LITERAL_CSTRING("Can't remove property '") + aPropertyName +
1123 NS_LITERAL_CSTRING("' from computed style"));
1126 void nsComputedDOMStyle::GetPropertyPriority(const nsACString& aPropertyName,
1127 nsAString& aReturn) {
1128 aReturn.Truncate();
1131 void nsComputedDOMStyle::SetProperty(const nsACString& aPropertyName,
1132 const nsACString& aValue,
1133 const nsAString& aPriority,
1134 nsIPrincipal* aSubjectPrincipal,
1135 ErrorResult& aRv) {
1136 // Note: not using nsPrintfCString here in case aPropertyName contains
1137 // nulls.
1138 aRv.ThrowNoModificationAllowedError(
1139 NS_LITERAL_CSTRING("Can't set value for property '") + aPropertyName +
1140 NS_LITERAL_CSTRING("' in computed style"));
1143 void nsComputedDOMStyle::IndexedGetter(uint32_t aIndex, bool& aFound,
1144 nsACString& aPropName) {
1145 ComputedStyleMap* map = GetComputedStyleMap();
1146 uint32_t length = map->Length();
1148 if (aIndex < length) {
1149 aFound = true;
1150 aPropName.Assign(nsCSSProps::GetStringValue(map->PropertyAt(aIndex)));
1151 return;
1154 // Custom properties are exposed with indexed properties just after all
1155 // of the built-in properties.
1156 UpdateCurrentStyleSources(eCSSPropertyExtra_variable);
1157 if (!mComputedStyle) {
1158 aFound = false;
1159 return;
1162 uint32_t count = Servo_GetCustomPropertiesCount(mComputedStyle);
1164 const uint32_t index = aIndex - length;
1165 if (index < count) {
1166 aFound = true;
1167 aPropName.AssignLiteral("--");
1168 if (nsAtom* atom = Servo_GetCustomPropertyNameAt(mComputedStyle, index)) {
1169 aPropName.Append(nsAtomCString(atom));
1171 } else {
1172 aFound = false;
1175 ClearCurrentStyleSources();
1178 // Property getters...
1180 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBottom() {
1181 return GetOffsetWidthFor(eSideBottom);
1184 /* static */
1185 void nsComputedDOMStyle::SetToRGBAColor(nsROCSSPrimitiveValue* aValue,
1186 nscolor aColor) {
1187 nsAutoString string;
1188 nsStyleUtil::GetSerializedColorValue(aColor, string);
1189 aValue->SetString(string);
1192 void nsComputedDOMStyle::SetValueFromComplexColor(
1193 nsROCSSPrimitiveValue* aValue, const mozilla::StyleColor& aColor) {
1194 SetToRGBAColor(aValue, aColor.CalcColor(*mComputedStyle));
1197 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetColumnRuleWidth() {
1198 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1199 val->SetAppUnits(StyleColumn()->GetComputedColumnRuleWidth());
1200 return val.forget();
1203 static Position MaybeResolvePositionForTransform(const LengthPercentage& aX,
1204 const LengthPercentage& aY,
1205 nsIFrame* aInnerFrame) {
1206 if (!aInnerFrame) {
1207 return {aX, aY};
1209 nsStyleTransformMatrix::TransformReferenceBox refBox(aInnerFrame);
1210 CSSPoint p = nsStyleTransformMatrix::Convert2DPosition(aX, aY, refBox);
1211 return {LengthPercentage::FromPixels(p.x), LengthPercentage::FromPixels(p.y)};
1214 /* Convert the stored representation into a list of two values and then hand
1215 * it back.
1217 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransformOrigin() {
1218 /* We need to build up a list of two values. We'll call them
1219 * width and height.
1222 /* Store things as a value list */
1223 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1225 /* Now, get the values. */
1226 const auto& origin = StyleDisplay()->mTransformOrigin;
1228 RefPtr<nsROCSSPrimitiveValue> width = new nsROCSSPrimitiveValue;
1229 auto position = MaybeResolvePositionForTransform(
1230 origin.horizontal, origin.vertical, mInnerFrame);
1231 SetValueToPosition(position, valueList);
1232 if (!origin.depth.IsZero()) {
1233 RefPtr<nsROCSSPrimitiveValue> depth = new nsROCSSPrimitiveValue;
1234 depth->SetAppUnits(origin.depth.ToAppUnits());
1235 valueList->AppendCSSValue(depth.forget());
1237 return valueList.forget();
1240 /* Convert the stored representation into a list of two values and then hand
1241 * it back.
1243 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPerspectiveOrigin() {
1244 /* We need to build up a list of two values. We'll call them
1245 * width and height.
1248 /* Store things as a value list */
1249 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1251 /* Now, get the values. */
1252 const auto& origin = StyleDisplay()->mPerspectiveOrigin;
1254 auto position = MaybeResolvePositionForTransform(
1255 origin.horizontal, origin.vertical, mInnerFrame);
1256 SetValueToPosition(position, valueList);
1257 return valueList.forget();
1260 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransform() {
1261 const nsStyleDisplay* display = StyleDisplay();
1262 return GetTransformValue(display->mTransform);
1265 /* static */
1266 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::MatrixToCSSValue(
1267 const mozilla::gfx::Matrix4x4& matrix) {
1268 bool is3D = !matrix.Is2D();
1270 nsAutoString resultString(NS_LITERAL_STRING("matrix"));
1271 if (is3D) {
1272 resultString.AppendLiteral("3d");
1275 resultString.Append('(');
1276 resultString.AppendFloat(matrix._11);
1277 resultString.AppendLiteral(", ");
1278 resultString.AppendFloat(matrix._12);
1279 resultString.AppendLiteral(", ");
1280 if (is3D) {
1281 resultString.AppendFloat(matrix._13);
1282 resultString.AppendLiteral(", ");
1283 resultString.AppendFloat(matrix._14);
1284 resultString.AppendLiteral(", ");
1286 resultString.AppendFloat(matrix._21);
1287 resultString.AppendLiteral(", ");
1288 resultString.AppendFloat(matrix._22);
1289 resultString.AppendLiteral(", ");
1290 if (is3D) {
1291 resultString.AppendFloat(matrix._23);
1292 resultString.AppendLiteral(", ");
1293 resultString.AppendFloat(matrix._24);
1294 resultString.AppendLiteral(", ");
1295 resultString.AppendFloat(matrix._31);
1296 resultString.AppendLiteral(", ");
1297 resultString.AppendFloat(matrix._32);
1298 resultString.AppendLiteral(", ");
1299 resultString.AppendFloat(matrix._33);
1300 resultString.AppendLiteral(", ");
1301 resultString.AppendFloat(matrix._34);
1302 resultString.AppendLiteral(", ");
1304 resultString.AppendFloat(matrix._41);
1305 resultString.AppendLiteral(", ");
1306 resultString.AppendFloat(matrix._42);
1307 if (is3D) {
1308 resultString.AppendLiteral(", ");
1309 resultString.AppendFloat(matrix._43);
1310 resultString.AppendLiteral(", ");
1311 resultString.AppendFloat(matrix._44);
1313 resultString.Append(')');
1315 /* Create a value to hold our result. */
1316 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1318 val->SetString(resultString);
1319 return val.forget();
1322 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetOsxFontSmoothing() {
1323 if (nsContentUtils::ShouldResistFingerprinting(
1324 mPresShell->GetPresContext()->GetDocShell())) {
1325 return nullptr;
1328 nsAutoString result;
1329 mComputedStyle->GetComputedPropertyValue(eCSSProperty__moz_osx_font_smoothing,
1330 result);
1331 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1332 val->SetString(result);
1333 return val.forget();
1336 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetImageLayerPosition(
1337 const nsStyleImageLayers& aLayers) {
1338 if (aLayers.mPositionXCount != aLayers.mPositionYCount) {
1339 // No value to return. We can't express this combination of
1340 // values as a shorthand.
1341 return nullptr;
1344 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
1345 for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) {
1346 RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
1348 SetValueToPosition(aLayers.mLayers[i].mPosition, itemList);
1349 valueList->AppendCSSValue(itemList.forget());
1352 return valueList.forget();
1355 void nsComputedDOMStyle::SetValueToPosition(const Position& aPosition,
1356 nsDOMCSSValueList* aValueList) {
1357 RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
1358 SetValueToLengthPercentage(valX, aPosition.horizontal, false);
1359 aValueList->AppendCSSValue(valX.forget());
1361 RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
1362 SetValueToLengthPercentage(valY, aPosition.vertical, false);
1363 aValueList->AppendCSSValue(valY.forget());
1366 void nsComputedDOMStyle::SetValueToURLValue(const StyleComputedUrl* aURL,
1367 nsROCSSPrimitiveValue* aValue) {
1368 if (!aURL) {
1369 aValue->SetString("none");
1370 return;
1373 // If we have a usable nsIURI in the URLValue, and the url() wasn't
1374 // a fragment-only URL, serialize the nsIURI.
1375 if (!aURL->IsLocalRef()) {
1376 if (nsIURI* uri = aURL->GetURI()) {
1377 aValue->SetURI(uri);
1378 return;
1382 // Otherwise, serialize the specified URL value.
1383 NS_ConvertUTF8toUTF16 source(aURL->SpecifiedSerialization());
1384 nsAutoString url;
1385 url.AppendLiteral(u"url(");
1386 nsStyleUtil::AppendEscapedCSSString(source, url, '"');
1387 url.Append(')');
1388 aValue->SetString(url);
1391 enum class Brackets { No, Yes };
1393 static void AppendGridLineNames(nsAString& 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 nsStyleUtil::AppendEscapedCSSIdent(
1408 nsDependentAtomString(aLineNames[i].AsAtom()), aResult);
1409 if (++i == numLines) {
1410 break;
1412 aResult.Append(' ');
1414 if (aBrackets == Brackets::Yes) {
1415 aResult.Append(']');
1419 static void AppendGridLineNames(nsDOMCSSValueList* aValueList,
1420 Span<const StyleCustomIdent> aLineNames,
1421 bool aSuppressEmptyList = true) {
1422 if (aLineNames.IsEmpty() && aSuppressEmptyList) {
1423 return;
1425 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1426 nsAutoString lineNamesString;
1427 AppendGridLineNames(lineNamesString, aLineNames, Brackets::Yes);
1428 val->SetString(lineNamesString);
1429 aValueList->AppendCSSValue(val.forget());
1432 static void AppendGridLineNames(nsDOMCSSValueList* aValueList,
1433 Span<const StyleCustomIdent> aLineNames1,
1434 Span<const StyleCustomIdent> aLineNames2) {
1435 if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) {
1436 return;
1438 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1439 nsAutoString lineNamesString;
1440 lineNamesString.Assign('[');
1441 if (!aLineNames1.IsEmpty()) {
1442 AppendGridLineNames(lineNamesString, aLineNames1, Brackets::No);
1444 if (!aLineNames2.IsEmpty()) {
1445 if (!aLineNames1.IsEmpty()) {
1446 lineNamesString.Append(' ');
1448 AppendGridLineNames(lineNamesString, aLineNames2, Brackets::No);
1450 lineNamesString.Append(']');
1451 val->SetString(lineNamesString);
1452 aValueList->AppendCSSValue(val.forget());
1455 void nsComputedDOMStyle::SetValueToTrackBreadth(
1456 nsROCSSPrimitiveValue* aValue, const StyleTrackBreadth& aBreadth) {
1457 using Tag = StyleTrackBreadth::Tag;
1458 switch (aBreadth.tag) {
1459 case Tag::MinContent:
1460 return aValue->SetString("min-content");
1461 case Tag::MaxContent:
1462 return aValue->SetString("max-content");
1463 case Tag::Auto:
1464 return aValue->SetString("auto");
1465 case Tag::Breadth:
1466 return SetValueToLengthPercentage(aValue, aBreadth.AsBreadth(), true);
1467 case Tag::Fr: {
1468 nsAutoString tmpStr;
1469 nsStyleUtil::AppendCSSNumber(aBreadth.AsFr(), tmpStr);
1470 tmpStr.AppendLiteral("fr");
1471 return aValue->SetString(tmpStr);
1473 default:
1474 MOZ_ASSERT_UNREACHABLE("Unknown breadth value");
1475 return;
1479 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackBreadth(
1480 const StyleTrackBreadth& aBreadth) {
1481 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1482 SetValueToTrackBreadth(val, aBreadth);
1483 return val.forget();
1486 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackSize(
1487 const StyleTrackSize& aTrackSize) {
1488 if (aTrackSize.IsFitContent()) {
1489 // A fit-content() function.
1490 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1491 nsAutoString argumentStr, fitContentStr;
1492 fitContentStr.AppendLiteral("fit-content(");
1493 MOZ_ASSERT(aTrackSize.AsFitContent().IsBreadth(),
1494 "unexpected unit for fit-content() argument value");
1495 SetValueToLengthPercentage(val, aTrackSize.AsFitContent().AsBreadth(),
1496 true);
1497 val->GetCssText(argumentStr, IgnoreErrors());
1498 fitContentStr.Append(argumentStr);
1499 fitContentStr.Append(char16_t(')'));
1500 val->SetString(fitContentStr);
1501 return val.forget();
1504 if (aTrackSize.IsBreadth()) {
1505 return GetGridTrackBreadth(aTrackSize.AsBreadth());
1508 MOZ_ASSERT(aTrackSize.IsMinmax());
1509 auto& min = aTrackSize.AsMinmax()._0;
1510 auto& max = aTrackSize.AsMinmax()._1;
1511 if (min == max) {
1512 return GetGridTrackBreadth(min);
1515 // minmax(auto, <flex>) is equivalent to (and is our internal representation
1516 // of) <flex>, and both compute to <flex>
1517 if (min.IsAuto() && max.IsFr()) {
1518 return GetGridTrackBreadth(max);
1521 nsAutoString argumentStr, minmaxStr;
1522 minmaxStr.AppendLiteral("minmax(");
1525 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(min);
1526 argValue->GetCssText(argumentStr, IgnoreErrors());
1527 minmaxStr.Append(argumentStr);
1528 argumentStr.Truncate();
1531 minmaxStr.AppendLiteral(", ");
1534 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(max);
1535 argValue->GetCssText(argumentStr, IgnoreErrors());
1536 minmaxStr.Append(argumentStr);
1539 minmaxStr.Append(char16_t(')'));
1540 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1541 val->SetString(minmaxStr);
1542 return val.forget();
1545 already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows(
1546 const StyleGridTemplateComponent& aTrackList,
1547 const ComputedGridTrackInfo& aTrackInfo) {
1548 if (aTrackInfo.mIsMasonry) {
1549 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1550 val->SetString("masonry");
1551 return val.forget();
1554 if (aTrackInfo.mIsSubgrid) {
1555 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1556 RefPtr<nsROCSSPrimitiveValue> subgridKeyword = new nsROCSSPrimitiveValue;
1557 subgridKeyword->SetString("subgrid");
1558 valueList->AppendCSSValue(subgridKeyword.forget());
1559 for (const auto& lineNames : aTrackInfo.mResolvedLineNames) {
1560 AppendGridLineNames(valueList, lineNames, /*aSuppressEmptyList*/ false);
1562 uint32_t line = aTrackInfo.mResolvedLineNames.Length();
1563 uint32_t lastLine = aTrackInfo.mNumExplicitTracks + 1;
1564 const Span<const StyleCustomIdent> empty;
1565 for (; line < lastLine; ++line) {
1566 AppendGridLineNames(valueList, empty, /*aSuppressEmptyList*/ false);
1568 return valueList.forget();
1571 const bool serializeImplicit =
1572 StaticPrefs::layout_css_serialize_grid_implicit_tracks();
1574 const nsTArray<nscoord>& trackSizes = aTrackInfo.mSizes;
1575 const uint32_t numExplicitTracks = aTrackInfo.mNumExplicitTracks;
1576 const uint32_t numLeadingImplicitTracks =
1577 aTrackInfo.mNumLeadingImplicitTracks;
1578 uint32_t numSizes = trackSizes.Length();
1579 MOZ_ASSERT(numSizes >= numLeadingImplicitTracks + numExplicitTracks);
1581 const bool hasTracksToSerialize =
1582 serializeImplicit ? !!numSizes : !!numExplicitTracks;
1583 const bool hasRepeatAuto = aTrackList.HasRepeatAuto();
1584 if (!hasTracksToSerialize && !hasRepeatAuto) {
1585 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1586 val->SetString("none");
1587 return val.forget();
1590 // We've done layout on the grid and have resolved the sizes of its tracks,
1591 // so we'll return those sizes here. The grid spec says we MAY use
1592 // repeat(<positive-integer>, Npx) here for consecutive tracks with the same
1593 // size, but that doesn't seem worth doing since even for repeat(auto-*)
1594 // the resolved size might differ for the repeated tracks.
1595 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1597 // Add any leading implicit tracks.
1598 if (serializeImplicit) {
1599 for (uint32_t i = 0; i < numLeadingImplicitTracks; ++i) {
1600 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1601 val->SetAppUnits(trackSizes[i]);
1602 valueList->AppendCSSValue(val.forget());
1606 if (hasRepeatAuto) {
1607 const auto* const autoRepeatValue = aTrackList.GetRepeatAutoValue();
1608 const auto repeatLineNames = autoRepeatValue->line_names.AsSpan();
1609 MOZ_ASSERT(repeatLineNames.Length() >= 2);
1610 // Number of tracks inside the repeat, not including any repetitions.
1611 const uint32_t numRepeatTracks = autoRepeatValue->track_sizes.len;
1612 MOZ_ASSERT(repeatLineNames.Length() == numRepeatTracks + 1);
1613 // The total of all tracks in all repetitions of the repeat.
1614 const uint32_t totalNumRepeatTracks =
1615 aTrackInfo.mRemovedRepeatTracks.Length();
1616 const uint32_t repeatStart = aTrackInfo.mRepeatFirstTrack;
1617 // We need to skip over any track sizes which were resolved to 0 by
1618 // collapsed tracks. Keep track of the iteration separately.
1619 const auto explicitTrackSizeBegin =
1620 trackSizes.cbegin() + numLeadingImplicitTracks;
1621 const auto explicitTrackSizeEnd =
1622 explicitTrackSizeBegin + numExplicitTracks;
1623 auto trackSizeIter = explicitTrackSizeBegin;
1624 // Write any leading explicit tracks before the repeat.
1625 for (uint32_t i = 0; i < repeatStart; i++) {
1626 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]);
1627 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1628 val->SetAppUnits(*trackSizeIter++);
1629 valueList->AppendCSSValue(val.forget());
1631 auto lineNameIter = aTrackInfo.mResolvedLineNames.cbegin() + repeatStart;
1632 // Write the track names at the start of the repeat, including the names
1633 // at the end of the last non-repeat track. Unlike all later repeat line
1634 // name lists, this one needs the resolved line name which includes both
1635 // the last non-repeat line names and the leading repeat line names.
1636 AppendGridLineNames(valueList, *lineNameIter++);
1638 // Write out the first repeat value, checking for size zero (removed
1639 // track).
1640 const nscoord firstRepeatTrackSize =
1641 (!aTrackInfo.mRemovedRepeatTracks[0]) ? *trackSizeIter++ : 0;
1642 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1643 val->SetAppUnits(firstRepeatTrackSize);
1644 valueList->AppendCSSValue(val.forget());
1646 // Write the line names and track sizes inside the repeat, checking for
1647 // removed tracks (size 0).
1648 for (uint32_t i = 1; i < totalNumRepeatTracks; i++) {
1649 const uint32_t repeatIndex = i % numRepeatTracks;
1650 // If we are rolling over from one repetition to the next, include track
1651 // names from both the end of the previous repeat and the start of the
1652 // next.
1653 if (repeatIndex == 0) {
1654 AppendGridLineNames(valueList,
1655 repeatLineNames[numRepeatTracks].AsSpan(),
1656 repeatLineNames[0].AsSpan());
1657 } else {
1658 AppendGridLineNames(valueList, repeatLineNames[repeatIndex].AsSpan());
1660 MOZ_ASSERT(aTrackInfo.mRemovedRepeatTracks[i] ||
1661 trackSizeIter != explicitTrackSizeEnd);
1662 const nscoord repeatTrackSize =
1663 (!aTrackInfo.mRemovedRepeatTracks[i]) ? *trackSizeIter++ : 0;
1664 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1665 val->SetAppUnits(repeatTrackSize);
1666 valueList->AppendCSSValue(val.forget());
1668 // The resolved line names include a single repetition of the auto-repeat
1669 // line names. Skip over those.
1670 lineNameIter += numRepeatTracks - 1;
1671 // Write out any more tracks after the repeat.
1672 while (trackSizeIter != explicitTrackSizeEnd) {
1673 AppendGridLineNames(valueList, *lineNameIter++);
1674 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1675 val->SetAppUnits(*trackSizeIter++);
1676 valueList->AppendCSSValue(val.forget());
1678 // Write the final trailing line name.
1679 AppendGridLineNames(valueList, *lineNameIter++);
1680 } else if (numExplicitTracks > 0) {
1681 // If there are explicit tracks but no repeat tracks, just serialize those.
1682 for (uint32_t i = 0;; i++) {
1683 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]);
1684 if (i == numExplicitTracks) {
1685 break;
1687 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1688 val->SetAppUnits(trackSizes[i + numLeadingImplicitTracks]);
1689 valueList->AppendCSSValue(val.forget());
1692 // Add any trailing implicit tracks.
1693 if (serializeImplicit) {
1694 for (uint32_t i = numLeadingImplicitTracks + numExplicitTracks;
1695 i < numSizes; ++i) {
1696 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1697 val->SetAppUnits(trackSizes[i]);
1698 valueList->AppendCSSValue(val.forget());
1702 return valueList.forget();
1705 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateColumns() {
1706 nsGridContainerFrame* gridFrame =
1707 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
1708 if (!gridFrame) {
1709 // The element doesn't have a box - return the computed value.
1710 // https://drafts.csswg.org/css-grid/#resolved-track-list
1711 nsAutoString string;
1712 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_columns,
1713 string);
1714 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1715 value->SetString(string);
1716 return value.forget();
1719 // GetGridFrameWithComputedInfo() above ensures that this returns non-null:
1720 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateColumns();
1721 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns,
1722 *info);
1725 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateRows() {
1726 nsGridContainerFrame* gridFrame =
1727 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
1728 if (!gridFrame) {
1729 // The element doesn't have a box - return the computed value.
1730 // https://drafts.csswg.org/css-grid/#resolved-track-list
1731 nsAutoString string;
1732 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_rows,
1733 string);
1734 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1735 value->SetString(string);
1736 return value.forget();
1739 // GetGridFrameWithComputedInfo() above ensures that this returns non-null:
1740 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateRows();
1741 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, *info);
1744 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingTop() {
1745 return GetPaddingWidthFor(eSideTop);
1748 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingBottom() {
1749 return GetPaddingWidthFor(eSideBottom);
1752 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingLeft() {
1753 return GetPaddingWidthFor(eSideLeft);
1756 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingRight() {
1757 return GetPaddingWidthFor(eSideRight);
1760 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderSpacing() {
1761 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1763 RefPtr<nsROCSSPrimitiveValue> xSpacing = new nsROCSSPrimitiveValue;
1764 RefPtr<nsROCSSPrimitiveValue> ySpacing = new nsROCSSPrimitiveValue;
1766 const nsStyleTableBorder* border = StyleTableBorder();
1767 xSpacing->SetAppUnits(border->mBorderSpacingCol);
1768 ySpacing->SetAppUnits(border->mBorderSpacingRow);
1770 valueList->AppendCSSValue(xSpacing.forget());
1771 valueList->AppendCSSValue(ySpacing.forget());
1773 return valueList.forget();
1776 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderTopWidth() {
1777 return GetBorderWidthFor(eSideTop);
1780 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderBottomWidth() {
1781 return GetBorderWidthFor(eSideBottom);
1784 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderLeftWidth() {
1785 return GetBorderWidthFor(eSideLeft);
1788 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderRightWidth() {
1789 return GetBorderWidthFor(eSideRight);
1792 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginTopWidth() {
1793 return GetMarginWidthFor(eSideTop);
1796 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginBottomWidth() {
1797 return GetMarginWidthFor(eSideBottom);
1800 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginLeftWidth() {
1801 return GetMarginWidthFor(eSideLeft);
1804 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginRightWidth() {
1805 return GetMarginWidthFor(eSideRight);
1808 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetLineHeight() {
1809 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1812 nscoord lineHeight;
1813 if (GetLineHeightCoord(lineHeight)) {
1814 val->SetAppUnits(lineHeight);
1815 return val.forget();
1819 auto& lh = StyleText()->mLineHeight;
1820 if (lh.IsLength()) {
1821 val->SetAppUnits(lh.AsLength().ToAppUnits());
1822 } else if (lh.IsNumber()) {
1823 val->SetNumber(lh.AsNumber());
1824 } else if (lh.IsMozBlockHeight()) {
1825 val->SetString("-moz-block-height");
1826 } else {
1827 MOZ_ASSERT(lh.IsNormal());
1828 val->SetString("normal");
1830 return val.forget();
1833 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTextDecoration() {
1834 auto getPropertyValue = [&](nsCSSPropertyID aID) {
1835 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1836 nsAutoString string;
1837 mComputedStyle->GetComputedPropertyValue(aID, string);
1838 value->SetString(string);
1839 return value.forget();
1842 const nsStyleTextReset* textReset = StyleTextReset();
1843 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1845 if (textReset->mTextDecorationLine != StyleTextDecorationLine::NONE) {
1846 valueList->AppendCSSValue(
1847 getPropertyValue(eCSSProperty_text_decoration_line));
1850 if (textReset->mTextDecorationStyle != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
1851 valueList->AppendCSSValue(
1852 getPropertyValue(eCSSProperty_text_decoration_style));
1855 // The resolved color shouldn't be currentColor, so we always serialize it.
1856 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1857 SetValueFromComplexColor(val, StyleTextReset()->mTextDecorationColor);
1858 valueList->AppendCSSValue(val.forget());
1860 if (!textReset->mTextDecorationThickness.IsAuto()) {
1861 valueList->AppendCSSValue(
1862 getPropertyValue(eCSSProperty_text_decoration_thickness));
1865 return valueList.forget();
1868 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetHeight() {
1869 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1871 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) {
1872 AssertFlushedPendingReflows();
1873 nsMargin adjustedValues = GetAdjustedValuesForBoxSizing();
1874 val->SetAppUnits(mInnerFrame->GetContentRect().height +
1875 adjustedValues.TopBottom());
1876 } else {
1877 SetValueToSize(val, StylePosition()->mHeight);
1880 return val.forget();
1883 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetWidth() {
1884 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1886 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) {
1887 AssertFlushedPendingReflows();
1888 nsMargin adjustedValues = GetAdjustedValuesForBoxSizing();
1889 val->SetAppUnits(mInnerFrame->GetContentRect().width +
1890 adjustedValues.LeftRight());
1891 } else {
1892 SetValueToSize(val, StylePosition()->mWidth);
1895 return val.forget();
1898 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxHeight() {
1899 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1900 SetValueToMaxSize(val, StylePosition()->mMaxHeight);
1901 return val.forget();
1904 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxWidth() {
1905 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1906 SetValueToMaxSize(val, StylePosition()->mMaxWidth);
1907 return val.forget();
1911 * This function indicates whether we should return "auto" as the
1912 * getComputedStyle() result for the (default) "min-width: auto" and
1913 * "min-height: auto" CSS values.
1915 * As of this writing, the CSS Sizing draft spec says this "auto" value
1916 * *always* computes to itself. However, for now, we only make it compute to
1917 * itself for grid and flex items (the containers where "auto" has special
1918 * significance), because those are the only areas where the CSSWG has actually
1919 * resolved on this "computes-to-itself" behavior. For elements in other sorts
1920 * of containers, this function returns false, which will make us resolve
1921 * "auto" to 0.
1923 bool nsComputedDOMStyle::ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis) {
1924 return mOuterFrame && mOuterFrame->IsFlexOrGridItem();
1927 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinHeight() {
1928 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1929 StyleSize minHeight = StylePosition()->mMinHeight;
1931 if (minHeight.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisVertical)) {
1932 minHeight = StyleSize::LengthPercentage(LengthPercentage::Zero());
1935 SetValueToSize(val, minHeight);
1936 return val.forget();
1939 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinWidth() {
1940 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1942 StyleSize minWidth = StylePosition()->mMinWidth;
1944 if (minWidth.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisHorizontal)) {
1945 minWidth = StyleSize::LengthPercentage(LengthPercentage::Zero());
1948 SetValueToSize(val, minWidth);
1949 return val.forget();
1952 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetLeft() {
1953 return GetOffsetWidthFor(eSideLeft);
1956 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetRight() {
1957 return GetOffsetWidthFor(eSideRight);
1960 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTop() {
1961 return GetOffsetWidthFor(eSideTop);
1964 already_AddRefed<CSSValue> nsComputedDOMStyle::GetOffsetWidthFor(
1965 mozilla::Side aSide) {
1966 const nsStyleDisplay* display = StyleDisplay();
1968 mozilla::StylePositionProperty position = display->mPosition;
1969 if (!mOuterFrame) {
1970 // GetNonStaticPositionOffset or GetAbsoluteOffset don't handle elements
1971 // without frames in any sensible way. GetStaticOffset, however, is perfect
1972 // for that case.
1973 position = StylePositionProperty::Static;
1976 switch (position) {
1977 case StylePositionProperty::Static:
1978 return GetStaticOffset(aSide);
1979 case StylePositionProperty::Sticky:
1980 return GetNonStaticPositionOffset(
1981 aSide, false, &nsComputedDOMStyle::GetScrollFrameContentWidth,
1982 &nsComputedDOMStyle::GetScrollFrameContentHeight);
1983 case StylePositionProperty::Absolute:
1984 case StylePositionProperty::Fixed:
1985 return GetAbsoluteOffset(aSide);
1986 case StylePositionProperty::Relative:
1987 return GetNonStaticPositionOffset(
1988 aSide, true, &nsComputedDOMStyle::GetCBContentWidth,
1989 &nsComputedDOMStyle::GetCBContentHeight);
1990 default:
1991 MOZ_ASSERT_UNREACHABLE("Invalid position");
1992 return nullptr;
1996 static_assert(eSideTop == 0 && eSideRight == 1 && eSideBottom == 2 &&
1997 eSideLeft == 3,
1998 "box side constants not as expected for NS_OPPOSITE_SIDE");
1999 #define NS_OPPOSITE_SIDE(s_) mozilla::Side(((s_) + 2) & 3)
2001 already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset(
2002 mozilla::Side aSide, bool aResolveAuto, PercentageBaseGetter aWidthGetter,
2003 PercentageBaseGetter aHeightGetter) {
2004 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2006 const nsStylePosition* positionData = StylePosition();
2007 int32_t sign = 1;
2008 LengthPercentageOrAuto coord = positionData->mOffset.Get(aSide);
2010 if (coord.IsAuto()) {
2011 if (!aResolveAuto) {
2012 val->SetString("auto");
2013 return val.forget();
2015 coord = positionData->mOffset.Get(NS_OPPOSITE_SIDE(aSide));
2016 sign = -1;
2019 PercentageBaseGetter baseGetter = (aSide == eSideLeft || aSide == eSideRight)
2020 ? aWidthGetter
2021 : aHeightGetter;
2023 val->SetAppUnits(sign * StyleCoordToNSCoord(coord, baseGetter, 0, false));
2024 return val.forget();
2027 already_AddRefed<CSSValue> nsComputedDOMStyle::GetAbsoluteOffset(
2028 mozilla::Side aSide) {
2029 const auto& offset = StylePosition()->mOffset;
2030 const auto& coord = offset.Get(aSide);
2031 const auto& oppositeCoord = offset.Get(NS_OPPOSITE_SIDE(aSide));
2033 if (coord.IsAuto() || oppositeCoord.IsAuto()) {
2034 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2035 val->SetAppUnits(GetUsedAbsoluteOffset(aSide));
2036 return val.forget();
2039 return GetNonStaticPositionOffset(
2040 aSide, false, &nsComputedDOMStyle::GetCBPaddingRectWidth,
2041 &nsComputedDOMStyle::GetCBPaddingRectHeight);
2044 nscoord nsComputedDOMStyle::GetUsedAbsoluteOffset(mozilla::Side aSide) {
2045 MOZ_ASSERT(mOuterFrame, "need a frame, so we can call GetContainingBlock()");
2047 nsIFrame* container = mOuterFrame->GetContainingBlock();
2048 nsMargin margin = mOuterFrame->GetUsedMargin();
2049 nsMargin border = container->GetUsedBorder();
2050 nsMargin scrollbarSizes(0, 0, 0, 0);
2051 nsRect rect = mOuterFrame->GetRect();
2052 nsRect containerRect = container->GetRect();
2054 if (container->IsViewportFrame()) {
2055 // For absolutely positioned frames scrollbars are taken into
2056 // account by virtue of getting a containing block that does
2057 // _not_ include the scrollbars. For fixed positioned frames,
2058 // the containing block is the viewport, which _does_ include
2059 // scrollbars. We have to do some extra work.
2060 // the first child in the default frame list is what we want
2061 nsIFrame* scrollingChild = container->PrincipalChildList().FirstChild();
2062 nsIScrollableFrame* scrollFrame = do_QueryFrame(scrollingChild);
2063 if (scrollFrame) {
2064 scrollbarSizes = scrollFrame->GetActualScrollbarSizes();
2067 // The viewport size might have been expanded by the visual viewport or
2068 // the minimum-scale size.
2069 const ViewportFrame* viewportFrame = do_QueryFrame(container);
2070 MOZ_ASSERT(viewportFrame);
2071 containerRect.SizeTo(
2072 viewportFrame->AdjustViewportSizeForFixedPosition(containerRect));
2073 } else if (container->IsGridContainerFrame() &&
2074 (mOuterFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
2075 containerRect = nsGridContainerFrame::GridItemCB(mOuterFrame);
2076 rect.MoveBy(-containerRect.x, -containerRect.y);
2079 nscoord offset = 0;
2080 switch (aSide) {
2081 case eSideTop:
2082 offset = rect.y - margin.top - border.top - scrollbarSizes.top;
2084 break;
2085 case eSideRight:
2086 offset = containerRect.width - rect.width - rect.x - margin.right -
2087 border.right - scrollbarSizes.right;
2089 break;
2090 case eSideBottom:
2091 offset = containerRect.height - rect.height - rect.y - margin.bottom -
2092 border.bottom - scrollbarSizes.bottom;
2094 break;
2095 case eSideLeft:
2096 offset = rect.x - margin.left - border.left - scrollbarSizes.left;
2098 break;
2099 default:
2100 NS_ERROR("Invalid side");
2101 break;
2104 return offset;
2107 already_AddRefed<CSSValue> nsComputedDOMStyle::GetStaticOffset(
2108 mozilla::Side aSide) {
2109 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2110 SetValueToLengthPercentageOrAuto(val, StylePosition()->mOffset.Get(aSide),
2111 false);
2112 return val.forget();
2115 already_AddRefed<CSSValue> nsComputedDOMStyle::GetPaddingWidthFor(
2116 mozilla::Side aSide) {
2117 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2119 auto& padding = StylePadding()->mPadding.Get(aSide);
2120 if (!mInnerFrame || !PaddingNeedsUsedValue(padding, *mComputedStyle)) {
2121 SetValueToLengthPercentage(val, padding, true);
2122 } else {
2123 AssertFlushedPendingReflows();
2124 val->SetAppUnits(mInnerFrame->GetUsedPadding().Side(aSide));
2127 return val.forget();
2130 bool nsComputedDOMStyle::GetLineHeightCoord(nscoord& aCoord) {
2131 nscoord blockHeight = NS_UNCONSTRAINEDSIZE;
2132 const auto& lh = StyleText()->mLineHeight;
2134 if (lh.IsNormal() &&
2135 StaticPrefs::layout_css_line_height_normal_as_resolved_value_enabled()) {
2136 return false;
2139 if (lh.IsMozBlockHeight()) {
2140 AssertFlushedPendingReflows();
2142 if (!mInnerFrame) {
2143 return false;
2146 if (nsLayoutUtils::IsNonWrapperBlock(mInnerFrame)) {
2147 blockHeight = mInnerFrame->GetContentRect().height;
2148 } else {
2149 GetCBContentHeight(blockHeight);
2153 nsPresContext* presContext = mPresShell->GetPresContext();
2155 // lie about font size inflation since we lie about font size (since
2156 // the inflation only applies to text)
2157 aCoord = ReflowInput::CalcLineHeight(mElement, mComputedStyle, presContext,
2158 blockHeight, 1.0f);
2160 // CalcLineHeight uses font->mFont.size, but we want to use
2161 // font->mSize as the font size. Adjust for that. Also adjust for
2162 // the text zoom, if any.
2163 const nsStyleFont* font = StyleFont();
2164 float fCoord = float(aCoord);
2165 if (font->mAllowZoomAndMinSize) {
2166 fCoord /= presContext->EffectiveTextZoom();
2168 if (font->mFont.size != font->mSize) {
2169 fCoord = fCoord * (float(font->mSize) / float(font->mFont.size));
2171 aCoord = NSToCoordRound(fCoord);
2173 return true;
2176 already_AddRefed<CSSValue> nsComputedDOMStyle::GetBorderWidthFor(
2177 mozilla::Side aSide) {
2178 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2180 nscoord width;
2181 if (mInnerFrame && mComputedStyle->StyleDisplay()->HasAppearance()) {
2182 AssertFlushedPendingReflows();
2183 width = mInnerFrame->GetUsedBorder().Side(aSide);
2184 } else {
2185 width = StyleBorder()->GetComputedBorderWidth(aSide);
2187 val->SetAppUnits(width);
2189 return val.forget();
2192 already_AddRefed<CSSValue> nsComputedDOMStyle::GetMarginWidthFor(
2193 mozilla::Side aSide) {
2194 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2196 auto& margin = StyleMargin()->mMargin.Get(aSide);
2197 if (!mInnerFrame || margin.ConvertsToLength()) {
2198 SetValueToLengthPercentageOrAuto(val, margin, false);
2199 } else {
2200 AssertFlushedPendingReflows();
2202 // For tables, GetUsedMargin always returns an empty margin, so we
2203 // should read the margin from the table wrapper frame instead.
2204 val->SetAppUnits(mOuterFrame->GetUsedMargin().Side(aSide));
2205 NS_ASSERTION(mOuterFrame == mInnerFrame ||
2206 mInnerFrame->GetUsedMargin() == nsMargin(0, 0, 0, 0),
2207 "Inner tables must have zero margins");
2210 return val.forget();
2213 void nsComputedDOMStyle::SetValueToExtremumLength(nsROCSSPrimitiveValue* aValue,
2214 StyleExtremumLength aSize) {
2215 switch (aSize) {
2216 case StyleExtremumLength::MaxContent:
2217 return aValue->SetString("max-content");
2218 case StyleExtremumLength::MinContent:
2219 return aValue->SetString("min-content");
2220 case StyleExtremumLength::MozAvailable:
2221 return aValue->SetString("-moz-available");
2222 case StyleExtremumLength::MozFitContent:
2223 return aValue->SetString("-moz-fit-content");
2225 MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
2228 void nsComputedDOMStyle::SetValueToSize(nsROCSSPrimitiveValue* aValue,
2229 const StyleSize& aSize) {
2230 if (aSize.IsAuto()) {
2231 return aValue->SetString("auto");
2233 if (aSize.IsExtremumLength()) {
2234 return SetValueToExtremumLength(aValue, aSize.AsExtremumLength());
2236 MOZ_ASSERT(aSize.IsLengthPercentage());
2237 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true);
2240 void nsComputedDOMStyle::SetValueToMaxSize(nsROCSSPrimitiveValue* aValue,
2241 const StyleMaxSize& aSize) {
2242 if (aSize.IsNone()) {
2243 return aValue->SetString("none");
2245 if (aSize.IsExtremumLength()) {
2246 return SetValueToExtremumLength(aValue, aSize.AsExtremumLength());
2248 MOZ_ASSERT(aSize.IsLengthPercentage());
2249 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true);
2252 void nsComputedDOMStyle::SetValueToLengthPercentageOrAuto(
2253 nsROCSSPrimitiveValue* aValue, const LengthPercentageOrAuto& aSize,
2254 bool aClampNegativeCalc) {
2255 if (aSize.IsAuto()) {
2256 return aValue->SetString("auto");
2258 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(),
2259 aClampNegativeCalc);
2262 void nsComputedDOMStyle::SetValueToLengthPercentage(
2263 nsROCSSPrimitiveValue* aValue, const mozilla::LengthPercentage& aLength,
2264 bool aClampNegativeCalc) {
2265 if (aLength.ConvertsToLength()) {
2266 nscoord result = aLength.ToLength();
2267 if (aClampNegativeCalc) {
2268 result = std::max(result, 0);
2270 return aValue->SetAppUnits(result);
2272 if (aLength.ConvertsToPercentage()) {
2273 float result = aLength.ToPercentage();
2274 if (aClampNegativeCalc) {
2275 result = std::max(result, 0.0f);
2277 return aValue->SetPercent(result);
2280 nsAutoString result;
2281 Servo_LengthPercentage_ToCss(&aLength, &result);
2282 aValue->SetString(result);
2285 nscoord nsComputedDOMStyle::StyleCoordToNSCoord(
2286 const LengthPercentage& aCoord, PercentageBaseGetter aPercentageBaseGetter,
2287 nscoord aDefaultValue, bool aClampNegativeCalc) {
2288 MOZ_ASSERT(aPercentageBaseGetter, "Must have a percentage base getter");
2289 if (aCoord.ConvertsToLength()) {
2290 return aCoord.ToLength();
2292 nscoord percentageBase;
2293 if ((this->*aPercentageBaseGetter)(percentageBase)) {
2294 nscoord result = aCoord.Resolve(percentageBase);
2295 if (aClampNegativeCalc && result < 0) {
2296 // It's expected that we can get a negative value here with calc().
2297 // We can also get a negative value with a percentage value if
2298 // percentageBase is negative; this isn't expected, but can happen
2299 // when large length values overflow.
2300 NS_WARNING_ASSERTION(percentageBase >= 0,
2301 "percentage base value overflowed to become "
2302 "negative for a property "
2303 "that disallows negative values");
2304 result = 0;
2306 return result;
2309 return aDefaultValue;
2312 bool nsComputedDOMStyle::GetCBContentWidth(nscoord& aWidth) {
2313 if (!mOuterFrame) {
2314 return false;
2317 AssertFlushedPendingReflows();
2319 aWidth = mOuterFrame->GetContainingBlock()->GetContentRect().width;
2320 return true;
2323 bool nsComputedDOMStyle::GetCBContentHeight(nscoord& aHeight) {
2324 if (!mOuterFrame) {
2325 return false;
2328 AssertFlushedPendingReflows();
2330 aHeight = mOuterFrame->GetContainingBlock()->GetContentRect().height;
2331 return true;
2334 bool nsComputedDOMStyle::GetCBPaddingRectWidth(nscoord& aWidth) {
2335 if (!mOuterFrame) {
2336 return false;
2339 AssertFlushedPendingReflows();
2341 aWidth = mOuterFrame->GetContainingBlock()->GetPaddingRect().width;
2342 return true;
2345 bool nsComputedDOMStyle::GetCBPaddingRectHeight(nscoord& aHeight) {
2346 if (!mOuterFrame) {
2347 return false;
2350 AssertFlushedPendingReflows();
2352 aHeight = mOuterFrame->GetContainingBlock()->GetPaddingRect().height;
2353 return true;
2356 bool nsComputedDOMStyle::GetScrollFrameContentWidth(nscoord& aWidth) {
2357 if (!mOuterFrame) {
2358 return false;
2361 AssertFlushedPendingReflows();
2363 nsIScrollableFrame* scrollableFrame =
2364 nsLayoutUtils::GetNearestScrollableFrame(
2365 mOuterFrame->GetParent(),
2366 nsLayoutUtils::SCROLLABLE_SAME_DOC |
2367 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
2369 if (!scrollableFrame) {
2370 return false;
2372 aWidth =
2373 scrollableFrame->GetScrolledFrame()->GetContentRectRelativeToSelf().width;
2374 return true;
2377 bool nsComputedDOMStyle::GetScrollFrameContentHeight(nscoord& aHeight) {
2378 if (!mOuterFrame) {
2379 return false;
2382 AssertFlushedPendingReflows();
2384 nsIScrollableFrame* scrollableFrame =
2385 nsLayoutUtils::GetNearestScrollableFrame(
2386 mOuterFrame->GetParent(),
2387 nsLayoutUtils::SCROLLABLE_SAME_DOC |
2388 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
2390 if (!scrollableFrame) {
2391 return false;
2393 aHeight = scrollableFrame->GetScrolledFrame()
2394 ->GetContentRectRelativeToSelf()
2395 .height;
2396 return true;
2399 bool nsComputedDOMStyle::GetFrameBorderRectWidth(nscoord& aWidth) {
2400 if (!mInnerFrame) {
2401 return false;
2404 AssertFlushedPendingReflows();
2406 aWidth = mInnerFrame->GetSize().width;
2407 return true;
2410 bool nsComputedDOMStyle::GetFrameBorderRectHeight(nscoord& aHeight) {
2411 if (!mInnerFrame) {
2412 return false;
2415 AssertFlushedPendingReflows();
2417 aHeight = mInnerFrame->GetSize().height;
2418 return true;
2421 /* If the property is "none", hand back "none" wrapped in a value.
2422 * Otherwise, compute the aggregate transform matrix and hands it back in a
2423 * "matrix" wrapper.
2425 already_AddRefed<CSSValue> nsComputedDOMStyle::GetTransformValue(
2426 const StyleTransform& aTransform) {
2427 /* If there are no transforms, then we should construct a single-element
2428 * entry and hand it back.
2430 if (aTransform.IsNone()) {
2431 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2432 val->SetString("none");
2433 return val.forget();
2436 /* Otherwise, we need to compute the current value of the transform matrix,
2437 * store it in a string, and hand it back to the caller.
2440 /* Use the inner frame for the reference box. If we don't have an inner
2441 * frame we use empty dimensions to allow us to continue (and percentage
2442 * values in the transform will simply give broken results).
2443 * TODO: There is no good way for us to represent the case where there's no
2444 * frame, which is problematic. The reason is that when we have percentage
2445 * transforms, there are a total of four stored matrix entries that influence
2446 * the transform based on the size of the element. However, this poses a
2447 * problem, because only two of these values can be explicitly referenced
2448 * using the named transforms. Until a real solution is found, we'll just
2449 * use this approach.
2451 nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame, nsRect());
2452 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
2453 aTransform, refBox, float(mozilla::AppUnitsPerCSSPixel()));
2455 return MatrixToCSSValue(matrix);
2458 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMask() {
2459 const nsStyleSVGReset* svg = StyleSVGReset();
2460 const nsStyleImageLayers::Layer& firstLayer = svg->mMask.mLayers[0];
2462 // Mask is now a shorthand, but it used to be a longhand, so that we
2463 // need to support computed style for the cases where it used to be
2464 // a longhand.
2465 if (svg->mMask.mImageCount > 1 ||
2466 firstLayer.mClip != StyleGeometryBox::BorderBox ||
2467 firstLayer.mOrigin != StyleGeometryBox::BorderBox ||
2468 firstLayer.mComposite != StyleMaskComposite::Add ||
2469 firstLayer.mMaskMode != StyleMaskMode::MatchSource ||
2470 firstLayer.mPosition != Position::FromPercentage(0.0f) ||
2471 !firstLayer.mRepeat.IsInitialValue() ||
2472 !firstLayer.mSize.IsInitialValue() ||
2473 !(firstLayer.mImage.IsNone() || firstLayer.mImage.IsUrl())) {
2474 return nullptr;
2477 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2479 SetValueToURLValue(firstLayer.mImage.GetImageRequestURLValue(), val);
2481 return val.forget();
2484 already_AddRefed<CSSValue> nsComputedDOMStyle::DummyGetter() {
2485 MOZ_CRASH("DummyGetter is not supposed to be invoked");
2488 static void MarkComputedStyleMapDirty(const char* aPref, void* aMap) {
2489 static_cast<ComputedStyleMap*>(aMap)->MarkDirty();
2492 void nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent) {
2493 NS_ASSERTION(mElement == aContent, "didn't we register mElement?");
2494 NS_ASSERTION(mResolvedComputedStyle,
2495 "should have only registered an observer when "
2496 "mResolvedComputedStyle is true");
2498 ClearComputedStyle();
2501 /* static */
2502 ComputedStyleMap* nsComputedDOMStyle::GetComputedStyleMap() {
2503 static ComputedStyleMap map{};
2504 return &map;
2507 static StaticAutoPtr<nsTArray<const char*>> gCallbackPrefs;
2509 /* static */
2510 void nsComputedDOMStyle::RegisterPrefChangeCallbacks() {
2511 // Note that this will register callbacks for all properties with prefs, not
2512 // just those that are implemented on computed style objects, as it's not
2513 // easy to grab specific property data from ServoCSSPropList.h based on the
2514 // entries iterated in nsComputedDOMStylePropertyList.h.
2516 AutoTArray<const char*, 64> prefs;
2517 for (const auto* p = nsCSSProps::kPropertyPrefTable;
2518 p->mPropID != eCSSProperty_UNKNOWN; p++) {
2519 // Many properties are controlled by the same preference, so de-duplicate
2520 // them before adding observers.
2522 // Note: This is done by pointer comparison, which works because the mPref
2523 // members are string literals from the same same translation unit, and are
2524 // therefore de-duplicated by the compiler. On the off chance that we wind
2525 // up with some duplicates with different pointers, though, it's not a bit
2526 // deal.
2527 if (!prefs.ContainsSorted(p->mPref)) {
2528 prefs.InsertElementSorted(p->mPref);
2531 prefs.AppendElement(nullptr);
2533 MOZ_ASSERT(!gCallbackPrefs);
2534 gCallbackPrefs = new nsTArray<const char*>(std::move(prefs));
2536 Preferences::RegisterCallbacks(MarkComputedStyleMapDirty,
2537 gCallbackPrefs->Elements(),
2538 GetComputedStyleMap());
2541 /* static */
2542 void nsComputedDOMStyle::UnregisterPrefChangeCallbacks() {
2543 if (!gCallbackPrefs) {
2544 return;
2547 Preferences::UnregisterCallbacks(MarkComputedStyleMapDirty,
2548 gCallbackPrefs->Elements(),
2549 GetComputedStyleMap());
2550 gCallbackPrefs = nullptr;