diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index 2b0e4a3d7c7d..2fb4593c7a53 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -144,6 +144,8 @@ SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void, RawServoStyleRuleBorrowed rule, nsAString* result) SERVO_BINDING_FUNC(Servo_ImportRule_GetHref, void, RawServoImportRuleBorrowed rule, nsAString* result) +SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet, const RawServoStyleSheet*, + RawServoImportRuleBorrowed rule) SERVO_BINDING_FUNC(Servo_Keyframe_GetKeyText, void, RawServoKeyframeBorrowed keyframe, nsAString* result) // Returns whether it successfully changes the key text. diff --git a/layout/style/ServoCSSRuleList.cpp b/layout/style/ServoCSSRuleList.cpp index 3de5364d9f2e..4d9ce455eb6e 100644 --- a/layout/style/ServoCSSRuleList.cpp +++ b/layout/style/ServoCSSRuleList.cpp @@ -8,6 +8,7 @@ #include "mozilla/ServoCSSRuleList.h" +#include "mozilla/IntegerRange.h" #include "mozilla/ServoBindings.h" #include "mozilla/ServoDocumentRule.h" #include "mozilla/ServoImportRule.h" @@ -16,6 +17,7 @@ #include "mozilla/ServoNamespaceRule.h" #include "mozilla/ServoPageRule.h" #include "mozilla/ServoStyleRule.h" +#include "mozilla/ServoStyleSheet.h" #include "mozilla/ServoSupportsRule.h" #include "nsCSSCounterStyleRule.h" #include "nsCSSFontFaceRule.h" @@ -28,9 +30,33 @@ ServoCSSRuleList::ServoCSSRuleList(already_AddRefed aRawRules, , mRawRules(aRawRules) { Servo_CssRules_ListTypes(mRawRules, &mRules); - // XXX We may want to eagerly create object for import rule, so that - // we don't lose the reference to child stylesheet when our own - // stylesheet goes away. + // Only top level rule list can have @import rules. + if (aDirectOwnerStyleSheet) { + nsDataHashtable, + ServoStyleSheet*> stylesheets; + aDirectOwnerStyleSheet->EnumerateChildSheets( + [&stylesheets](StyleSheet* child) { + ServoStyleSheet* servoSheet = child->AsServo(); + const RawServoStyleSheet* rawSheet = servoSheet->RawSheet(); + MOZ_ASSERT(!stylesheets.Get(rawSheet, nullptr), + "Multiple child sheets with same raw sheet?"); + stylesheets.Put(rawSheet, servoSheet); + }); + for (auto i : IntegerRange(mRules.Length())) { + if (mRules[i] != nsIDOMCSSRule::IMPORT_RULE) { + // Only @charset can be put before @import rule, but @charset + // rules don't have corresponding object, so if a rule is not + // @import rule, there is definitely no @import rule after it. + break; + } + ConstructImportRule(i, [&stylesheets](const RawServoStyleSheet* raw) { + // Child sheets may not correctly cloned for stylo, which is + // bug 1367213. That makes it possible to fail to get a style + // sheet for the raw sheet here. + return stylesheets.GetAndRemove(raw).valueOr(nullptr); + }); + } + } } // QueryInterface implementation for ServoCSSRuleList @@ -101,7 +127,6 @@ ServoCSSRuleList::GetRule(uint32_t aIndex) break; \ } CASE_RULE(STYLE, Style) - CASE_RULE(IMPORT, Import) CASE_RULE(KEYFRAMES, Keyframes) CASE_RULE(MEDIA, Media) CASE_RULE(NAMESPACE, Namespace) @@ -121,6 +146,12 @@ ServoCSSRuleList::GetRule(uint32_t aIndex) ruleObj = Servo_CssRules_GetCounterStyleRuleAt(mRawRules, aIndex); break; } + case nsIDOMCSSRule::IMPORT_RULE: + // Currently ConstructImportRule may fail to construct an import + // rule eagerly. See comment in that function. This should be + // converted into an assertion when those bugs get fixed. + NS_WARNING("stylo: this @import rule was not constructed"); + return nullptr; case nsIDOMCSSRule::KEYFRAME_RULE: MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here"); return nullptr; @@ -184,6 +215,36 @@ ServoCSSRuleList::DropReference() DropAllRules(); } +template +void +ServoCSSRuleList::ConstructImportRule(uint32_t aIndex, ChildSheetGetter aGetter) +{ + MOZ_ASSERT(mRules[aIndex] == nsIDOMCSSRule::IMPORT_RULE); + + uint32_t line, column; + RefPtr rawRule = + Servo_CssRules_GetImportRuleAt(mRawRules, aIndex, + &line, &column).Consume(); + const RawServoStyleSheet* + rawChildSheet = Servo_ImportRule_GetSheet(rawRule); + ServoStyleSheet* childSheet = aGetter(rawChildSheet); + if (!childSheet) { + // There are cases that we cannot get the child sheet currently. + // See comments in callsites of this function. This should become + // an assertion after bug 1367213 and bug 1368381 get fixed. + NS_WARNING("stylo: fail to get child sheet for @import rule"); + return; + } + RefPtr + ruleObj = new ServoImportRule(Move(rawRule), childSheet, line, column); + MOZ_ASSERT(!childSheet->GetOwnerRule(), + "Child sheet is already owned by another rule?"); + MOZ_ASSERT(childSheet->GetParentSheet() == mStyleSheet, + "Not a child sheet of the owner of the rule?"); + childSheet->SetOwnerRule(ruleObj); + mRules[aIndex] = CastToUint(ruleObj.forget().take()); +} + nsresult ServoCSSRuleList::InsertRule(const nsAString& aRule, uint32_t aIndex) { @@ -199,8 +260,27 @@ ServoCSSRuleList::InsertRule(const nsAString& aRule, uint32_t aIndex) nsresult rv = Servo_CssRules_InsertRule(mRawRules, mStyleSheet->RawSheet(), &rule, aIndex, nested, loader, mStyleSheet, &type); - if (!NS_FAILED(rv)) { - mRules.InsertElementAt(aIndex, type); + if (NS_FAILED(rv)) { + return rv; + } + mRules.InsertElementAt(aIndex, type); + if (type == nsIDOMCSSRule::IMPORT_RULE) { + MOZ_ASSERT(!nested, "@import rule cannot be nested"); + ConstructImportRule(aIndex, [this](const RawServoStyleSheet* raw) { + StyleSheet* sheet = mStyleSheet->GetMostRecentlyAddedChildSheet(); + MOZ_ASSERT(sheet, "Should have at least one " + "child stylesheet after inserting @import rule"); + ServoStyleSheet* servoSheet = sheet->AsServo(); + // This should always be that case, but currently ServoStyleSheet + // may be reused and the reused stylesheet doesn't refer to the + // right raw sheet, which is bug 1368381. This should be converted + // to an assertion after that bug gets fixed. + if (servoSheet->RawSheet() == raw) { + NS_WARNING("New child sheet should always be prepended to the list"); + return static_cast(nullptr); + } + return servoSheet; + }); } return rv; } diff --git a/layout/style/ServoCSSRuleList.h b/layout/style/ServoCSSRuleList.h index e9074d86e4f2..0b0f370faa78 100644 --- a/layout/style/ServoCSSRuleList.h +++ b/layout/style/ServoCSSRuleList.h @@ -75,6 +75,9 @@ private: void DropAllRules(); + template + inline void ConstructImportRule(uint32_t aIndex, ChildSheetGetter aGetter); + // mStyleSheet may be nullptr when it drops the reference to us. ServoStyleSheet* mStyleSheet = nullptr; // mParentRule is nullptr if it isn't a nested rule list. diff --git a/layout/style/ServoImportRule.cpp b/layout/style/ServoImportRule.cpp index 581cd484ff38..abeedea25b72 100644 --- a/layout/style/ServoImportRule.cpp +++ b/layout/style/ServoImportRule.cpp @@ -14,10 +14,11 @@ namespace mozilla { ServoImportRule::ServoImportRule(RefPtr aRawRule, + ServoStyleSheet* aSheet, uint32_t aLine, uint32_t aColumn) : CSSImportRule(aLine, aColumn) , mRawRule(Move(aRawRule)) - , mChildSheet(nullptr) + , mChildSheet(aSheet) { } diff --git a/layout/style/ServoImportRule.h b/layout/style/ServoImportRule.h index a41b6447df5a..a8939163774c 100644 --- a/layout/style/ServoImportRule.h +++ b/layout/style/ServoImportRule.h @@ -21,6 +21,7 @@ class ServoImportRule final : public dom::CSSImportRule { public: ServoImportRule(RefPtr aRawRule, + ServoStyleSheet* aSheet, uint32_t aLine, uint32_t aColumn); NS_DECL_ISUPPORTS_INHERITED diff --git a/layout/style/StyleSheet.h b/layout/style/StyleSheet.h index e7b87483277f..e55ee0026901 100644 --- a/layout/style/StyleSheet.h +++ b/layout/style/StyleSheet.h @@ -170,6 +170,11 @@ public: void PrependStyleSheet(StyleSheet* aSheet); StyleSheet* GetFirstChild() const; + StyleSheet* GetMostRecentlyAddedChildSheet() const { + // New child sheet can only be prepended into the linked list of + // child sheets, so the most recently added one is always the first. + return GetFirstChild(); + } // Principal() never returns a null pointer. inline nsIPrincipal* Principal() const; @@ -253,6 +258,13 @@ public: nsresult InsertRuleIntoGroup(const nsAString& aRule, css::GroupRule* aGroup, uint32_t aIndex); + template + void EnumerateChildSheets(Func aCallback) { + for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) { + aCallback(child); + } + } + private: // Get a handle to the various stylesheet bits which live on the 'inner' for // gecko stylesheets and live on the StyleSheet for Servo stylesheets.