1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_StyleSheet_h
#define mozilla_StyleSheet_h
#include "mozilla/css/SheetParsingMode.h"
#include "mozilla/dom/CSSStyleSheetBinding.h"
#include "mozilla/dom/SRIMetadata.h"
#include "mozilla/CORSMode.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/ServoTypes.h"
#include "mozilla/StyleSheetInfo.h"
#include "nsICSSLoaderObserver.h"
#include "nsIPrincipal.h"
#include "nsWrapperCache.h"
#include "nsStringFwd.h"
class nsIGlobalObject;
class nsINode;
class nsIPrincipal;
struct ServoCssRules;
class nsIReferrerInfo;
namespace mozilla {
class ServoCSSRuleList;
class ServoStyleSet;
typedef MozPromise</* Dummy */ bool,
/* Dummy */ bool,
/* IsExclusive = */ true>
StyleSheetParsePromise;
enum class StyleRuleChangeKind : uint32_t;
namespace css {
class GroupRule;
class Loader;
class LoaderReusableStyleSheets;
class Rule;
class SheetLoadData;
} // namespace css
namespace dom {
class CSSImportRule;
class CSSRuleList;
class DocumentOrShadowRoot;
class MediaList;
class ShadowRoot;
struct CSSStyleSheetInit;
} // namespace dom
enum class StyleSheetState : uint8_t {
// Whether the sheet is disabled. Sheets can be made disabled via CSSOM, or
// via alternate links and such.
Disabled = 1 << 0,
// Whether the sheet is complete. The sheet is complete if it's finished
// loading. See StyleSheet::SetComplete.
Complete = 1 << 1,
// Whether we've forced a unique inner. StyleSheet objects share an 'inner'
// StyleSheetInfo object if they share URL, CORS mode, etc.
//
// See the Loader's `mCompleteSheets` and `mLoadingSheets`.
ForcedUniqueInner = 1 << 2,
// Whether this stylesheet has suffered any modification to the rules via
// CSSOM.
ModifiedRules = 1 << 3,
// Same flag, but devtools clears it in some specific situations.
//
// Used to control whether devtools shows the rule in its authored form or
// not.
ModifiedRulesForDevtools = 1 << 4,
// Whether modifications to the sheet are currently disallowed.
// This flag is set during the async Replace() function to ensure
// that the sheet is not modified until the promise is resolved.
ModificationDisallowed = 1 << 5,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StyleSheetState)
class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
dom::CSSImportRule* aOwnerRuleToUse,
dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
dom::Document* aConstructorDocToUse, nsINode* aOwningNodeToUse);
virtual ~StyleSheet();
using State = StyleSheetState;
public:
StyleSheet(css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
const dom::SRIMetadata& aIntegrity);
static already_AddRefed<StyleSheet> Constructor(const dom::GlobalObject&,
const dom::CSSStyleSheetInit&,
ErrorResult&);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StyleSheet)
already_AddRefed<StyleSheet> CreateEmptyChildSheet(
already_AddRefed<dom::MediaList> aMediaList) const;
bool HasRules() const;
// Parses a stylesheet. The load data argument corresponds to the
// SheetLoadData for this stylesheet.
// NOTE: ParseSheet can run synchronously or asynchronously
// based on the result of `AllowParallelParse`
RefPtr<StyleSheetParsePromise> ParseSheet(css::Loader&,
const nsACString& aBytes,
css::SheetLoadData&);
// Common code that needs to be called after servo finishes parsing. This is
// shared between the parallel and sequential paths.
void FinishAsyncParse(already_AddRefed<RawServoStyleSheetContents>);
// Similar to `ParseSheet`, but guarantees that
// parsing will be performed synchronously.
// NOTE: ParseSheet can still run synchronously.
// This is not a strict alternative.
//
// The load data may be null sometimes.
void ParseSheetSync(
css::Loader* aLoader, const nsACString& aBytes,
css::SheetLoadData* aLoadData, uint32_t aLineNumber,
css::LoaderReusableStyleSheets* aReusableSheets = nullptr);
void ReparseSheet(const nsACString& aInput, ErrorResult& aRv);
const RawServoStyleSheetContents* RawContents() const {
return Inner().mContents;
}
void SetContentsForImport(const RawServoStyleSheetContents* aContents) {
MOZ_ASSERT(!Inner().mContents);
Inner().mContents = aContents;
}
URLExtraData* URLData() const { return Inner().mURLData; }
// nsICSSLoaderObserver interface
NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
nsresult aStatus) final;
// Internal GetCssRules methods which do not have security check and
// completeness check.
ServoCSSRuleList* GetCssRulesInternal();
// Returns the stylesheet's Servo origin as a StyleOrigin value.
StyleOrigin GetOrigin() const;
void SetOwningNode(nsINode* aOwningNode) { mOwningNode = aOwningNode; }
css::SheetParsingMode ParsingMode() const { return mParsingMode; }
dom::CSSStyleSheetParsingMode ParsingModeDOM();
/**
* Whether the sheet is complete.
*/
bool IsComplete() const { return bool(mState & State::Complete); }
void SetComplete();
void SetEnabled(bool aEnabled) { SetDisabled(!aEnabled); }
// Whether the sheet is for an inline <style> element.
bool IsInline() const { return !GetOriginalURI(); }
nsIURI* GetSheetURI() const { return Inner().mSheetURI; }
/**
* Get the URI this sheet was originally loaded from, if any. Can return null.
*/
nsIURI* GetOriginalURI() const { return Inner().mOriginalSheetURI; }
nsIURI* GetBaseURI() const { return Inner().mBaseURI; }
/**
* SetURIs must be called on all sheets before parsing into them.
* SetURIs may only be called while the sheet is 1) incomplete and 2)
* has no rules in it.
*
* FIXME(emilio): Can we pass this down when constructing the sheet instead?
*/
inline void SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
nsIURI* aBaseURI);
/**
* Whether the sheet is applicable. A sheet that is not applicable
* should never be inserted into a style set. A sheet may not be
* applicable for a variety of reasons including being disabled and
* being incomplete.
*/
bool IsApplicable() const { return !Disabled() && IsComplete(); }
already_AddRefed<StyleSheet> Clone(
StyleSheet* aCloneParent, dom::CSSImportRule* aCloneOwnerRule,
dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
nsINode* aCloneOwningNode) const;
/**
* Creates a clone of the adopted style sheet as though it were constructed
* by aConstructorDocument. This should only be used for printing.
*/
already_AddRefed<StyleSheet> CloneAdoptedSheet(
dom::Document& aConstructorDocument) const;
bool HasForcedUniqueInner() const {
return bool(mState & State::ForcedUniqueInner);
}
bool HasModifiedRules() const { return bool(mState & State::ModifiedRules); }
bool HasModifiedRulesForDevtools() const {
return bool(mState & State::ModifiedRulesForDevtools);
}
bool HasUniqueInner() const { return Inner().mSheets.Length() == 1; }
void AssertHasUniqueInner() const { MOZ_ASSERT(HasUniqueInner()); }
void EnsureUniqueInner();
// Returns the DocumentOrShadowRoot* that owns us, if any.
//
// TODO(emilio): Maybe rename to GetOwner*() or such? Might be
// confusing with nsINode::OwnerDoc and such.
dom::DocumentOrShadowRoot* GetAssociatedDocumentOrShadowRoot() const;
// Whether this stylesheet is kept alive by the associated or constructor
// document somehow, and thus at least has the same lifetime as
// GetAssociatedDocument().
dom::Document* GetKeptAliveByDocument() const;
// If this is a constructed style sheet, return mConstructorDocument.
// Otherwise return the document we're associated to,
// via mDocumentOrShadowRoot.
//
// Non-null iff GetAssociatedDocumentOrShadowRoot is non-null.
dom::Document* GetAssociatedDocument() const;
void SetAssociatedDocumentOrShadowRoot(dom::DocumentOrShadowRoot*);
void ClearAssociatedDocumentOrShadowRoot() {
SetAssociatedDocumentOrShadowRoot(nullptr);
}
nsINode* GetOwnerNode() const { return mOwningNode; }
StyleSheet* GetParentSheet() const { return mParentSheet; }
void SetOwnerRule(dom::CSSImportRule* aOwnerRule) {
mOwnerRule = aOwnerRule; /* Not ref counted */
}
dom::CSSImportRule* GetOwnerRule() const { return mOwnerRule; }
void AppendStyleSheet(StyleSheet&);
// Append a stylesheet to the child list without calling WillDirty.
void AppendStyleSheetSilently(StyleSheet&);
const nsTArray<RefPtr<StyleSheet>>& ChildSheets() const {
#ifdef DEBUG
for (StyleSheet* child : Inner().mChildren) {
MOZ_ASSERT(child->GetParentSheet());
MOZ_ASSERT(child->GetParentSheet()->mInner == mInner);
}
#endif
return Inner().mChildren;
}
// Principal() never returns a null pointer.
nsIPrincipal* Principal() const { return Inner().mPrincipal; }
/**
* SetPrincipal should be called on all sheets before parsing into them.
* This can only be called once with a non-null principal.
*
* Calling this with a null pointer is allowed and is treated as a no-op.
*
* FIXME(emilio): Can we get this at construction time instead?
*/
void SetPrincipal(nsIPrincipal* aPrincipal) {
StyleSheetInfo& info = Inner();
MOZ_ASSERT(!info.mPrincipalSet, "Should only set principal once");
if (aPrincipal) {
info.mPrincipal = aPrincipal;
#ifdef DEBUG
info.mPrincipalSet = true;
#endif
}
}
void SetTitle(const nsAString& aTitle) { mTitle = aTitle; }
void SetMedia(already_AddRefed<dom::MediaList> aMedia);
// Get this style sheet's CORS mode
CORSMode GetCORSMode() const { return Inner().mCORSMode; }
// Get this style sheet's ReferrerInfo
nsIReferrerInfo* GetReferrerInfo() const { return Inner().mReferrerInfo; }
// Set this style sheet's ReferrerInfo
void SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
Inner().mReferrerInfo = aReferrerInfo;
}
// Get this style sheet's integrity metadata
void GetIntegrity(dom::SRIMetadata& aResult) const {
aResult = Inner().mIntegrity;
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
#if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
void List(FILE* aOut = stdout, int32_t aIndex = 0);
#endif
// WebIDL StyleSheet API
void GetType(nsAString& aType);
void GetHref(nsAString& aHref, ErrorResult& aRv);
// GetOwnerNode is defined above.
StyleSheet* GetParentStyleSheet() const { return GetParentSheet(); }
void GetTitle(nsAString& aTitle);
dom::MediaList* Media();
bool Disabled() const { return bool(mState & State::Disabled); }
void SetDisabled(bool aDisabled);
void GetSourceMapURL(nsAString& aTitle);
void SetSourceMapURL(const nsAString& aSourceMapURL);
void SetSourceMapURLFromComment(const nsAString& aSourceMapURLFromComment);
void GetSourceURL(nsAString& aSourceURL);
void SetSourceURL(const nsAString& aSourceURL);
// WebIDL CSSStyleSheet API
// Can't be inline because we can't include ImportRule here. And can't be
// called GetOwnerRule because that would be ambiguous with the ImportRule
// version.
css::Rule* GetDOMOwnerRule() const;
dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal, ErrorResult&);
uint32_t InsertRule(const nsACString& aRule, uint32_t aIndex,
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
void DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
int32_t AddRule(const nsACString& aSelector, const nsACString& aBlock,
const dom::Optional<uint32_t>& aIndex,
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
already_AddRefed<dom::Promise> Replace(const nsACString& aText, ErrorResult&);
void ReplaceSync(const nsACString& aText, ErrorResult&);
bool ModificationDisallowed() const {
return bool(mState & State::ModificationDisallowed);
}
// Called before and after the asynchronous Replace() function
// to disable/re-enable modification while there is a pending promise.
void SetModificationDisallowed(bool aDisallowed) {
MOZ_ASSERT(IsConstructed());
MOZ_ASSERT(!IsReadOnly());
if (aDisallowed) {
mState |= State::ModificationDisallowed;
// Sheet will be re-set to complete when its rules are replaced
mState &= ~State::Complete;
if (!Disabled()) {
ApplicableStateChanged(false);
}
} else {
mState &= ~State::ModificationDisallowed;
}
}
// True if the sheet was created through the Constructable StyleSheets API
bool IsConstructed() const { return !!mConstructorDocument; }
// True if any of this sheet's ancestors were created through the
// Constructable StyleSheets API
bool SelfOrAncestorIsConstructed() const {
return OutermostSheet().IsConstructed();
}
// Ture if the sheet's constructor document matches the given document
bool ConstructorDocumentMatches(const dom::Document& aDocument) const {
return mConstructorDocument == &aDocument;
}
// Add a document or shadow root to the list of adopters.
// Adopters will be notified when styles are changed.
void AddAdopter(dom::DocumentOrShadowRoot& aAdopter) {
MOZ_ASSERT(IsConstructed());
MOZ_ASSERT(!mAdopters.Contains(&aAdopter));
mAdopters.AppendElement(&aAdopter);
}
// Remove a document or shadow root from the list of adopters.
void RemoveAdopter(dom::DocumentOrShadowRoot& aAdopter) {
// Cannot assert IsConstructed() because this can run after unlink.
mAdopters.RemoveElement(&aAdopter);
}
// WebIDL miscellaneous bits
inline dom::ParentObject GetParentObject() const;
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
// Changes to sheets should be after a WillDirty call.
void WillDirty();
// Called when a rule changes from CSSOM.
//
// FIXME(emilio): This shouldn't allow null, but MediaList doesn't know about
// its owning media rule, plus it's used for the stylesheet media itself.
void RuleChanged(css::Rule*, StyleRuleChangeKind);
void AddStyleSet(ServoStyleSet* aStyleSet);
void DropStyleSet(ServoStyleSet* aStyleSet);
nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex);
nsresult InsertRuleIntoGroup(const nsACString& aRule, css::GroupRule* aGroup,
uint32_t aIndex);
// Find the ID of the owner inner window.
uint64_t FindOwningWindowInnerID() const;
// Copy the contents of this style sheet into the shared memory buffer managed
// by aBuilder. Returns the pointer into the buffer that the sheet contents
// were stored at. (The returned pointer is to an Arc<Locked<Rules>> value,
// or null, with a filled in aErrorMessage, on failure.)
const ServoCssRules* ToShared(RawServoSharedMemoryBuilder* aBuilder,
nsCString& aErrorMessage);
// Sets the contents of this style sheet to the specified aSharedRules
// pointer, which must be a pointer somewhere in the aSharedMemory buffer
// as previously returned by a ToShared() call.
void SetSharedContents(const ServoCssRules* aSharedRules);
// Whether this style sheet should not allow any modifications.
//
// This is true for any User Agent sheets once they are complete.
bool IsReadOnly() const;
// Removes a stylesheet from its parent sheet child list, if any.
void RemoveFromParent();
// Resolves mReplacePromise with this sheet.
void MaybeResolveReplacePromise();
// Rejects mReplacePromise with a NetworkError.
void MaybeRejectReplacePromise();
private:
void SetModifiedRules() {
mState |= State::ModifiedRules | State::ModifiedRulesForDevtools;
}
const StyleSheet& OutermostSheet() const {
auto* current = this;
while (current->mParentSheet) {
MOZ_ASSERT(!current->mDocumentOrShadowRoot,
"Shouldn't be set on child sheets");
MOZ_ASSERT(!current->mConstructorDocument,
"Shouldn't be set on child sheets");
current = current->mParentSheet;
}
return *current;
}
StyleSheetInfo& Inner() {
MOZ_ASSERT(mInner);
return *mInner;
}
const StyleSheetInfo& Inner() const {
MOZ_ASSERT(mInner);
return *mInner;
}
// Check if the rules are available for read and write.
// It does the security check as well as whether the rules have been
// completely loaded. aRv will have an exception set if this function
// returns false.
bool AreRulesAvailable(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
void SetURLExtraData();
protected:
// Internal methods which do not have security check and completeness check.
uint32_t InsertRuleInternal(const nsACString& aRule, uint32_t aIndex,
ErrorResult&);
void DeleteRuleInternal(uint32_t aIndex, ErrorResult&);
nsresult InsertRuleIntoGroupInternal(const nsACString& aRule,
css::GroupRule* aGroup, uint32_t aIndex);
// Common tail routine for the synchronous and asynchronous parsing paths.
void FinishParse();
// Take the recently cloned sheets from the `@import` rules, and reparent them
// correctly to `aPrimarySheet`.
void BuildChildListAfterInnerClone();
void DropRuleList();
// Called when a rule is removed from the sheet from CSSOM.
void RuleAdded(css::Rule&);
// Called when a rule is added to the sheet from CSSOM.
void RuleRemoved(css::Rule&);
// Called when a stylesheet is cloned.
void StyleSheetCloned(StyleSheet&);
// Notifies that the applicable state changed.
// aApplicable is the value that we expect to get from IsApplicable().
// assertion will fail if the expectation does not match reality.
void ApplicableStateChanged(bool aApplicable);
void LastRelease();
// Return success if the subject principal subsumes the principal of our
// inner, error otherwise. This will also succeed if access is allowed by
// CORS. In that case, it will set the principal of the inner to the
// subject principal.
void SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
// Drop our reference to mMedia
void DropMedia();
// Unlink our inner, if needed, for cycle collection.
void UnlinkInner();
// Traverse our inner, if needed, for cycle collection
void TraverseInner(nsCycleCollectionTraversalCallback&);
// Return whether the given @import rule has pending child sheet.
static bool RuleHasPendingChildSheet(css::Rule* aRule);
StyleSheet* mParentSheet; // weak ref
// A pointer to the sheets relevant global object.
// This is populated when the sheet gets an associated document.
// This is required for the sheet to be able to create a promise.
// https://html.spec.whatwg.org/#concept-relevant-everything
nsCOMPtr<nsIGlobalObject> mRelevantGlobal;
RefPtr<dom::Document> mConstructorDocument;
// Will be set in the Replace() function and resolved/rejected by the
// sheet once its rules have been replaced and the sheet is complete again.
RefPtr<dom::Promise> mReplacePromise;
nsString mTitle;
// weak ref; parents maintain this for their children
dom::DocumentOrShadowRoot* mDocumentOrShadowRoot;
nsINode* mOwningNode; // weak ref
dom::CSSImportRule* mOwnerRule; // weak ref
RefPtr<dom::MediaList> mMedia;
// mParsingMode controls access to nonstandard style constructs that
// are not safe for use on the public Web but necessary in UA sheets
// and/or useful in user sheets.
//
// FIXME(emilio): Given we store the parsed contents in the Inner, this should
// probably also move there.
css::SheetParsingMode mParsingMode;
State mState;
// Core information we get from parsed sheets, which are shared amongst
// StyleSheet clones.
//
// Always nonnull until LastRelease().
StyleSheetInfo* mInner;
nsTArray<ServoStyleSet*> mStyleSets;
RefPtr<ServoCSSRuleList> mRuleList;
MozPromiseHolder<StyleSheetParsePromise> mParsePromise;
nsTArray<dom::DocumentOrShadowRoot*> mAdopters;
// Make StyleSheetInfo and subclasses into friends so they can use
// ChildSheetListBuilder.
friend struct StyleSheetInfo;
};
} // namespace mozilla
#endif // mozilla_StyleSheet_h
|