1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /* loading of CSS style sheets using the network APIs */
39 :
40 : #ifndef mozilla_css_Loader_h
41 : #define mozilla_css_Loader_h
42 :
43 : #include "nsIPrincipal.h"
44 : #include "nsAString.h"
45 : #include "nsAutoPtr.h"
46 : #include "nsCompatibility.h"
47 : #include "nsDataHashtable.h"
48 : #include "nsInterfaceHashtable.h"
49 : #include "nsRefPtrHashtable.h"
50 : #include "nsTArray.h"
51 : #include "nsTObserverArray.h"
52 : #include "nsURIHashKey.h"
53 :
54 : class nsIAtom;
55 : class nsICSSLoaderObserver;
56 : class nsCSSStyleSheet;
57 : class nsIContent;
58 : class nsIDocument;
59 : class nsCSSParser;
60 : class nsMediaList;
61 : class nsIStyleSheetLinkingElement;
62 :
63 : namespace mozilla {
64 :
65 : class URIAndPrincipalHashKey : public nsURIHashKey
66 : {
67 : public:
68 : typedef URIAndPrincipalHashKey* KeyType;
69 : typedef const URIAndPrincipalHashKey* KeyTypePointer;
70 :
71 0 : URIAndPrincipalHashKey(const URIAndPrincipalHashKey* aKey)
72 0 : : nsURIHashKey(aKey->mKey), mPrincipal(aKey->mPrincipal)
73 : {
74 0 : MOZ_COUNT_CTOR(URIAndPrincipalHashKey);
75 0 : }
76 0 : URIAndPrincipalHashKey(nsIURI* aURI, nsIPrincipal* aPrincipal)
77 0 : : nsURIHashKey(aURI), mPrincipal(aPrincipal)
78 : {
79 0 : MOZ_COUNT_CTOR(URIAndPrincipalHashKey);
80 0 : }
81 : URIAndPrincipalHashKey(const URIAndPrincipalHashKey& toCopy)
82 : : nsURIHashKey(toCopy), mPrincipal(toCopy.mPrincipal)
83 : {
84 : MOZ_COUNT_CTOR(URIAndPrincipalHashKey);
85 : }
86 0 : ~URIAndPrincipalHashKey()
87 0 : {
88 0 : MOZ_COUNT_DTOR(URIAndPrincipalHashKey);
89 0 : }
90 :
91 0 : URIAndPrincipalHashKey* GetKey() const {
92 0 : return const_cast<URIAndPrincipalHashKey*>(this);
93 : }
94 : const URIAndPrincipalHashKey* GetKeyPointer() const { return this; }
95 :
96 0 : bool KeyEquals(const URIAndPrincipalHashKey* aKey) const {
97 0 : if (!nsURIHashKey::KeyEquals(aKey->mKey)) {
98 0 : return false;
99 : }
100 :
101 0 : if (!mPrincipal != !aKey->mPrincipal) {
102 : // One or the other has a principal, but not both... not equal
103 0 : return false;
104 : }
105 :
106 : bool eq;
107 0 : return !mPrincipal ||
108 0 : (NS_SUCCEEDED(mPrincipal->Equals(aKey->mPrincipal, &eq)) && eq);
109 : }
110 :
111 : static const URIAndPrincipalHashKey*
112 0 : KeyToPointer(URIAndPrincipalHashKey* aKey) { return aKey; }
113 0 : static PLDHashNumber HashKey(const URIAndPrincipalHashKey* aKey) {
114 0 : return nsURIHashKey::HashKey(aKey->mKey);
115 : }
116 :
117 : enum { ALLOW_MEMMOVE = true };
118 :
119 : protected:
120 : nsCOMPtr<nsIPrincipal> mPrincipal;
121 : };
122 :
123 :
124 :
125 : namespace css {
126 :
127 : class SheetLoadData;
128 : class ImportRule;
129 :
130 : /***********************************************************************
131 : * Enum that describes the state of the sheet returned by CreateSheet. *
132 : ***********************************************************************/
133 : enum StyleSheetState {
134 : eSheetStateUnknown = 0,
135 : eSheetNeedsParser,
136 : eSheetPending,
137 : eSheetLoading,
138 : eSheetComplete
139 : };
140 :
141 : class Loader {
142 : public:
143 : Loader();
144 : Loader(nsIDocument*);
145 : ~Loader();
146 :
147 : // This isn't a COM class but it's reference-counted like one.
148 : NS_IMETHOD_(nsrefcnt) AddRef();
149 : NS_IMETHOD_(nsrefcnt) Release();
150 :
151 : void DropDocumentReference(); // notification that doc is going away
152 :
153 1975 : void SetCompatibilityMode(nsCompatibility aCompatMode)
154 1975 : { mCompatMode = aCompatMode; }
155 110 : nsCompatibility GetCompatibilityMode() { return mCompatMode; }
156 : nsresult SetPreferredSheet(const nsAString& aTitle);
157 :
158 : // XXXbz sort out what the deal is with events! When should they fire?
159 :
160 : /**
161 : * Load an inline style sheet. If a successful result is returned and
162 : * *aCompleted is false, then aObserver is guaranteed to be notified
163 : * asynchronously once the sheet is marked complete. If an error is
164 : * returned, or if *aCompleted is true, aObserver will not be notified. In
165 : * addition to parsing the sheet, this method will insert it into the
166 : * stylesheet list of this CSSLoader's document.
167 : *
168 : * @param aElement the element linking to the stylesheet. This must not be
169 : * null and must implement nsIStyleSheetLinkingElement.
170 : * @param aBuffer the stylesheet data
171 : * @param aLineNumber the line number at which the stylesheet data started.
172 : * @param aTitle the title of the sheet.
173 : * @param aMedia the media string for the sheet.
174 : * @param aObserver the observer to notify when the load completes.
175 : * May be null.
176 : * @param [out] aCompleted whether parsing of the sheet completed.
177 : * @param [out] aIsAlternate whether the stylesheet ended up being an
178 : * alternate sheet.
179 : */
180 : nsresult LoadInlineStyle(nsIContent* aElement,
181 : const nsAString& aBuffer,
182 : PRUint32 aLineNumber,
183 : const nsAString& aTitle,
184 : const nsAString& aMedia,
185 : nsICSSLoaderObserver* aObserver,
186 : bool* aCompleted,
187 : bool* aIsAlternate);
188 :
189 : /**
190 : * Load a linked (document) stylesheet. If a successful result is returned,
191 : * aObserver is guaranteed to be notified asynchronously once the sheet is
192 : * loaded and marked complete. If an error is returned, aObserver will not
193 : * be notified. In addition to loading the sheet, this method will insert it
194 : * into the stylesheet list of this CSSLoader's document.
195 : *
196 : * @param aElement the element linking to the the stylesheet. May be null.
197 : * @param aURL the URL of the sheet.
198 : * @param aTitle the title of the sheet.
199 : * @param aMedia the media string for the sheet.
200 : * @param aHasAlternateRel whether the rel for this link included
201 : * "alternate".
202 : * @param aObserver the observer to notify when the load completes.
203 : * May be null.
204 : * @param [out] aIsAlternate whether the stylesheet actually ended up beinga
205 : * an alternate sheet. Note that this need not match
206 : * aHasAlternateRel.
207 : */
208 : nsresult LoadStyleLink(nsIContent* aElement,
209 : nsIURI* aURL,
210 : const nsAString& aTitle,
211 : const nsAString& aMedia,
212 : bool aHasAlternateRel,
213 : nsICSSLoaderObserver* aObserver,
214 : bool* aIsAlternate);
215 :
216 : /**
217 : * Load a child (@import-ed) style sheet. In addition to loading the sheet,
218 : * this method will insert it into the child sheet list of aParentSheet. If
219 : * there is no sheet currently being parsed and the child sheet is not
220 : * complete when this method returns, then when the child sheet becomes
221 : * complete aParentSheet will be QIed to nsICSSLoaderObserver and
222 : * asynchronously notified, just like for LoadStyleLink. Note that if the
223 : * child sheet is already complete when this method returns, no
224 : * nsICSSLoaderObserver notification will be sent.
225 : *
226 : * @param aParentSheet the parent of this child sheet
227 : * @param aURL the URL of the child sheet
228 : * @param aMedia the already-parsed media list for the child sheet
229 : * @param aRule the @import rule importing this child. This is used to
230 : * properly order the child sheet list of aParentSheet.
231 : */
232 : nsresult LoadChildSheet(nsCSSStyleSheet* aParentSheet,
233 : nsIURI* aURL,
234 : nsMediaList* aMedia,
235 : ImportRule* aRule);
236 :
237 : /**
238 : * Synchronously load and return the stylesheet at aURL. Any child sheets
239 : * will also be loaded synchronously. Note that synchronous loads over some
240 : * protocols may involve spinning up a new event loop, so use of this method
241 : * does NOT guarantee not receiving any events before the sheet loads. This
242 : * method can be used to load sheets not associated with a document.
243 : *
244 : * @param aURL the URL of the sheet to load
245 : * @param aEnableUnsafeRules whether unsafe rules are enabled for this
246 : * sheet load
247 : * Unsafe rules are rules that can violate key Gecko invariants if misused.
248 : * In particular, most anonymous box pseudoelements must be very carefully
249 : * styled or we will have severe problems. Therefore unsafe rules should
250 : * never be enabled for stylesheets controlled by untrusted sites; preferably
251 : * unsafe rules should only be enabled for agent sheets.
252 : * @param aUseSystemPrincipal if true, give the resulting sheet the system
253 : * principal no matter where it's being loaded from.
254 : * @param [out] aSheet the loaded, complete sheet.
255 : *
256 : * NOTE: At the moment, this method assumes the sheet will be UTF-8, but
257 : * ideally it would allow arbitrary encodings. Callers should NOT depend on
258 : * non-UTF8 sheets being treated as UTF-8 by this method.
259 : *
260 : * NOTE: A successful return from this method doesn't indicate anything about
261 : * whether the data could be parsed as CSS and doesn't indicate anything
262 : * about the status of child sheets of the returned sheet.
263 : */
264 : nsresult LoadSheetSync(nsIURI* aURL, bool aEnableUnsafeRules,
265 : bool aUseSystemPrincipal,
266 : nsCSSStyleSheet** aSheet);
267 :
268 : /**
269 : * As above, but aUseSystemPrincipal and aEnableUnsafeRules are assumed false.
270 : */
271 0 : nsresult LoadSheetSync(nsIURI* aURL, nsCSSStyleSheet** aSheet) {
272 0 : return LoadSheetSync(aURL, false, false, aSheet);
273 : }
274 :
275 : /**
276 : * Asynchronously load the stylesheet at aURL. If a successful result is
277 : * returned, aObserver is guaranteed to be notified asynchronously once the
278 : * sheet is loaded and marked complete. This method can be used to load
279 : * sheets not associated with a document.
280 : *
281 : * @param aURL the URL of the sheet to load
282 : * @param aOriginPrincipal the principal to use for security checks. This
283 : * can be null to indicate that these checks should
284 : * be skipped.
285 : * @param aCharset the encoding to use for converting the sheet data
286 : * from bytes to Unicode. May be empty to indicate that the
287 : * charset of the CSSLoader's document should be used. This
288 : * is only used if neither the network transport nor the
289 : * sheet itself indicate an encoding.
290 : * @param aObserver the observer to notify when the load completes.
291 : * Must not be null.
292 : * @param [out] aSheet the sheet to load. Note that the sheet may well
293 : * not be loaded by the time this method returns.
294 : */
295 : nsresult LoadSheet(nsIURI* aURL,
296 : nsIPrincipal* aOriginPrincipal,
297 : const nsCString& aCharset,
298 : nsICSSLoaderObserver* aObserver,
299 : nsCSSStyleSheet** aSheet);
300 :
301 : /**
302 : * Same as above, to be used when the caller doesn't care about the
303 : * not-yet-loaded sheet.
304 : */
305 : nsresult LoadSheet(nsIURI* aURL,
306 : nsIPrincipal* aOriginPrincipal,
307 : const nsCString& aCharset,
308 : nsICSSLoaderObserver* aObserver);
309 :
310 : /**
311 : * Stop loading all sheets. All nsICSSLoaderObservers involved will be
312 : * notified with NS_BINDING_ABORTED as the status, possibly synchronously.
313 : */
314 : nsresult Stop(void);
315 :
316 : /**
317 : * nsresult Loader::StopLoadingSheet(nsIURI* aURL), which notifies the
318 : * nsICSSLoaderObserver with NS_BINDING_ABORTED, was removed in Bug 556446.
319 : * It can be found in revision 2c44a32052ad.
320 : */
321 :
322 : /**
323 : * Whether the loader is enabled or not.
324 : * When disabled, processing of new styles is disabled and an attempt
325 : * to do so will fail with a return code of
326 : * NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable
327 : * currently loading styles or already processed styles.
328 : */
329 62 : bool GetEnabled() { return mEnabled; }
330 1038 : void SetEnabled(bool aEnabled) { mEnabled = aEnabled; }
331 :
332 : /**
333 : * Get the document we live for. May return null.
334 : */
335 0 : nsIDocument* GetDocument() const { return mDocument; }
336 :
337 : /**
338 : * Return true if this loader has pending loads (ones that would send
339 : * notifications to an nsICSSLoaderObserver attached to this loader).
340 : * If called from inside nsICSSLoaderObserver::StyleSheetLoaded, this will
341 : * return false if and only if that is the last StyleSheetLoaded
342 : * notification the CSSLoader knows it's going to send. In other words, if
343 : * two sheets load at once (via load coalescing, e.g.), HasPendingLoads()
344 : * will return true during notification for the first one, and false
345 : * during notification for the second one.
346 : */
347 : bool HasPendingLoads();
348 :
349 : /**
350 : * Add an observer to this loader. The observer will be notified
351 : * for all loads that would have notified their own observers (even
352 : * if those loads don't have observers attached to them).
353 : * Load-specific observers will be notified before generic
354 : * observers. The loader holds a reference to the observer.
355 : *
356 : * aObserver must not be null.
357 : */
358 : nsresult AddObserver(nsICSSLoaderObserver* aObserver);
359 :
360 : /**
361 : * Remove an observer added via AddObserver.
362 : */
363 : void RemoveObserver(nsICSSLoaderObserver* aObserver);
364 :
365 : // These interfaces are public only for the benefit of static functions
366 : // within nsCSSLoader.cpp.
367 :
368 : // IsAlternate can change our currently selected style set if none
369 : // is selected and aHasAlternateRel is false.
370 : bool IsAlternate(const nsAString& aTitle, bool aHasAlternateRel);
371 :
372 : typedef nsTArray<nsRefPtr<SheetLoadData> > LoadDataArray;
373 :
374 : private:
375 : friend class SheetLoadData;
376 :
377 : // Note: null aSourcePrincipal indicates that the content policy and
378 : // CheckLoadURI checks should be skipped.
379 : nsresult CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
380 : nsIURI* aTargetURI,
381 : nsISupports* aContext);
382 :
383 :
384 : // For inline style, the aURI param is null, but the aLinkingContent
385 : // must be non-null then. The loader principal must never be null
386 : // if aURI is not null.
387 : // *aIsAlternate is set based on aTitle and aHasAlternateRel.
388 : nsresult CreateSheet(nsIURI* aURI,
389 : nsIContent* aLinkingContent,
390 : nsIPrincipal* aLoaderPrincipal,
391 : bool aSyncLoad,
392 : bool aHasAlternateRel,
393 : const nsAString& aTitle,
394 : StyleSheetState& aSheetState,
395 : bool *aIsAlternate,
396 : nsCSSStyleSheet** aSheet);
397 :
398 : // Pass in either a media string or the nsMediaList from the
399 : // CSSParser. Don't pass both.
400 : // This method will set the sheet's enabled state based on isAlternate
401 : nsresult PrepareSheet(nsCSSStyleSheet* aSheet,
402 : const nsAString& aTitle,
403 : const nsAString& aMediaString,
404 : nsMediaList* aMediaList,
405 : bool isAlternate);
406 :
407 : nsresult InsertSheetInDoc(nsCSSStyleSheet* aSheet,
408 : nsIContent* aLinkingContent,
409 : nsIDocument* aDocument);
410 :
411 : nsresult InsertChildSheet(nsCSSStyleSheet* aSheet,
412 : nsCSSStyleSheet* aParentSheet,
413 : ImportRule* aParentRule);
414 :
415 : nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
416 : bool aAllowUnsafeRules,
417 : bool aUseSystemPrincipal,
418 : nsIPrincipal* aOriginPrincipal,
419 : const nsCString& aCharset,
420 : nsCSSStyleSheet** aSheet,
421 : nsICSSLoaderObserver* aObserver);
422 :
423 : // Post a load event for aObserver to be notified about aSheet. The
424 : // notification will be sent with status NS_OK unless the load event is
425 : // canceled at some point (in which case it will be sent with
426 : // NS_BINDING_ABORTED). aWasAlternate indicates the state when the load was
427 : // initiated, not the state at some later time. aURI should be the URI the
428 : // sheet was loaded from (may be null for inline sheets). aElement is the
429 : // owning element for this sheet.
430 : nsresult PostLoadEvent(nsIURI* aURI,
431 : nsCSSStyleSheet* aSheet,
432 : nsICSSLoaderObserver* aObserver,
433 : bool aWasAlternate,
434 : nsIStyleSheetLinkingElement* aElement);
435 :
436 : // Start the loads of all the sheets in mPendingDatas
437 : void StartAlternateLoads();
438 :
439 : // Handle an event posted by PostLoadEvent
440 : void HandleLoadEvent(SheetLoadData* aEvent);
441 :
442 : // Note: LoadSheet is responsible for releasing aLoadData and setting the
443 : // sheet to complete on failure.
444 : nsresult LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState);
445 :
446 : // Parse the stylesheet in aLoadData. The sheet data comes from aInput.
447 : // Set aCompleted to true if the parse finished, false otherwise (e.g. if the
448 : // sheet had an @import). If aCompleted is true when this returns, then
449 : // ParseSheet also called SheetComplete on aLoadData.
450 : nsresult ParseSheet(const nsAString& aInput,
451 : SheetLoadData* aLoadData,
452 : bool& aCompleted);
453 :
454 : // The load of the sheet in aLoadData is done, one way or another. Do final
455 : // cleanup, including releasing aLoadData.
456 : void SheetComplete(SheetLoadData* aLoadData, nsresult aStatus);
457 :
458 : // The guts of SheetComplete. This may be called recursively on parent datas
459 : // or datas that had glommed on to a single load. The array is there so load
460 : // datas whose observers need to be notified can be added to it.
461 : void DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
462 : LoadDataArray& aDatasToNotify);
463 :
464 : nsRefPtrHashtable<URIAndPrincipalHashKey, nsCSSStyleSheet>
465 : mCompleteSheets;
466 : nsDataHashtable<URIAndPrincipalHashKey, SheetLoadData*>
467 : mLoadingDatas; // weak refs
468 : nsDataHashtable<URIAndPrincipalHashKey, SheetLoadData*>
469 : mPendingDatas; // weak refs
470 :
471 : // We're not likely to have many levels of @import... But likely to have
472 : // some. Allocate some storage, what the hell.
473 : nsAutoTArray<SheetLoadData*, 8> mParsingDatas;
474 :
475 : // The array of posted stylesheet loaded events (SheetLoadDatas) we have.
476 : // Note that these are rare.
477 : LoadDataArray mPostedEvents;
478 :
479 : // Our array of "global" observers
480 : // XXXbz these are strong refs; should we be cycle collecting CSS loaders?
481 : nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> > mObservers;
482 :
483 : // the load data needs access to the document...
484 : nsIDocument* mDocument; // the document we live for
485 :
486 : // Refcounting
487 : nsAutoRefCnt mRefCnt;
488 : NS_DECL_OWNINGTHREAD
489 :
490 : // Number of datas still waiting to be notified on if we're notifying on a
491 : // whole bunch at once (e.g. in one of the stop methods). This is used to
492 : // make sure that HasPendingLoads() won't return false until we're notifying
493 : // on the last data we're working with.
494 : PRUint32 mDatasToNotifyOn;
495 :
496 : nsCompatibility mCompatMode;
497 : nsString mPreferredSheet; // title of preferred sheet
498 :
499 : bool mEnabled; // is enabled to load new styles
500 :
501 : #ifdef DEBUG
502 : bool mSyncCallback;
503 : #endif
504 : };
505 :
506 : } // namespace css
507 : } // namespace mozilla
508 :
509 : #endif /* mozilla_css_Loader_h */
|