nsComputedDOMStyle.cpp (85217B)
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/. */ 6 7 /* DOM object returned from element.getComputedStyle() */ 8 9 #include "nsComputedDOMStyle.h" 10 11 #include <algorithm> 12 13 #include "AnchorPositioningUtils.h" 14 #include "NonCustomCSSPropertyId.h" 15 #include "mozilla/AppUnits.h" 16 #include "mozilla/ComputedStyle.h" 17 #include "mozilla/ComputedStyleInlines.h" 18 #include "mozilla/EffectSet.h" 19 #include "mozilla/FontPropertyTypes.h" 20 #include "mozilla/IntegerRange.h" 21 #include "mozilla/Preferences.h" 22 #include "mozilla/PresShell.h" 23 #include "mozilla/PresShellInlines.h" 24 #include "mozilla/ReflowInput.h" 25 #include "mozilla/RestyleManager.h" 26 #include "mozilla/ScopeExit.h" 27 #include "mozilla/ScrollContainerFrame.h" 28 #include "mozilla/ServoStyleSet.h" 29 #include "mozilla/StaticPrefs_layout.h" 30 #include "mozilla/StaticPtr.h" 31 #include "mozilla/ViewportFrame.h" 32 #include "mozilla/dom/Document.h" 33 #include "mozilla/dom/Element.h" 34 #include "mozilla/dom/ElementInlines.h" 35 #include "nsCSSProps.h" 36 #include "nsCSSPseudoElements.h" 37 #include "nsContentUtils.h" 38 #include "nsDOMCSSDeclaration.h" 39 #include "nsDOMCSSValueList.h" 40 #include "nsDisplayList.h" 41 #include "nsDocShell.h" 42 #include "nsError.h" 43 #include "nsFlexContainerFrame.h" 44 #include "nsGkAtoms.h" 45 #include "nsGridContainerFrame.h" 46 #include "nsIContent.h" 47 #include "nsIFrame.h" 48 #include "nsIFrameInlines.h" 49 #include "nsLayoutUtils.h" 50 #include "nsPresContext.h" 51 #include "nsPrintfCString.h" 52 #include "nsROCSSPrimitiveValue.h" 53 #include "nsStyleConsts.h" 54 #include "nsStyleStructInlines.h" 55 #include "nsStyleTransformMatrix.h" 56 #include "nsStyleUtil.h" 57 #include "nsWrapperCacheInlines.h" 58 #include "prtime.h" 59 60 using namespace mozilla; 61 using namespace mozilla::dom; 62 63 /* 64 * This is the implementation of the readonly CSSStyleDeclaration that is 65 * returned by the getComputedStyle() function. 66 */ 67 68 already_AddRefed<nsComputedDOMStyle> NS_NewComputedDOMStyle( 69 dom::Element* aElement, const nsAString& aPseudoElt, Document* aDocument, 70 nsComputedDOMStyle::StyleType aStyleType, mozilla::ErrorResult&) { 71 auto request = nsCSSPseudoElements::ParsePseudoElement( 72 aPseudoElt, CSSEnabledState::ForAllContent); 73 auto returnEmpty = nsComputedDOMStyle::AlwaysReturnEmptyStyle::No; 74 if (!request) { 75 if (!aPseudoElt.IsEmpty() && aPseudoElt.First() == u':') { 76 returnEmpty = nsComputedDOMStyle::AlwaysReturnEmptyStyle::Yes; 77 } 78 request.emplace(PseudoStyleRequest()); 79 } 80 return MakeAndAddRef<nsComputedDOMStyle>(aElement, std::move(*request), 81 aDocument, aStyleType, returnEmpty); 82 } 83 84 static nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited) { 85 return new nsDOMCSSValueList(aCommaDelimited); 86 } 87 88 // Whether aDocument needs to restyle for aElement 89 static bool ElementNeedsRestyle(Element* aElement, 90 const PseudoStyleRequest& aPseudo, 91 bool aMayNeedToFlushLayout) { 92 const Document* doc = aElement->GetComposedDoc(); 93 if (!doc) { 94 // If the element is out of the document we don't return styles for it, so 95 // nothing to do. 96 return false; 97 } 98 99 PresShell* presShell = doc->GetPresShell(); 100 if (!presShell) { 101 // If there's no pres-shell we'll just either mint a new style from our 102 // caller document, or return no styles, so nothing to do (unless our owner 103 // element needs to get restyled, which could cause us to gain a pres shell, 104 // but the caller checks that). 105 return false; 106 } 107 108 // Unfortunately we don't know if the sheet change affects mElement or not, so 109 // just assume it will and that we need to flush normally. 110 ServoStyleSet* styleSet = presShell->StyleSet(); 111 if (styleSet->StyleSheetsHaveChanged()) { 112 return true; 113 } 114 115 nsPresContext* presContext = presShell->GetPresContext(); 116 MOZ_ASSERT(presContext); 117 118 // Pending media query updates can definitely change style on the element. For 119 // example, if you change the zoom factor and then call getComputedStyle, you 120 // should be able to observe the style with the new media queries. 121 // 122 // TODO(emilio): Does this need to also check the user font set? (it affects 123 // ch / ex units). 124 if (presContext->HasPendingMediaQueryUpdates()) { 125 // So gotta flush. 126 return true; 127 } 128 129 // If the pseudo-element is animating, make sure to flush. 130 if (aElement->MayHaveAnimations() && !aPseudo.IsNotPseudo() && 131 AnimationUtils::IsSupportedPseudoForAnimations(aPseudo)) { 132 if (EffectSet::Get(aElement, aPseudo)) { 133 return true; 134 } 135 } 136 137 // For Servo, we need to process the restyle-hint-invalidations first, to 138 // expand LaterSiblings hint, so that we can look whether ancestors need 139 // restyling. 140 RestyleManager* restyleManager = presContext->RestyleManager(); 141 restyleManager->ProcessAllPendingAttributeAndStateInvalidations(); 142 143 if (!presContext->EffectCompositor()->HasPendingStyleUpdates() && 144 !doc->GetServoRestyleRoot()) { 145 return false; 146 } 147 148 // If there's a pseudo, we need to prefer that element, as the pseudo itself 149 // may have explicit restyles. 150 const Element* styledElement = aElement->GetPseudoElement(aPseudo); 151 // Try to skip the restyle otherwise. 152 return Servo_HasPendingRestyleAncestor( 153 styledElement ? styledElement : aElement, aMayNeedToFlushLayout); 154 } 155 156 /** 157 * An object that represents the ordered set of properties that are exposed on 158 * an nsComputedDOMStyle object and how their computed values can be obtained. 159 */ 160 struct ComputedStyleMap { 161 friend class nsComputedDOMStyle; 162 163 struct Entry { 164 // Create a pointer-to-member-function type. 165 using ComputeMethod = already_AddRefed<CSSValue> (nsComputedDOMStyle::*)(); 166 167 NonCustomCSSPropertyId mProperty; 168 169 // Whether the property can ever be exposed in getComputedStyle(). For 170 // example, @page descriptors implemented as CSS properties or other 171 // internal properties, would have this flag set to `false`. 172 bool mCanBeExposed = false; 173 174 ComputeMethod mGetter = nullptr; 175 176 bool IsEnumerable() const { 177 return IsEnabled() && !nsCSSProps::IsShorthand(mProperty); 178 } 179 180 bool IsEnabled() const { 181 return mCanBeExposed && 182 nsCSSProps::IsEnabled(mProperty, CSSEnabledState::ForAllContent); 183 } 184 }; 185 186 // This generated file includes definition of kEntries which is typed 187 // Entry[] and used below, so this #include has to be put here. 188 #include "nsComputedDOMStyleGenerated.inc" 189 190 /** 191 * Returns the number of properties that should be exposed on an 192 * nsComputedDOMStyle, ecxluding any disabled properties. 193 */ 194 uint32_t Length() { 195 Update(); 196 return mEnumerablePropertyCount; 197 } 198 199 /** 200 * Returns the property at the given index in the list of properties 201 * that should be exposed on an nsComputedDOMStyle, excluding any 202 * disabled properties. 203 */ 204 NonCustomCSSPropertyId PropertyAt(uint32_t aIndex) { 205 Update(); 206 return kEntries[EntryIndex(aIndex)].mProperty; 207 } 208 209 /** 210 * Searches for and returns the computed style map entry for the given 211 * property, or nullptr if the property is not exposed on nsComputedDOMStyle 212 * or is currently disabled. 213 */ 214 const Entry* FindEntryForProperty(NonCustomCSSPropertyId aPropId) { 215 if (size_t(aPropId) >= std::size(kEntryIndices)) { 216 MOZ_ASSERT(aPropId == eCSSProperty_UNKNOWN); 217 return nullptr; 218 } 219 MOZ_ASSERT(kEntryIndices[aPropId] < std::size(kEntries)); 220 const auto& entry = kEntries[kEntryIndices[aPropId]]; 221 if (!entry.IsEnabled()) { 222 return nullptr; 223 } 224 return &entry; 225 } 226 227 /** 228 * Records that mIndexMap needs updating, due to prefs changing that could 229 * affect the set of properties exposed on an nsComputedDOMStyle. 230 */ 231 void MarkDirty() { mEnumerablePropertyCount = 0; } 232 233 // The member variables are public so that we can use an initializer in 234 // nsComputedDOMStyle::GetComputedStyleMap. Use the member functions 235 // above to get information from this object. 236 237 /** 238 * The number of properties that should be exposed on an nsComputedDOMStyle. 239 * This will be less than eComputedStyleProperty_COUNT if some property 240 * prefs are disabled. A value of 0 indicates that it and mIndexMap are out 241 * of date. 242 */ 243 uint32_t mEnumerablePropertyCount = 0; 244 245 /** 246 * A map of indexes on the nsComputedDOMStyle object to indexes into kEntries. 247 */ 248 uint32_t mIndexMap[std::size(kEntries)]; 249 250 private: 251 /** 252 * Returns whether mEnumerablePropertyCount and mIndexMap are out of date. 253 */ 254 bool IsDirty() { return mEnumerablePropertyCount == 0; } 255 256 /** 257 * Updates mEnumerablePropertyCount and mIndexMap to take into account 258 * properties whose prefs are currently disabled. 259 */ 260 void Update(); 261 262 /** 263 * Maps an nsComputedDOMStyle indexed getter index to an index into kEntries. 264 */ 265 uint32_t EntryIndex(uint32_t aIndex) const { 266 MOZ_ASSERT(aIndex < mEnumerablePropertyCount); 267 return mIndexMap[aIndex]; 268 } 269 }; 270 271 constexpr ComputedStyleMap::Entry 272 ComputedStyleMap::kEntries[std::size(kEntries)]; 273 274 constexpr size_t ComputedStyleMap::kEntryIndices[std::size(kEntries)]; 275 276 void ComputedStyleMap::Update() { 277 if (!IsDirty()) { 278 return; 279 } 280 281 uint32_t index = 0; 282 for (uint32_t i = 0; i < std::size(kEntries); i++) { 283 if (kEntries[i].IsEnumerable()) { 284 mIndexMap[index++] = i; 285 } 286 } 287 mEnumerablePropertyCount = index; 288 } 289 290 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement, 291 PseudoStyleRequest&& aPseudo, 292 Document* aDocument, 293 StyleType aStyleType, 294 AlwaysReturnEmptyStyle aAlwaysEmpty) 295 : mDocumentWeak(nullptr), 296 mOuterFrame(nullptr), 297 mInnerFrame(nullptr), 298 mPresShell(nullptr), 299 mPseudo(std::move(aPseudo)), 300 mStyleType(aStyleType), 301 mAlwaysReturnEmpty(aAlwaysEmpty) { 302 MOZ_ASSERT(aElement); 303 MOZ_ASSERT(aDocument); 304 // TODO(emilio, bug 548397, https://github.com/w3c/csswg-drafts/issues/2403): 305 // Should use aElement->OwnerDoc() instead. 306 mDocumentWeak = aDocument; 307 mElement = aElement; 308 SetEnabledCallbacks(nsIMutationObserver::kParentChainChanged); 309 } 310 311 nsComputedDOMStyle::~nsComputedDOMStyle() { 312 MOZ_ASSERT(!mResolvedComputedStyle, 313 "Should have called ClearComputedStyle() during last release."); 314 } 315 316 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsComputedDOMStyle) 317 318 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle) 319 tmp->ClearComputedStyle(); // remove observer before clearing mElement 320 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement) 321 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 322 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 323 324 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsComputedDOMStyle) 325 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement) 326 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 327 328 // We can skip the nsComputedDOMStyle if it has no wrapper and its 329 // element is skippable, because it will have no outgoing edges, so 330 // it can't be part of a cycle. 331 332 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle) 333 if (!tmp->GetWrapperPreserveColor()) { 334 return !tmp->mElement || 335 mozilla::dom::FragmentOrElement::CanSkip(tmp->mElement, true); 336 } 337 return tmp->HasKnownLiveWrapper(); 338 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 339 340 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle) 341 if (!tmp->GetWrapperPreserveColor()) { 342 return !tmp->mElement || 343 mozilla::dom::FragmentOrElement::CanSkipInCC(tmp->mElement); 344 } 345 return tmp->HasKnownLiveWrapper(); 346 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 347 348 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsComputedDOMStyle) 349 if (!tmp->GetWrapperPreserveColor()) { 350 return !tmp->mElement || 351 mozilla::dom::FragmentOrElement::CanSkipThis(tmp->mElement); 352 } 353 return tmp->HasKnownLiveWrapper(); 354 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 355 356 // QueryInterface implementation for nsComputedDOMStyle 357 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle) 358 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 359 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 360 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) 361 362 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle) 363 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsComputedDOMStyle, 364 ClearComputedStyle()) 365 366 void nsComputedDOMStyle::GetPropertyValue(const NonCustomCSSPropertyId aPropId, 367 nsACString& aValue) { 368 return GetPropertyValue(aPropId, EmptyCString(), aValue); 369 } 370 371 void nsComputedDOMStyle::SetPropertyValue(const NonCustomCSSPropertyId aPropId, 372 const nsACString& aValue, 373 nsIPrincipal* aSubjectPrincipal, 374 ErrorResult& aRv) { 375 aRv.ThrowNoModificationAllowedError(nsPrintfCString( 376 "Can't set value for property '%s' in computed style", 377 PromiseFlatCString(nsCSSProps::GetStringValue(aPropId)).get())); 378 } 379 380 void nsComputedDOMStyle::GetCssText(nsACString& aCssText) { 381 aCssText.Truncate(); 382 } 383 384 void nsComputedDOMStyle::SetCssText(const nsACString& aCssText, 385 nsIPrincipal* aSubjectPrincipal, 386 ErrorResult& aRv) { 387 aRv.ThrowNoModificationAllowedError("Can't set cssText on computed style"); 388 } 389 390 uint32_t nsComputedDOMStyle::Length() { 391 // Make sure we have up to date style so that we can include custom 392 // properties. 393 UpdateCurrentStyleSources(eCSSPropertyExtra_variable); 394 if (!mComputedStyle) { 395 return 0; 396 } 397 398 uint32_t length = GetComputedStyleMap()->Length() + 399 Servo_GetCustomPropertiesCount(mComputedStyle); 400 401 ClearCurrentStyleSources(); 402 403 return length; 404 } 405 406 css::Rule* nsComputedDOMStyle::GetParentRule() { return nullptr; } 407 408 void nsComputedDOMStyle::GetPropertyValue(const nsACString& aPropertyName, 409 nsACString& aReturn) { 410 NonCustomCSSPropertyId prop = nsCSSProps::LookupProperty(aPropertyName); 411 GetPropertyValue(prop, aPropertyName, aReturn); 412 } 413 414 void nsComputedDOMStyle::GetPropertyValue( 415 NonCustomCSSPropertyId aPropId, const nsACString& aMaybeCustomPropertyName, 416 nsACString& aReturn) { 417 MOZ_ASSERT(aReturn.IsEmpty()); 418 419 const ComputedStyleMap::Entry* entry = nullptr; 420 if (aPropId != eCSSPropertyExtra_variable) { 421 entry = GetComputedStyleMap()->FindEntryForProperty(aPropId); 422 if (!entry) { 423 return; 424 } 425 } 426 427 UpdateCurrentStyleSources(aPropId); 428 if (!mComputedStyle) { 429 return; 430 } 431 432 auto cleanup = mozilla::MakeScopeExit([&] { ClearCurrentStyleSources(); }); 433 434 if (!entry) { 435 MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aMaybeCustomPropertyName)); 436 const nsACString& name = 437 Substring(aMaybeCustomPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH); 438 Servo_GetCustomPropertyValue(mComputedStyle, &name, 439 mPresShell->StyleSet()->RawData(), &aReturn); 440 return; 441 } 442 443 if (nsCSSProps::PropHasFlags(aPropId, CSSPropFlags::IsLogical)) { 444 MOZ_ASSERT(entry); 445 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter); 446 447 DebugOnly<NonCustomCSSPropertyId> logicalProp = aPropId; 448 449 aPropId = Servo_ResolveLogicalProperty(aPropId, mComputedStyle); 450 entry = GetComputedStyleMap()->FindEntryForProperty(aPropId); 451 452 MOZ_ASSERT(NeedsToFlushLayout(logicalProp) == NeedsToFlushLayout(aPropId), 453 "Logical and physical property don't agree on whether layout is " 454 "needed"); 455 } 456 457 if (!nsCSSProps::PropHasFlags(aPropId, CSSPropFlags::SerializedByServo)) { 458 if (RefPtr<CSSValue> value = (this->*entry->mGetter)()) { 459 nsAutoString text; 460 value->GetCssText(text); 461 CopyUTF16toUTF8(text, aReturn); 462 } 463 return; 464 } 465 466 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter); 467 Servo_GetResolvedValue(mComputedStyle, aPropId, 468 mPresShell->StyleSet()->RawData(), mElement, &aReturn); 469 } 470 471 /* static */ 472 already_AddRefed<const ComputedStyle> nsComputedDOMStyle::GetComputedStyle( 473 Element* aElement, const PseudoStyleRequest& aPseudo, 474 StyleType aStyleType) { 475 if (Document* doc = aElement->GetComposedDoc()) { 476 doc->FlushPendingNotifications(FlushType::Style); 477 } 478 return GetComputedStyleNoFlush(aElement, aPseudo, aStyleType); 479 } 480 481 /** 482 * The following function checks whether we need to explicitly resolve the style 483 * again, even though we have a style coming from the frame. 484 * 485 * This basically checks whether the style is or may be under a ::first-line or 486 * ::first-letter frame, in which case we can't return the frame style, and we 487 * need to resolve it. See bug 505515. 488 */ 489 static bool MustReresolveStyle(const ComputedStyle* aStyle) { 490 MOZ_ASSERT(aStyle); 491 492 // TODO(emilio): We may want to avoid re-resolving pseudo-element styles 493 // more often. 494 return aStyle->IsInFirstLineSubtree() && !aStyle->IsPseudoElement(); 495 } 496 497 static bool IsInFlatTree(const Element& aElement) { 498 const auto* topmost = &aElement; 499 while (true) { 500 if (topmost->HasServoData()) { 501 // If we have styled this element then we know it's in the flat tree. 502 return true; 503 } 504 const Element* parent = topmost->GetFlattenedTreeParentElement(); 505 if (!parent) { 506 break; 507 } 508 topmost = parent; 509 } 510 auto* root = topmost->GetFlattenedTreeParentNode(); 511 return root && root->IsDocument(); 512 } 513 514 already_AddRefed<const ComputedStyle> 515 nsComputedDOMStyle::DoGetComputedStyleNoFlush(const Element* aElement, 516 const PseudoStyleRequest& aPseudo, 517 PresShell* aPresShell, 518 StyleType aStyleType) { 519 MOZ_ASSERT(aElement, "NULL element"); 520 521 // If the content has a pres shell, we must use it. Otherwise we'd 522 // potentially mix rule trees by using the wrong pres shell's style 523 // set. Using the pres shell from the content also means that any 524 // content that's actually *in* a document will get the style from the 525 // correct document. 526 PresShell* presShell = nsContentUtils::GetPresShellForContent(aElement); 527 bool inDocWithShell = true; 528 if (!presShell) { 529 inDocWithShell = false; 530 presShell = aPresShell; 531 if (!presShell) { 532 return nullptr; 533 } 534 } 535 536 MOZ_ASSERT(aPseudo.IsPseudoElementOrNotPseudo()); 537 if (!aElement->IsInComposedDoc()) { 538 // Don't return styles for disconnected elements, that makes no sense. This 539 // can only happen with a non-null presShell for cross-document calls. 540 return nullptr; 541 } 542 543 if (!IsInFlatTree(*aElement)) { 544 return nullptr; 545 } 546 547 // XXX the !aElement->IsHTMLElement(nsGkAtoms::area) 548 // check is needed due to bug 135040 (to avoid using 549 // mPrimaryFrame). Remove it once that's fixed. 550 if (inDocWithShell && aStyleType == StyleType::All && 551 !aElement->IsHTMLElement(nsGkAtoms::area)) { 552 if (const Element* element = aElement->GetPseudoElement(aPseudo)) { 553 if (element->HasServoData()) { 554 const ComputedStyle* result = 555 Servo_Element_GetMaybeOutOfDateStyle(element); 556 return do_AddRef(result); 557 } 558 } 559 } 560 561 // No frame has been created, or we have a pseudo, or we're looking 562 // for the default style, so resolve the style ourselves. 563 ServoStyleSet* styleSet = presShell->StyleSet(); 564 565 StyleRuleInclusion rules = aStyleType == StyleType::DefaultOnly 566 ? StyleRuleInclusion::DefaultOnly 567 : StyleRuleInclusion::All; 568 RefPtr<ComputedStyle> result = 569 styleSet->ResolveStyleLazily(*aElement, aPseudo, rules); 570 return result.forget(); 571 } 572 573 already_AddRefed<const ComputedStyle> 574 nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush( 575 Element* aElement, const PseudoStyleRequest& aPseudo) { 576 RefPtr<const ComputedStyle> style = 577 GetComputedStyleNoFlush(aElement, aPseudo); 578 if (!style) { 579 return nullptr; 580 } 581 582 PresShell* presShell = aElement->OwnerDoc()->GetPresShell(); 583 MOZ_ASSERT(presShell, 584 "How in the world did we get a style a few lines above?"); 585 586 Element* elementOrPseudoElement = aElement->GetPseudoElement(aPseudo); 587 if (!elementOrPseudoElement) { 588 return nullptr; 589 } 590 591 return presShell->StyleSet()->GetBaseContextForElement(elementOrPseudoElement, 592 style); 593 } 594 595 nsMargin nsComputedDOMStyle::GetAdjustedValuesForBoxSizing() { 596 // We want the width/height of whatever parts 'width' or 'height' controls, 597 // which can be different depending on the value of the 'box-sizing' property. 598 const nsStylePosition* stylePos = StylePosition(); 599 600 nsMargin adjustment; 601 if (stylePos->mBoxSizing == StyleBoxSizing::Border) { 602 adjustment = mInnerFrame->GetUsedBorderAndPadding(); 603 } 604 605 return adjustment; 606 } 607 608 static void AddImageURL(nsIURI& aURI, nsTArray<nsCString>& aURLs) { 609 nsCString spec; 610 nsresult rv = aURI.GetSpec(spec); 611 if (NS_FAILED(rv)) { 612 return; 613 } 614 615 aURLs.AppendElement(std::move(spec)); 616 } 617 618 static void AddImageURL(const StyleComputedUrl& aURL, 619 nsTArray<nsCString>& aURLs) { 620 if (aURL.IsLocalRef()) { 621 return; 622 } 623 624 if (nsIURI* uri = aURL.GetURI()) { 625 AddImageURL(*uri, aURLs); 626 } 627 } 628 629 static void AddImageURL(const StyleImage& aImage, nsTArray<nsCString>& aURLs) { 630 if (auto* urlValue = aImage.GetImageRequestURLValue()) { 631 AddImageURL(*urlValue, aURLs); 632 } 633 } 634 635 static void AddImageURL(const StyleShapeOutside& aShapeOutside, 636 nsTArray<nsCString>& aURLs) { 637 if (aShapeOutside.IsImage()) { 638 AddImageURL(aShapeOutside.AsImage(), aURLs); 639 } 640 } 641 642 static void AddImageURL(const StyleClipPath& aClipPath, 643 nsTArray<nsCString>& aURLs) { 644 if (aClipPath.IsUrl()) { 645 AddImageURL(aClipPath.AsUrl(), aURLs); 646 } 647 } 648 649 static void AddImageURLs(const nsStyleImageLayers& aLayers, 650 nsTArray<nsCString>& aURLs) { 651 for (auto i : IntegerRange(aLayers.mLayers.Length())) { 652 AddImageURL(aLayers.mLayers[i].mImage, aURLs); 653 } 654 } 655 656 static void CollectImageURLsForProperty(NonCustomCSSPropertyId aProp, 657 const ComputedStyle& aStyle, 658 nsTArray<nsCString>& aURLs) { 659 if (nsCSSProps::IsShorthand(aProp)) { 660 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProp, 661 CSSEnabledState::ForAllContent) { 662 CollectImageURLsForProperty(*p, aStyle, aURLs); 663 } 664 return; 665 } 666 667 switch (aProp) { 668 case eCSSProperty_cursor: 669 for (auto& image : aStyle.StyleUI()->Cursor().images.AsSpan()) { 670 AddImageURL(image.image, aURLs); 671 } 672 break; 673 case eCSSProperty_background_image: 674 AddImageURLs(aStyle.StyleBackground()->mImage, aURLs); 675 break; 676 case eCSSProperty_mask_clip: 677 AddImageURLs(aStyle.StyleSVGReset()->mMask, aURLs); 678 break; 679 case eCSSProperty_list_style_image: { 680 const auto& image = aStyle.StyleList()->mListStyleImage; 681 if (image.IsUrl()) { 682 AddImageURL(image.AsUrl(), aURLs); 683 } 684 break; 685 } 686 case eCSSProperty_border_image_source: 687 AddImageURL(aStyle.StyleBorder()->mBorderImageSource, aURLs); 688 break; 689 case eCSSProperty_clip_path: 690 AddImageURL(aStyle.StyleSVGReset()->mClipPath, aURLs); 691 break; 692 case eCSSProperty_shape_outside: 693 AddImageURL(aStyle.StyleDisplay()->mShapeOutside, aURLs); 694 break; 695 default: 696 break; 697 } 698 } 699 700 float nsComputedDOMStyle::UsedFontSize() { 701 UpdateCurrentStyleSources(eCSSProperty_font_size); 702 703 if (!mComputedStyle) { 704 return -1.0; 705 } 706 707 return mComputedStyle->StyleFont()->mFont.size.ToCSSPixels(); 708 } 709 710 void nsComputedDOMStyle::GetCSSImageURLs(const nsACString& aPropertyName, 711 nsTArray<nsCString>& aImageURLs, 712 mozilla::ErrorResult& aRv) { 713 NonCustomCSSPropertyId prop = nsCSSProps::LookupProperty(aPropertyName); 714 if (prop == eCSSProperty_UNKNOWN) { 715 // Note: not using nsPrintfCString here in case aPropertyName contains 716 // nulls. 717 aRv.ThrowSyntaxError("Invalid property name '"_ns + aPropertyName + "'"_ns); 718 return; 719 } 720 721 UpdateCurrentStyleSources(prop); 722 723 if (!mComputedStyle) { 724 return; 725 } 726 727 CollectImageURLsForProperty(prop, *mComputedStyle, aImageURLs); 728 ClearCurrentStyleSources(); 729 } 730 731 // nsDOMCSSDeclaration abstract methods which should never be called 732 // on a nsComputedDOMStyle object, but must be defined to avoid 733 // compile errors. 734 DeclarationBlock* nsComputedDOMStyle::GetOrCreateCSSDeclaration( 735 Operation aOperation, DeclarationBlock** aCreated) { 736 MOZ_CRASH("called nsComputedDOMStyle::GetCSSDeclaration"); 737 } 738 739 nsresult nsComputedDOMStyle::SetCSSDeclaration(DeclarationBlock*, 740 MutationClosureData*) { 741 MOZ_CRASH("called nsComputedDOMStyle::SetCSSDeclaration"); 742 } 743 744 Document* nsComputedDOMStyle::DocToUpdate() { 745 MOZ_CRASH("called nsComputedDOMStyle::DocToUpdate"); 746 } 747 748 nsDOMCSSDeclaration::ParsingEnvironment 749 nsComputedDOMStyle::GetParsingEnvironment( 750 nsIPrincipal* aSubjectPrincipal) const { 751 MOZ_CRASH("called nsComputedDOMStyle::GetParsingEnvironment"); 752 } 753 754 void nsComputedDOMStyle::ClearComputedStyle() { 755 if (mResolvedComputedStyle) { 756 mResolvedComputedStyle = false; 757 mElement->RemoveMutationObserver(this); 758 } 759 mComputedStyle = nullptr; 760 } 761 762 void nsComputedDOMStyle::SetResolvedComputedStyle( 763 RefPtr<const ComputedStyle> aStyle, uint64_t aGeneration) { 764 if (!mResolvedComputedStyle) { 765 mResolvedComputedStyle = true; 766 mElement->AddMutationObserver(this); 767 } 768 mComputedStyle = std::move(aStyle); 769 mComputedStyleGeneration = aGeneration; 770 mPresShellId = mPresShell->GetPresShellId(); 771 } 772 773 void nsComputedDOMStyle::SetFrameComputedStyle( 774 RefPtr<const ComputedStyle> aStyle, uint64_t aGeneration) { 775 ClearComputedStyle(); 776 mComputedStyle = std::move(aStyle); 777 mComputedStyleGeneration = aGeneration; 778 mPresShellId = mPresShell->GetPresShellId(); 779 } 780 781 static bool MayNeedToFlushLayout(NonCustomCSSPropertyId aPropId) { 782 switch (aPropId) { 783 case eCSSProperty_max_width: 784 case eCSSProperty_max_height: 785 case eCSSProperty_min_width: 786 case eCSSProperty_min_height: 787 case eCSSProperty_max_inline_size: 788 case eCSSProperty_max_block_size: 789 case eCSSProperty_min_inline_size: 790 case eCSSProperty_min_block_size: 791 case eCSSProperty_width: 792 case eCSSProperty_height: 793 case eCSSProperty_block_size: 794 case eCSSProperty_inline_size: 795 case eCSSProperty_line_height: 796 case eCSSProperty_grid_template_rows: 797 case eCSSProperty_grid_template_columns: 798 case eCSSProperty_perspective_origin: 799 case eCSSProperty_transform_origin: 800 case eCSSProperty_transform: 801 case eCSSProperty_top: 802 case eCSSProperty_right: 803 case eCSSProperty_bottom: 804 case eCSSProperty_left: 805 case eCSSProperty_inset_block_start: 806 case eCSSProperty_inset_block_end: 807 case eCSSProperty_inset_inline_start: 808 case eCSSProperty_inset_inline_end: 809 case eCSSProperty_padding_top: 810 case eCSSProperty_padding_right: 811 case eCSSProperty_padding_bottom: 812 case eCSSProperty_padding_left: 813 case eCSSProperty_padding_block_start: 814 case eCSSProperty_padding_block_end: 815 case eCSSProperty_padding_inline_start: 816 case eCSSProperty_padding_inline_end: 817 case eCSSProperty_margin_top: 818 case eCSSProperty_margin_right: 819 case eCSSProperty_margin_bottom: 820 case eCSSProperty_margin_left: 821 case eCSSProperty_margin_block_start: 822 case eCSSProperty_margin_block_end: 823 case eCSSProperty_margin_inline_start: 824 case eCSSProperty_margin_inline_end: 825 return true; 826 default: 827 return false; 828 } 829 } 830 831 bool nsComputedDOMStyle::NeedsToFlushStyle( 832 NonCustomCSSPropertyId aPropId) const { 833 bool mayNeedToFlushLayout = MayNeedToFlushLayout(aPropId); 834 835 // We always compute styles from the element's owner document. 836 if (ElementNeedsRestyle(mElement, mPseudo, mayNeedToFlushLayout)) { 837 return true; 838 } 839 840 Document* doc = mElement->OwnerDoc(); 841 // If parent document is there, also needs to check if there is some change 842 // that needs to flush this document (e.g. size change for iframe). 843 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) { 844 if (Element* element = doc->GetEmbedderElement()) { 845 if (ElementNeedsRestyle(element, {}, mayNeedToFlushLayout)) { 846 return true; 847 } 848 } 849 850 doc = doc->GetInProcessParentDocument(); 851 } 852 853 return false; 854 } 855 856 static bool IsNonReplacedInline(nsIFrame* aFrame) { 857 // FIXME: this should be IsInlineInsideStyle() since width/height 858 // doesn't apply to ruby boxes. 859 return aFrame->StyleDisplay()->IsInlineFlow() && !aFrame->IsReplaced() && 860 !aFrame->IsFieldSetFrame() && !aFrame->IsBlockFrame() && 861 !aFrame->IsScrollContainerFrame() && 862 !aFrame->IsColumnSetWrapperFrame(); 863 } 864 865 static Side SideForPaddingOrMarginOrInsetProperty( 866 NonCustomCSSPropertyId aPropId) { 867 switch (aPropId) { 868 case eCSSProperty_top: 869 case eCSSProperty_margin_top: 870 case eCSSProperty_padding_top: 871 return eSideTop; 872 case eCSSProperty_right: 873 case eCSSProperty_margin_right: 874 case eCSSProperty_padding_right: 875 return eSideRight; 876 case eCSSProperty_bottom: 877 case eCSSProperty_margin_bottom: 878 case eCSSProperty_padding_bottom: 879 return eSideBottom; 880 case eCSSProperty_left: 881 case eCSSProperty_margin_left: 882 case eCSSProperty_padding_left: 883 return eSideLeft; 884 default: 885 MOZ_ASSERT_UNREACHABLE("Unexpected property"); 886 return eSideTop; 887 } 888 } 889 890 static bool PaddingNeedsUsedValue(const LengthPercentage& aValue, 891 const ComputedStyle& aStyle) { 892 return !aValue.ConvertsToLength() || aStyle.StyleDisplay()->HasAppearance(); 893 } 894 895 static bool HasPositionFallbacks(nsIFrame* aFrame) { 896 return aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && 897 !aFrame->StylePosition()->mPositionTryFallbacks._0.IsEmpty(); 898 } 899 900 bool nsComputedDOMStyle::NeedsToFlushLayout( 901 NonCustomCSSPropertyId aPropId) const { 902 MOZ_ASSERT(aPropId != eCSSProperty_UNKNOWN); 903 if (aPropId == eCSSPropertyExtra_variable) { 904 return false; 905 } 906 nsIFrame* outerFrame = GetOuterFrame(); 907 if (!outerFrame) { 908 return false; 909 } 910 nsIFrame* frame = nsLayoutUtils::GetStyleFrame(outerFrame); 911 auto* style = frame->Style(); 912 if (nsCSSProps::PropHasFlags(aPropId, CSSPropFlags::IsLogical)) { 913 aPropId = Servo_ResolveLogicalProperty(aPropId, style); 914 } 915 916 switch (aPropId) { 917 case eCSSProperty_max_width: 918 return HasPositionFallbacks(frame) || 919 frame->StylePosition()->mMaxWidth.HasAnchorPositioningFunction(); 920 case eCSSProperty_max_height: 921 return HasPositionFallbacks(frame) || 922 frame->StylePosition()->mMaxHeight.HasAnchorPositioningFunction(); 923 case eCSSProperty_min_width: 924 return HasPositionFallbacks(frame) || 925 frame->StylePosition()->mMinWidth.HasAnchorPositioningFunction(); 926 case eCSSProperty_min_height: 927 return HasPositionFallbacks(frame) || 928 frame->StylePosition()->mMinHeight.HasAnchorPositioningFunction(); 929 case eCSSProperty_width: 930 case eCSSProperty_height: 931 return !IsNonReplacedInline(frame); 932 case eCSSProperty_line_height: 933 return frame->StyleFont()->mLineHeight.IsMozBlockHeight(); 934 case eCSSProperty_grid_template_rows: 935 case eCSSProperty_grid_template_columns: 936 return !!nsGridContainerFrame::GetGridContainerFrame(frame); 937 case eCSSProperty_perspective_origin: 938 return style->StyleDisplay()->mPerspectiveOrigin.HasPercent(); 939 case eCSSProperty_transform_origin: 940 return style->StyleDisplay()->mTransformOrigin.HasPercent(); 941 case eCSSProperty_transform: 942 return style->StyleDisplay()->mTransform.HasPercent(); 943 case eCSSProperty_top: 944 case eCSSProperty_right: 945 case eCSSProperty_bottom: 946 case eCSSProperty_left: 947 // Doing better than this is actually hard. 948 return style->StyleDisplay()->mPosition != StylePositionProperty::Static; 949 case eCSSProperty_padding_top: 950 case eCSSProperty_padding_right: 951 case eCSSProperty_padding_bottom: 952 case eCSSProperty_padding_left: { 953 Side side = SideForPaddingOrMarginOrInsetProperty(aPropId); 954 // Theming can override used padding, sigh. 955 // 956 // TODO(emilio): If we make GetUsedPadding() 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 PaddingNeedsUsedValue(style->StylePadding()->mPadding.Get(side), 960 *style); 961 } 962 case eCSSProperty_margin_top: 963 case eCSSProperty_margin_right: 964 case eCSSProperty_margin_bottom: 965 case eCSSProperty_margin_left: { 966 // NOTE(emilio): This is dubious, but matches other browsers. 967 // See https://github.com/w3c/csswg-drafts/issues/2328 968 // NOTE(dshin): Raw margin value access since we want to flush 969 // anchor-dependent values here. 970 Side side = SideForPaddingOrMarginOrInsetProperty(aPropId); 971 return !style->StyleMargin()->mMargin.Get(side).ConvertsToLength() || 972 HasPositionFallbacks(frame); 973 } 974 default: 975 return false; 976 } 977 } 978 979 bool nsComputedDOMStyle::NeedsToFlushLayoutForContainerQuery() const { 980 const auto* outerFrame = GetOuterFrame(); 981 if (!outerFrame) { 982 return false; 983 } 984 const auto* innerFrame = nsLayoutUtils::GetStyleFrame(outerFrame); 985 MOZ_ASSERT(innerFrame, "No valid inner frame?"); 986 // It's possible that potential containers are styled but not yet reflowed, 987 // i.e. They don't have a correct size, which makes any container query 988 // evaluation against them invalid. 989 return innerFrame->HasUnreflowedContainerQueryAncestor(); 990 } 991 992 void nsComputedDOMStyle::Flush(Document& aDocument, FlushType aFlushType) { 993 MOZ_ASSERT(mElement->IsInComposedDoc()); 994 MOZ_ASSERT(mDocumentWeak == &aDocument); 995 996 if (MOZ_UNLIKELY(&aDocument != mElement->OwnerDoc())) { 997 aDocument.FlushPendingNotifications(aFlushType); 998 } 999 // This performs the flush, and also guarantees that content-visibility: 1000 // hidden elements get laid out, if needed. 1001 mElement->GetPrimaryFrame(aFlushType); 1002 } 1003 1004 nsIFrame* nsComputedDOMStyle::GetOuterFrame() const { 1005 if (mPseudo.mType == PseudoStyleType::NotPseudo) { 1006 return mElement->GetPrimaryFrame(); 1007 } 1008 auto* pseudo = mElement->GetPseudoElement(mPseudo); 1009 return pseudo ? pseudo->GetPrimaryFrame() : nullptr; 1010 } 1011 1012 void nsComputedDOMStyle::UpdateCurrentStyleSources( 1013 NonCustomCSSPropertyId aPropId) { 1014 nsCOMPtr<Document> document(mDocumentWeak); 1015 if (!document) { 1016 ClearComputedStyle(); 1017 return; 1018 } 1019 1020 // We don't return styles for disconnected elements anymore, so don't go 1021 // through the trouble of flushing or what not. 1022 // 1023 // TODO(emilio): We may want to return earlier for elements outside of the 1024 // flat tree too: https://github.com/w3c/csswg-drafts/issues/1964 1025 if (!mElement->IsInComposedDoc()) { 1026 ClearComputedStyle(); 1027 return; 1028 } 1029 1030 if (mAlwaysReturnEmpty == AlwaysReturnEmptyStyle::Yes) { 1031 ClearComputedStyle(); 1032 return; 1033 } 1034 1035 DebugOnly<bool> didFlush = false; 1036 if (NeedsToFlushStyle(aPropId)) { 1037 didFlush = true; 1038 // We look at the frame in NeedsToFlushLayout, so flush frames, not only 1039 // styles. 1040 Flush(*document, FlushType::Frames); 1041 } 1042 1043 const bool needsToFlushLayoutForProp = NeedsToFlushLayout(aPropId); 1044 if (needsToFlushLayoutForProp || NeedsToFlushLayoutForContainerQuery()) { 1045 MOZ_ASSERT_IF(needsToFlushLayoutForProp, MayNeedToFlushLayout(aPropId)); 1046 didFlush = true; 1047 Flush(*document, FlushType::Layout); 1048 #ifdef DEBUG 1049 mFlushedPendingReflows = true; 1050 #endif 1051 } else { 1052 #ifdef DEBUG 1053 mFlushedPendingReflows = false; 1054 #endif 1055 } 1056 1057 mPresShell = document->GetPresShell(); 1058 if (!mPresShell || !mPresShell->GetPresContext()) { 1059 ClearComputedStyle(); 1060 return; 1061 } 1062 1063 // We need to use GetUndisplayedRestyleGeneration instead of 1064 // GetRestyleGeneration, because the caching of mComputedStyle is an 1065 // optimization that is useful only for displayed elements. 1066 // For undisplayed elements we need to take into account any DOM changes that 1067 // might cause a restyle, because Servo will not increase the generation for 1068 // undisplayed elements. 1069 uint64_t currentGeneration = 1070 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration(); 1071 1072 if (mComputedStyle && mComputedStyleGeneration == currentGeneration && 1073 mPresShellId == mPresShell->GetPresShellId()) { 1074 // Our cached style is still valid. 1075 return; 1076 } 1077 1078 mComputedStyle = nullptr; 1079 1080 // XXX the !mElement->IsHTMLElement(nsGkAtoms::area) check is needed due to 1081 // bug 135040 (to avoid using mPrimaryFrame). Remove it once that's fixed. 1082 if (mStyleType == StyleType::All && 1083 !mElement->IsHTMLElement(nsGkAtoms::area)) { 1084 mOuterFrame = GetOuterFrame(); 1085 mInnerFrame = mOuterFrame; 1086 if (mOuterFrame) { 1087 mInnerFrame = nsLayoutUtils::GetStyleFrame(mOuterFrame); 1088 const auto* style = mInnerFrame->Style(); 1089 if (auto* data = mInnerFrame->GetProperty( 1090 nsIFrame::LastSuccessfulPositionFallback())) { 1091 style = data->mStyle.get(); 1092 } 1093 SetFrameComputedStyle(std::move(style), currentGeneration); 1094 NS_ASSERTION(mComputedStyle, "Frame without style?"); 1095 } 1096 } 1097 1098 if (!mComputedStyle || MustReresolveStyle(mComputedStyle)) { 1099 PresShell* presShellForContent = mElement->OwnerDoc()->GetPresShell(); 1100 // Need to resolve a style. 1101 RefPtr<const ComputedStyle> resolvedComputedStyle = 1102 DoGetComputedStyleNoFlush( 1103 mElement, mPseudo, 1104 presShellForContent ? presShellForContent : mPresShell, mStyleType); 1105 if (!resolvedComputedStyle) { 1106 ClearComputedStyle(); 1107 return; 1108 } 1109 1110 // No need to re-get the generation, even though GetComputedStyle 1111 // will flush, since we flushed style at the top of this function. 1112 // We don't need to check this if we only flushed the parent. 1113 NS_ASSERTION( 1114 !didFlush || 1115 currentGeneration == 1116 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration(), 1117 "why should we have flushed style again?"); 1118 1119 SetResolvedComputedStyle(std::move(resolvedComputedStyle), 1120 currentGeneration); 1121 } 1122 1123 // mExposeVisitedStyle is set to true only by testing APIs that 1124 // require chrome privilege. 1125 MOZ_ASSERT(!mExposeVisitedStyle || nsContentUtils::IsCallerChrome(), 1126 "mExposeVisitedStyle set incorrectly"); 1127 if (mExposeVisitedStyle && mComputedStyle->RelevantLinkVisited()) { 1128 if (const auto* styleIfVisited = mComputedStyle->GetStyleIfVisited()) { 1129 mComputedStyle = styleIfVisited; 1130 } 1131 } 1132 } 1133 1134 void nsComputedDOMStyle::ClearCurrentStyleSources() { 1135 // Release the current style if we got it off the frame. 1136 // 1137 // For a style we resolved, keep it around so that we can re-use it next time 1138 // this object is queried, but not if it-s a re-resolved style because we were 1139 // inside a pseudo-element. 1140 if (!mResolvedComputedStyle || mOuterFrame) { 1141 ClearComputedStyle(); 1142 } 1143 1144 mOuterFrame = nullptr; 1145 mInnerFrame = nullptr; 1146 mPresShell = nullptr; 1147 } 1148 1149 void nsComputedDOMStyle::RemoveProperty(const nsACString& aPropertyName, 1150 nsACString& aReturn, ErrorResult& aRv) { 1151 // Note: not using nsPrintfCString here in case aPropertyName contains 1152 // nulls. 1153 aRv.ThrowNoModificationAllowedError("Can't remove property '"_ns + 1154 aPropertyName + 1155 "' from computed style"_ns); 1156 } 1157 1158 void nsComputedDOMStyle::GetPropertyPriority(const nsACString& aPropertyName, 1159 nsACString& aReturn) { 1160 aReturn.Truncate(); 1161 } 1162 1163 void nsComputedDOMStyle::SetProperty(const nsACString& aPropertyName, 1164 const nsACString& aValue, 1165 const nsACString& aPriority, 1166 nsIPrincipal* aSubjectPrincipal, 1167 ErrorResult& aRv) { 1168 // Note: not using nsPrintfCString here in case aPropertyName contains 1169 // nulls. 1170 aRv.ThrowNoModificationAllowedError("Can't set value for property '"_ns + 1171 aPropertyName + "' in computed style"_ns); 1172 } 1173 1174 void nsComputedDOMStyle::IndexedGetter(uint32_t aIndex, bool& aFound, 1175 nsACString& aPropName) { 1176 ComputedStyleMap* map = GetComputedStyleMap(); 1177 uint32_t length = map->Length(); 1178 1179 if (aIndex < length) { 1180 aFound = true; 1181 aPropName.Assign(nsCSSProps::GetStringValue(map->PropertyAt(aIndex))); 1182 return; 1183 } 1184 1185 // Custom properties are exposed with indexed properties just after all 1186 // of the built-in properties. 1187 UpdateCurrentStyleSources(eCSSPropertyExtra_variable); 1188 if (!mComputedStyle) { 1189 aFound = false; 1190 return; 1191 } 1192 1193 uint32_t count = Servo_GetCustomPropertiesCount(mComputedStyle); 1194 1195 const uint32_t index = aIndex - length; 1196 if (index < count) { 1197 aFound = true; 1198 aPropName.AssignLiteral("--"); 1199 if (nsAtom* atom = Servo_GetCustomPropertyNameAt(mComputedStyle, index)) { 1200 aPropName.Append(nsAtomCString(atom)); 1201 } 1202 } else { 1203 aFound = false; 1204 } 1205 1206 ClearCurrentStyleSources(); 1207 } 1208 1209 // Property getters... 1210 1211 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBottom() { 1212 return GetOffsetWidthFor(eSideBottom); 1213 } 1214 1215 static Position MaybeResolvePositionForTransform(const LengthPercentage& aX, 1216 const LengthPercentage& aY, 1217 nsIFrame* aInnerFrame) { 1218 if (!aInnerFrame) { 1219 return {aX, aY}; 1220 } 1221 nsStyleTransformMatrix::TransformReferenceBox refBox(aInnerFrame); 1222 CSSPoint p = nsStyleTransformMatrix::Convert2DPosition(aX, aY, refBox); 1223 return {LengthPercentage::FromPixels(p.x), LengthPercentage::FromPixels(p.y)}; 1224 } 1225 1226 /* Convert the stored representation into a list of two values and then hand 1227 * it back. 1228 */ 1229 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransformOrigin() { 1230 /* Store things as a value list */ 1231 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false); 1232 1233 /* Now, get the values. */ 1234 const auto& origin = StyleDisplay()->mTransformOrigin; 1235 1236 auto position = MaybeResolvePositionForTransform( 1237 origin.horizontal, origin.vertical, mInnerFrame); 1238 SetValueToPosition(position, valueList); 1239 if (!origin.depth.IsZero()) { 1240 valueList->AppendCSSValue(PixelsToCSSValue(origin.depth.ToCSSPixels())); 1241 } 1242 return valueList.forget(); 1243 } 1244 1245 /* Convert the stored representation into a list of two values and then hand 1246 * it back. 1247 */ 1248 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPerspectiveOrigin() { 1249 /* Store things as a value list */ 1250 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false); 1251 1252 /* Now, get the values. */ 1253 const auto& origin = StyleDisplay()->mPerspectiveOrigin; 1254 1255 auto position = MaybeResolvePositionForTransform( 1256 origin.horizontal, origin.vertical, mInnerFrame); 1257 SetValueToPosition(position, valueList); 1258 return valueList.forget(); 1259 } 1260 1261 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransform() { 1262 const nsStyleDisplay* display = StyleDisplay(); 1263 return GetTransformValue(display->mTransform); 1264 } 1265 1266 /* static */ 1267 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::MatrixToCSSValue( 1268 const mozilla::gfx::Matrix4x4& matrix) { 1269 bool is3D = !matrix.Is2D(); 1270 1271 nsAutoString resultString(u"matrix"_ns); 1272 if (is3D) { 1273 resultString.AppendLiteral("3d"); 1274 } 1275 1276 resultString.Append('('); 1277 resultString.AppendFloat(matrix._11); 1278 resultString.AppendLiteral(", "); 1279 resultString.AppendFloat(matrix._12); 1280 resultString.AppendLiteral(", "); 1281 if (is3D) { 1282 resultString.AppendFloat(matrix._13); 1283 resultString.AppendLiteral(", "); 1284 resultString.AppendFloat(matrix._14); 1285 resultString.AppendLiteral(", "); 1286 } 1287 resultString.AppendFloat(matrix._21); 1288 resultString.AppendLiteral(", "); 1289 resultString.AppendFloat(matrix._22); 1290 resultString.AppendLiteral(", "); 1291 if (is3D) { 1292 resultString.AppendFloat(matrix._23); 1293 resultString.AppendLiteral(", "); 1294 resultString.AppendFloat(matrix._24); 1295 resultString.AppendLiteral(", "); 1296 resultString.AppendFloat(matrix._31); 1297 resultString.AppendLiteral(", "); 1298 resultString.AppendFloat(matrix._32); 1299 resultString.AppendLiteral(", "); 1300 resultString.AppendFloat(matrix._33); 1301 resultString.AppendLiteral(", "); 1302 resultString.AppendFloat(matrix._34); 1303 resultString.AppendLiteral(", "); 1304 } 1305 resultString.AppendFloat(matrix._41); 1306 resultString.AppendLiteral(", "); 1307 resultString.AppendFloat(matrix._42); 1308 if (is3D) { 1309 resultString.AppendLiteral(", "); 1310 resultString.AppendFloat(matrix._43); 1311 resultString.AppendLiteral(", "); 1312 resultString.AppendFloat(matrix._44); 1313 } 1314 resultString.Append(')'); 1315 1316 /* Create a value to hold our result. */ 1317 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1318 val->SetString(resultString); 1319 return val.forget(); 1320 } 1321 1322 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::AppUnitsToCSSValue( 1323 nscoord aAppUnits) { 1324 return PixelsToCSSValue(CSSPixel::FromAppUnits(aAppUnits)); 1325 } 1326 1327 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::PixelsToCSSValue( 1328 float aPixels) { 1329 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1330 SetValueToPixels(val, aPixels); 1331 return val.forget(); 1332 } 1333 1334 void nsComputedDOMStyle::SetValueToPixels(nsROCSSPrimitiveValue* aValue, 1335 float aPixels) { 1336 MOZ_ASSERT(mComputedStyle); 1337 aValue->SetPixels(mComputedStyle->EffectiveZoom().Unzoom(aPixels)); 1338 } 1339 1340 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMozOsxFontSmoothing() { 1341 if (nsContentUtils::ShouldResistFingerprinting( 1342 mPresShell->GetPresContext()->GetDocShell(), 1343 RFPTarget::DOMStyleOsxFontSmoothing)) { 1344 return nullptr; 1345 } 1346 1347 nsAutoCString result; 1348 mComputedStyle->GetComputedPropertyValue(eCSSProperty__moz_osx_font_smoothing, 1349 result); 1350 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1351 val->SetString(result); 1352 return val.forget(); 1353 } 1354 1355 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetImageLayerPosition( 1356 const nsStyleImageLayers& aLayers) { 1357 if (aLayers.mPositionXCount != aLayers.mPositionYCount) { 1358 // No value to return. We can't express this combination of 1359 // values as a shorthand. 1360 return nullptr; 1361 } 1362 1363 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true); 1364 for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) { 1365 RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false); 1366 1367 SetValueToPosition(aLayers.mLayers[i].mPosition, itemList); 1368 valueList->AppendCSSValue(itemList.forget()); 1369 } 1370 1371 return valueList.forget(); 1372 } 1373 1374 void nsComputedDOMStyle::SetValueToPosition(const Position& aPosition, 1375 nsDOMCSSValueList* aValueList) { 1376 auto valX = MakeRefPtr<nsROCSSPrimitiveValue>(); 1377 SetValueToLengthPercentage(valX, aPosition.horizontal, false); 1378 aValueList->AppendCSSValue(valX.forget()); 1379 1380 auto valY = MakeRefPtr<nsROCSSPrimitiveValue>(); 1381 SetValueToLengthPercentage(valY, aPosition.vertical, false); 1382 aValueList->AppendCSSValue(valY.forget()); 1383 } 1384 1385 enum class Brackets { No, Yes }; 1386 1387 static void AppendGridLineNames(nsACString& aResult, 1388 Span<const StyleCustomIdent> aLineNames, 1389 Brackets aBrackets) { 1390 if (aLineNames.IsEmpty()) { 1391 if (aBrackets == Brackets::Yes) { 1392 aResult.AppendLiteral("[]"); 1393 } 1394 return; 1395 } 1396 uint32_t numLines = aLineNames.Length(); 1397 if (aBrackets == Brackets::Yes) { 1398 aResult.Append('['); 1399 } 1400 for (uint32_t i = 0;;) { 1401 // TODO: Maybe use servo to do this and avoid the silly utf16->utf8 dance? 1402 nsAutoString name; 1403 nsStyleUtil::AppendEscapedCSSIdent( 1404 nsDependentAtomString(aLineNames[i].AsAtom()), name); 1405 AppendUTF16toUTF8(name, aResult); 1406 1407 if (++i == numLines) { 1408 break; 1409 } 1410 aResult.Append(' '); 1411 } 1412 if (aBrackets == Brackets::Yes) { 1413 aResult.Append(']'); 1414 } 1415 } 1416 1417 static void AppendGridLineNames(nsDOMCSSValueList* aValueList, 1418 Span<const StyleCustomIdent> aLineNames, 1419 bool aSuppressEmptyList = true) { 1420 if (aLineNames.IsEmpty() && aSuppressEmptyList) { 1421 return; 1422 } 1423 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1424 nsAutoCString lineNamesString; 1425 AppendGridLineNames(lineNamesString, aLineNames, Brackets::Yes); 1426 val->SetString(lineNamesString); 1427 aValueList->AppendCSSValue(val.forget()); 1428 } 1429 1430 static void AppendGridLineNames(nsDOMCSSValueList* aValueList, 1431 Span<const StyleCustomIdent> aLineNames1, 1432 Span<const StyleCustomIdent> aLineNames2) { 1433 if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) { 1434 return; 1435 } 1436 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1437 nsAutoCString lineNamesString; 1438 lineNamesString.Assign('['); 1439 if (!aLineNames1.IsEmpty()) { 1440 AppendGridLineNames(lineNamesString, aLineNames1, Brackets::No); 1441 } 1442 if (!aLineNames2.IsEmpty()) { 1443 if (!aLineNames1.IsEmpty()) { 1444 lineNamesString.Append(' '); 1445 } 1446 AppendGridLineNames(lineNamesString, aLineNames2, Brackets::No); 1447 } 1448 lineNamesString.Append(']'); 1449 val->SetString(lineNamesString); 1450 aValueList->AppendCSSValue(val.forget()); 1451 } 1452 1453 void nsComputedDOMStyle::SetValueToTrackBreadth( 1454 nsROCSSPrimitiveValue* aValue, const StyleTrackBreadth& aBreadth) { 1455 using Tag = StyleTrackBreadth::Tag; 1456 switch (aBreadth.tag) { 1457 case Tag::MinContent: 1458 return aValue->SetString("min-content"); 1459 case Tag::MaxContent: 1460 return aValue->SetString("max-content"); 1461 case Tag::Auto: 1462 return aValue->SetString("auto"); 1463 case Tag::Breadth: 1464 return SetValueToLengthPercentage(aValue, aBreadth.AsBreadth(), true); 1465 case Tag::Fr: { 1466 nsAutoString tmpStr; 1467 nsStyleUtil::AppendCSSNumber(aBreadth.AsFr(), tmpStr); 1468 tmpStr.AppendLiteral("fr"); 1469 return aValue->SetString(tmpStr); 1470 } 1471 default: 1472 MOZ_ASSERT_UNREACHABLE("Unknown breadth value"); 1473 return; 1474 } 1475 } 1476 1477 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackBreadth( 1478 const StyleTrackBreadth& aBreadth) { 1479 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1480 SetValueToTrackBreadth(val, aBreadth); 1481 return val.forget(); 1482 } 1483 1484 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackSize( 1485 const StyleTrackSize& aTrackSize) { 1486 if (aTrackSize.IsFitContent()) { 1487 // A fit-content() function. 1488 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1489 MOZ_ASSERT(aTrackSize.AsFitContent().IsBreadth(), 1490 "unexpected unit for fit-content() argument value"); 1491 SetValueFromFitContentFunction(val, aTrackSize.AsFitContent().AsBreadth()); 1492 return val.forget(); 1493 } 1494 1495 if (aTrackSize.IsBreadth()) { 1496 return GetGridTrackBreadth(aTrackSize.AsBreadth()); 1497 } 1498 1499 MOZ_ASSERT(aTrackSize.IsMinmax()); 1500 const auto& min = aTrackSize.AsMinmax()._0; 1501 const auto& max = aTrackSize.AsMinmax()._1; 1502 if (min == max) { 1503 return GetGridTrackBreadth(min); 1504 } 1505 1506 // minmax(auto, <flex>) is equivalent to (and is our internal representation 1507 // of) <flex>, and both compute to <flex> 1508 if (min.IsAuto() && max.IsFr()) { 1509 return GetGridTrackBreadth(max); 1510 } 1511 1512 nsAutoString argumentStr, minmaxStr; 1513 minmaxStr.AppendLiteral("minmax("); 1514 1515 { 1516 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(min); 1517 argValue->GetCssText(argumentStr); 1518 minmaxStr.Append(argumentStr); 1519 argumentStr.Truncate(); 1520 } 1521 1522 minmaxStr.AppendLiteral(", "); 1523 1524 { 1525 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(max); 1526 argValue->GetCssText(argumentStr); 1527 minmaxStr.Append(argumentStr); 1528 } 1529 1530 minmaxStr.Append(char16_t(')')); 1531 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1532 val->SetString(minmaxStr); 1533 return val.forget(); 1534 } 1535 1536 already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows( 1537 const StyleGridTemplateComponent& aTrackList, 1538 const ComputedGridTrackInfo& aTrackInfo) { 1539 if (aTrackInfo.mIsMasonry) { 1540 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1541 val->SetString("masonry"); 1542 return val.forget(); 1543 } 1544 1545 if (aTrackInfo.mIsSubgrid) { 1546 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false); 1547 auto subgridKeyword = MakeRefPtr<nsROCSSPrimitiveValue>(); 1548 subgridKeyword->SetString("subgrid"); 1549 valueList->AppendCSSValue(subgridKeyword.forget()); 1550 for (const auto& lineNames : aTrackInfo.mResolvedLineNames) { 1551 AppendGridLineNames(valueList, lineNames, /*aSuppressEmptyList*/ false); 1552 } 1553 uint32_t line = aTrackInfo.mResolvedLineNames.Length(); 1554 uint32_t lastLine = aTrackInfo.mNumExplicitTracks + 1; 1555 const Span<const StyleCustomIdent> empty; 1556 for (; line < lastLine; ++line) { 1557 AppendGridLineNames(valueList, empty, /*aSuppressEmptyList*/ false); 1558 } 1559 return valueList.forget(); 1560 } 1561 1562 const bool serializeImplicit = 1563 StaticPrefs::layout_css_serialize_grid_implicit_tracks(); 1564 1565 const nsTArray<nscoord>& trackSizes = aTrackInfo.mSizes; 1566 const uint32_t numExplicitTracks = aTrackInfo.mNumExplicitTracks; 1567 const uint32_t numLeadingImplicitTracks = 1568 aTrackInfo.mNumLeadingImplicitTracks; 1569 uint32_t numSizes = trackSizes.Length(); 1570 MOZ_ASSERT(numSizes >= numLeadingImplicitTracks + numExplicitTracks); 1571 1572 const bool hasTracksToSerialize = 1573 serializeImplicit ? !!numSizes : !!numExplicitTracks; 1574 const bool hasRepeatAuto = aTrackList.HasRepeatAuto(); 1575 if (!hasTracksToSerialize && !hasRepeatAuto) { 1576 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1577 val->SetString("none"); 1578 return val.forget(); 1579 } 1580 1581 // We've done layout on the grid and have resolved the sizes of its tracks, 1582 // so we'll return those sizes here. The grid spec says we MAY use 1583 // repeat(<positive-integer>, Npx) here for consecutive tracks with the same 1584 // size, but that doesn't seem worth doing since even for repeat(auto-*) 1585 // the resolved size might differ for the repeated tracks. 1586 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false); 1587 1588 // Add any leading implicit tracks. 1589 if (serializeImplicit) { 1590 for (uint32_t i = 0; i < numLeadingImplicitTracks; ++i) { 1591 valueList->AppendCSSValue(AppUnitsToCSSValue(trackSizes[i])); 1592 } 1593 } 1594 1595 if (hasRepeatAuto) { 1596 const auto* const autoRepeatValue = aTrackList.GetRepeatAutoValue(); 1597 const auto repeatLineNames = autoRepeatValue->line_names.AsSpan(); 1598 MOZ_ASSERT(repeatLineNames.Length() >= 2); 1599 // Number of tracks inside the repeat, not including any repetitions. 1600 // Check that if we have truncated the number of tracks due to overflowing 1601 // the maximum track limit then we also truncate this repeat count. 1602 MOZ_ASSERT(repeatLineNames.Length() == 1603 autoRepeatValue->track_sizes.len + 1); 1604 // If we have truncated the first repetition of repeat tracks, then we 1605 // can't index using autoRepeatValue->track_sizes.len, and 1606 // aTrackInfo.mRemovedRepeatTracks.Length() will account for all repeat 1607 // tracks that haven't been truncated. 1608 const uint32_t numRepeatTracks = 1609 std::min(aTrackInfo.mRemovedRepeatTracks.Length(), 1610 autoRepeatValue->track_sizes.len); 1611 MOZ_ASSERT(repeatLineNames.Length() >= numRepeatTracks + 1); 1612 // The total of all tracks in all repetitions of the repeat. 1613 const uint32_t totalNumRepeatTracks = 1614 aTrackInfo.mRemovedRepeatTracks.Length(); 1615 const uint32_t repeatStart = aTrackInfo.mRepeatFirstTrack; 1616 // We need to skip over any track sizes which were resolved to 0 by 1617 // collapsed tracks. Keep track of the iteration separately. 1618 const auto explicitTrackSizeBegin = 1619 trackSizes.cbegin() + numLeadingImplicitTracks; 1620 const auto explicitTrackSizeEnd = 1621 explicitTrackSizeBegin + numExplicitTracks; 1622 auto trackSizeIter = explicitTrackSizeBegin; 1623 // Write any leading explicit tracks before the repeat. 1624 for (uint32_t i = 0; i < repeatStart; i++) { 1625 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]); 1626 valueList->AppendCSSValue(AppUnitsToCSSValue(*trackSizeIter++)); 1627 } 1628 auto lineNameIter = aTrackInfo.mResolvedLineNames.cbegin() + repeatStart; 1629 // Write the track names at the start of the repeat, including the names 1630 // at the end of the last non-repeat track. Unlike all later repeat line 1631 // name lists, this one needs the resolved line name which includes both 1632 // the last non-repeat line names and the leading repeat line names. 1633 AppendGridLineNames(valueList, *lineNameIter++); 1634 { 1635 // Write out the first repeat value, checking for size zero (removed 1636 // track). 1637 const nscoord firstRepeatTrackSize = 1638 (!aTrackInfo.mRemovedRepeatTracks[0]) ? *trackSizeIter++ : 0; 1639 valueList->AppendCSSValue(AppUnitsToCSSValue(firstRepeatTrackSize)); 1640 } 1641 // Write the line names and track sizes inside the repeat, checking for 1642 // removed tracks (size 0). 1643 for (uint32_t i = 1; i < totalNumRepeatTracks; i++) { 1644 const uint32_t repeatIndex = i % numRepeatTracks; 1645 // If we are rolling over from one repetition to the next, include track 1646 // names from both the end of the previous repeat and the start of the 1647 // next. 1648 if (repeatIndex == 0) { 1649 AppendGridLineNames(valueList, 1650 repeatLineNames[numRepeatTracks].AsSpan(), 1651 repeatLineNames[0].AsSpan()); 1652 } else { 1653 AppendGridLineNames(valueList, repeatLineNames[repeatIndex].AsSpan()); 1654 } 1655 MOZ_ASSERT(aTrackInfo.mRemovedRepeatTracks[i] || 1656 trackSizeIter != explicitTrackSizeEnd); 1657 const nscoord repeatTrackSize = 1658 (!aTrackInfo.mRemovedRepeatTracks[i]) ? *trackSizeIter++ : 0; 1659 valueList->AppendCSSValue(AppUnitsToCSSValue(repeatTrackSize)); 1660 } 1661 // The resolved line names include a single repetition of the auto-repeat 1662 // line names. Skip over those. 1663 lineNameIter += numRepeatTracks - 1; 1664 // Write out any more tracks after the repeat. 1665 while (trackSizeIter != explicitTrackSizeEnd) { 1666 AppendGridLineNames(valueList, *lineNameIter++); 1667 valueList->AppendCSSValue(AppUnitsToCSSValue(*trackSizeIter++)); 1668 } 1669 // Write the final trailing line name. 1670 AppendGridLineNames(valueList, *lineNameIter++); 1671 } else if (numExplicitTracks > 0) { 1672 // If there are explicit tracks but no repeat tracks, just serialize those. 1673 for (uint32_t i = 0;; i++) { 1674 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]); 1675 if (i == numExplicitTracks) { 1676 break; 1677 } 1678 valueList->AppendCSSValue( 1679 AppUnitsToCSSValue(trackSizes[i + numLeadingImplicitTracks])); 1680 } 1681 } 1682 // Add any trailing implicit tracks. 1683 if (serializeImplicit) { 1684 for (uint32_t i = numLeadingImplicitTracks + numExplicitTracks; 1685 i < numSizes; ++i) { 1686 valueList->AppendCSSValue(AppUnitsToCSSValue(trackSizes[i])); 1687 } 1688 } 1689 1690 return valueList.forget(); 1691 } 1692 1693 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateColumns() { 1694 nsGridContainerFrame* gridFrame = 1695 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame); 1696 if (!gridFrame) { 1697 // The element doesn't have a box - return the computed value. 1698 // https://drafts.csswg.org/css-grid/#resolved-track-list 1699 nsAutoCString string; 1700 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_columns, 1701 string); 1702 auto value = MakeRefPtr<nsROCSSPrimitiveValue>(); 1703 value->SetString(string); 1704 return value.forget(); 1705 } 1706 1707 // GetGridFrameWithComputedInfo() above ensures that this returns non-null: 1708 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateColumns(); 1709 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns, 1710 *info); 1711 } 1712 1713 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateRows() { 1714 nsGridContainerFrame* gridFrame = 1715 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame); 1716 if (!gridFrame) { 1717 // The element doesn't have a box - return the computed value. 1718 // https://drafts.csswg.org/css-grid/#resolved-track-list 1719 nsAutoCString string; 1720 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_rows, 1721 string); 1722 auto value = MakeRefPtr<nsROCSSPrimitiveValue>(); 1723 value->SetString(string); 1724 return value.forget(); 1725 } 1726 1727 // GetGridFrameWithComputedInfo() above ensures that this returns non-null: 1728 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateRows(); 1729 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, *info); 1730 } 1731 1732 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingTop() { 1733 return GetPaddingWidthFor(eSideTop); 1734 } 1735 1736 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingBottom() { 1737 return GetPaddingWidthFor(eSideBottom); 1738 } 1739 1740 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingLeft() { 1741 return GetPaddingWidthFor(eSideLeft); 1742 } 1743 1744 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingRight() { 1745 return GetPaddingWidthFor(eSideRight); 1746 } 1747 1748 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginTop() { 1749 return GetMarginFor(eSideTop); 1750 } 1751 1752 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginBottom() { 1753 return GetMarginFor(eSideBottom); 1754 } 1755 1756 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginLeft() { 1757 return GetMarginFor(eSideLeft); 1758 } 1759 1760 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginRight() { 1761 return GetMarginFor(eSideRight); 1762 } 1763 1764 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetHeight() { 1765 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) { 1766 AssertFlushedPendingReflows(); 1767 const nsMargin adjustedValues = GetAdjustedValuesForBoxSizing(); 1768 return AppUnitsToCSSValue(mInnerFrame->GetContentRect().height + 1769 adjustedValues.TopBottom()); 1770 } 1771 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1772 SetValueToSize( 1773 val, StylePosition()->GetHeight(AnchorPosResolutionParams::From(this))); 1774 return val.forget(); 1775 } 1776 1777 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetWidth() { 1778 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) { 1779 AssertFlushedPendingReflows(); 1780 nsMargin adjustedValues = GetAdjustedValuesForBoxSizing(); 1781 return AppUnitsToCSSValue(mInnerFrame->GetContentRect().width + 1782 adjustedValues.LeftRight()); 1783 } 1784 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1785 SetValueToSize( 1786 val, StylePosition()->GetWidth(AnchorPosResolutionParams::From(this))); 1787 return val.forget(); 1788 } 1789 1790 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxHeight() { 1791 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1792 SetValueToMaxSize(val, StylePosition()->GetMaxHeight( 1793 AnchorPosResolutionParams::From(this))); 1794 return val.forget(); 1795 } 1796 1797 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxWidth() { 1798 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1799 SetValueToMaxSize( 1800 val, StylePosition()->GetMaxWidth(AnchorPosResolutionParams::From(this))); 1801 return val.forget(); 1802 } 1803 1804 /** 1805 * This function indicates whether we should return "auto" as the 1806 * getComputedStyle() result for the (default) "min-width: auto" and 1807 * "min-height: auto" CSS values. 1808 * 1809 * https://drafts.csswg.org/css-sizing-3/#valdef-width-auto says that 1810 * "Unless otherwise defined by the relevant layout module ... it resolves to a 1811 * used value of 0." 1812 * 1813 * Exceptions to this are explained in the various 'return true' early-returns 1814 * here. 1815 */ 1816 bool nsComputedDOMStyle::ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis) { 1817 if (!mOuterFrame) { 1818 // Per https://drafts.csswg.org/css-sizing/#valdef-width-auto 1819 // min-{width,height}:auto "resolves to zero when no box is generated". 1820 return false; 1821 } 1822 if (mOuterFrame->IsFlexOrGridItem()) { 1823 // Flex and grid items have special significance for min-width:auto and 1824 // min-height:auto, so we faithfully report "auto" from getComputedStyle 1825 // for those frames: 1826 return true; 1827 } 1828 if (mOuterFrame->StylePosition()->mAspectRatio != StyleAspectRatio::Auto()) { 1829 // Frames with non-default CSS 'aspect-ratio' have special significance 1830 // for min-{width,height}:auto as well, so we faithfully report "auto" 1831 // for them too, per https://github.com/w3c/csswg-drafts/issues/11716 1832 return true; 1833 } 1834 1835 // If we're not in one of the special cases above, then we return false 1836 // which means that "auto" will get reported as "0" in getComputedStyle. 1837 return false; 1838 } 1839 1840 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinHeight() { 1841 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1842 auto minHeight = 1843 StylePosition()->GetMinHeight(AnchorPosResolutionParams::From(this)); 1844 1845 if (minHeight->IsAuto() && 1846 !ShouldHonorMinSizeAutoInAxis(PhysicalAxis::Vertical)) { 1847 minHeight = AnchorResolvedSizeHelper::Zero(); 1848 } 1849 1850 SetValueToSize(val, minHeight); 1851 return val.forget(); 1852 } 1853 1854 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinWidth() { 1855 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1856 1857 auto minWidth = 1858 StylePosition()->GetMinWidth(AnchorPosResolutionParams::From(this)); 1859 1860 if (minWidth->IsAuto() && 1861 !ShouldHonorMinSizeAutoInAxis(PhysicalAxis::Horizontal)) { 1862 minWidth = AnchorResolvedSizeHelper::Zero(); 1863 } 1864 1865 SetValueToSize(val, minWidth); 1866 return val.forget(); 1867 } 1868 1869 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetLeft() { 1870 return GetOffsetWidthFor(eSideLeft); 1871 } 1872 1873 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetRight() { 1874 return GetOffsetWidthFor(eSideRight); 1875 } 1876 1877 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTop() { 1878 return GetOffsetWidthFor(eSideTop); 1879 } 1880 1881 already_AddRefed<CSSValue> nsComputedDOMStyle::GetOffsetWidthFor( 1882 mozilla::Side aSide) { 1883 const nsStyleDisplay* display = StyleDisplay(); 1884 1885 mozilla::StylePositionProperty position = display->mPosition; 1886 if (!mOuterFrame) { 1887 // GetNonStaticPositionOffset or GetAbsoluteOffset don't handle elements 1888 // without frames in any sensible way. GetStaticOffset, however, is perfect 1889 // for that case. 1890 position = StylePositionProperty::Static; 1891 } 1892 1893 switch (position) { 1894 case StylePositionProperty::Static: 1895 return GetStaticOffset(aSide); 1896 case StylePositionProperty::Sticky: 1897 return GetNonStaticPositionOffset( 1898 aSide, false, &nsComputedDOMStyle::GetScrollFrameContentWidth, 1899 &nsComputedDOMStyle::GetScrollFrameContentHeight); 1900 case StylePositionProperty::Absolute: 1901 case StylePositionProperty::Fixed: 1902 return GetAbsoluteOffset(aSide); 1903 case StylePositionProperty::Relative: 1904 return GetNonStaticPositionOffset( 1905 aSide, true, &nsComputedDOMStyle::GetCBContentWidth, 1906 &nsComputedDOMStyle::GetCBContentHeight); 1907 default: 1908 MOZ_ASSERT_UNREACHABLE("Invalid position"); 1909 return nullptr; 1910 } 1911 } 1912 1913 static_assert(eSideTop == 0 && eSideRight == 1 && eSideBottom == 2 && 1914 eSideLeft == 3, 1915 "box side constants not as expected for NS_OPPOSITE_SIDE"); 1916 #define NS_OPPOSITE_SIDE(s_) mozilla::Side(((s_) + 2) & 3) 1917 1918 already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset( 1919 mozilla::Side aSide, bool aResolveAuto, PercentageBaseGetter aWidthGetter, 1920 PercentageBaseGetter aHeightGetter) { 1921 const nsStylePosition* positionData = StylePosition(); 1922 int32_t sign = 1; 1923 const auto anchorResolutionParams = 1924 AnchorPosOffsetResolutionParams::UseCBFrameSize( 1925 AnchorPosResolutionParams::From(this)); 1926 auto coord = 1927 positionData->GetAnchorResolvedInset(aSide, anchorResolutionParams); 1928 1929 if (coord->IsAuto()) { 1930 if (!aResolveAuto) { 1931 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 1932 val->SetString("auto"); 1933 return val.forget(); 1934 } 1935 coord = positionData->GetAnchorResolvedInset(NS_OPPOSITE_SIDE(aSide), 1936 anchorResolutionParams); 1937 sign = -1; 1938 } 1939 if (coord->IsAuto()) { 1940 return PixelsToCSSValue(0.0f); 1941 } 1942 1943 const auto& lp = coord->AsLengthPercentage(); 1944 if (lp.ConvertsToLength()) { 1945 return PixelsToCSSValue(sign * lp.ToLengthInCSSPixels()); 1946 } 1947 1948 PercentageBaseGetter baseGetter = (aSide == eSideLeft || aSide == eSideRight) 1949 ? aWidthGetter 1950 : aHeightGetter; 1951 nscoord percentageBase; 1952 if (!(this->*baseGetter)(percentageBase)) { 1953 return PixelsToCSSValue(0.0f); 1954 } 1955 1956 return AppUnitsToCSSValue(sign * lp.Resolve(percentageBase)); 1957 } 1958 1959 already_AddRefed<CSSValue> nsComputedDOMStyle::GetAbsoluteOffset( 1960 mozilla::Side aSide) { 1961 const auto anchorResolutionParams = 1962 AnchorPosOffsetResolutionParams::UseCBFrameSize( 1963 AnchorPosResolutionParams::From(this)); 1964 const auto coord = 1965 StylePosition()->GetAnchorResolvedInset(aSide, anchorResolutionParams); 1966 const auto oppositeCoord = StylePosition()->GetAnchorResolvedInset( 1967 NS_OPPOSITE_SIDE(aSide), anchorResolutionParams); 1968 1969 if (coord->IsAuto() || oppositeCoord->IsAuto()) { 1970 return AppUnitsToCSSValue(GetUsedAbsoluteOffset(aSide)); 1971 } 1972 1973 // TODO(dshin): We're resolving anchor offset potentially twice... 1974 return GetNonStaticPositionOffset( 1975 aSide, false, &nsComputedDOMStyle::GetCBPaddingRectWidth, 1976 &nsComputedDOMStyle::GetCBPaddingRectHeight); 1977 } 1978 1979 nscoord nsComputedDOMStyle::GetUsedAbsoluteOffset(mozilla::Side aSide) { 1980 MOZ_ASSERT(mOuterFrame, "need a frame, so we can call GetContainingBlock()"); 1981 1982 nsIFrame* container = mOuterFrame->GetContainingBlock(); 1983 nsMargin margin = mOuterFrame->GetUsedMargin(); 1984 nsMargin border = container->GetUsedBorder(); 1985 nsMargin scrollbarSizes(0, 0, 0, 0); 1986 nsRect rect = mOuterFrame->GetRect(); 1987 nsRect containerRect = container->GetRect(); 1988 1989 if (container->IsViewportFrame()) { 1990 // For absolutely positioned frames scrollbars are taken into 1991 // account by virtue of getting a containing block that does 1992 // _not_ include the scrollbars. For fixed positioned frames, 1993 // the containing block is the viewport, which _does_ include 1994 // scrollbars. We have to do some extra work. 1995 // the first child in the default frame list is what we want 1996 nsIFrame* scrollingChild = container->PrincipalChildList().FirstChild(); 1997 ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(scrollingChild); 1998 if (scrollContainerFrame) { 1999 scrollbarSizes = scrollContainerFrame->GetActualScrollbarSizes(); 2000 } 2001 2002 // The viewport size might have been expanded by the visual viewport or 2003 // the minimum-scale size. 2004 const ViewportFrame* viewportFrame = do_QueryFrame(container); 2005 MOZ_ASSERT(viewportFrame); 2006 containerRect.SizeTo( 2007 viewportFrame->AdjustViewportSizeForFixedPosition(containerRect)); 2008 } else if (container->IsGridContainerFrame() && 2009 mOuterFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 2010 containerRect = nsGridContainerFrame::GridItemCB(mOuterFrame); 2011 rect.MoveBy(-containerRect.x, -containerRect.y); 2012 } 2013 2014 nscoord offset = 0; 2015 switch (aSide) { 2016 case eSideTop: 2017 offset = rect.y - margin.top - border.top - scrollbarSizes.top; 2018 2019 break; 2020 case eSideRight: 2021 offset = containerRect.width - rect.width - rect.x - margin.right - 2022 border.right - scrollbarSizes.right; 2023 2024 break; 2025 case eSideBottom: 2026 offset = containerRect.height - rect.height - rect.y - margin.bottom - 2027 border.bottom - scrollbarSizes.bottom; 2028 2029 break; 2030 case eSideLeft: 2031 offset = rect.x - margin.left - border.left - scrollbarSizes.left; 2032 2033 break; 2034 default: 2035 NS_ERROR("Invalid side"); 2036 break; 2037 } 2038 2039 return offset; 2040 } 2041 2042 already_AddRefed<CSSValue> nsComputedDOMStyle::GetStaticOffset( 2043 mozilla::Side aSide) { 2044 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 2045 const auto resolved = StylePosition()->GetAnchorResolvedInset( 2046 aSide, AnchorPosOffsetResolutionParams::UseCBFrameSize( 2047 AnchorPosResolutionParams::From(this))); 2048 if (resolved->IsAuto()) { 2049 val->SetString("auto"); 2050 } else { 2051 // Any calc node containing anchor should have been resolved as invalid by 2052 // this point. 2053 SetValueToLengthPercentage(val, resolved->AsLengthPercentage(), false); 2054 } 2055 return val.forget(); 2056 } 2057 2058 already_AddRefed<CSSValue> nsComputedDOMStyle::GetPaddingWidthFor( 2059 mozilla::Side aSide) { 2060 const auto& padding = StylePadding()->mPadding.Get(aSide); 2061 if (!mInnerFrame || !PaddingNeedsUsedValue(padding, *mComputedStyle)) { 2062 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 2063 SetValueToLengthPercentage(val, padding, true); 2064 return val.forget(); 2065 } 2066 AssertFlushedPendingReflows(); 2067 return AppUnitsToCSSValue(mInnerFrame->GetUsedPadding().Side(aSide)); 2068 } 2069 2070 already_AddRefed<CSSValue> nsComputedDOMStyle::GetMarginFor(Side aSide) { 2071 // Use raw margin here, layout-dependent margins should be stored in used 2072 // margin. 2073 const auto& margin = StyleMargin()->mMargin.Get(aSide); 2074 if (!mInnerFrame || margin.ConvertsToLength()) { 2075 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 2076 SetValueToMargin(val, margin); 2077 return val.forget(); 2078 } 2079 AssertFlushedPendingReflows(); 2080 // For tables, GetUsedMargin always returns an empty margin, so we 2081 // should read the margin from the table wrapper frame instead. 2082 NS_ASSERTION( 2083 mOuterFrame == mInnerFrame || mInnerFrame->GetUsedMargin() == nsMargin(), 2084 "Inner tables must have zero margins"); 2085 return AppUnitsToCSSValue(mOuterFrame->GetUsedMargin().Side(aSide)); 2086 } 2087 2088 static void SetValueToExtremumLength(nsROCSSPrimitiveValue* aValue, 2089 nsIFrame::ExtremumLength aSize) { 2090 switch (aSize) { 2091 case nsIFrame::ExtremumLength::MaxContent: 2092 return aValue->SetString("max-content"); 2093 case nsIFrame::ExtremumLength::MinContent: 2094 return aValue->SetString("min-content"); 2095 case nsIFrame::ExtremumLength::MozAvailable: 2096 return aValue->SetString("-moz-available"); 2097 case nsIFrame::ExtremumLength::Stretch: { 2098 // By default we serialize this value using the standard "stretch" 2099 // keyword. The exception is when that keyword is explicitly preffed off 2100 // and the legacy "-webkit-fill-available" keyword is preffed on; in 2101 // that case, we serialize to the legacy webkit-prefixed alias, to 2102 // ensure that we can round-trip properly. 2103 if (!StaticPrefs::layout_css_stretch_size_keyword_enabled() && 2104 StaticPrefs::layout_css_webkit_fill_available_enabled()) { 2105 return aValue->SetString("-webkit-fill-available"); 2106 } 2107 return aValue->SetString("stretch"); 2108 } 2109 case nsIFrame::ExtremumLength::FitContent: 2110 return aValue->SetString("fit-content"); 2111 case nsIFrame::ExtremumLength::FitContentFunction: 2112 MOZ_ASSERT_UNREACHABLE("fit-content() should be handled separately"); 2113 } 2114 MOZ_ASSERT_UNREACHABLE("Unknown extremum length?"); 2115 } 2116 2117 void nsComputedDOMStyle::SetValueFromFitContentFunction( 2118 nsROCSSPrimitiveValue* aValue, const LengthPercentage& aLength) { 2119 nsAutoString argumentStr; 2120 SetValueToLengthPercentage(aValue, aLength, true); 2121 aValue->GetCssText(argumentStr); 2122 2123 nsAutoString fitContentStr; 2124 fitContentStr.AppendLiteral("fit-content("); 2125 fitContentStr.Append(argumentStr); 2126 fitContentStr.Append(u')'); 2127 aValue->SetString(fitContentStr); 2128 } 2129 2130 void nsComputedDOMStyle::SetValueToSize(nsROCSSPrimitiveValue* aValue, 2131 const AnchorResolvedSize& aSize) { 2132 if (aSize->IsAuto()) { 2133 return aValue->SetString("auto"); 2134 } 2135 if (aSize->IsFitContentFunction()) { 2136 return SetValueFromFitContentFunction(aValue, 2137 aSize->AsFitContentFunction()); 2138 } 2139 if (auto length = nsIFrame::ToExtremumLength(*aSize)) { 2140 return SetValueToExtremumLength(aValue, *length); 2141 } 2142 MOZ_ASSERT(aSize->IsLengthPercentage()); 2143 SetValueToLengthPercentage(aValue, aSize->AsLengthPercentage(), true); 2144 } 2145 2146 void nsComputedDOMStyle::SetValueToMaxSize(nsROCSSPrimitiveValue* aValue, 2147 const AnchorResolvedMaxSize& aSize) { 2148 if (aSize->IsNone()) { 2149 return aValue->SetString("none"); 2150 } 2151 if (aSize->IsFitContentFunction()) { 2152 return SetValueFromFitContentFunction(aValue, 2153 aSize->AsFitContentFunction()); 2154 } 2155 if (auto length = nsIFrame::ToExtremumLength(*aSize)) { 2156 return SetValueToExtremumLength(aValue, *length); 2157 } 2158 MOZ_ASSERT(aSize->IsLengthPercentage()); 2159 SetValueToLengthPercentage(aValue, aSize->AsLengthPercentage(), true); 2160 } 2161 2162 void nsComputedDOMStyle::SetValueToLengthPercentageOrAuto( 2163 nsROCSSPrimitiveValue* aValue, const LengthPercentageOrAuto& aSize, 2164 bool aClampNegativeCalc) { 2165 if (aSize.IsAuto()) { 2166 return aValue->SetString("auto"); 2167 } 2168 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), 2169 aClampNegativeCalc); 2170 } 2171 2172 void nsComputedDOMStyle::SetValueToMargin(nsROCSSPrimitiveValue* aValue, 2173 const mozilla::StyleMargin& aMargin) { 2174 // May have to compute `anchor-size()` value here. 2175 if (!aMargin.IsLengthPercentage()) { 2176 aValue->SetString("auto"); 2177 return; 2178 } 2179 SetValueToLengthPercentage(aValue, aMargin.AsLengthPercentage(), false); 2180 } 2181 2182 void nsComputedDOMStyle::SetValueToLengthPercentage( 2183 nsROCSSPrimitiveValue* aValue, const mozilla::LengthPercentage& aLength, 2184 bool aClampNegativeCalc) { 2185 if (aLength.ConvertsToLength()) { 2186 CSSCoord length = aLength.ToLengthInCSSPixels(); 2187 if (aClampNegativeCalc) { 2188 length = std::max(float(length), 0.0f); 2189 } 2190 return SetValueToPixels(aValue, length); 2191 } 2192 if (aLength.ConvertsToPercentage()) { 2193 float result = aLength.ToPercentage(); 2194 if (aClampNegativeCalc) { 2195 result = std::max(result, 0.0f); 2196 } 2197 return aValue->SetPercent(result); 2198 } 2199 2200 nsAutoCString result; 2201 Servo_LengthPercentage_ToCss(&aLength, &result); 2202 aValue->SetString(result); 2203 } 2204 2205 bool nsComputedDOMStyle::GetCBContentWidth(nscoord& aWidth) { 2206 if (!mOuterFrame) { 2207 return false; 2208 } 2209 2210 AssertFlushedPendingReflows(); 2211 2212 aWidth = mOuterFrame->GetContainingBlock()->GetContentRect().width; 2213 return true; 2214 } 2215 2216 bool nsComputedDOMStyle::GetCBContentHeight(nscoord& aHeight) { 2217 if (!mOuterFrame) { 2218 return false; 2219 } 2220 2221 AssertFlushedPendingReflows(); 2222 2223 aHeight = mOuterFrame->GetContainingBlock()->GetContentRect().height; 2224 return true; 2225 } 2226 2227 bool nsComputedDOMStyle::GetCBPaddingRectWidth(nscoord& aWidth) { 2228 if (!mOuterFrame) { 2229 return false; 2230 } 2231 2232 AssertFlushedPendingReflows(); 2233 2234 aWidth = mOuterFrame->GetContainingBlock()->GetPaddingRect().width; 2235 return true; 2236 } 2237 2238 bool nsComputedDOMStyle::GetCBPaddingRectHeight(nscoord& aHeight) { 2239 if (!mOuterFrame) { 2240 return false; 2241 } 2242 2243 AssertFlushedPendingReflows(); 2244 2245 aHeight = mOuterFrame->GetContainingBlock()->GetPaddingRect().height; 2246 return true; 2247 } 2248 2249 bool nsComputedDOMStyle::GetScrollFrameContentWidth(nscoord& aWidth) { 2250 if (!mOuterFrame) { 2251 return false; 2252 } 2253 2254 AssertFlushedPendingReflows(); 2255 2256 ScrollContainerFrame* scrollContainerFrame = 2257 nsLayoutUtils::GetNearestScrollContainerFrame( 2258 mOuterFrame->GetParent(), 2259 nsLayoutUtils::SCROLLABLE_SAME_DOC | 2260 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); 2261 2262 if (!scrollContainerFrame) { 2263 return false; 2264 } 2265 aWidth = scrollContainerFrame->GetScrolledFrame() 2266 ->GetContentRectRelativeToSelf() 2267 .width; 2268 return true; 2269 } 2270 2271 bool nsComputedDOMStyle::GetScrollFrameContentHeight(nscoord& aHeight) { 2272 if (!mOuterFrame) { 2273 return false; 2274 } 2275 2276 AssertFlushedPendingReflows(); 2277 2278 ScrollContainerFrame* scrollContainerFrame = 2279 nsLayoutUtils::GetNearestScrollContainerFrame( 2280 mOuterFrame->GetParent(), 2281 nsLayoutUtils::SCROLLABLE_SAME_DOC | 2282 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); 2283 2284 if (!scrollContainerFrame) { 2285 return false; 2286 } 2287 aHeight = scrollContainerFrame->GetScrolledFrame() 2288 ->GetContentRectRelativeToSelf() 2289 .height; 2290 return true; 2291 } 2292 2293 bool nsComputedDOMStyle::GetFrameBorderRectWidth(nscoord& aWidth) { 2294 if (!mInnerFrame) { 2295 return false; 2296 } 2297 2298 AssertFlushedPendingReflows(); 2299 2300 aWidth = mInnerFrame->GetSize().width; 2301 return true; 2302 } 2303 2304 bool nsComputedDOMStyle::GetFrameBorderRectHeight(nscoord& aHeight) { 2305 if (!mInnerFrame) { 2306 return false; 2307 } 2308 2309 AssertFlushedPendingReflows(); 2310 2311 aHeight = mInnerFrame->GetSize().height; 2312 return true; 2313 } 2314 2315 /* If the property is "none", hand back "none" wrapped in a value. 2316 * Otherwise, compute the aggregate transform matrix and hands it back in a 2317 * "matrix" wrapper. 2318 */ 2319 already_AddRefed<CSSValue> nsComputedDOMStyle::GetTransformValue( 2320 const StyleTransform& aTransform) { 2321 /* If there are no transforms, then we should construct a single-element 2322 * entry and hand it back. 2323 */ 2324 if (aTransform.IsNone()) { 2325 auto val = MakeRefPtr<nsROCSSPrimitiveValue>(); 2326 val->SetString("none"); 2327 return val.forget(); 2328 } 2329 2330 /* Otherwise, we need to compute the current value of the transform matrix, 2331 * store it in a string, and hand it back to the caller. 2332 */ 2333 2334 /* Use the inner frame for the reference box. If we don't have an inner 2335 * frame we use empty dimensions to allow us to continue (and percentage 2336 * values in the transform will simply give broken results). 2337 * TODO: There is no good way for us to represent the case where there's no 2338 * frame, which is problematic. The reason is that when we have percentage 2339 * transforms, there are a total of four stored matrix entries that influence 2340 * the transform based on the size of the element. However, this poses a 2341 * problem, because only two of these values can be explicitly referenced 2342 * using the named transforms. Until a real solution is found, we'll just 2343 * use this approach. 2344 */ 2345 nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame, nsRect()); 2346 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms( 2347 aTransform, refBox, float(mozilla::AppUnitsPerCSSPixel())); 2348 2349 return MatrixToCSSValue(matrix); 2350 } 2351 2352 already_AddRefed<CSSValue> nsComputedDOMStyle::DummyGetter() { 2353 MOZ_CRASH("DummyGetter is not supposed to be invoked"); 2354 } 2355 2356 static void MarkComputedStyleMapDirty(const char* aPref, void* aMap) { 2357 static_cast<ComputedStyleMap*>(aMap)->MarkDirty(); 2358 } 2359 2360 void nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent) { 2361 NS_ASSERTION(mElement == aContent, "didn't we register mElement?"); 2362 NS_ASSERTION(mResolvedComputedStyle, 2363 "should have only registered an observer when " 2364 "mResolvedComputedStyle is true"); 2365 2366 ClearComputedStyle(); 2367 } 2368 2369 /* static */ 2370 ComputedStyleMap* nsComputedDOMStyle::GetComputedStyleMap() { 2371 static ComputedStyleMap map{}; 2372 return ↦ 2373 } 2374 2375 static StaticAutoPtr<nsTArray<const char*>> gCallbackPrefs; 2376 2377 /* static */ 2378 void nsComputedDOMStyle::RegisterPrefChangeCallbacks() { 2379 // Note that this will register callbacks for all properties with prefs, not 2380 // just those that are implemented on computed style objects, as it's not 2381 // easy to grab specific property data from ServoCSSPropList.h based on the 2382 // entries iterated in nsComputedDOMStylePropertyList.h. 2383 2384 AutoTArray<const char*, 64> prefs; 2385 for (const auto* p = nsCSSProps::kPropertyPrefTable; 2386 p->mPropId != eCSSProperty_UNKNOWN; p++) { 2387 // Many properties are controlled by the same preference, so de-duplicate 2388 // them before adding observers. 2389 // 2390 // Note: This is done by pointer comparison, which works because the mPref 2391 // members are string literals from the same same translation unit, and are 2392 // therefore de-duplicated by the compiler. On the off chance that we wind 2393 // up with some duplicates with different pointers, though, it's not a bit 2394 // deal. 2395 if (!prefs.ContainsSorted(p->mPref)) { 2396 prefs.InsertElementSorted(p->mPref); 2397 } 2398 } 2399 2400 prefs.AppendElement(nullptr); 2401 2402 MOZ_ASSERT(!gCallbackPrefs); 2403 gCallbackPrefs = new nsTArray<const char*>(std::move(prefs)); 2404 2405 Preferences::RegisterCallbacks(MarkComputedStyleMapDirty, 2406 gCallbackPrefs->Elements(), 2407 GetComputedStyleMap()); 2408 } 2409 2410 /* static */ 2411 void nsComputedDOMStyle::UnregisterPrefChangeCallbacks() { 2412 if (!gCallbackPrefs) { 2413 return; 2414 } 2415 2416 Preferences::UnregisterCallbacks(MarkComputedStyleMapDirty, 2417 gCallbackPrefs->Elements(), 2418 GetComputedStyleMap()); 2419 gCallbackPrefs = nullptr; 2420 }