Line data Source code
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/ServoStyleSheet.h"
8 :
9 : #include "mozilla/css/Rule.h"
10 : #include "mozilla/StyleBackendType.h"
11 : #include "mozilla/ServoBindings.h"
12 : #include "mozilla/ServoImportRule.h"
13 : #include "mozilla/ServoMediaList.h"
14 : #include "mozilla/ServoCSSRuleList.h"
15 : #include "mozilla/css/GroupRule.h"
16 : #include "mozilla/dom/CSSRuleList.h"
17 : #include "mozilla/dom/MediaList.h"
18 : #include "nsIStyleSheetLinkingElement.h"
19 : #include "Loader.h"
20 :
21 :
22 : #include "mozAutoDocUpdate.h"
23 : #include "nsIDOMCSSStyleSheet.h"
24 :
25 : using namespace mozilla::dom;
26 :
27 : namespace mozilla {
28 :
29 : // -------------------------------
30 : // CSS Style Sheet Inner Data Container
31 : //
32 :
33 0 : ServoStyleSheetInner::ServoStyleSheetInner(CORSMode aCORSMode,
34 : ReferrerPolicy aReferrerPolicy,
35 : const SRIMetadata& aIntegrity,
36 0 : css::SheetParsingMode aParsingMode)
37 0 : : StyleSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity)
38 : {
39 0 : mContents = Servo_StyleSheet_Empty(aParsingMode).Consume();
40 0 : mURLData = URLExtraData::Dummy();
41 0 : MOZ_COUNT_CTOR(ServoStyleSheetInner);
42 0 : }
43 :
44 0 : ServoStyleSheetInner::ServoStyleSheetInner(ServoStyleSheetInner& aCopy,
45 0 : ServoStyleSheet* aPrimarySheet)
46 : : StyleSheetInfo(aCopy, aPrimarySheet)
47 0 : , mURLData(aCopy.mURLData)
48 : {
49 0 : MOZ_COUNT_CTOR(ServoStyleSheetInner);
50 :
51 : // Actually clone aCopy's mContents and use that as ours.
52 0 : mContents = Servo_StyleSheet_Clone(
53 0 : aCopy.mContents.get(), aPrimarySheet).Consume();
54 :
55 : // Our child list is fixed up by our parent.
56 0 : }
57 :
58 : void
59 0 : ServoStyleSheet::BuildChildListAfterInnerClone()
60 : {
61 0 : MOZ_ASSERT(Inner()->mSheets.Length() == 1, "Should've just cloned");
62 0 : MOZ_ASSERT(Inner()->mSheets[0] == this);
63 0 : MOZ_ASSERT(!Inner()->mFirstChild);
64 :
65 0 : auto* contents = Inner()->mContents.get();
66 : RefPtr<ServoCssRules> rules =
67 0 : Servo_StyleSheet_GetRules(contents).Consume();
68 :
69 0 : uint32_t index = 0;
70 : while (true) {
71 : uint32_t line, column; // Actually unused.
72 : RefPtr<RawServoImportRule> import =
73 0 : Servo_CssRules_GetImportRuleAt(rules, index, &line, &column).Consume();
74 0 : if (!import) {
75 : // Note that only @charset rules come before @import rules, and @charset
76 : // rules are parsed but skipped, so we can stop iterating as soon as we
77 : // find something that isn't an @import rule.
78 0 : break;
79 : }
80 : auto* sheet =
81 0 : const_cast<ServoStyleSheet*>(Servo_ImportRule_GetSheet(import));
82 0 : MOZ_ASSERT(sheet);
83 0 : PrependStyleSheetSilently(sheet);
84 0 : index++;
85 0 : }
86 0 : }
87 :
88 : already_AddRefed<ServoStyleSheet>
89 0 : ServoStyleSheet::CreateEmptyChildSheet(
90 : already_AddRefed<dom::MediaList> aMediaList) const
91 : {
92 : RefPtr<ServoStyleSheet> child =
93 : new ServoStyleSheet(
94 0 : ParsingMode(),
95 : CORSMode::CORS_NONE,
96 0 : GetReferrerPolicy(),
97 0 : SRIMetadata());
98 :
99 0 : child->mMedia = aMediaList;
100 0 : return child.forget();
101 : }
102 :
103 :
104 0 : ServoStyleSheetInner::~ServoStyleSheetInner()
105 : {
106 0 : MOZ_COUNT_DTOR(ServoStyleSheetInner);
107 0 : }
108 :
109 : StyleSheetInfo*
110 0 : ServoStyleSheetInner::CloneFor(StyleSheet* aPrimarySheet)
111 : {
112 : return new ServoStyleSheetInner(*this,
113 0 : static_cast<ServoStyleSheet*>(aPrimarySheet));
114 : }
115 :
116 0 : MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf)
117 :
118 : size_t
119 0 : ServoStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
120 : {
121 0 : size_t n = aMallocSizeOf(this);
122 0 : n += Servo_StyleSheet_SizeOfIncludingThis(
123 : ServoStyleSheetMallocSizeOf, mContents);
124 0 : return n;
125 : }
126 :
127 0 : ServoStyleSheet::ServoStyleSheet(css::SheetParsingMode aParsingMode,
128 : CORSMode aCORSMode,
129 : net::ReferrerPolicy aReferrerPolicy,
130 0 : const dom::SRIMetadata& aIntegrity)
131 0 : : StyleSheet(StyleBackendType::Servo, aParsingMode)
132 : {
133 0 : mInner = new ServoStyleSheetInner(
134 0 : aCORSMode, aReferrerPolicy, aIntegrity, aParsingMode);
135 0 : mInner->AddSheet(this);
136 0 : }
137 :
138 0 : ServoStyleSheet::ServoStyleSheet(const ServoStyleSheet& aCopy,
139 : ServoStyleSheet* aParentToUse,
140 : dom::CSSImportRule* aOwnerRuleToUse,
141 : nsIDocument* aDocumentToUse,
142 0 : nsINode* aOwningNodeToUse)
143 : : StyleSheet(aCopy,
144 : aParentToUse,
145 : aOwnerRuleToUse,
146 : aDocumentToUse,
147 0 : aOwningNodeToUse)
148 : {
149 0 : if (mDirty) { // CSSOM's been there, force full copy now
150 0 : NS_ASSERTION(mInner->mComplete,
151 : "Why have rules been accessed on an incomplete sheet?");
152 : // FIXME: handle failure?
153 : //
154 : // NOTE: It's important to call this from the subclass, since this could
155 : // access uninitialized members otherwise.
156 0 : EnsureUniqueInner();
157 : }
158 0 : }
159 :
160 0 : ServoStyleSheet::~ServoStyleSheet()
161 : {
162 0 : }
163 :
164 : void
165 0 : ServoStyleSheet::LastRelease()
166 : {
167 0 : DropRuleList();
168 0 : }
169 :
170 : // QueryInterface implementation for ServoStyleSheet
171 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServoStyleSheet)
172 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMCSSStyleSheet)
173 0 : if (aIID.Equals(NS_GET_IID(ServoStyleSheet)))
174 0 : foundInterface = reinterpret_cast<nsISupports*>(this);
175 : else
176 0 : NS_INTERFACE_MAP_END_INHERITING(StyleSheet)
177 :
178 0 : NS_IMPL_ADDREF_INHERITED(ServoStyleSheet, StyleSheet)
179 0 : NS_IMPL_RELEASE_INHERITED(ServoStyleSheet, StyleSheet)
180 :
181 : NS_IMPL_CYCLE_COLLECTION_CLASS(ServoStyleSheet)
182 :
183 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ServoStyleSheet)
184 0 : tmp->DropRuleList();
185 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(StyleSheet)
186 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoStyleSheet, StyleSheet)
187 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleList)
188 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
189 :
190 : bool
191 0 : ServoStyleSheet::HasRules() const
192 : {
193 0 : return Servo_StyleSheet_HasRules(Inner()->mContents);
194 : }
195 :
196 : nsresult
197 0 : ServoStyleSheet::ParseSheet(css::Loader* aLoader,
198 : const nsAString& aInput,
199 : nsIURI* aSheetURI,
200 : nsIURI* aBaseURI,
201 : nsIPrincipal* aSheetPrincipal,
202 : uint32_t aLineNumber,
203 : nsCompatibility aCompatMode,
204 : css::LoaderReusableStyleSheets* aReusableSheets)
205 : {
206 0 : MOZ_ASSERT(!mMedia || mMedia->IsServo());
207 : RefPtr<URLExtraData> extraData =
208 0 : new URLExtraData(aBaseURI, aSheetURI, aSheetPrincipal);
209 :
210 0 : NS_ConvertUTF16toUTF8 input(aInput);
211 0 : Inner()->mContents =
212 0 : Servo_StyleSheet_FromUTF8Bytes(
213 0 : aLoader, this, &input, mParsingMode, extraData,
214 : aLineNumber, aCompatMode
215 0 : ).Consume();
216 :
217 0 : Inner()->mURLData = extraData.forget();
218 0 : return NS_OK;
219 : }
220 :
221 : nsresult
222 0 : ServoStyleSheet::ReparseSheet(const nsAString& aInput)
223 : {
224 0 : if (!mInner->mComplete) {
225 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
226 : }
227 :
228 : // Hold strong ref to the CSSLoader in case the document update
229 : // kills the document
230 0 : RefPtr<css::Loader> loader;
231 0 : if (mDocument) {
232 0 : loader = mDocument->CSSLoader();
233 0 : NS_ASSERTION(loader, "Document with no CSS loader!");
234 : } else {
235 0 : loader = new css::Loader(StyleBackendType::Servo, nullptr);
236 : }
237 :
238 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
239 :
240 0 : WillDirty();
241 :
242 : // cache child sheets to reuse
243 0 : css::LoaderReusableStyleSheets reusableSheets;
244 0 : for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {
245 0 : if (child->GetOriginalURI()) {
246 0 : reusableSheets.AddReusableSheet(child);
247 : }
248 : }
249 :
250 : // clean up child sheets list
251 0 : for (StyleSheet* child = GetFirstChild(); child; ) {
252 0 : StyleSheet* next = child->mNext;
253 0 : child->mParent = nullptr;
254 0 : child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
255 0 : child->mNext = nullptr;
256 0 : child = next;
257 : }
258 0 : Inner()->mFirstChild = nullptr;
259 :
260 0 : uint32_t lineNumber = 1;
261 0 : if (mOwningNode) {
262 0 : nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(mOwningNode);
263 0 : if (link) {
264 0 : lineNumber = link->GetLineNumber();
265 : }
266 : }
267 :
268 : // Notify mDocument that all our rules are removed.
269 0 : if (mDocument) {
270 : // Get the rule list.
271 0 : ServoCSSRuleList* ruleList = GetCssRulesInternal();
272 0 : MOZ_ASSERT(ruleList);
273 :
274 0 : uint32_t ruleCount = ruleList->Length();
275 0 : for (uint32_t i = 0; i < ruleCount; ++i) {
276 0 : css::Rule* rule = ruleList->GetRule(i);
277 0 : MOZ_ASSERT(rule);
278 0 : if (rule->GetType() == css::Rule::IMPORT_RULE &&
279 0 : RuleHasPendingChildSheet(rule)) {
280 0 : continue; // notify when loaded (see StyleSheetLoaded)
281 : }
282 0 : mDocument->StyleRuleRemoved(this, rule);
283 :
284 : // Document observers could possibly detach document from this sheet.
285 0 : if (!mDocument) {
286 : // If detached, don't process any more rules.
287 0 : break;
288 : }
289 : }
290 : }
291 :
292 0 : DropRuleList();
293 :
294 0 : nsresult rv = ParseSheet(loader, aInput, mInner->mSheetURI, mInner->mBaseURI,
295 0 : mInner->mPrincipal, lineNumber,
296 0 : eCompatibility_FullStandards, &reusableSheets);
297 0 : DidDirty();
298 0 : NS_ENSURE_SUCCESS(rv, rv);
299 :
300 : // Notify mDocument that all our new rules are added.
301 0 : if (mDocument) {
302 : // Get the rule list (which will need to be regenerated after ParseSheet).
303 0 : ServoCSSRuleList* ruleList = GetCssRulesInternal();
304 0 : MOZ_ASSERT(ruleList);
305 :
306 0 : uint32_t ruleCount = ruleList->Length();
307 0 : for (uint32_t i = 0; i < ruleCount; ++i) {
308 0 : css::Rule* rule = ruleList->GetRule(i);
309 0 : MOZ_ASSERT(rule);
310 0 : if (rule->GetType() == css::Rule::IMPORT_RULE &&
311 0 : RuleHasPendingChildSheet(rule)) {
312 0 : continue; // notify when loaded (see StyleSheetLoaded)
313 : }
314 :
315 0 : mDocument->StyleRuleAdded(this, rule);
316 :
317 : // Document observers could possibly detach document from this sheet.
318 0 : if (!mDocument) {
319 : // If detached, don't process any more rules.
320 0 : break;
321 : }
322 : }
323 : }
324 :
325 0 : return NS_OK;
326 : }
327 :
328 : // nsICSSLoaderObserver implementation
329 : NS_IMETHODIMP
330 0 : ServoStyleSheet::StyleSheetLoaded(StyleSheet* aSheet,
331 : bool aWasAlternate,
332 : nsresult aStatus)
333 : {
334 0 : MOZ_ASSERT(aSheet->IsServo(),
335 : "why we were called back with a CSSStyleSheet?");
336 :
337 0 : ServoStyleSheet* sheet = aSheet->AsServo();
338 0 : if (sheet->GetParentSheet() == nullptr) {
339 0 : return NS_OK; // ignore if sheet has been detached already
340 : }
341 0 : NS_ASSERTION(this == sheet->GetParentSheet(),
342 : "We are being notified of a sheet load for a sheet that is not our child!");
343 :
344 0 : if (mDocument && NS_SUCCEEDED(aStatus)) {
345 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
346 0 : mDocument->StyleRuleAdded(this, sheet->GetOwnerRule());
347 : }
348 :
349 0 : return NS_OK;
350 : }
351 :
352 : void
353 0 : ServoStyleSheet::DropRuleList()
354 : {
355 0 : if (mRuleList) {
356 0 : mRuleList->DropReference();
357 0 : mRuleList = nullptr;
358 : }
359 0 : }
360 :
361 : already_AddRefed<StyleSheet>
362 0 : ServoStyleSheet::Clone(StyleSheet* aCloneParent,
363 : dom::CSSImportRule* aCloneOwnerRule,
364 : nsIDocument* aCloneDocument,
365 : nsINode* aCloneOwningNode) const
366 : {
367 : RefPtr<StyleSheet> clone = new ServoStyleSheet(*this,
368 : static_cast<ServoStyleSheet*>(aCloneParent),
369 : aCloneOwnerRule,
370 : aCloneDocument,
371 0 : aCloneOwningNode);
372 0 : return clone.forget();
373 : }
374 :
375 : ServoCSSRuleList*
376 0 : ServoStyleSheet::GetCssRulesInternal()
377 : {
378 0 : if (!mRuleList) {
379 0 : EnsureUniqueInner();
380 :
381 : RefPtr<ServoCssRules> rawRules =
382 0 : Servo_StyleSheet_GetRules(Inner()->mContents).Consume();
383 0 : MOZ_ASSERT(rawRules);
384 0 : mRuleList = new ServoCSSRuleList(rawRules.forget(), this);
385 : }
386 0 : return mRuleList;
387 : }
388 :
389 : uint32_t
390 0 : ServoStyleSheet::InsertRuleInternal(const nsAString& aRule,
391 : uint32_t aIndex, ErrorResult& aRv)
392 : {
393 : // Ensure mRuleList is constructed.
394 0 : GetCssRulesInternal();
395 :
396 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
397 0 : aRv = mRuleList->InsertRule(aRule, aIndex);
398 0 : if (aRv.Failed()) {
399 0 : return 0;
400 : }
401 0 : if (mDocument) {
402 0 : if (mRuleList->GetRuleType(aIndex) != css::Rule::IMPORT_RULE ||
403 0 : !RuleHasPendingChildSheet(mRuleList->GetRule(aIndex))) {
404 : // XXX We may not want to get the rule when stylesheet change event
405 : // is not enabled.
406 0 : mDocument->StyleRuleAdded(this, mRuleList->GetRule(aIndex));
407 : }
408 : }
409 0 : return aIndex;
410 : }
411 :
412 : void
413 0 : ServoStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
414 : {
415 : // Ensure mRuleList is constructed.
416 0 : GetCssRulesInternal();
417 0 : if (aIndex > mRuleList->Length()) {
418 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
419 0 : return;
420 : }
421 :
422 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
423 : // Hold a strong ref to the rule so it doesn't die when we remove it
424 : // from the list. XXX We may not want to hold it if stylesheet change
425 : // event is not enabled.
426 0 : RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
427 0 : aRv = mRuleList->DeleteRule(aIndex);
428 0 : MOZ_ASSERT(!aRv.ErrorCodeIs(NS_ERROR_DOM_INDEX_SIZE_ERR),
429 : "IndexSizeError should have been handled earlier");
430 0 : if (!aRv.Failed() && mDocument) {
431 0 : mDocument->StyleRuleRemoved(this, rule);
432 : }
433 : }
434 :
435 : nsresult
436 0 : ServoStyleSheet::InsertRuleIntoGroupInternal(const nsAString& aRule,
437 : css::GroupRule* aGroup,
438 : uint32_t aIndex)
439 : {
440 0 : auto rules = static_cast<ServoCSSRuleList*>(aGroup->CssRules());
441 0 : MOZ_ASSERT(rules->GetParentRule() == aGroup);
442 0 : return rules->InsertRule(aRule, aIndex);
443 : }
444 :
445 : size_t
446 0 : ServoStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
447 : {
448 0 : size_t n = StyleSheet::SizeOfIncludingThis(aMallocSizeOf);
449 0 : const ServoStyleSheet* s = this;
450 0 : while (s) {
451 : // See the comment in CSSStyleSheet::SizeOfIncludingThis() for an
452 : // explanation of this.
453 0 : if (s->Inner()->mSheets.LastElement() == s) {
454 0 : n += s->Inner()->SizeOfIncludingThis(aMallocSizeOf);
455 : }
456 :
457 : // Measurement of the following members may be added later if DMD finds it
458 : // is worthwhile:
459 : // - s->mRuleList
460 :
461 0 : s = s->mNext ? s->mNext->AsServo() : nullptr;
462 : }
463 0 : return n;
464 : }
465 :
466 : } // namespace mozilla
|