Source code

Revision control

Other Tools

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
/* loading of CSS style sheets using the network APIs */
8
9
#ifndef mozilla_css_Loader_h
10
#define mozilla_css_Loader_h
11
12
#include "nsIPrincipal.h"
13
#include "nsCompatibility.h"
14
#include "nsCycleCollectionParticipant.h"
15
#include "nsDataHashtable.h"
16
#include "nsRefPtrHashtable.h"
17
#include "nsStringFwd.h"
18
#include "nsTArray.h"
19
#include "nsTObserverArray.h"
20
#include "nsURIHashKey.h"
21
#include "nsIStyleSheetLinkingElement.h"
22
#include "mozilla/Attributes.h"
23
#include "mozilla/CORSMode.h"
24
#include "mozilla/StyleSheetInlines.h"
25
#include "mozilla/Maybe.h"
26
#include "mozilla/MemoryReporting.h"
27
#include "mozilla/Move.h"
28
#include "mozilla/StyleSheet.h"
29
#include "mozilla/UniquePtr.h"
30
31
class nsICSSLoaderObserver;
32
class nsIConsoleReportCollector;
33
class nsIContent;
34
35
namespace mozilla {
36
namespace dom {
37
class DocGroup;
38
class Element;
39
} // namespace dom
40
} // namespace mozilla
41
42
namespace mozilla {
43
44
namespace css {
45
46
class SheetLoadData;
47
class ImportRule;
48
49
/*********************
50
* Style sheet reuse *
51
*********************/
52
53
class MOZ_RAII LoaderReusableStyleSheets {
54
public:
55
LoaderReusableStyleSheets() {}
56
57
/**
58
* Look for a reusable sheet (see AddReusableSheet) matching the
59
* given URL. If found, set aResult, remove the reused sheet from
60
* the internal list, and return true. If not found, return false;
61
* in this case, aResult is not modified.
62
*
63
* @param aURL the url to match
64
* @param aResult [out] the style sheet which can be reused
65
*/
66
bool FindReusableStyleSheet(nsIURI* aURL, RefPtr<StyleSheet>& aResult);
67
68
/**
69
* Indicate that a certain style sheet is available for reuse if its
70
* URI matches the URI of an @import. Sheets should be added in the
71
* opposite order in which they are intended to be reused.
72
*
73
* @param aSheet the sheet which can be reused
74
*/
75
void AddReusableSheet(StyleSheet* aSheet) {
76
mReusableSheets.AppendElement(aSheet);
77
}
78
79
private:
80
LoaderReusableStyleSheets(const LoaderReusableStyleSheets&) = delete;
81
LoaderReusableStyleSheets& operator=(const LoaderReusableStyleSheets&) =
82
delete;
83
84
// The sheets that can be reused.
85
nsTArray<RefPtr<StyleSheet>> mReusableSheets;
86
};
87
88
class Loader final {
89
using ReferrerPolicy = dom::ReferrerPolicy;
90
91
public:
92
typedef nsIStyleSheetLinkingElement::Completed Completed;
93
typedef nsIStyleSheetLinkingElement::HasAlternateRel HasAlternateRel;
94
typedef nsIStyleSheetLinkingElement::IsAlternate IsAlternate;
95
typedef nsIStyleSheetLinkingElement::IsInline IsInline;
96
typedef nsIStyleSheetLinkingElement::IsExplicitlyEnabled IsExplicitlyEnabled;
97
typedef nsIStyleSheetLinkingElement::MediaMatched MediaMatched;
98
typedef nsIStyleSheetLinkingElement::Update LoadSheetResult;
99
typedef nsIStyleSheetLinkingElement::SheetInfo SheetInfo;
100
101
Loader();
102
// aDocGroup is used for dispatching SheetLoadData in PostLoadEvent(). It
103
// can be null if you want to use this constructor, and there's no
104
// document when the Loader is constructed.
105
explicit Loader(dom::DocGroup*);
106
explicit Loader(dom::Document*);
107
108
private:
109
// Private destructor, to discourage deletion outside of Release():
110
~Loader();
111
112
public:
113
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Loader)
114
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Loader)
115
116
void DropDocumentReference(); // notification that doc is going away
117
118
void SetCompatibilityMode(nsCompatibility aCompatMode) {
119
mCompatMode = aCompatMode;
120
}
121
nsCompatibility GetCompatibilityMode() { return mCompatMode; }
122
123
// TODO(emilio): Is the complexity of this method and carrying the titles
124
// around worth it? The alternate sheets will load anyhow eventually...
125
void DocumentStyleSheetSetChanged();
126
127
// XXXbz sort out what the deal is with events! When should they fire?
128
129
/**
130
* Load an inline style sheet. If a successful result is returned and
131
* result.WillNotify() is true, then aObserver is guaranteed to be notified
132
* asynchronously once the sheet is marked complete. If an error is
133
* returned, or if result.WillNotify() is false, aObserver will not be
134
* notified. In addition to parsing the sheet, this method will insert it
135
* into the stylesheet list of this CSSLoader's document.
136
* @param aObserver the observer to notify when the load completes.
137
* May be null.
138
* @param aBuffer the stylesheet data
139
* @param aLineNumber the line number at which the stylesheet data started.
140
*/
141
Result<LoadSheetResult, nsresult> LoadInlineStyle(
142
const SheetInfo&, const nsAString& aBuffer, uint32_t aLineNumber,
143
nsICSSLoaderObserver* aObserver);
144
145
/**
146
* Load a linked (document) stylesheet. If a successful result is returned,
147
* aObserver is guaranteed to be notified asynchronously once the sheet is
148
* loaded and marked complete, i.e., result.WillNotify() will always return
149
* true. If an error is returned, aObserver will not be notified. In
150
* addition to loading the sheet, this method will insert it into the
151
* stylesheet list of this CSSLoader's document.
152
* @param aObserver the observer to notify when the load completes.
153
* May be null.
154
*/
155
Result<LoadSheetResult, nsresult> LoadStyleLink(
156
const SheetInfo&, nsICSSLoaderObserver* aObserver);
157
158
/**
159
* Load a child (@import-ed) style sheet. In addition to loading the sheet,
160
* this method will insert it into the child sheet list of aParentSheet. If
161
* there is no sheet currently being parsed and the child sheet is not
162
* complete when this method returns, then when the child sheet becomes
163
* complete aParentSheet will be QIed to nsICSSLoaderObserver and
164
* asynchronously notified, just like for LoadStyleLink. Note that if the
165
* child sheet is already complete when this method returns, no
166
* nsICSSLoaderObserver notification will be sent.
167
*
168
* @param aParentSheet the parent of this child sheet
169
* @param aParentData the SheetLoadData corresponding to the load of the
170
* parent sheet. May be null for @import rules inserted via
171
* CSSOM.
172
* @param aURL the URL of the child sheet
173
* @param aMedia the already-parsed media list for the child sheet
174
* @param aSavedSheets any saved style sheets which could be reused
175
* for this load
176
*/
177
nsresult LoadChildSheet(StyleSheet& aParentSheet, SheetLoadData* aParentData,
178
nsIURI* aURL, dom::MediaList* aMedia,
179
LoaderReusableStyleSheets* aSavedSheets);
180
181
enum class UseSystemPrincipal { No, Yes };
182
183
/**
184
* Synchronously load and return the stylesheet at aURL. Any child sheets
185
* will also be loaded synchronously. Note that synchronous loads over some
186
* protocols may involve spinning up a new event loop, so use of this method
187
* does NOT guarantee not receiving any events before the sheet loads. This
188
* method can be used to load sheets not associated with a document.
189
*
190
* @param aURL the URL of the sheet to load
191
* @param aParsingMode the mode in which to parse the sheet
192
* (see comments at enum SheetParsingMode, above).
193
* @param aUseSystemPrincipal if true, give the resulting sheet the system
194
* principal no matter where it's being loaded from.
195
*
196
* NOTE: At the moment, this method assumes the sheet will be UTF-8, but
197
* ideally it would allow arbitrary encodings. Callers should NOT depend on
198
* non-UTF8 sheets being treated as UTF-8 by this method.
199
*
200
* NOTE: A successful return from this method doesn't indicate anything about
201
* whether the data could be parsed as CSS and doesn't indicate anything
202
* about the status of child sheets of the returned sheet.
203
*/
204
Result<RefPtr<StyleSheet>, nsresult> LoadSheetSync(
205
nsIURI*, SheetParsingMode = eAuthorSheetFeatures,
206
UseSystemPrincipal = UseSystemPrincipal::No);
207
208
enum class IsPreload {
209
No,
210
// This is a speculative load initiated by a <link rel=stylesheet> tag
211
// scanned by the parser, or @import rules found in a <style> tag.
212
FromParser,
213
// This is a speculative load as well, but initiated by
214
// <link rel="preload" as="style">
215
FromLink,
216
};
217
218
/**
219
* Asynchronously load the stylesheet at aURL. If a successful result is
220
* returned, aObserver is guaranteed to be notified asynchronously once the
221
* sheet is loaded and marked complete. This method can be used to load
222
* sheets not associated with a document.
223
*
224
* @param aURL the URL of the sheet to load
225
* @param aParsingMode the mode in which to parse the sheet
226
* (see comments at enum SheetParsingMode, above).
227
* @param aUseSystemPrincipal if true, give the resulting sheet the system
228
* principal no matter where it's being loaded from.
229
* @param aReferrerInfo referrer information of the sheet.
230
* @param aObserver the observer to notify when the load completes.
231
* Must not be null.
232
* @return the sheet to load. Note that the sheet may well not be loaded by
233
* the time this method returns.
234
*
235
* NOTE: At the moment, this method assumes the sheet will be UTF-8, but
236
* ideally it would allow arbitrary encodings. Callers should NOT depend on
237
* non-UTF8 sheets being treated as UTF-8 by this method.
238
*/
239
Result<RefPtr<StyleSheet>, nsresult> LoadSheet(
240
nsIURI* aURI, IsPreload, nsIPrincipal* aOriginPrincipal,
241
const Encoding* aPreloadEncoding, nsIReferrerInfo* aReferrerInfo,
242
nsICSSLoaderObserver* aObserver, CORSMode aCORSMode = CORS_NONE,
243
const nsAString& aIntegrity = EmptyString());
244
245
/**
246
* As above, but without caring for a couple things.
247
*/
248
Result<RefPtr<StyleSheet>, nsresult> LoadSheet(nsIURI*, SheetParsingMode,
249
UseSystemPrincipal,
250
nsICSSLoaderObserver*);
251
252
/**
253
* Stop loading all sheets. All nsICSSLoaderObservers involved will be
254
* notified with NS_BINDING_ABORTED as the status, possibly synchronously.
255
*/
256
void Stop();
257
258
/**
259
* nsresult Loader::StopLoadingSheet(nsIURI* aURL), which notifies the
260
* nsICSSLoaderObserver with NS_BINDING_ABORTED, was removed in Bug 556446.
261
* It can be found in revision 2c44a32052ad.
262
*/
263
264
/**
265
* Whether the loader is enabled or not.
266
* When disabled, processing of new styles is disabled and an attempt
267
* to do so will fail with a return code of
268
* NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable
269
* currently loading styles or already processed styles.
270
*/
271
bool GetEnabled() { return mEnabled; }
272
void SetEnabled(bool aEnabled) { mEnabled = aEnabled; }
273
274
/**
275
* Get the document we live for. May return null.
276
*/
277
dom::Document* GetDocument() const { return mDocument; }
278
279
/**
280
* Return true if this loader has pending loads (ones that would send
281
* notifications to an nsICSSLoaderObserver attached to this loader).
282
* If called from inside nsICSSLoaderObserver::StyleSheetLoaded, this will
283
* return false if and only if that is the last StyleSheetLoaded
284
* notification the CSSLoader knows it's going to send. In other words, if
285
* two sheets load at once (via load coalescing, e.g.), HasPendingLoads()
286
* will return true during notification for the first one, and false
287
* during notification for the second one.
288
*/
289
bool HasPendingLoads();
290
291
/**
292
* Add an observer to this loader. The observer will be notified
293
* for all loads that would have notified their own observers (even
294
* if those loads don't have observers attached to them).
295
* Load-specific observers will be notified before generic
296
* observers. The loader holds a reference to the observer.
297
*
298
* aObserver must not be null.
299
*/
300
void AddObserver(nsICSSLoaderObserver* aObserver);
301
302
/**
303
* Remove an observer added via AddObserver.
304
*/
305
void RemoveObserver(nsICSSLoaderObserver* aObserver);
306
307
// These interfaces are public only for the benefit of static functions
308
// within nsCSSLoader.cpp.
309
310
// IsAlternateSheet can change our currently selected style set if none is
311
// selected and aHasAlternateRel is false.
312
IsAlternate IsAlternateSheet(const nsAString& aTitle, bool aHasAlternateRel);
313
314
typedef nsTArray<RefPtr<SheetLoadData>> LoadDataArray;
315
316
// Measure our size.
317
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
318
319
private:
320
friend class SheetLoadData;
321
friend class StreamLoader;
322
323
// Helpers to conditionally block onload if mDocument is non-null.
324
void BlockOnload();
325
void UnblockOnload(bool aFireSync);
326
327
// Helper to select the correct dispatch target for asynchronous events for
328
// this loader.
329
already_AddRefed<nsISerialEventTarget> DispatchTarget();
330
331
nsresult CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
332
nsIPrincipal* aTriggeringPrincipal,
333
nsIURI* aTargetURI, nsINode* aRequestingNode,
334
IsPreload);
335
336
enum class SheetState : uint8_t {
337
Unknown = 0,
338
NeedsParser,
339
Pending,
340
Loading,
341
Complete
342
};
343
344
Tuple<RefPtr<StyleSheet>, SheetState> CreateSheet(
345
const SheetInfo& aInfo, nsIPrincipal* aLoaderPrincipal,
346
css::SheetParsingMode aParsingMode, bool aSyncLoad) {
347
return CreateSheet(aInfo.mURI, aInfo.mContent, aLoaderPrincipal,
348
aParsingMode, aInfo.mCORSMode, aInfo.mReferrerInfo,
349
aInfo.mIntegrity, aSyncLoad);
350
}
351
352
// For inline style, the aURI param is null, but the aLinkingContent
353
// must be non-null then. The loader principal must never be null
354
// if aURI is not null.
355
Tuple<RefPtr<StyleSheet>, SheetState> CreateSheet(
356
nsIURI* aURI, nsIContent* aLinkingContent, nsIPrincipal* aLoaderPrincipal,
357
css::SheetParsingMode, CORSMode, nsIReferrerInfo* aLoadingReferrerInfo,
358
const nsAString& aIntegrity, bool aSyncLoad);
359
360
// Pass in either a media string or the MediaList from the CSSParser. Don't
361
// pass both.
362
//
363
// This method will set the sheet's enabled state based on IsAlternate and co.
364
MediaMatched PrepareSheet(StyleSheet&, const nsAString& aTitle,
365
const nsAString& aMediaString, dom::MediaList*,
366
IsAlternate, IsExplicitlyEnabled);
367
368
// Inserts a style sheet in a document or a ShadowRoot.
369
void InsertSheetInTree(StyleSheet& aSheet, nsIContent* aLinkingContent);
370
// Inserts a style sheet into a parent style sheet.
371
void InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet);
372
373
Result<RefPtr<StyleSheet>, nsresult> InternalLoadNonDocumentSheet(
374
nsIURI* aURL, IsPreload, SheetParsingMode aParsingMode,
375
UseSystemPrincipal, nsIPrincipal* aOriginPrincipal,
376
const Encoding* aPreloadEncoding, nsIReferrerInfo* aReferrerInfo,
377
nsICSSLoaderObserver* aObserver, CORSMode aCORSMode,
378
const nsAString& aIntegrity);
379
380
// Post a load event for aObserver to be notified about aSheet. The
381
// notification will be sent with status NS_OK unless the load event is
382
// canceled at some point (in which case it will be sent with
383
// NS_BINDING_ABORTED). aWasAlternate indicates the state when the load was
384
// initiated, not the state at some later time. aURI should be the URI the
385
// sheet was loaded from (may be null for inline sheets). aElement is the
386
// owning element for this sheet.
387
nsresult PostLoadEvent(nsIURI* aURI, StyleSheet* aSheet,
388
nsICSSLoaderObserver* aObserver,
389
IsAlternate aWasAlternate, MediaMatched aMediaMatched,
390
nsIReferrerInfo* aReferrerInfo,
391
nsIStyleSheetLinkingElement* aElement);
392
393
// Start the loads of all the sheets in mPendingDatas
394
void StartDeferredLoads();
395
396
void HandleLoadEvent(SheetLoadData&);
397
398
// Note: LoadSheet is responsible for setting the sheet to complete on
399
// failure.
400
nsresult LoadSheet(SheetLoadData&, SheetState, IsPreload);
401
402
enum class AllowAsyncParse {
403
Yes,
404
No,
405
};
406
407
// Parse the stylesheet in the load data.
408
//
409
// Returns whether the parse finished. It may not finish e.g. if the sheet had
410
// an @import.
411
//
412
// If this function returns Completed::Yes, then ParseSheet also called
413
// SheetComplete on aLoadData.
414
Completed ParseSheet(const nsACString&, SheetLoadData&, AllowAsyncParse);
415
416
// The load of the sheet in the load data is done, one way or another.
417
// Do final cleanup.
418
void SheetComplete(SheetLoadData&, nsresult aStatus);
419
420
// The guts of SheetComplete. This may be called recursively on parent datas
421
// or datas that had glommed on to a single load. The array is there so load
422
// datas whose observers need to be notified can be added to it.
423
void DoSheetComplete(SheetLoadData&, LoadDataArray& aDatasToNotify);
424
425
// Mark the given SheetLoadData, as well as any of its siblings, parents, etc
426
// transitively, as failed. The idea is to mark as failed any load that was
427
// directly or indirectly @importing the sheet this SheetLoadData represents.
428
void MarkLoadTreeFailed(SheetLoadData&);
429
430
struct Sheets;
431
UniquePtr<Sheets> mSheets;
432
433
// The array of posted stylesheet loaded events (SheetLoadDatas) we have.
434
// Note that these are rare.
435
LoadDataArray mPostedEvents;
436
437
// Our array of "global" observers
438
nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>> mObservers;
439
440
// This reference is nulled by the Document in it's destructor through
441
// DropDocumentReference().
442
dom::Document* MOZ_NON_OWNING_REF mDocument; // the document we live for
443
444
// For dispatching events via DocGroup::Dispatch() when mDocument is nullptr.
445
RefPtr<dom::DocGroup> mDocGroup;
446
447
// Number of datas still waiting to be notified on if we're notifying on a
448
// whole bunch at once (e.g. in one of the stop methods). This is used to
449
// make sure that HasPendingLoads() won't return false until we're notifying
450
// on the last data we're working with.
451
uint32_t mDatasToNotifyOn;
452
453
nsCompatibility mCompatMode;
454
455
bool mEnabled; // is enabled to load new styles
456
457
nsCOMPtr<nsIConsoleReportCollector> mReporter;
458
459
#ifdef DEBUG
460
// Whether we're in a necko callback atm.
461
bool mSyncCallback = false;
462
#endif
463
};
464
465
} // namespace css
466
} // namespace mozilla
467
468
#endif /* mozilla_css_Loader_h */