tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CSSStyleRule.cpp (14410B)


      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 #include "mozilla/dom/CSSStyleRule.h"
      8 
      9 #include "mozilla/CSSEnabledState.h"
     10 #include "mozilla/DeclarationBlock.h"
     11 #include "mozilla/PseudoStyleType.h"
     12 #include "mozilla/ServoBindings.h"
     13 #include "mozilla/dom/CSSScopeRule.h"
     14 #include "mozilla/dom/CSSStyleRuleBinding.h"
     15 #include "mozilla/dom/ShadowRoot.h"
     16 #include "mozilla/dom/StylePropertyMap.h"
     17 #include "nsCSSPseudoElements.h"
     18 #include "nsISupports.h"
     19 
     20 namespace mozilla::dom {
     21 
     22 // -- CSSStyleRuleDeclaration ---------------------------------------
     23 
     24 CSSStyleRuleDeclaration::CSSStyleRuleDeclaration(
     25    already_AddRefed<StyleLockedDeclarationBlock> aDecls)
     26    : mDecls(new DeclarationBlock(std::move(aDecls))) {
     27  mDecls->SetOwningRule(Rule());
     28 }
     29 
     30 CSSStyleRuleDeclaration::~CSSStyleRuleDeclaration() {
     31  mDecls->SetOwningRule(nullptr);
     32 }
     33 
     34 // QueryInterface implementation for CSSStyleRuleDeclaration
     35 NS_INTERFACE_MAP_BEGIN(CSSStyleRuleDeclaration)
     36  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     37  // We forward the cycle collection interfaces to Rule(), which is
     38  // never null (in fact, we're part of that object!)
     39  if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
     40      aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
     41    return Rule()->QueryInterface(aIID, aInstancePtr);
     42  }
     43 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
     44 
     45 NS_IMPL_ADDREF_USING_AGGREGATOR(CSSStyleRuleDeclaration, Rule())
     46 NS_IMPL_RELEASE_USING_AGGREGATOR(CSSStyleRuleDeclaration, Rule())
     47 
     48 /* nsDOMCSSDeclaration implementation */
     49 
     50 css::Rule* CSSStyleRuleDeclaration::GetParentRule() { return Rule(); }
     51 
     52 nsINode* CSSStyleRuleDeclaration::GetAssociatedNode() const {
     53  return Rule()->GetAssociatedDocumentOrShadowRoot();
     54 }
     55 
     56 nsISupports* CSSStyleRuleDeclaration::GetParentObject() const {
     57  return Rule()->GetParentObject();
     58 }
     59 
     60 DeclarationBlock* CSSStyleRuleDeclaration::GetOrCreateCSSDeclaration(
     61    Operation aOperation, DeclarationBlock** aCreated) {
     62  if (aOperation != Operation::Read) {
     63    if (StyleSheet* sheet = Rule()->GetStyleSheet()) {
     64      sheet->WillDirty();
     65    }
     66  }
     67  return mDecls;
     68 }
     69 
     70 void CSSStyleRuleDeclaration::SetRawAfterClone(
     71    RefPtr<StyleLockedDeclarationBlock> aRaw) {
     72  auto block = MakeRefPtr<DeclarationBlock>(aRaw.forget());
     73  mDecls->SetOwningRule(nullptr);
     74  mDecls = std::move(block);
     75  mDecls->SetOwningRule(Rule());
     76 }
     77 
     78 void CSSStyleRule::SetRawAfterClone(RefPtr<StyleLockedStyleRule> aRaw) {
     79  mRawRule = std::move(aRaw);
     80  mDecls.SetRawAfterClone(Servo_StyleRule_GetStyle(mRawRule).Consume());
     81  GroupRule::DidSetRawAfterClone();
     82 }
     83 
     84 already_AddRefed<StyleLockedCssRules> CSSStyleRule::GetOrCreateRawRules() {
     85  return Servo_StyleRule_EnsureRules(mRawRule, IsReadOnly()).Consume();
     86 }
     87 
     88 nsresult CSSStyleRuleDeclaration::SetCSSDeclaration(
     89    DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
     90  CSSStyleRule* rule = Rule();
     91  RefPtr<DeclarationBlock> oldDecls;
     92  if (aDecl != mDecls) {
     93    oldDecls = std::move(mDecls);
     94    oldDecls->SetOwningRule(nullptr);
     95    Servo_StyleRule_SetStyle(rule->Raw(), aDecl->Raw());
     96    mDecls = aDecl;
     97    mDecls->SetOwningRule(rule);
     98  }
     99  if (StyleSheet* sheet = rule->GetStyleSheet()) {
    100    sheet->RuleChanged(rule, {StyleRuleChangeKind::StyleRuleDeclarations,
    101                              oldDecls ? oldDecls.get() : aDecl, aDecl});
    102  }
    103  return NS_OK;
    104 }
    105 
    106 nsDOMCSSDeclaration::ParsingEnvironment
    107 CSSStyleRuleDeclaration::GetParsingEnvironment(nsIPrincipal*) const {
    108  return GetParsingEnvironmentForRule(Rule(), StyleCssRuleType::Style);
    109 }
    110 
    111 // -- CSSStyleRule --------------------------------------------------
    112 
    113 CSSStyleRule::CSSStyleRule(already_AddRefed<StyleLockedStyleRule> aRawRule,
    114                           StyleSheet* aSheet, css::Rule* aParentRule,
    115                           uint32_t aLine, uint32_t aColumn)
    116    : GroupRule(aSheet, aParentRule, aLine, aColumn),
    117      mRawRule(aRawRule),
    118      mDecls(Servo_StyleRule_GetStyle(mRawRule).Consume()) {}
    119 
    120 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSStyleRule, GroupRule)
    121 
    122 NS_IMPL_CYCLE_COLLECTION_CLASS(CSSStyleRule)
    123 
    124 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSStyleRule, GroupRule)
    125  // Keep this in sync with IsCCLeaf.
    126 
    127  // XXX The comment below seems to be incorrect/confusing, the TraceWrapper
    128  // call is needed because CSSStyleRuleDeclaration doesn't support cycle
    129  // collection?
    130  // This appears to have been done for performance reasons, but it's unclear
    131  // whether it's still a good idea.
    132 
    133  // Trace the wrapper for our declaration.  This just expands out
    134  // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
    135  // directly because the wrapper is on the declaration, not on us.
    136  tmp->mDecls.TraceWrapper(aCallbacks, aClosure);
    137 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    138 
    139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSStyleRule)
    140  // Keep this in sync with IsCCLeaf.
    141 
    142  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleMap)
    143 
    144  // Unlink the wrapper for our declaration.
    145  //
    146  // Note that this has to happen before unlinking css::Rule.
    147  tmp->UnlinkDeclarationWrapper(tmp->mDecls);
    148  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
    149 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(GroupRule)
    150 
    151 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSStyleRule, GroupRule)
    152  // Keep this in sync with IsCCLeaf.
    153 
    154  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleMap)
    155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    156 
    157 bool CSSStyleRule::IsCCLeaf() const {
    158  if (!GroupRule::IsCCLeaf()) {
    159    return false;
    160  }
    161 
    162  if (mStyleMap) {
    163    return false;
    164  }
    165 
    166  return !mDecls.PreservingWrapper();
    167 }
    168 
    169 size_t CSSStyleRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    170  size_t n = aMallocSizeOf(this);
    171 
    172  // Measurement of the following members may be added later if DMD finds it
    173  // is worthwhile:
    174  // - mRawRule
    175  // - mStyleMap
    176  // - mDecls
    177 
    178  return n;
    179 }
    180 
    181 #ifdef DEBUG
    182 void CSSStyleRule::List(FILE* out, int32_t aIndent) const {
    183  nsAutoCString str;
    184  for (int32_t i = 0; i < aIndent; i++) {
    185    str.AppendLiteral("  ");
    186  }
    187  Servo_StyleRule_Debug(mRawRule, &str);
    188  fprintf_stderr(out, "%s\n", str.get());
    189 }
    190 #endif
    191 
    192 /* CSSRule implementation */
    193 
    194 StyleCssRuleType CSSStyleRule::Type() const { return StyleCssRuleType::Style; }
    195 
    196 void CSSStyleRule::GetCssText(nsACString& aCssText) const {
    197  Servo_StyleRule_GetCssText(mRawRule, &aCssText);
    198 }
    199 
    200 /* CSSStyleRule implementation */
    201 
    202 const StyleLockedDeclarationBlock* CSSStyleRule::RawStyle() const {
    203  return mDecls.mDecls->Raw();
    204 }
    205 
    206 DeclarationBlock& CSSStyleRule::GetDeclarationBlock() const {
    207  return *mDecls.mDecls;
    208 }
    209 
    210 void CSSStyleRule::GetSelectorText(nsACString& aSelectorText) {
    211  Servo_StyleRule_GetSelectorText(mRawRule, &aSelectorText);
    212 }
    213 
    214 void CSSStyleRule::SetSelectorText(const nsACString& aSelectorText) {
    215  if (IsReadOnly()) {
    216    return;
    217  }
    218 
    219  StyleSheet* sheet = GetStyleSheet();
    220  if (!sheet) {
    221    return;
    222  }
    223  sheet->WillDirty();
    224 
    225  auto state = ContainingRuleState::From(mParentRule);
    226 
    227  // TODO(emilio): May actually be more efficient to handle this as rule
    228  // removal + addition, from the point of view of invalidation...
    229  const StyleStylesheetContents* contents = sheet->RawContents();
    230  if (Servo_StyleRule_SetSelectorText(
    231          contents, mRawRule, &aSelectorText,
    232          state.mParseRelativeType.ptrOr(nullptr))) {
    233    sheet->RuleChanged(this, StyleRuleChangeKind::Generic);
    234  }
    235 }
    236 
    237 uint32_t CSSStyleRule::SelectorCount() const {
    238  return Servo_StyleRule_GetSelectorCount(mRawRule);
    239 }
    240 
    241 static void CollectStyleRules(CSSStyleRule& aDeepestRule, bool aDesugared,
    242                              nsTArray<const StyleLockedStyleRule*>& aResult,
    243                              nsTArray<StyleScopeRuleData>* aScopes = nullptr) {
    244  aResult.AppendElement(aDeepestRule.Raw());
    245  if (aDesugared) {
    246    for (auto* rule = aDeepestRule.GetParentRule(); rule;
    247         rule = rule->GetParentRule()) {
    248      if (rule->Type() == StyleCssRuleType::Style) {
    249        aResult.AppendElement(static_cast<CSSStyleRule*>(rule)->Raw());
    250      } else if (aScopes && rule->Type() == StyleCssRuleType::Scope) {
    251        MOZ_ASSERT(aResult.Length() > 0, "Innermost rule wasn't a style rule?");
    252        aScopes->AppendElement(StyleScopeRuleData{
    253            static_cast<CSSScopeRule*>(rule)->Raw(),
    254            rule->GetStyleSheet(),
    255            aResult.Length() - 1,
    256        });
    257      }
    258    }
    259  }
    260 }
    261 
    262 void CSSStyleRule::GetSelectorDataAtIndex(uint32_t aSelectorIndex,
    263                                          bool aDesugared, nsACString* aText,
    264                                          uint64_t* aSpecificity) {
    265  AutoTArray<const StyleLockedStyleRule*, 8> rules;
    266  CollectStyleRules(*this, aDesugared, rules);
    267  Servo_StyleRule_GetSelectorDataAtIndex(&rules, aSelectorIndex, aText,
    268                                         aSpecificity);
    269 }
    270 
    271 void CSSStyleRule::SelectorTextAt(uint32_t aSelectorIndex, bool aDesugared,
    272                                  nsACString& aText) {
    273  GetSelectorDataAtIndex(aSelectorIndex, aDesugared, &aText, nullptr);
    274 }
    275 
    276 uint64_t CSSStyleRule::SelectorSpecificityAt(uint32_t aSelectorIndex,
    277                                             bool aDesugared) {
    278  uint64_t s = 0;
    279  GetSelectorDataAtIndex(aSelectorIndex, aDesugared, nullptr, &s);
    280  return s;
    281 }
    282 
    283 static void GetHosts(StyleSheet* aSheet, const Element& aElement,
    284                     nsTArray<Element*>& aHosts) {
    285  if (!aSheet) {
    286    return;
    287  }
    288 
    289  if (auto* owner = aSheet->GetAssociatedDocumentOrShadowRoot()) {
    290    if (auto* shadow = ShadowRoot::FromNode(owner->AsNode())) {
    291      aHosts.AppendElement(shadow->Host());
    292    }
    293  }
    294 
    295  for (auto* adopter : aSheet->SelfOrAncestorAdopters()) {
    296    auto* shadow = ShadowRoot::FromNode(adopter->AsNode());
    297    if (!shadow) {
    298      continue;
    299    }
    300    if (shadow->Host() == &aElement ||
    301        shadow == aElement.GetContainingShadow()) {
    302      aHosts.AppendElement(shadow->Host());
    303    }
    304  }
    305 }
    306 
    307 Element* GetHost(StyleSheet* aSheet, const Element& aElement) {
    308  nsTArray<Element*> hosts;
    309  GetHosts(aSheet, aElement, hosts);
    310  return hosts.SafeElementAt(0, nullptr);
    311 }
    312 
    313 bool CSSStyleRule::SelectorMatchesElement(uint32_t aSelectorIndex,
    314                                          Element& aElement,
    315                                          const nsAString& aPseudo,
    316                                          bool aRelevantLinkVisited) {
    317  const auto pseudo = nsCSSPseudoElements::ParsePseudoElement(
    318      aPseudo, CSSEnabledState::IgnoreEnabledState);
    319  if (!pseudo) {
    320    return false;
    321  }
    322 
    323  AutoTArray<StyleScopeRuleData, 1> scopes;
    324  AutoTArray<const StyleLockedStyleRule*, 8> rules;
    325  CollectStyleRules(*this, /* aDesugared = */ true, rules, &scopes);
    326 
    327  AutoTArray<Element*, 4> hosts;
    328  GetHosts(GetStyleSheet(), aElement, hosts);
    329  if (hosts.IsEmpty()) {
    330    hosts.AppendElement(nullptr);
    331  }
    332 
    333  for (auto* host : hosts) {
    334    if (Servo_StyleRule_SelectorMatchesElement(
    335            &rules, &scopes, &aElement, aSelectorIndex, host, pseudo->mType,
    336            pseudo->mIdentifier, aRelevantLinkVisited)) {
    337      return true;
    338    }
    339  }
    340 
    341  return false;
    342 }
    343 
    344 Element* CSSStyleRule::GetScopeRootFor(uint32_t aSelectorIndex,
    345                                       dom::Element& aElement,
    346                                       const nsAString& aPseudo,
    347                                       bool aRelevantLinkVisited) {
    348  const auto pseudo = nsCSSPseudoElements::ParsePseudoElement(
    349      aPseudo, CSSEnabledState::IgnoreEnabledState);
    350  if (!pseudo) {
    351    return nullptr;
    352  }
    353 
    354  auto* host = GetHost(GetStyleSheet(), aElement);
    355  AutoTArray<const StyleLockedStyleRule*, 8> rules;
    356  AutoTArray<StyleScopeRuleData, 1> scopes;
    357  CollectStyleRules(*this, /* aDesugared = */ true, rules, &scopes);
    358  return const_cast<Element*>(Servo_StyleRule_GetScopeRootFor(
    359      &rules, &scopes, &aElement, aSelectorIndex, host, pseudo->mType,
    360      pseudo->mIdentifier, aRelevantLinkVisited));
    361 }
    362 
    363 SelectorWarningKind ToWebIDLSelectorWarningKind(
    364    StyleSelectorWarningKind aKind) {
    365  // Whenever an entry is modified here, file a DevTools follow-up bug to make
    366  // use of the warning, e.g. Like it is done in
    367  // `css-selector-warnings-tooltip-helper.js`.
    368  switch (aKind) {
    369    case StyleSelectorWarningKind::UnconstraintedRelativeSelector:
    370      return SelectorWarningKind::UnconstrainedHas;
    371    case StyleSelectorWarningKind::SiblingCombinatorAfterScopeSelector:
    372      return SelectorWarningKind::SiblingCombinatorAfterScope;
    373  }
    374  MOZ_ASSERT_UNREACHABLE("Unhandled selector warning kind");
    375  // Return something for assert-disabled builds.
    376  return SelectorWarningKind::UnconstrainedHas;
    377 }
    378 
    379 void CSSStyleRule::GetSelectorWarnings(
    380    nsTArray<SelectorWarning>& aResult) const {
    381  nsTArray<StyleSelectorWarningData> result;
    382  Servo_GetSelectorWarnings(mRawRule, &result);
    383  for (const auto& warning : result) {
    384    auto& entry = *aResult.AppendElement();
    385    entry.mIndex = warning.index;
    386    entry.mKind = ToWebIDLSelectorWarningKind(warning.kind);
    387  }
    388 }
    389 
    390 already_AddRefed<nsINodeList> CSSStyleRule::QuerySelectorAll(nsINode& aRoot) {
    391  AutoTArray<const StyleLockedStyleRule*, 8> rules;
    392  AutoTArray<StyleScopeRuleData, 1> scopes;
    393  CollectStyleRules(*this, /* aDesugared = */ true, rules, &scopes);
    394  auto contentList = MakeRefPtr<nsSimpleContentList>(&aRoot);
    395  if (scopes.IsEmpty()) {
    396    StyleSelectorList* list = Servo_StyleRule_GetSelectorList(&rules);
    397    Servo_SelectorList_QueryAll(&aRoot, list, contentList.get(),
    398                                /* useInvalidation */ false);
    399    Servo_SelectorList_Drop(list);
    400  } else {
    401    // TODO(dshin): This division is annoying, but `querySelectorAll` path has
    402    // fast-path options that we can take advantage of.
    403    Servo_SelectorList_QueryAllWithScope(&aRoot, &rules, &scopes,
    404                                         contentList.get());
    405  }
    406 
    407  return contentList.forget();
    408 }
    409 
    410 StylePropertyMap* CSSStyleRule::StyleMap() {
    411  if (!mStyleMap) {
    412    mStyleMap = MakeRefPtr<StylePropertyMap>(this);
    413  }
    414 
    415  return mStyleMap;
    416 }
    417 
    418 /* virtual */
    419 JSObject* CSSStyleRule::WrapObject(JSContext* aCx,
    420                                   JS::Handle<JSObject*> aGivenProto) {
    421  return CSSStyleRule_Binding::Wrap(aCx, this, aGivenProto);
    422 }
    423 
    424 }  // namespace mozilla::dom