1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: ft=cpp tw=78 sw=2 et ts=2
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1999
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK *****
39 : *
40 : * This Original Code has been modified by IBM Corporation.
41 : * Modifications made by IBM described herein are Copyright (c)
42 : * International Business Machines Corporation, 2000. Modifications
43 : * to Mozilla code or documentation identified per MPL Section 3.3
44 : *
45 : * Date Modified by Description of modification
46 : * 04/20/2000 IBM Corp. OS/2 VisualAge build.
47 : */
48 :
49 : /* loading of CSS style sheets using the network APIs */
50 :
51 : #include "mozilla/Util.h"
52 :
53 : #include "mozilla/css/Loader.h"
54 : #include "nsIRunnable.h"
55 : #include "nsIUnicharStreamLoader.h"
56 : #include "nsSyncLoadService.h"
57 : #include "nsCOMPtr.h"
58 : #include "nsCOMArray.h"
59 : #include "nsString.h"
60 : #include "nsIContent.h"
61 : #include "nsIDocument.h"
62 : #include "nsIDOMNode.h"
63 : #include "nsIDOMDocument.h"
64 : #include "nsIDOMWindow.h"
65 : #include "nsHashtable.h"
66 : #include "nsIURI.h"
67 : #include "nsIServiceManager.h"
68 : #include "nsNetUtil.h"
69 : #include "nsContentUtils.h"
70 : #include "nsCRT.h"
71 : #include "nsIScriptSecurityManager.h"
72 : #include "nsContentPolicyUtils.h"
73 : #include "nsIHttpChannel.h"
74 : #include "nsIScriptError.h"
75 : #include "nsMimeTypes.h"
76 : #include "nsIAtom.h"
77 : #include "nsCSSStyleSheet.h"
78 : #include "nsIStyleSheetLinkingElement.h"
79 : #include "nsICSSLoaderObserver.h"
80 : #include "nsCSSParser.h"
81 : #include "mozilla/css/ImportRule.h"
82 : #include "nsThreadUtils.h"
83 : #include "nsGkAtoms.h"
84 : #include "nsDocShellCID.h"
85 : #include "nsIThreadInternal.h"
86 :
87 : #ifdef MOZ_XUL
88 : #include "nsXULPrototypeCache.h"
89 : #endif
90 :
91 : #include "nsIMediaList.h"
92 : #include "nsIDOMStyleSheet.h"
93 : #include "nsIDOMCSSStyleSheet.h"
94 : #include "nsContentErrors.h"
95 :
96 : #include "nsIChannelPolicy.h"
97 : #include "nsIContentSecurityPolicy.h"
98 :
99 : #include "mozilla/FunctionTimer.h"
100 :
101 : /**
102 : * OVERALL ARCHITECTURE
103 : *
104 : * The CSS Loader gets requests to load various sorts of style sheets:
105 : * inline style from <style> elements, linked style, @import-ed child
106 : * sheets, non-document sheets. The loader handles the following tasks:
107 : *
108 : * 1) Checking whether the load is allowed: CheckLoadAllowed()
109 : * 2) Creation of the actual style sheet objects: CreateSheet()
110 : * 3) setting of the right media, title, enabled state, etc on the
111 : * sheet: PrepareSheet()
112 : * 4) Insertion of the sheet in the proper cascade order:
113 : * InsertSheetInDoc() and InsertChildSheet()
114 : * 5) Load of the sheet: LoadSheet()
115 : * 6) Parsing of the sheet: ParseSheet()
116 : * 7) Cleanup: SheetComplete()
117 : *
118 : * The detailed documentation for these functions is found with the
119 : * function implementations.
120 : *
121 : * The following helper object is used:
122 : * SheetLoadData -- a small class that is used to store all the
123 : * information needed for the loading of a sheet;
124 : * this class handles listening for the stream
125 : * loader completion and also handles charset
126 : * determination.
127 : */
128 :
129 : namespace mozilla {
130 : namespace css {
131 :
132 : /*********************************************
133 : * Data needed to properly load a stylesheet *
134 : *********************************************/
135 :
136 : class SheetLoadData : public nsIRunnable,
137 : public nsIUnicharStreamLoaderObserver,
138 : public nsIThreadObserver
139 : {
140 : public:
141 : virtual ~SheetLoadData(void);
142 : // Data for loading a sheet linked from a document
143 : SheetLoadData(Loader* aLoader,
144 : const nsSubstring& aTitle,
145 : nsIURI* aURI,
146 : nsCSSStyleSheet* aSheet,
147 : nsIStyleSheetLinkingElement* aOwningElement,
148 : bool aIsAlternate,
149 : nsICSSLoaderObserver* aObserver,
150 : nsIPrincipal* aLoaderPrincipal);
151 :
152 : // Data for loading a sheet linked from an @import rule
153 : SheetLoadData(Loader* aLoader,
154 : nsIURI* aURI,
155 : nsCSSStyleSheet* aSheet,
156 : SheetLoadData* aParentData,
157 : nsICSSLoaderObserver* aObserver,
158 : nsIPrincipal* aLoaderPrincipal);
159 :
160 : // Data for loading a non-document sheet
161 : SheetLoadData(Loader* aLoader,
162 : nsIURI* aURI,
163 : nsCSSStyleSheet* aSheet,
164 : bool aSyncLoad,
165 : bool aAllowUnsafeRules,
166 : bool aUseSystemPrincipal,
167 : const nsCString& aCharset,
168 : nsICSSLoaderObserver* aObserver,
169 : nsIPrincipal* aLoaderPrincipal);
170 :
171 : already_AddRefed<nsIURI> GetReferrerURI();
172 :
173 : void ScheduleLoadEventIfNeeded(nsresult aStatus);
174 :
175 : NS_DECL_ISUPPORTS
176 : NS_DECL_NSIRUNNABLE
177 : NS_DECL_NSITHREADOBSERVER
178 : NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
179 :
180 : // Hold a ref to the CSSLoader so we can call back to it to let it
181 : // know the load finished
182 : Loader* mLoader; // strong ref
183 :
184 : // Title needed to pull datas out of the pending datas table when
185 : // the preferred title is changed
186 : nsString mTitle;
187 :
188 : // Charset we decided to use for the sheet
189 : nsCString mCharset;
190 :
191 : // URI we're loading. Null for inline sheets
192 : nsCOMPtr<nsIURI> mURI;
193 :
194 : // Should be 1 for non-inline sheets.
195 : PRUint32 mLineNumber;
196 :
197 : // The sheet we're loading data for
198 : nsRefPtr<nsCSSStyleSheet> mSheet;
199 :
200 : // Linked list of datas for the same URI as us
201 : SheetLoadData* mNext; // strong ref
202 :
203 : // Load data for the sheet that @import-ed us if we were @import-ed
204 : // during the parse
205 : SheetLoadData* mParentData; // strong ref
206 :
207 : // Number of sheets we @import-ed that are still loading
208 : PRUint32 mPendingChildren;
209 :
210 : // mSyncLoad is true when the load needs to be synchronous -- right
211 : // now only for LoadSheetSync and children of sync loads.
212 : bool mSyncLoad : 1;
213 :
214 : // mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
215 : // LoadSheet or an @import from such a sheet. Non-document sheet loads can
216 : // proceed even if we have no document.
217 : bool mIsNonDocumentSheet : 1;
218 :
219 : // mIsLoading is true from the moment we are placed in the loader's
220 : // "loading datas" table (right after the async channel is opened)
221 : // to the moment we are removed from said table (due to the load
222 : // completing or being cancelled).
223 : bool mIsLoading : 1;
224 :
225 : // mIsCancelled is set to true when a sheet load is stopped by
226 : // Stop() or StopLoadingSheet() (which was removed in Bug 556446).
227 : // SheetLoadData::OnStreamComplete() checks this to avoid parsing
228 : // sheets that have been cancelled and such.
229 : bool mIsCancelled : 1;
230 :
231 : // mMustNotify is true if the load data is being loaded async and
232 : // the original function call that started the load has returned.
233 : // This applies only to observer notifications; load/error events
234 : // are fired for any SheetLoadData that has a non-null
235 : // mOwningElement.
236 : bool mMustNotify : 1;
237 :
238 : // mWasAlternate is true if the sheet was an alternate when the load data was
239 : // created.
240 : bool mWasAlternate : 1;
241 :
242 : // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed
243 : // in the loaded sheet.
244 : bool mAllowUnsafeRules : 1;
245 :
246 : // mUseSystemPrincipal is true if the system principal should be used for
247 : // this sheet, no matter what the channel principal is. Only true for sync
248 : // loads.
249 : bool mUseSystemPrincipal : 1;
250 :
251 : // If true, this SheetLoadData is being used as a way to handle
252 : // async observer notification for an already-complete sheet.
253 : bool mSheetAlreadyComplete : 1;
254 :
255 : // This is the element that imported the sheet. Needed to get the
256 : // charset set on it and to fire load/error events.
257 : nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
258 :
259 : // The observer that wishes to be notified of load completion
260 : nsCOMPtr<nsICSSLoaderObserver> mObserver;
261 :
262 : // The principal that identifies who started loading us.
263 : nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
264 :
265 : // The charset to use if the transport and sheet don't indicate one.
266 : // May be empty. Must be empty if mOwningElement is non-null.
267 : nsCString mCharsetHint;
268 :
269 : // The status our load ended up with; this determines whether we
270 : // should fire error events or load events. This gets initialized
271 : // by ScheduleLoadEventIfNeeded, and is only used after that has
272 : // been called.
273 : nsresult mStatus;
274 :
275 : private:
276 : void FireLoadEvent(nsIThreadInternal* aThread);
277 : };
278 :
279 : #ifdef MOZ_LOGGING
280 : // #define FORCE_PR_LOG /* Allow logging in the release build */
281 : #endif /* MOZ_LOGGING */
282 : #include "prlog.h"
283 :
284 : #ifdef PR_LOGGING
285 1464 : static PRLogModuleInfo *gLoaderLog = PR_NewLogModule("nsCSSLoader");
286 : #endif /* PR_LOGGING */
287 :
288 : #define LOG_FORCE(args) PR_LOG(gLoaderLog, PR_LOG_ALWAYS, args)
289 : #define LOG_ERROR(args) PR_LOG(gLoaderLog, PR_LOG_ERROR, args)
290 : #define LOG_WARN(args) PR_LOG(gLoaderLog, PR_LOG_WARNING, args)
291 : #define LOG_DEBUG(args) PR_LOG(gLoaderLog, PR_LOG_DEBUG, args)
292 : #define LOG(args) LOG_DEBUG(args)
293 :
294 : #define LOG_FORCE_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_ALWAYS)
295 : #define LOG_ERROR_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_ERROR)
296 : #define LOG_WARN_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_WARNING)
297 : #define LOG_DEBUG_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_DEBUG)
298 : #define LOG_ENABLED() LOG_DEBUG_ENABLED()
299 :
300 : #ifdef PR_LOGGING
301 : #define LOG_URI(format, uri) \
302 : PR_BEGIN_MACRO \
303 : NS_ASSERTION(uri, "Logging null uri"); \
304 : if (LOG_ENABLED()) { \
305 : nsCAutoString _logURISpec; \
306 : uri->GetSpec(_logURISpec); \
307 : LOG((format, _logURISpec.get())); \
308 : } \
309 : PR_END_MACRO
310 : #else // PR_LOGGING
311 : #define LOG_URI(format, uri)
312 : #endif // PR_LOGGING
313 :
314 : // And some convenience strings...
315 : #ifdef PR_LOGGING
316 : static const char* const gStateStrings[] = {
317 : "eSheetStateUnknown",
318 : "eSheetNeedsParser",
319 : "eSheetPending",
320 : "eSheetLoading",
321 : "eSheetComplete"
322 : };
323 : #endif
324 :
325 : /********************************
326 : * SheetLoadData implementation *
327 : ********************************/
328 0 : NS_IMPL_ISUPPORTS3(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable,
329 : nsIThreadObserver)
330 :
331 0 : SheetLoadData::SheetLoadData(Loader* aLoader,
332 : const nsSubstring& aTitle,
333 : nsIURI* aURI,
334 : nsCSSStyleSheet* aSheet,
335 : nsIStyleSheetLinkingElement* aOwningElement,
336 : bool aIsAlternate,
337 : nsICSSLoaderObserver* aObserver,
338 : nsIPrincipal* aLoaderPrincipal)
339 : : mLoader(aLoader),
340 : mTitle(aTitle),
341 : mURI(aURI),
342 : mLineNumber(1),
343 : mSheet(aSheet),
344 : mNext(nsnull),
345 : mParentData(nsnull),
346 : mPendingChildren(0),
347 : mSyncLoad(false),
348 : mIsNonDocumentSheet(false),
349 : mIsLoading(false),
350 : mIsCancelled(false),
351 : mMustNotify(false),
352 : mWasAlternate(aIsAlternate),
353 : mAllowUnsafeRules(false),
354 : mUseSystemPrincipal(false),
355 : mSheetAlreadyComplete(false),
356 : mOwningElement(aOwningElement),
357 : mObserver(aObserver),
358 0 : mLoaderPrincipal(aLoaderPrincipal)
359 : {
360 0 : NS_PRECONDITION(mLoader, "Must have a loader!");
361 0 : NS_ADDREF(mLoader);
362 0 : }
363 :
364 0 : SheetLoadData::SheetLoadData(Loader* aLoader,
365 : nsIURI* aURI,
366 : nsCSSStyleSheet* aSheet,
367 : SheetLoadData* aParentData,
368 : nsICSSLoaderObserver* aObserver,
369 : nsIPrincipal* aLoaderPrincipal)
370 : : mLoader(aLoader),
371 : mURI(aURI),
372 : mLineNumber(1),
373 : mSheet(aSheet),
374 : mNext(nsnull),
375 : mParentData(aParentData),
376 : mPendingChildren(0),
377 : mSyncLoad(false),
378 : mIsNonDocumentSheet(false),
379 : mIsLoading(false),
380 : mIsCancelled(false),
381 : mMustNotify(false),
382 : mWasAlternate(false),
383 : mAllowUnsafeRules(false),
384 : mUseSystemPrincipal(false),
385 : mSheetAlreadyComplete(false),
386 : mOwningElement(nsnull),
387 : mObserver(aObserver),
388 0 : mLoaderPrincipal(aLoaderPrincipal)
389 : {
390 0 : NS_PRECONDITION(mLoader, "Must have a loader!");
391 0 : NS_ADDREF(mLoader);
392 0 : if (mParentData) {
393 0 : NS_ADDREF(mParentData);
394 0 : mSyncLoad = mParentData->mSyncLoad;
395 0 : mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
396 0 : mAllowUnsafeRules = mParentData->mAllowUnsafeRules;
397 0 : mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
398 0 : ++(mParentData->mPendingChildren);
399 : }
400 :
401 0 : NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
402 : "Shouldn't use system principal for async loads");
403 0 : }
404 :
405 0 : SheetLoadData::SheetLoadData(Loader* aLoader,
406 : nsIURI* aURI,
407 : nsCSSStyleSheet* aSheet,
408 : bool aSyncLoad,
409 : bool aAllowUnsafeRules,
410 : bool aUseSystemPrincipal,
411 : const nsCString& aCharset,
412 : nsICSSLoaderObserver* aObserver,
413 : nsIPrincipal* aLoaderPrincipal)
414 : : mLoader(aLoader),
415 : mURI(aURI),
416 : mLineNumber(1),
417 : mSheet(aSheet),
418 : mNext(nsnull),
419 : mParentData(nsnull),
420 : mPendingChildren(0),
421 : mSyncLoad(aSyncLoad),
422 : mIsNonDocumentSheet(true),
423 : mIsLoading(false),
424 : mIsCancelled(false),
425 : mMustNotify(false),
426 : mWasAlternate(false),
427 : mAllowUnsafeRules(aAllowUnsafeRules),
428 : mUseSystemPrincipal(aUseSystemPrincipal),
429 : mSheetAlreadyComplete(false),
430 : mOwningElement(nsnull),
431 : mObserver(aObserver),
432 : mLoaderPrincipal(aLoaderPrincipal),
433 0 : mCharsetHint(aCharset)
434 : {
435 0 : NS_PRECONDITION(mLoader, "Must have a loader!");
436 0 : NS_ADDREF(mLoader);
437 :
438 0 : NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
439 : "Shouldn't use system principal for async loads");
440 0 : }
441 :
442 0 : SheetLoadData::~SheetLoadData()
443 : {
444 0 : NS_RELEASE(mLoader);
445 0 : NS_IF_RELEASE(mParentData);
446 0 : NS_IF_RELEASE(mNext);
447 0 : }
448 :
449 : NS_IMETHODIMP
450 0 : SheetLoadData::Run()
451 : {
452 0 : mLoader->HandleLoadEvent(this);
453 0 : return NS_OK;
454 : }
455 :
456 : NS_IMETHODIMP
457 0 : SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
458 : {
459 0 : return NS_OK;
460 : }
461 :
462 : NS_IMETHODIMP
463 0 : SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
464 : bool aMayWait,
465 : PRUint32 aRecursionDepth)
466 : {
467 : // We want to fire our load even before or after event processing,
468 : // whichever comes first.
469 0 : FireLoadEvent(aThread);
470 0 : return NS_OK;
471 : }
472 :
473 : NS_IMETHODIMP
474 0 : SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
475 : PRUint32 aRecursionDepth)
476 : {
477 : // We want to fire our load even before or after event processing,
478 : // whichever comes first.
479 0 : FireLoadEvent(aThread);
480 0 : return NS_OK;
481 : }
482 :
483 : void
484 0 : SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
485 : {
486 :
487 : // First remove ourselves as a thread observer. But we need to keep
488 : // ourselves alive while doing that!
489 0 : nsRefPtr<SheetLoadData> kungFuDeathGrip(this);
490 0 : aThread->RemoveObserver(this);
491 :
492 : // Now fire the event
493 0 : nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
494 0 : NS_ASSERTION(node, "How did that happen???");
495 :
496 : nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
497 : node,
498 0 : NS_SUCCEEDED(mStatus) ?
499 0 : NS_LITERAL_STRING("load") :
500 0 : NS_LITERAL_STRING("error"),
501 0 : false, false);
502 :
503 : // And unblock onload
504 0 : if (mLoader->mDocument) {
505 0 : mLoader->mDocument->UnblockOnload(true);
506 : }
507 0 : }
508 :
509 : void
510 0 : SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
511 : {
512 0 : if (!mOwningElement) {
513 0 : return;
514 : }
515 :
516 0 : mStatus = aStatus;
517 :
518 0 : nsCOMPtr<nsIThread> thread = do_GetMainThread();
519 0 : nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
520 0 : if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
521 : // Make sure to block onload here
522 0 : if (mLoader->mDocument) {
523 0 : mLoader->mDocument->BlockOnload();
524 : }
525 : }
526 : }
527 :
528 : /*************************
529 : * Loader Implementation *
530 : *************************/
531 :
532 0 : Loader::Loader(void)
533 : : mDocument(nsnull)
534 : , mDatasToNotifyOn(0)
535 : , mCompatMode(eCompatibility_FullStandards)
536 : , mEnabled(true)
537 : #ifdef DEBUG
538 0 : , mSyncCallback(false)
539 : #endif
540 : {
541 0 : }
542 :
543 1273 : Loader::Loader(nsIDocument* aDocument)
544 : : mDocument(aDocument)
545 : , mDatasToNotifyOn(0)
546 : , mCompatMode(eCompatibility_FullStandards)
547 : , mEnabled(true)
548 : #ifdef DEBUG
549 1273 : , mSyncCallback(false)
550 : #endif
551 : {
552 : // We can just use the preferred set, since there are no sheets in the
553 : // document yet (if there are, how did they get there? _we_ load the sheets!)
554 : // and hence the selected set makes no sense at this time.
555 2546 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
556 1273 : if (domDoc) {
557 1273 : domDoc->GetPreferredStyleSheetSet(mPreferredSheet);
558 : }
559 1273 : }
560 :
561 2542 : Loader::~Loader()
562 : {
563 1271 : NS_ASSERTION((!mLoadingDatas.IsInitialized()) || mLoadingDatas.Count() == 0,
564 : "How did we get destroyed when there are loading data?");
565 1271 : NS_ASSERTION((!mPendingDatas.IsInitialized()) || mPendingDatas.Count() == 0,
566 : "How did we get destroyed when there are pending data?");
567 : // Note: no real need to revoke our stylesheet loaded events -- they
568 : // hold strong references to us, so if we're going away that means
569 : // they're all done.
570 1271 : }
571 :
572 2545 : NS_IMPL_ADDREF(Loader)
573 3814 : NS_IMPL_RELEASE(Loader)
574 :
575 : void
576 1271 : Loader::DropDocumentReference(void)
577 : {
578 1271 : mDocument = nsnull;
579 : // Flush out pending datas just so we don't leak by accident. These
580 : // loads should short-circuit through the mDocument check in
581 : // LoadSheet and just end up in SheetComplete immediately
582 1271 : if (mPendingDatas.IsInitialized()) {
583 0 : StartAlternateLoads();
584 : }
585 1271 : }
586 :
587 : static PLDHashOperator
588 0 : CollectNonAlternates(URIAndPrincipalHashKey *aKey,
589 : SheetLoadData* &aData,
590 : void* aClosure)
591 : {
592 0 : NS_PRECONDITION(aData, "Must have a data");
593 0 : NS_PRECONDITION(aClosure, "Must have an array");
594 :
595 : // Note that we don't want to affect what the selected style set is,
596 : // so use true for aHasAlternateRel.
597 0 : if (aData->mLoader->IsAlternate(aData->mTitle, true)) {
598 0 : return PL_DHASH_NEXT;
599 : }
600 :
601 0 : static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
602 0 : return PL_DHASH_REMOVE;
603 : }
604 :
605 : nsresult
606 0 : Loader::SetPreferredSheet(const nsAString& aTitle)
607 : {
608 : #ifdef DEBUG
609 0 : nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
610 0 : if (doc) {
611 0 : nsAutoString currentPreferred;
612 0 : doc->GetLastStyleSheetSet(currentPreferred);
613 0 : if (DOMStringIsNull(currentPreferred)) {
614 0 : doc->GetPreferredStyleSheetSet(currentPreferred);
615 : }
616 0 : NS_ASSERTION(currentPreferred.Equals(aTitle),
617 : "Unexpected argument to SetPreferredSheet");
618 : }
619 : #endif
620 :
621 0 : mPreferredSheet = aTitle;
622 :
623 : // start any pending alternates that aren't alternates anymore
624 0 : if (mPendingDatas.IsInitialized()) {
625 0 : LoadDataArray arr(mPendingDatas.Count());
626 0 : mPendingDatas.Enumerate(CollectNonAlternates, &arr);
627 :
628 0 : mDatasToNotifyOn += arr.Length();
629 0 : for (PRUint32 i = 0; i < arr.Length(); ++i) {
630 0 : --mDatasToNotifyOn;
631 0 : LoadSheet(arr[i], eSheetNeedsParser);
632 : }
633 : }
634 :
635 0 : return NS_OK;
636 : }
637 :
638 : static const char kCharsetSym[] = "@charset \"";
639 :
640 0 : static nsresult GetCharsetFromData(const unsigned char* aStyleSheetData,
641 : PRUint32 aDataLength,
642 : nsACString& aCharset)
643 : {
644 0 : aCharset.Truncate();
645 0 : if (aDataLength <= sizeof(kCharsetSym) - 1)
646 0 : return NS_ERROR_NOT_AVAILABLE;
647 0 : PRUint32 step = 1;
648 0 : PRUint32 pos = 0;
649 0 : bool bigEndian = false;
650 : // Determine the encoding type. If we have a BOM, set aCharset to the
651 : // charset listed for that BOM in http://www.w3.org/TR/REC-xml#sec-guessing;
652 : // that way even if we don't have a valid @charset rule we can use the BOM to
653 : // get a reasonable charset. If we do have an @charset rule, the string from
654 : // that will override this fallback setting of aCharset.
655 0 : if (*aStyleSheetData == 0x40 && *(aStyleSheetData+1) == 0x63 /* '@c' */ ) {
656 : // 1-byte ASCII-based encoding (ISO-8859-*, UTF-8, etc), no BOM
657 0 : step = 1;
658 0 : pos = 0;
659 : }
660 0 : else if (nsContentUtils::CheckForBOM(aStyleSheetData,
661 : aDataLength, aCharset, &bigEndian)) {
662 0 : if (aCharset.Equals("UTF-8")) {
663 0 : step = 1;
664 0 : pos = 3;
665 : }
666 0 : else if (aCharset.Equals("UTF-16")) {
667 0 : step = 2;
668 0 : pos = bigEndian ? 3 : 2;
669 : }
670 : }
671 0 : else if (aStyleSheetData[0] == 0x00 &&
672 0 : aStyleSheetData[1] == 0x40 &&
673 0 : aStyleSheetData[2] == 0x00 &&
674 0 : aStyleSheetData[3] == 0x63) {
675 : // 2-byte big-endian encoding, no BOM
676 0 : step = 2;
677 0 : pos = 1;
678 : }
679 0 : else if (aStyleSheetData[0] == 0x40 &&
680 0 : aStyleSheetData[1] == 0x00 &&
681 0 : aStyleSheetData[2] == 0x63 &&
682 0 : aStyleSheetData[3] == 0x00) {
683 : // 2-byte little-endian encoding, no BOM
684 0 : step = 2;
685 0 : pos = 0;
686 : }
687 : else {
688 : // no clue what this is
689 0 : return NS_ERROR_UNEXPECTED;
690 : }
691 :
692 0 : PRUint32 index = 0;
693 0 : while (pos < aDataLength && index < sizeof(kCharsetSym) - 1) {
694 0 : if (aStyleSheetData[pos] != kCharsetSym[index]) {
695 : // If we have a guess as to the charset based on the BOM, then
696 : // we can just return NS_OK even if there is no valid @charset
697 : // rule.
698 0 : return aCharset.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
699 : }
700 0 : ++index;
701 0 : pos += step;
702 : }
703 :
704 0 : nsCAutoString charset;
705 0 : while (pos < aDataLength) {
706 0 : if (aStyleSheetData[pos] == '"') {
707 0 : break;
708 : }
709 :
710 : // casting to avoid ambiguities
711 0 : charset.Append(char(aStyleSheetData[pos]));
712 0 : pos += step;
713 : }
714 :
715 : // Check for the ending ';'
716 0 : pos += step;
717 0 : if (pos >= aDataLength || aStyleSheetData[pos] != ';') {
718 0 : return aCharset.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
719 : }
720 :
721 0 : aCharset = charset;
722 0 : return NS_OK;
723 : }
724 :
725 : NS_IMETHODIMP
726 0 : SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
727 : nsISupports* aContext,
728 : nsACString const& aSegment,
729 : nsACString& aCharset)
730 : {
731 0 : NS_PRECONDITION(!mOwningElement || mCharsetHint.IsEmpty(),
732 : "Can't have element _and_ charset hint");
733 :
734 0 : LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
735 0 : nsCOMPtr<nsIChannel> channel;
736 0 : nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
737 0 : if (NS_FAILED(result))
738 0 : channel = nsnull;
739 :
740 0 : aCharset.Truncate();
741 :
742 : /*
743 : * First determine the charset (if one is indicated)
744 : * 1) Check nsIChannel::contentCharset
745 : * 2) Check @charset rules in the data
746 : * 3) Check "charset" attribute of the <LINK> or <?xml-stylesheet?>
747 : *
748 : * If all these fail to give us a charset, fall back on our default
749 : * (parent sheet charset, document charset or ISO-8859-1 in that order)
750 : */
751 0 : if (channel) {
752 0 : channel->GetContentCharset(aCharset);
753 : }
754 :
755 0 : result = NS_ERROR_NOT_AVAILABLE;
756 :
757 : #ifdef PR_LOGGING
758 0 : if (! aCharset.IsEmpty()) {
759 0 : LOG((" Setting from HTTP to: %s", PromiseFlatCString(aCharset).get()));
760 : }
761 : #endif
762 :
763 0 : if (aCharset.IsEmpty()) {
764 : // We have no charset
765 : // Try @charset rule and BOM
766 0 : result = GetCharsetFromData((const unsigned char*)aSegment.BeginReading(),
767 0 : aSegment.Length(), aCharset);
768 : #ifdef PR_LOGGING
769 0 : if (NS_SUCCEEDED(result)) {
770 0 : LOG((" Setting from @charset rule or BOM: %s",
771 : PromiseFlatCString(aCharset).get()));
772 : }
773 : #endif
774 : }
775 :
776 0 : if (aCharset.IsEmpty()) {
777 : // Now try the charset on the <link> or processing instruction
778 : // that loaded us
779 0 : if (mOwningElement) {
780 0 : nsAutoString elementCharset;
781 0 : mOwningElement->GetCharset(elementCharset);
782 0 : LossyCopyUTF16toASCII(elementCharset, aCharset);
783 : #ifdef PR_LOGGING
784 0 : if (! aCharset.IsEmpty()) {
785 0 : LOG((" Setting from property on element: %s",
786 : PromiseFlatCString(aCharset).get()));
787 : }
788 : #endif
789 : } else {
790 : // If mCharsetHint is empty, that's ok; aCharset is known empty here
791 0 : aCharset = mCharsetHint;
792 : }
793 : }
794 :
795 0 : if (aCharset.IsEmpty() && mParentData) {
796 0 : aCharset = mParentData->mCharset;
797 : #ifdef PR_LOGGING
798 0 : if (! aCharset.IsEmpty()) {
799 0 : LOG((" Setting from parent sheet: %s",
800 : PromiseFlatCString(aCharset).get()));
801 : }
802 : #endif
803 : }
804 :
805 0 : if (aCharset.IsEmpty() && mLoader->mDocument) {
806 : // no useful data on charset. Try the document charset.
807 0 : aCharset = mLoader->mDocument->GetDocumentCharacterSet();
808 : #ifdef PR_LOGGING
809 0 : LOG((" Set from document: %s", PromiseFlatCString(aCharset).get()));
810 : #endif
811 : }
812 :
813 0 : if (aCharset.IsEmpty()) {
814 0 : NS_WARNING("Unable to determine charset for sheet, using ISO-8859-1!");
815 : #ifdef PR_LOGGING
816 0 : LOG_WARN((" Falling back to ISO-8859-1"));
817 : #endif
818 0 : aCharset.AssignLiteral("ISO-8859-1");
819 : }
820 :
821 0 : mCharset = aCharset;
822 0 : return NS_OK;
823 : }
824 :
825 : already_AddRefed<nsIURI>
826 0 : SheetLoadData::GetReferrerURI()
827 : {
828 0 : nsCOMPtr<nsIURI> uri;
829 0 : if (mParentData)
830 0 : uri = mParentData->mSheet->GetSheetURI();
831 0 : if (!uri && mLoader->mDocument)
832 0 : uri = mLoader->mDocument->GetDocumentURI();
833 0 : return uri.forget();
834 : }
835 :
836 : /*
837 : * Here we need to check that the load did not give us an http error
838 : * page and check the mimetype on the channel to make sure we're not
839 : * loading non-text/css data in standards mode.
840 : */
841 : NS_IMETHODIMP
842 0 : SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
843 : nsISupports* aContext,
844 : nsresult aStatus,
845 : const nsAString& aBuffer)
846 : {
847 0 : LOG(("SheetLoadData::OnStreamComplete"));
848 0 : NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
849 :
850 0 : if (mIsCancelled) {
851 : // Just return. Don't call SheetComplete -- it's already been
852 : // called and calling it again will lead to an extra NS_RELEASE on
853 : // this data and a likely crash.
854 0 : return NS_OK;
855 : }
856 :
857 0 : if (!mLoader->mDocument && !mIsNonDocumentSheet) {
858 : // Sorry, we don't care about this load anymore
859 0 : LOG_WARN((" No document and not non-document sheet; dropping load"));
860 0 : mLoader->SheetComplete(this, NS_BINDING_ABORTED);
861 0 : return NS_OK;
862 : }
863 :
864 0 : if (NS_FAILED(aStatus)) {
865 0 : LOG_WARN((" Load failed: status 0x%x", aStatus));
866 0 : mLoader->SheetComplete(this, aStatus);
867 0 : return NS_OK;
868 : }
869 :
870 0 : nsCOMPtr<nsIChannel> channel;
871 0 : nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
872 0 : if (NS_FAILED(result)) {
873 0 : LOG_WARN((" No channel from loader"));
874 0 : mLoader->SheetComplete(this, result);
875 0 : return NS_OK;
876 : }
877 :
878 0 : nsCOMPtr<nsIURI> originalURI;
879 0 : channel->GetOriginalURI(getter_AddRefs(originalURI));
880 :
881 : // If the channel's original URI is "chrome:", we want that, since
882 : // the observer code in nsXULPrototypeCache depends on chrome stylesheets
883 : // having a chrome URI. (Whether or not chrome stylesheets come through
884 : // this codepath seems nondeterministic.)
885 : // Otherwise we want the potentially-HTTP-redirected URI.
886 0 : nsCOMPtr<nsIURI> channelURI;
887 0 : NS_GetFinalChannelURI(channel, getter_AddRefs(channelURI));
888 :
889 0 : if (!channelURI || !originalURI) {
890 0 : NS_ERROR("Someone just violated the nsIRequest contract");
891 0 : LOG_WARN((" Channel without a URI. Bad!"));
892 0 : mLoader->SheetComplete(this, NS_ERROR_UNEXPECTED);
893 0 : return NS_OK;
894 : }
895 :
896 0 : nsCOMPtr<nsIPrincipal> principal;
897 0 : nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
898 0 : result = NS_ERROR_NOT_AVAILABLE;
899 0 : if (secMan) { // Could be null if we already shut down
900 0 : if (mUseSystemPrincipal) {
901 0 : result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
902 : } else {
903 0 : result = secMan->GetChannelPrincipal(channel, getter_AddRefs(principal));
904 : }
905 : }
906 :
907 0 : if (NS_FAILED(result)) {
908 0 : LOG_WARN((" Couldn't get principal"));
909 0 : mLoader->SheetComplete(this, result);
910 0 : return NS_OK;
911 : }
912 :
913 0 : mSheet->SetPrincipal(principal);
914 :
915 : // If it's an HTTP channel, we want to make sure this is not an
916 : // error document we got.
917 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
918 0 : if (httpChannel) {
919 : bool requestSucceeded;
920 0 : result = httpChannel->GetRequestSucceeded(&requestSucceeded);
921 0 : if (NS_SUCCEEDED(result) && !requestSucceeded) {
922 0 : LOG((" Load returned an error page"));
923 0 : mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
924 0 : return NS_OK;
925 : }
926 : }
927 :
928 0 : nsCAutoString contentType;
929 0 : if (channel) {
930 0 : channel->GetContentType(contentType);
931 : }
932 :
933 : // In standards mode, a style sheet must have one of these MIME
934 : // types to be processed at all. In quirks mode, we accept any
935 : // MIME type, but only if the style sheet is same-origin with the
936 : // requesting document or parent sheet. See bug 524223.
937 :
938 0 : bool validType = contentType.EqualsLiteral("text/css") ||
939 0 : contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
940 0 : contentType.IsEmpty();
941 :
942 0 : if (!validType) {
943 : const char *errorMessage;
944 : PRUint32 errorFlag;
945 0 : bool sameOrigin = true;
946 :
947 0 : if (mLoaderPrincipal) {
948 : bool subsumed;
949 0 : result = mLoaderPrincipal->Subsumes(principal, &subsumed);
950 0 : if (NS_FAILED(result) || !subsumed) {
951 0 : sameOrigin = false;
952 : }
953 : }
954 :
955 0 : if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
956 0 : errorMessage = "MimeNotCssWarn";
957 0 : errorFlag = nsIScriptError::warningFlag;
958 : } else {
959 0 : errorMessage = "MimeNotCss";
960 0 : errorFlag = nsIScriptError::errorFlag;
961 : }
962 :
963 0 : nsCAutoString spec;
964 0 : channelURI->GetSpec(spec);
965 :
966 0 : const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec);
967 0 : const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
968 0 : const PRUnichar *strings[] = { specUTF16.get(), ctypeUTF16.get() };
969 :
970 0 : nsCOMPtr<nsIURI> referrer = GetReferrerURI();
971 : nsContentUtils::ReportToConsole(errorFlag,
972 : "CSS Loader", mLoader->mDocument,
973 : nsContentUtils::eCSS_PROPERTIES,
974 : errorMessage,
975 : strings, ArrayLength(strings),
976 0 : referrer);
977 :
978 0 : if (errorFlag == nsIScriptError::errorFlag) {
979 0 : LOG_WARN((" Ignoring sheet with improper MIME type %s",
980 : contentType.get()));
981 0 : mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
982 0 : return NS_OK;
983 : }
984 : }
985 :
986 : // Enough to set the URIs on mSheet, since any sibling datas we have share
987 : // the same mInner as mSheet and will thus get the same URI.
988 0 : mSheet->SetURIs(channelURI, originalURI, channelURI);
989 :
990 : bool completed;
991 0 : result = mLoader->ParseSheet(aBuffer, this, completed);
992 0 : NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
993 0 : return result;
994 : }
995 :
996 : #ifdef MOZ_XUL
997 0 : static bool IsChromeURI(nsIURI* aURI)
998 : {
999 0 : NS_ASSERTION(aURI, "Have to pass in a URI");
1000 0 : bool isChrome = false;
1001 0 : aURI->SchemeIs("chrome", &isChrome);
1002 0 : return isChrome;
1003 : }
1004 : #endif
1005 :
1006 : bool
1007 0 : Loader::IsAlternate(const nsAString& aTitle, bool aHasAlternateRel)
1008 : {
1009 : // A sheet is alternate if it has a nonempty title that doesn't match the
1010 : // currently selected style set. But if there _is_ no currently selected
1011 : // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
1012 : // is nonempty, we should select the style set corresponding to aTitle, since
1013 : // that's a preferred sheet.
1014 0 : if (aTitle.IsEmpty()) {
1015 0 : return false;
1016 : }
1017 :
1018 0 : if (!aHasAlternateRel && mDocument && mPreferredSheet.IsEmpty()) {
1019 : // There's no preferred set yet, and we now have a sheet with a title.
1020 : // Make that be the preferred set.
1021 0 : mDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, aTitle);
1022 : // We're definitely not an alternate
1023 0 : return false;
1024 : }
1025 :
1026 0 : return !aTitle.Equals(mPreferredSheet);
1027 : }
1028 :
1029 : /**
1030 : * CheckLoadAllowed will return success if the load is allowed,
1031 : * failure otherwise.
1032 : *
1033 : * @param aSourcePrincipal the principal of the node or document or parent
1034 : * sheet loading the sheet
1035 : * @param aTargetURI the uri of the sheet to be loaded
1036 : * @param aContext the node owning the sheet. This is the element or document
1037 : * owning the stylesheet (possibly indirectly, for child sheets)
1038 : */
1039 : nsresult
1040 0 : Loader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
1041 : nsIURI* aTargetURI,
1042 : nsISupports* aContext)
1043 : {
1044 0 : LOG(("css::Loader::CheckLoadAllowed"));
1045 :
1046 : nsresult rv;
1047 :
1048 0 : if (aSourcePrincipal) {
1049 : // Check with the security manager
1050 0 : nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
1051 : rv =
1052 : secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
1053 0 : nsIScriptSecurityManager::ALLOW_CHROME);
1054 0 : if (NS_FAILED(rv)) { // failure is normal here; don't warn
1055 0 : return rv;
1056 : }
1057 :
1058 0 : LOG((" Passed security check"));
1059 :
1060 : // Check with content policy
1061 :
1062 0 : PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
1063 : rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
1064 : aTargetURI,
1065 : aSourcePrincipal,
1066 : aContext,
1067 0 : NS_LITERAL_CSTRING("text/css"),
1068 : nsnull, //extra param
1069 : &shouldLoad,
1070 : nsContentUtils::GetContentPolicy(),
1071 0 : nsContentUtils::GetSecurityManager());
1072 :
1073 0 : if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
1074 0 : LOG((" Load blocked by content policy"));
1075 0 : return NS_ERROR_CONTENT_BLOCKED;
1076 : }
1077 : }
1078 :
1079 0 : return NS_OK;
1080 : }
1081 :
1082 : /**
1083 : * CreateSheet() creates an nsCSSStyleSheet object for the given URI,
1084 : * if any. If there is no URI given, we just create a new style sheet
1085 : * object. Otherwise, we check for an existing style sheet object for
1086 : * that uri in various caches and clone it if we find it. Cloned
1087 : * sheets will have the title/media/enabled state of the sheet they
1088 : * are clones off; make sure to call PrepareSheet() on the result of
1089 : * CreateSheet().
1090 : */
1091 : nsresult
1092 0 : Loader::CreateSheet(nsIURI* aURI,
1093 : nsIContent* aLinkingContent,
1094 : nsIPrincipal* aLoaderPrincipal,
1095 : bool aSyncLoad,
1096 : bool aHasAlternateRel,
1097 : const nsAString& aTitle,
1098 : StyleSheetState& aSheetState,
1099 : bool *aIsAlternate,
1100 : nsCSSStyleSheet** aSheet)
1101 : {
1102 0 : LOG(("css::Loader::CreateSheet"));
1103 0 : NS_PRECONDITION(aSheet, "Null out param!");
1104 :
1105 0 : NS_ENSURE_TRUE((mCompleteSheets.IsInitialized() || mCompleteSheets.Init()) &&
1106 : (mLoadingDatas.IsInitialized() || mLoadingDatas.Init()) &&
1107 : (mPendingDatas.IsInitialized() || mPendingDatas.Init()),
1108 : NS_ERROR_OUT_OF_MEMORY);
1109 :
1110 0 : nsresult rv = NS_OK;
1111 0 : *aSheet = nsnull;
1112 0 : aSheetState = eSheetStateUnknown;
1113 :
1114 : // Check the alternate state before doing anything else, because it
1115 : // can mess with our hashtables.
1116 0 : *aIsAlternate = IsAlternate(aTitle, aHasAlternateRel);
1117 :
1118 0 : if (aURI) {
1119 0 : aSheetState = eSheetComplete;
1120 0 : nsRefPtr<nsCSSStyleSheet> sheet;
1121 :
1122 : // First, the XUL cache
1123 : #ifdef MOZ_XUL
1124 0 : if (IsChromeURI(aURI)) {
1125 0 : nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1126 0 : if (cache) {
1127 0 : if (cache->IsEnabled()) {
1128 0 : sheet = cache->GetStyleSheet(aURI);
1129 0 : LOG((" From XUL cache: %p", sheet.get()));
1130 : }
1131 : }
1132 : }
1133 : #endif
1134 :
1135 0 : if (!sheet) {
1136 : // Then our per-document complete sheets.
1137 0 : URIAndPrincipalHashKey key(aURI, aLoaderPrincipal);
1138 :
1139 0 : mCompleteSheets.Get(&key, getter_AddRefs(sheet));
1140 0 : LOG((" From completed: %p", sheet.get()));
1141 : }
1142 :
1143 0 : if (sheet) {
1144 : // This sheet came from the XUL cache or our per-document hashtable; it
1145 : // better be a complete sheet.
1146 0 : NS_ASSERTION(sheet->IsComplete(),
1147 : "Sheet thinks it's not complete while we think it is");
1148 :
1149 : // Make sure it hasn't been modified; if it has, we can't use it
1150 0 : if (sheet->IsModified()) {
1151 0 : LOG((" Not cloning completed sheet %p because it's been modified",
1152 : sheet.get()));
1153 0 : sheet = nsnull;
1154 : }
1155 : }
1156 :
1157 : // Then loading sheets
1158 0 : if (!sheet && !aSyncLoad) {
1159 0 : aSheetState = eSheetLoading;
1160 0 : SheetLoadData* loadData = nsnull;
1161 0 : URIAndPrincipalHashKey key(aURI, aLoaderPrincipal);
1162 0 : mLoadingDatas.Get(&key, &loadData);
1163 0 : if (loadData) {
1164 0 : sheet = loadData->mSheet;
1165 0 : LOG((" From loading: %p", sheet.get()));
1166 :
1167 : #ifdef DEBUG
1168 : bool debugEqual;
1169 0 : NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
1170 : (aLoaderPrincipal && loadData->mLoaderPrincipal &&
1171 : NS_SUCCEEDED(aLoaderPrincipal->
1172 : Equals(loadData->mLoaderPrincipal,
1173 : &debugEqual)) && debugEqual),
1174 : "Principals should be the same");
1175 : #endif
1176 : }
1177 :
1178 : // Then alternate sheets
1179 0 : if (!sheet) {
1180 0 : aSheetState = eSheetPending;
1181 0 : SheetLoadData* loadData = nsnull;
1182 0 : mPendingDatas.Get(&key, &loadData);
1183 0 : if (loadData) {
1184 0 : sheet = loadData->mSheet;
1185 0 : LOG((" From pending: %p", sheet.get()));
1186 :
1187 : #ifdef DEBUG
1188 : bool debugEqual;
1189 0 : NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
1190 : (aLoaderPrincipal && loadData->mLoaderPrincipal &&
1191 : NS_SUCCEEDED(aLoaderPrincipal->
1192 : Equals(loadData->mLoaderPrincipal,
1193 : &debugEqual)) && debugEqual),
1194 : "Principals should be the same");
1195 : #endif
1196 : }
1197 : }
1198 : }
1199 :
1200 0 : if (sheet) {
1201 : // The sheet we have now should be either incomplete or unmodified
1202 0 : NS_ASSERTION(!sheet->IsModified() || !sheet->IsComplete(),
1203 : "Unexpected modified complete sheet");
1204 0 : NS_ASSERTION(sheet->IsComplete() || aSheetState != eSheetComplete,
1205 : "Sheet thinks it's not complete while we think it is");
1206 :
1207 0 : *aSheet = sheet->Clone(nsnull, nsnull, nsnull, nsnull).get();
1208 : }
1209 : }
1210 :
1211 0 : if (!*aSheet) {
1212 0 : aSheetState = eSheetNeedsParser;
1213 : nsIURI *sheetURI;
1214 0 : nsCOMPtr<nsIURI> baseURI;
1215 : nsIURI* originalURI;
1216 0 : if (!aURI) {
1217 : // Inline style. Use the document's base URL so that @import in
1218 : // the inline sheet picks up the right base.
1219 0 : NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?");
1220 0 : baseURI = aLinkingContent->GetBaseURI();
1221 0 : sheetURI = aLinkingContent->GetDocument()->GetDocumentURI();
1222 0 : originalURI = nsnull;
1223 : } else {
1224 0 : baseURI = aURI;
1225 0 : sheetURI = aURI;
1226 0 : originalURI = aURI;
1227 : }
1228 :
1229 0 : rv = NS_NewCSSStyleSheet(aSheet);
1230 0 : NS_ENSURE_SUCCESS(rv, rv);
1231 0 : (*aSheet)->SetURIs(sheetURI, originalURI, baseURI);
1232 : }
1233 :
1234 0 : NS_ASSERTION(*aSheet, "We should have a sheet by now!");
1235 0 : NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
1236 0 : LOG((" State: %s", gStateStrings[aSheetState]));
1237 :
1238 0 : return NS_OK;
1239 : }
1240 :
1241 : /**
1242 : * PrepareSheet() handles setting the media and title on the sheet, as
1243 : * well as setting the enabled state based on the title and whether
1244 : * the sheet had "alternate" in its rel.
1245 : */
1246 : nsresult
1247 0 : Loader::PrepareSheet(nsCSSStyleSheet* aSheet,
1248 : const nsSubstring& aTitle,
1249 : const nsSubstring& aMediaString,
1250 : nsMediaList* aMediaList,
1251 : bool isAlternate)
1252 : {
1253 0 : NS_PRECONDITION(aSheet, "Must have a sheet!");
1254 :
1255 : nsresult rv;
1256 0 : nsRefPtr<nsMediaList> mediaList(aMediaList);
1257 :
1258 0 : if (!aMediaString.IsEmpty()) {
1259 0 : NS_ASSERTION(!aMediaList,
1260 : "must not provide both aMediaString and aMediaList");
1261 0 : mediaList = new nsMediaList();
1262 0 : NS_ENSURE_TRUE(mediaList, NS_ERROR_OUT_OF_MEMORY);
1263 :
1264 0 : nsCSSParser mediumParser(this);
1265 :
1266 : // We have aMediaString only when linked from link elements, style
1267 : // elements, or PIs, so pass true.
1268 : rv = mediumParser.ParseMediaList(aMediaString, nsnull, 0, mediaList,
1269 0 : true);
1270 0 : NS_ENSURE_SUCCESS(rv, rv);
1271 : }
1272 :
1273 0 : aSheet->SetMedia(mediaList);
1274 :
1275 0 : aSheet->SetTitle(aTitle);
1276 0 : aSheet->SetEnabled(! isAlternate);
1277 0 : return NS_OK;
1278 : }
1279 :
1280 : /**
1281 : * InsertSheetInDoc handles ordering of sheets in the document. Here
1282 : * we have two types of sheets -- those with linking elements and
1283 : * those without. The latter are loaded by Link: headers.
1284 : * The following constraints are observed:
1285 : * 1) Any sheet with a linking element comes after all sheets without
1286 : * linking elements
1287 : * 2) Sheets without linking elements are inserted in the order in
1288 : * which the inserting requests come in, since all of these are
1289 : * inserted during header data processing in the content sink
1290 : * 3) Sheets with linking elements are ordered based on document order
1291 : * as determined by CompareDocumentPosition.
1292 : */
1293 : nsresult
1294 0 : Loader::InsertSheetInDoc(nsCSSStyleSheet* aSheet,
1295 : nsIContent* aLinkingContent,
1296 : nsIDocument* aDocument)
1297 : {
1298 0 : LOG(("css::Loader::InsertSheetInDoc"));
1299 0 : NS_PRECONDITION(aSheet, "Nothing to insert");
1300 0 : NS_PRECONDITION(aDocument, "Must have a document to insert into");
1301 :
1302 : // XXX Need to cancel pending sheet loads for this element, if any
1303 :
1304 0 : PRInt32 sheetCount = aDocument->GetNumberOfStyleSheets();
1305 :
1306 : /*
1307 : * Start the walk at the _end_ of the list, since in the typical
1308 : * case we'll just want to append anyway. We want to break out of
1309 : * the loop when insertionPoint points to just before the index we
1310 : * want to insert at. In other words, when we leave the loop
1311 : * insertionPoint is the index of the stylesheet that immediately
1312 : * precedes the one we're inserting.
1313 : */
1314 : PRInt32 insertionPoint;
1315 0 : for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) {
1316 0 : nsIStyleSheet *curSheet = aDocument->GetStyleSheetAt(insertionPoint);
1317 0 : NS_ASSERTION(curSheet, "There must be a sheet here!");
1318 0 : nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(curSheet);
1319 0 : NS_ASSERTION(domSheet, "All the \"normal\" sheets implement nsIDOMStyleSheet");
1320 0 : nsCOMPtr<nsIDOMNode> sheetOwner;
1321 0 : domSheet->GetOwnerNode(getter_AddRefs(sheetOwner));
1322 0 : if (sheetOwner && !aLinkingContent) {
1323 : // Keep moving; all sheets with a sheetOwner come after all
1324 : // sheets without a linkingNode
1325 0 : continue;
1326 : }
1327 :
1328 0 : if (!sheetOwner) {
1329 : // Aha! The current sheet has no sheet owner, so we want to
1330 : // insert after it no matter whether we have a linkingNode
1331 : break;
1332 : }
1333 :
1334 0 : nsCOMPtr<nsINode> sheetOwnerNode = do_QueryInterface(sheetOwner);
1335 0 : NS_ASSERTION(aLinkingContent != sheetOwnerNode,
1336 : "Why do we still have our old sheet?");
1337 :
1338 : // Have to compare
1339 0 : if (nsContentUtils::PositionIsBefore(sheetOwnerNode, aLinkingContent)) {
1340 : // The current sheet comes before us, and it better be the first
1341 : // such, because now we break
1342 : break;
1343 : }
1344 : }
1345 :
1346 0 : ++insertionPoint; // adjust the index to the spot we want to insert in
1347 :
1348 : // XXX <meta> elements do not implement nsIStyleSheetLinkingElement;
1349 : // need to fix this for them to be ordered correctly.
1350 : nsCOMPtr<nsIStyleSheetLinkingElement>
1351 0 : linkingElement = do_QueryInterface(aLinkingContent);
1352 0 : if (linkingElement) {
1353 0 : linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
1354 : }
1355 :
1356 0 : aDocument->BeginUpdate(UPDATE_STYLE);
1357 0 : aDocument->InsertStyleSheetAt(aSheet, insertionPoint);
1358 0 : aDocument->EndUpdate(UPDATE_STYLE);
1359 0 : LOG((" Inserting into document at position %d", insertionPoint));
1360 :
1361 0 : return NS_OK;
1362 : }
1363 :
1364 : /**
1365 : * InsertChildSheet handles ordering of @import-ed sheet in their
1366 : * parent sheets. Here we want to just insert based on order of the
1367 : * @import rules that imported the sheets. In theory we can't just
1368 : * append to the end because the CSSOM can insert @import rules. In
1369 : * practice, we get the call to load the child sheet before the CSSOM
1370 : * has finished inserting the @import rule, so we have no idea where
1371 : * to put it anyway. So just append for now.
1372 : */
1373 : nsresult
1374 0 : Loader::InsertChildSheet(nsCSSStyleSheet* aSheet,
1375 : nsCSSStyleSheet* aParentSheet,
1376 : ImportRule* aParentRule)
1377 : {
1378 0 : LOG(("css::Loader::InsertChildSheet"));
1379 0 : NS_PRECONDITION(aSheet, "Nothing to insert");
1380 0 : NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
1381 0 : NS_PRECONDITION(aParentSheet, "How did we get imported?");
1382 :
1383 : // child sheets should always start out enabled, even if they got
1384 : // cloned off of top-level sheets which were disabled
1385 0 : aSheet->SetEnabled(true);
1386 :
1387 0 : aParentSheet->AppendStyleSheet(aSheet);
1388 0 : aParentRule->SetSheet(aSheet); // This sets the ownerRule on the sheet
1389 :
1390 0 : LOG((" Inserting into parent sheet"));
1391 : // LOG((" Inserting into parent sheet at position %d", insertionPoint));
1392 :
1393 0 : return NS_OK;
1394 : }
1395 :
1396 : /**
1397 : * LoadSheet handles the actual load of a sheet. If the load is
1398 : * supposed to be synchronous it just opens a channel synchronously
1399 : * using the given uri, wraps the resulting stream in a converter
1400 : * stream and calls ParseSheet. Otherwise it tries to look for an
1401 : * existing load for this URI and piggyback on it. Failing all that,
1402 : * a new load is kicked off asynchronously.
1403 : */
1404 : nsresult
1405 0 : Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
1406 : {
1407 0 : LOG(("css::Loader::LoadSheet"));
1408 0 : NS_PRECONDITION(aLoadData, "Need a load data");
1409 0 : NS_PRECONDITION(aLoadData->mURI, "Need a URI to load");
1410 0 : NS_PRECONDITION(aLoadData->mSheet, "Need a sheet to load into");
1411 0 : NS_PRECONDITION(aSheetState != eSheetComplete, "Why bother?");
1412 0 : NS_PRECONDITION(!aLoadData->mUseSystemPrincipal || aLoadData->mSyncLoad,
1413 : "Shouldn't use system principal for async loads");
1414 0 : NS_ASSERTION(mLoadingDatas.IsInitialized(), "mLoadingDatas should be initialized by now.");
1415 :
1416 : #ifdef NS_FUNCTION_TIMER
1417 : nsCAutoString spec__("N/A");
1418 : if (aLoadData->mURI) aLoadData->mURI->GetSpec(spec__);
1419 : NS_TIME_FUNCTION_FMT("Loading stylesheet (url: %s, %ssync)",
1420 : spec__.get(), aLoadData->mSyncLoad ? "" : "a");
1421 : #endif
1422 :
1423 0 : LOG_URI(" Load from: '%s'", aLoadData->mURI);
1424 :
1425 0 : nsresult rv = NS_OK;
1426 :
1427 0 : if (!mDocument && !aLoadData->mIsNonDocumentSheet) {
1428 : // No point starting the load; just release all the data and such.
1429 0 : LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1430 0 : SheetComplete(aLoadData, NS_BINDING_ABORTED);
1431 0 : return NS_BINDING_ABORTED;
1432 : }
1433 :
1434 0 : if (aLoadData->mSyncLoad) {
1435 0 : LOG((" Synchronous load"));
1436 0 : NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
1437 0 : NS_ASSERTION(aSheetState == eSheetNeedsParser,
1438 : "Sync loads can't reuse existing async loads");
1439 :
1440 : // Create a nsIUnicharStreamLoader instance to which we will feed
1441 : // the data from the sync load. Do this before creating the
1442 : // channel to make error recovery simpler.
1443 0 : nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
1444 0 : rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
1445 0 : if (NS_FAILED(rv)) {
1446 0 : LOG_ERROR((" Failed to create stream loader for sync load"));
1447 0 : SheetComplete(aLoadData, rv);
1448 0 : return rv;
1449 : }
1450 :
1451 : // Just load it
1452 0 : nsCOMPtr<nsIInputStream> stream;
1453 0 : nsCOMPtr<nsIChannel> channel;
1454 0 : rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI, nsnull,
1455 : nsnull, nsnull, nsIRequest::LOAD_NORMAL,
1456 0 : getter_AddRefs(channel));
1457 0 : if (NS_FAILED(rv)) {
1458 0 : LOG_ERROR((" Failed to open URI synchronously"));
1459 0 : SheetComplete(aLoadData, rv);
1460 0 : return rv;
1461 : }
1462 :
1463 0 : NS_ASSERTION(channel, "NS_OpenURI lied?");
1464 :
1465 : // Force UA sheets to be UTF-8.
1466 : // XXX this is only necessary because the default in
1467 : // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1468 0 : channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
1469 :
1470 : // Manually feed the streamloader the contents of the stream we
1471 : // got from NS_OpenURI. This will call back into OnStreamComplete
1472 : // and thence to ParseSheet. Regardless of whether this fails,
1473 : // SheetComplete has been called.
1474 : return nsSyncLoadService::PushSyncStreamToListener(stream,
1475 : streamLoader,
1476 0 : channel);
1477 : }
1478 :
1479 0 : SheetLoadData* existingData = nsnull;
1480 :
1481 0 : URIAndPrincipalHashKey key(aLoadData->mURI, aLoadData->mLoaderPrincipal);
1482 0 : if (aSheetState == eSheetLoading) {
1483 0 : mLoadingDatas.Get(&key, &existingData);
1484 0 : NS_ASSERTION(existingData, "CreateSheet lied about the state");
1485 : }
1486 0 : else if (aSheetState == eSheetPending){
1487 0 : mPendingDatas.Get(&key, &existingData);
1488 0 : NS_ASSERTION(existingData, "CreateSheet lied about the state");
1489 : }
1490 :
1491 0 : if (existingData) {
1492 0 : LOG((" Glomming on to existing load"));
1493 0 : SheetLoadData* data = existingData;
1494 0 : while (data->mNext) {
1495 0 : data = data->mNext;
1496 : }
1497 0 : data->mNext = aLoadData; // transfer ownership
1498 0 : if (aSheetState == eSheetPending && !aLoadData->mWasAlternate) {
1499 : // Kick the load off; someone cares about it right away
1500 :
1501 : #ifdef DEBUG
1502 : SheetLoadData* removedData;
1503 0 : NS_ASSERTION(mPendingDatas.Get(&key, &removedData) &&
1504 : removedData == existingData,
1505 : "Bad pending table.");
1506 : #endif
1507 :
1508 0 : mPendingDatas.Remove(&key);
1509 :
1510 0 : LOG((" Forcing load of pending data"));
1511 0 : return LoadSheet(existingData, eSheetNeedsParser);
1512 : }
1513 : // All done here; once the load completes we'll be marked complete
1514 : // automatically
1515 0 : return NS_OK;
1516 : }
1517 :
1518 : #ifdef DEBUG
1519 0 : mSyncCallback = true;
1520 : #endif
1521 0 : nsCOMPtr<nsILoadGroup> loadGroup;
1522 : // Content Security Policy information to pass into channel
1523 0 : nsCOMPtr<nsIChannelPolicy> channelPolicy;
1524 0 : if (mDocument) {
1525 0 : loadGroup = mDocument->GetDocumentLoadGroup();
1526 0 : NS_ASSERTION(loadGroup,
1527 : "No loadgroup for stylesheet; onload will fire early");
1528 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
1529 0 : rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
1530 0 : NS_ENSURE_SUCCESS(rv, rv);
1531 0 : if (csp) {
1532 0 : channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
1533 0 : channelPolicy->SetContentSecurityPolicy(csp);
1534 0 : channelPolicy->SetLoadType(nsIContentPolicy::TYPE_STYLESHEET);
1535 : }
1536 : }
1537 :
1538 0 : nsCOMPtr<nsIChannel> channel;
1539 0 : rv = NS_NewChannel(getter_AddRefs(channel),
1540 : aLoadData->mURI, nsnull, loadGroup, nsnull,
1541 : nsIChannel::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI,
1542 0 : channelPolicy);
1543 :
1544 0 : if (NS_FAILED(rv)) {
1545 : #ifdef DEBUG
1546 0 : mSyncCallback = false;
1547 : #endif
1548 0 : LOG_ERROR((" Failed to create channel"));
1549 0 : SheetComplete(aLoadData, rv);
1550 0 : return rv;
1551 : }
1552 :
1553 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
1554 0 : if (httpChannel) {
1555 : // send a minimal Accept header for text/css
1556 0 : httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
1557 0 : NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
1558 0 : false);
1559 0 : nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
1560 0 : if (referrerURI)
1561 0 : httpChannel->SetReferrer(referrerURI);
1562 : }
1563 :
1564 : // Now tell the channel we expect text/css data back.... We do
1565 : // this before opening it, so it's only treated as a hint.
1566 0 : channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
1567 :
1568 0 : if (aLoadData->mLoaderPrincipal) {
1569 : bool inherit;
1570 : rv = NS_URIChainHasFlags(aLoadData->mURI,
1571 : nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
1572 0 : &inherit);
1573 0 : if ((NS_SUCCEEDED(rv) && inherit) ||
1574 0 : (nsContentUtils::URIIsLocalFile(aLoadData->mURI) &&
1575 0 : NS_SUCCEEDED(aLoadData->mLoaderPrincipal->
1576 : CheckMayLoad(aLoadData->mURI, false)))) {
1577 0 : channel->SetOwner(aLoadData->mLoaderPrincipal);
1578 : }
1579 : }
1580 :
1581 : // We don't have to hold on to the stream loader. The ownership
1582 : // model is: Necko owns the stream loader, which owns the load data,
1583 : // which owns us
1584 0 : nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
1585 0 : rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
1586 :
1587 0 : if (NS_SUCCEEDED(rv))
1588 0 : rv = channel->AsyncOpen(streamLoader, nsnull);
1589 :
1590 : #ifdef DEBUG
1591 0 : mSyncCallback = false;
1592 : #endif
1593 :
1594 0 : if (NS_FAILED(rv)) {
1595 0 : LOG_ERROR((" Failed to create stream loader"));
1596 0 : SheetComplete(aLoadData, rv);
1597 0 : return rv;
1598 : }
1599 :
1600 0 : if (!mLoadingDatas.Put(&key, aLoadData)) {
1601 0 : LOG_ERROR((" Failed to put data in loading table"));
1602 0 : aLoadData->mIsCancelled = true;
1603 0 : channel->Cancel(NS_ERROR_OUT_OF_MEMORY);
1604 0 : SheetComplete(aLoadData, NS_ERROR_OUT_OF_MEMORY);
1605 0 : return NS_ERROR_OUT_OF_MEMORY;
1606 : }
1607 :
1608 0 : aLoadData->mIsLoading = true;
1609 :
1610 0 : return NS_OK;
1611 : }
1612 :
1613 : /**
1614 : * ParseSheet handles parsing the data stream. The main idea here is
1615 : * to push the current load data onto the parse stack before letting
1616 : * the CSS parser at the data stream. That lets us handle @import
1617 : * correctly.
1618 : */
1619 : nsresult
1620 0 : Loader::ParseSheet(const nsAString& aInput,
1621 : SheetLoadData* aLoadData,
1622 : bool& aCompleted)
1623 : {
1624 0 : LOG(("css::Loader::ParseSheet"));
1625 0 : NS_PRECONDITION(aLoadData, "Must have load data");
1626 0 : NS_PRECONDITION(aLoadData->mSheet, "Must have sheet to parse into");
1627 :
1628 : #ifdef NS_FUNCTION_TIMER
1629 : nsCAutoString spec__("N/A");
1630 : if (aLoadData->mURI) aLoadData->mURI->GetSpec(spec__);
1631 : NS_TIME_FUNCTION_FMT("Parsing stylesheet (url: %s)", spec__.get());
1632 : #endif
1633 :
1634 0 : aCompleted = false;
1635 :
1636 0 : nsCSSParser parser(this, aLoadData->mSheet);
1637 :
1638 : // Push our load data on the stack so any kids can pick it up
1639 0 : mParsingDatas.AppendElement(aLoadData);
1640 0 : nsIURI* sheetURI = aLoadData->mSheet->GetSheetURI();
1641 0 : nsIURI* baseURI = aLoadData->mSheet->GetBaseURI();
1642 : nsresult rv = parser.ParseSheet(aInput, sheetURI, baseURI,
1643 : aLoadData->mSheet->Principal(),
1644 : aLoadData->mLineNumber,
1645 0 : aLoadData->mAllowUnsafeRules);
1646 0 : mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
1647 :
1648 0 : if (NS_FAILED(rv)) {
1649 0 : LOG_ERROR((" Low-level error in parser!"));
1650 0 : SheetComplete(aLoadData, rv);
1651 0 : return rv;
1652 : }
1653 :
1654 0 : NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad,
1655 : "Sync load has leftover pending children!");
1656 :
1657 0 : if (aLoadData->mPendingChildren == 0) {
1658 0 : LOG((" No pending kids from parse"));
1659 0 : aCompleted = true;
1660 0 : SheetComplete(aLoadData, NS_OK);
1661 : }
1662 : // Otherwise, the children are holding strong refs to the data and
1663 : // will call SheetComplete() on it when they complete.
1664 :
1665 0 : return NS_OK;
1666 : }
1667 :
1668 : /**
1669 : * SheetComplete is the do-it-all cleanup function. It removes the
1670 : * load data from the "loading" hashtable, adds the sheet to the
1671 : * "completed" hashtable, massages the XUL cache, handles siblings of
1672 : * the load data (other loads for the same URI), handles unblocking
1673 : * blocked parent loads as needed, and most importantly calls
1674 : * NS_RELEASE on the load data to destroy the whole mess.
1675 : */
1676 : void
1677 0 : Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
1678 : {
1679 0 : LOG(("css::Loader::SheetComplete"));
1680 :
1681 : // 8 is probably big enough for all our common cases. It's not likely that
1682 : // imports will nest more than 8 deep, and multiple sheets with the same URI
1683 : // are rare.
1684 0 : nsAutoTArray<nsRefPtr<SheetLoadData>, 8> datasToNotify;
1685 0 : DoSheetComplete(aLoadData, aStatus, datasToNotify);
1686 :
1687 : // Now it's safe to go ahead and notify observers
1688 0 : PRUint32 count = datasToNotify.Length();
1689 0 : mDatasToNotifyOn += count;
1690 0 : for (PRUint32 i = 0; i < count; ++i) {
1691 0 : --mDatasToNotifyOn;
1692 :
1693 0 : SheetLoadData* data = datasToNotify[i];
1694 0 : NS_ASSERTION(data && data->mMustNotify, "How did this data get here?");
1695 0 : if (data->mObserver) {
1696 0 : LOG((" Notifying observer 0x%x for data 0x%x. wasAlternate: %d",
1697 : data->mObserver.get(), data, data->mWasAlternate));
1698 0 : data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate,
1699 0 : aStatus);
1700 : }
1701 :
1702 0 : nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers);
1703 0 : nsCOMPtr<nsICSSLoaderObserver> obs;
1704 0 : while (iter.HasMore()) {
1705 0 : obs = iter.GetNext();
1706 0 : LOG((" Notifying global observer 0x%x for data 0x%s. wasAlternate: %d",
1707 : obs.get(), data, data->mWasAlternate));
1708 0 : obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus);
1709 : }
1710 : }
1711 :
1712 0 : if (mLoadingDatas.Count() == 0 && mPendingDatas.Count() > 0) {
1713 0 : LOG((" No more loading sheets; starting alternates"));
1714 0 : StartAlternateLoads();
1715 : }
1716 0 : }
1717 :
1718 : void
1719 0 : Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
1720 : LoadDataArray& aDatasToNotify)
1721 : {
1722 0 : LOG(("css::Loader::DoSheetComplete"));
1723 0 : NS_PRECONDITION(aLoadData, "Must have a load data!");
1724 0 : NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
1725 0 : NS_ASSERTION(mLoadingDatas.IsInitialized(),"mLoadingDatas should be initialized by now.");
1726 :
1727 0 : LOG(("Load completed, status: 0x%x", aStatus));
1728 :
1729 : // Twiddle the hashtables
1730 0 : if (aLoadData->mURI) {
1731 0 : LOG_URI(" Finished loading: '%s'", aLoadData->mURI);
1732 : // Remove the data from the list of loading datas
1733 0 : if (aLoadData->mIsLoading) {
1734 : URIAndPrincipalHashKey key(aLoadData->mURI,
1735 0 : aLoadData->mLoaderPrincipal);
1736 : #ifdef DEBUG
1737 : SheetLoadData *loadingData;
1738 0 : NS_ASSERTION(mLoadingDatas.Get(&key, &loadingData) &&
1739 : loadingData == aLoadData,
1740 : "Bad loading table");
1741 : #endif
1742 :
1743 0 : mLoadingDatas.Remove(&key);
1744 0 : aLoadData->mIsLoading = false;
1745 : }
1746 : }
1747 :
1748 : // Go through and deal with the whole linked list.
1749 0 : SheetLoadData* data = aLoadData;
1750 0 : while (data) {
1751 0 : if (!data->mSheetAlreadyComplete) {
1752 : // If mSheetAlreadyComplete, then the sheet could well be modified between
1753 : // when we posted the async call to SheetComplete and now, since the sheet
1754 : // was page-accessible during that whole time.
1755 0 : NS_ABORT_IF_FALSE(!data->mSheet->IsModified(),
1756 : "should not get marked modified during parsing");
1757 0 : data->mSheet->SetComplete();
1758 0 : data->ScheduleLoadEventIfNeeded(aStatus);
1759 : }
1760 0 : if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
1761 : // Don't notify here so we don't trigger script. Remember the
1762 : // info we need to notify, then do it later when it's safe.
1763 0 : aDatasToNotify.AppendElement(data);
1764 :
1765 : // On append failure, just press on. We'll fail to notify the observer,
1766 : // but not much we can do about that....
1767 : }
1768 :
1769 0 : NS_ASSERTION(!data->mParentData ||
1770 : data->mParentData->mPendingChildren != 0,
1771 : "Broken pending child count on our parent");
1772 :
1773 : // If we have a parent, our parent is no longer being parsed, and
1774 : // we are the last pending child, then our load completion
1775 : // completes the parent too. Note that the parent _can_ still be
1776 : // being parsed (eg if the child (us) failed to open the channel
1777 : // or some such).
1778 0 : if (data->mParentData &&
1779 : --(data->mParentData->mPendingChildren) == 0 &&
1780 0 : !mParsingDatas.Contains(data->mParentData)) {
1781 0 : DoSheetComplete(data->mParentData, aStatus, aDatasToNotify);
1782 : }
1783 :
1784 0 : data = data->mNext;
1785 : }
1786 :
1787 : // Now that it's marked complete, put the sheet in our cache.
1788 : // If we ever start doing this for failure aStatus, we'll need to
1789 : // adjust the PostLoadEvent code that thinks anything already
1790 : // complete must have loaded succesfully.
1791 0 : if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
1792 : #ifdef MOZ_XUL
1793 0 : if (IsChromeURI(aLoadData->mURI)) {
1794 0 : nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1795 0 : if (cache && cache->IsEnabled()) {
1796 0 : if (!cache->GetStyleSheet(aLoadData->mURI)) {
1797 0 : LOG((" Putting sheet in XUL prototype cache"));
1798 0 : cache->PutStyleSheet(aLoadData->mSheet);
1799 : }
1800 : }
1801 : }
1802 : else {
1803 : #endif
1804 : URIAndPrincipalHashKey key(aLoadData->mURI,
1805 0 : aLoadData->mLoaderPrincipal);
1806 0 : mCompleteSheets.Put(&key, aLoadData->mSheet);
1807 : #ifdef MOZ_XUL
1808 : }
1809 : #endif
1810 : }
1811 :
1812 0 : NS_RELEASE(aLoadData); // this will release parents and siblings and all that
1813 0 : }
1814 :
1815 : nsresult
1816 0 : Loader::LoadInlineStyle(nsIContent* aElement,
1817 : const nsAString& aBuffer,
1818 : PRUint32 aLineNumber,
1819 : const nsAString& aTitle,
1820 : const nsAString& aMedia,
1821 : nsICSSLoaderObserver* aObserver,
1822 : bool* aCompleted,
1823 : bool* aIsAlternate)
1824 : {
1825 0 : LOG(("css::Loader::LoadInlineStyle"));
1826 0 : NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
1827 :
1828 0 : *aCompleted = true;
1829 :
1830 0 : if (!mEnabled) {
1831 0 : LOG_WARN((" Not enabled"));
1832 0 : return NS_ERROR_NOT_AVAILABLE;
1833 : }
1834 :
1835 0 : NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
1836 :
1837 0 : nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
1838 0 : NS_ASSERTION(owningElement, "Element is not a style linking element!");
1839 :
1840 : // Since we're not planning to load a URI, no need to hand a principal to the
1841 : // load data or to CreateSheet().
1842 : StyleSheetState state;
1843 0 : nsRefPtr<nsCSSStyleSheet> sheet;
1844 : nsresult rv = CreateSheet(nsnull, aElement, nsnull, false, false,
1845 0 : aTitle, state, aIsAlternate, getter_AddRefs(sheet));
1846 0 : NS_ENSURE_SUCCESS(rv, rv);
1847 0 : NS_ASSERTION(state == eSheetNeedsParser,
1848 : "Inline sheets should not be cached");
1849 :
1850 0 : LOG((" Sheet is alternate: %d", *aIsAlternate));
1851 :
1852 0 : rv = PrepareSheet(sheet, aTitle, aMedia, nsnull, *aIsAlternate);
1853 0 : NS_ENSURE_SUCCESS(rv, rv);
1854 :
1855 0 : rv = InsertSheetInDoc(sheet, aElement, mDocument);
1856 0 : NS_ENSURE_SUCCESS(rv, rv);
1857 :
1858 : SheetLoadData* data = new SheetLoadData(this, aTitle, nsnull, sheet,
1859 : owningElement, *aIsAlternate,
1860 0 : aObserver, nsnull);
1861 :
1862 : // We never actually load this, so just set its principal directly
1863 0 : sheet->SetPrincipal(aElement->NodePrincipal());
1864 :
1865 0 : NS_ADDREF(data);
1866 0 : data->mLineNumber = aLineNumber;
1867 : // Parse completion releases the load data
1868 0 : rv = ParseSheet(aBuffer, data, *aCompleted);
1869 0 : NS_ENSURE_SUCCESS(rv, rv);
1870 :
1871 : // If aCompleted is true, |data| may well be deleted by now.
1872 0 : if (!*aCompleted) {
1873 0 : data->mMustNotify = true;
1874 : }
1875 0 : return rv;
1876 : }
1877 :
1878 : nsresult
1879 0 : Loader::LoadStyleLink(nsIContent* aElement,
1880 : nsIURI* aURL,
1881 : const nsAString& aTitle,
1882 : const nsAString& aMedia,
1883 : bool aHasAlternateRel,
1884 : nsICSSLoaderObserver* aObserver,
1885 : bool* aIsAlternate)
1886 : {
1887 0 : LOG(("css::Loader::LoadStyleLink"));
1888 0 : NS_PRECONDITION(aURL, "Must have URL to load");
1889 0 : NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
1890 :
1891 0 : LOG_URI(" Link uri: '%s'", aURL);
1892 0 : LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle).get()));
1893 0 : LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia).get()));
1894 0 : LOG((" Link alternate rel: %d", aHasAlternateRel));
1895 :
1896 0 : if (!mEnabled) {
1897 0 : LOG_WARN((" Not enabled"));
1898 0 : return NS_ERROR_NOT_AVAILABLE;
1899 : }
1900 :
1901 0 : NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
1902 :
1903 : nsIPrincipal* principal =
1904 0 : aElement ? aElement->NodePrincipal() : mDocument->NodePrincipal();
1905 :
1906 0 : nsISupports* context = aElement;
1907 0 : if (!context) {
1908 0 : context = mDocument;
1909 : }
1910 0 : nsresult rv = CheckLoadAllowed(principal, aURL, context);
1911 0 : if (NS_FAILED(rv)) return rv;
1912 :
1913 0 : LOG((" Passed load check"));
1914 :
1915 : StyleSheetState state;
1916 0 : nsRefPtr<nsCSSStyleSheet> sheet;
1917 : rv = CreateSheet(aURL, aElement, principal, false, aHasAlternateRel,
1918 0 : aTitle, state, aIsAlternate, getter_AddRefs(sheet));
1919 0 : NS_ENSURE_SUCCESS(rv, rv);
1920 :
1921 0 : LOG((" Sheet is alternate: %d", *aIsAlternate));
1922 :
1923 0 : rv = PrepareSheet(sheet, aTitle, aMedia, nsnull, *aIsAlternate);
1924 0 : NS_ENSURE_SUCCESS(rv, rv);
1925 :
1926 0 : rv = InsertSheetInDoc(sheet, aElement, mDocument);
1927 0 : NS_ENSURE_SUCCESS(rv, rv);
1928 :
1929 0 : nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
1930 :
1931 0 : if (state == eSheetComplete) {
1932 0 : LOG((" Sheet already complete: 0x%p",
1933 : static_cast<void*>(sheet.get())));
1934 0 : if (aObserver || !mObservers.IsEmpty() || owningElement) {
1935 : rv = PostLoadEvent(aURL, sheet, aObserver, *aIsAlternate,
1936 0 : owningElement);
1937 0 : return rv;
1938 : }
1939 :
1940 0 : return NS_OK;
1941 : }
1942 :
1943 : // Now we need to actually load it
1944 : SheetLoadData* data = new SheetLoadData(this, aTitle, aURL, sheet,
1945 : owningElement, *aIsAlternate,
1946 0 : aObserver, principal);
1947 0 : NS_ADDREF(data);
1948 :
1949 : // If we have to parse and it's an alternate non-inline, defer it
1950 0 : if (aURL && state == eSheetNeedsParser && mLoadingDatas.Count() != 0 &&
1951 : *aIsAlternate) {
1952 0 : LOG((" Deferring alternate sheet load"));
1953 0 : URIAndPrincipalHashKey key(data->mURI, data->mLoaderPrincipal);
1954 0 : if (!mPendingDatas.Put(&key, data)) {
1955 0 : return NS_ERROR_OUT_OF_MEMORY;
1956 : }
1957 :
1958 0 : data->mMustNotify = true;
1959 0 : return NS_OK;
1960 : }
1961 :
1962 : // Load completion will free the data
1963 0 : rv = LoadSheet(data, state);
1964 0 : NS_ENSURE_SUCCESS(rv, rv);
1965 :
1966 0 : data->mMustNotify = true;
1967 0 : return rv;
1968 : }
1969 :
1970 : static bool
1971 0 : HaveAncestorDataWithURI(SheetLoadData *aData, nsIURI *aURI)
1972 : {
1973 0 : if (!aData->mURI) {
1974 : // Inline style; this won't have any ancestors
1975 0 : NS_ABORT_IF_FALSE(!aData->mParentData,
1976 : "How does inline style have a parent?");
1977 0 : return false;
1978 : }
1979 :
1980 : bool equal;
1981 0 : if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) {
1982 0 : return true;
1983 : }
1984 :
1985 : // Datas down the mNext chain have the same URI as aData, so we
1986 : // don't have to compare to them. But they might have different
1987 : // parents, and we have to check all of those.
1988 0 : while (aData) {
1989 0 : if (aData->mParentData &&
1990 0 : HaveAncestorDataWithURI(aData->mParentData, aURI)) {
1991 0 : return true;
1992 : }
1993 :
1994 0 : aData = aData->mNext;
1995 : }
1996 :
1997 0 : return false;
1998 : }
1999 :
2000 : nsresult
2001 0 : Loader::LoadChildSheet(nsCSSStyleSheet* aParentSheet,
2002 : nsIURI* aURL,
2003 : nsMediaList* aMedia,
2004 : ImportRule* aParentRule)
2005 : {
2006 0 : LOG(("css::Loader::LoadChildSheet"));
2007 0 : NS_PRECONDITION(aURL, "Must have a URI to load");
2008 0 : NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
2009 :
2010 0 : if (!mEnabled) {
2011 0 : LOG_WARN((" Not enabled"));
2012 0 : return NS_ERROR_NOT_AVAILABLE;
2013 : }
2014 :
2015 0 : LOG_URI(" Child uri: '%s'", aURL);
2016 :
2017 0 : nsCOMPtr<nsIDOMNode> owningNode;
2018 :
2019 : // check for an owning document: if none, don't bother walking up the parent
2020 : // sheets
2021 0 : if (aParentSheet->GetOwningDocument()) {
2022 0 : nsCOMPtr<nsIDOMStyleSheet> nextParentSheet(aParentSheet);
2023 0 : NS_ENSURE_TRUE(nextParentSheet, NS_ERROR_FAILURE); //Not a stylesheet!?
2024 :
2025 0 : nsCOMPtr<nsIDOMStyleSheet> topSheet;
2026 : //traverse our way to the top-most sheet
2027 0 : do {
2028 0 : topSheet.swap(nextParentSheet);
2029 0 : topSheet->GetParentStyleSheet(getter_AddRefs(nextParentSheet));
2030 0 : } while (nextParentSheet);
2031 :
2032 0 : topSheet->GetOwnerNode(getter_AddRefs(owningNode));
2033 : }
2034 :
2035 0 : nsISupports* context = owningNode;
2036 0 : if (!context) {
2037 0 : context = mDocument;
2038 : }
2039 :
2040 0 : nsIPrincipal* principal = aParentSheet->Principal();
2041 0 : nsresult rv = CheckLoadAllowed(principal, aURL, context);
2042 0 : if (NS_FAILED(rv)) return rv;
2043 :
2044 0 : LOG((" Passed load check"));
2045 :
2046 0 : SheetLoadData* parentData = nsnull;
2047 0 : nsCOMPtr<nsICSSLoaderObserver> observer;
2048 :
2049 0 : PRInt32 count = mParsingDatas.Length();
2050 0 : if (count > 0) {
2051 0 : LOG((" Have a parent load"));
2052 0 : parentData = mParsingDatas.ElementAt(count - 1);
2053 : // Check for cycles
2054 0 : if (HaveAncestorDataWithURI(parentData, aURL)) {
2055 : // Houston, we have a loop, blow off this child and pretend this never
2056 : // happened
2057 0 : LOG_ERROR((" @import cycle detected, dropping load"));
2058 0 : return NS_OK;
2059 : }
2060 :
2061 0 : NS_ASSERTION(parentData->mSheet == aParentSheet,
2062 : "Unexpected call to LoadChildSheet");
2063 : } else {
2064 0 : LOG((" No parent load; must be CSSOM"));
2065 : // No parent load data, so the sheet will need to be notified when
2066 : // we finish, if it can be, if we do the load asynchronously.
2067 0 : observer = aParentSheet;
2068 : }
2069 :
2070 : // Now that we know it's safe to load this (passes security check and not a
2071 : // loop) do so
2072 0 : nsRefPtr<nsCSSStyleSheet> sheet;
2073 : bool isAlternate;
2074 : StyleSheetState state;
2075 0 : const nsSubstring& empty = EmptyString();
2076 : rv = CreateSheet(aURL, nsnull, principal,
2077 : parentData ? parentData->mSyncLoad : false,
2078 0 : false, empty, state, &isAlternate, getter_AddRefs(sheet));
2079 0 : NS_ENSURE_SUCCESS(rv, rv);
2080 :
2081 0 : rv = PrepareSheet(sheet, empty, empty, aMedia, isAlternate);
2082 0 : NS_ENSURE_SUCCESS(rv, rv);
2083 :
2084 0 : rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
2085 0 : NS_ENSURE_SUCCESS(rv, rv);
2086 :
2087 0 : if (state == eSheetComplete) {
2088 0 : LOG((" Sheet already complete"));
2089 : // We're completely done. No need to notify, even, since the
2090 : // @import rule addition/modification will trigger the right style
2091 : // changes automatically.
2092 0 : return NS_OK;
2093 : }
2094 :
2095 : SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
2096 0 : observer, principal);
2097 :
2098 0 : NS_ADDREF(data);
2099 0 : bool syncLoad = data->mSyncLoad;
2100 :
2101 : // Load completion will release the data
2102 0 : rv = LoadSheet(data, state);
2103 0 : NS_ENSURE_SUCCESS(rv, rv);
2104 :
2105 : // If syncLoad is true, |data| will be deleted by now.
2106 0 : if (!syncLoad) {
2107 0 : data->mMustNotify = true;
2108 : }
2109 0 : return rv;
2110 : }
2111 :
2112 : nsresult
2113 0 : Loader::LoadSheetSync(nsIURI* aURL, bool aAllowUnsafeRules,
2114 : bool aUseSystemPrincipal,
2115 : nsCSSStyleSheet** aSheet)
2116 : {
2117 0 : LOG(("css::Loader::LoadSheetSync"));
2118 : return InternalLoadNonDocumentSheet(aURL, aAllowUnsafeRules,
2119 : aUseSystemPrincipal, nsnull,
2120 0 : EmptyCString(), aSheet, nsnull);
2121 : }
2122 :
2123 : nsresult
2124 0 : Loader::LoadSheet(nsIURI* aURL,
2125 : nsIPrincipal* aOriginPrincipal,
2126 : const nsCString& aCharset,
2127 : nsICSSLoaderObserver* aObserver,
2128 : nsCSSStyleSheet** aSheet)
2129 : {
2130 0 : LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
2131 0 : NS_PRECONDITION(aSheet, "aSheet is null");
2132 : return InternalLoadNonDocumentSheet(aURL, false, false,
2133 : aOriginPrincipal, aCharset,
2134 0 : aSheet, aObserver);
2135 : }
2136 :
2137 : nsresult
2138 0 : Loader::LoadSheet(nsIURI* aURL,
2139 : nsIPrincipal* aOriginPrincipal,
2140 : const nsCString& aCharset,
2141 : nsICSSLoaderObserver* aObserver)
2142 : {
2143 0 : LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2144 : return InternalLoadNonDocumentSheet(aURL, false, false,
2145 : aOriginPrincipal, aCharset,
2146 0 : nsnull, aObserver);
2147 : }
2148 :
2149 : nsresult
2150 0 : Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
2151 : bool aAllowUnsafeRules,
2152 : bool aUseSystemPrincipal,
2153 : nsIPrincipal* aOriginPrincipal,
2154 : const nsCString& aCharset,
2155 : nsCSSStyleSheet** aSheet,
2156 : nsICSSLoaderObserver* aObserver)
2157 : {
2158 0 : NS_PRECONDITION(aURL, "Must have a URI to load");
2159 0 : NS_PRECONDITION(aSheet || aObserver, "Sheet and observer can't both be null");
2160 0 : NS_PRECONDITION(!aUseSystemPrincipal || !aObserver,
2161 : "Shouldn't load system-principal sheets async");
2162 0 : NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
2163 :
2164 0 : LOG_URI(" Non-document sheet uri: '%s'", aURL);
2165 :
2166 0 : if (aSheet) {
2167 0 : *aSheet = nsnull;
2168 : }
2169 :
2170 0 : if (!mEnabled) {
2171 0 : LOG_WARN((" Not enabled"));
2172 0 : return NS_ERROR_NOT_AVAILABLE;
2173 : }
2174 :
2175 0 : nsresult rv = CheckLoadAllowed(aOriginPrincipal, aURL, mDocument);
2176 0 : if (NS_FAILED(rv)) {
2177 0 : return rv;
2178 : }
2179 :
2180 : StyleSheetState state;
2181 : bool isAlternate;
2182 0 : nsRefPtr<nsCSSStyleSheet> sheet;
2183 0 : bool syncLoad = (aObserver == nsnull);
2184 0 : const nsSubstring& empty = EmptyString();
2185 :
2186 : rv = CreateSheet(aURL, nsnull, aOriginPrincipal, syncLoad, false, empty,
2187 0 : state, &isAlternate, getter_AddRefs(sheet));
2188 0 : NS_ENSURE_SUCCESS(rv, rv);
2189 :
2190 0 : rv = PrepareSheet(sheet, empty, empty, nsnull, isAlternate);
2191 0 : NS_ENSURE_SUCCESS(rv, rv);
2192 :
2193 0 : if (state == eSheetComplete) {
2194 0 : LOG((" Sheet already complete"));
2195 0 : if (aObserver || !mObservers.IsEmpty()) {
2196 0 : rv = PostLoadEvent(aURL, sheet, aObserver, false, nsnull);
2197 : }
2198 0 : if (aSheet) {
2199 0 : sheet.swap(*aSheet);
2200 : }
2201 0 : return rv;
2202 : }
2203 :
2204 : SheetLoadData* data =
2205 : new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules,
2206 : aUseSystemPrincipal, aCharset, aObserver,
2207 0 : aOriginPrincipal);
2208 :
2209 0 : NS_ADDREF(data);
2210 0 : rv = LoadSheet(data, state);
2211 0 : NS_ENSURE_SUCCESS(rv, rv);
2212 :
2213 0 : if (aSheet) {
2214 0 : sheet.swap(*aSheet);
2215 : }
2216 0 : if (aObserver) {
2217 0 : data->mMustNotify = true;
2218 : }
2219 :
2220 0 : return rv;
2221 : }
2222 :
2223 : nsresult
2224 0 : Loader::PostLoadEvent(nsIURI* aURI,
2225 : nsCSSStyleSheet* aSheet,
2226 : nsICSSLoaderObserver* aObserver,
2227 : bool aWasAlternate,
2228 : nsIStyleSheetLinkingElement* aElement)
2229 : {
2230 0 : LOG(("css::Loader::PostLoadEvent"));
2231 0 : NS_PRECONDITION(aSheet, "Must have sheet");
2232 0 : NS_PRECONDITION(aObserver || !mObservers.IsEmpty() || aElement,
2233 : "Must have observer or element");
2234 :
2235 : nsRefPtr<SheetLoadData> evt =
2236 0 : new SheetLoadData(this, EmptyString(), // title doesn't matter here
2237 : aURI,
2238 : aSheet,
2239 : aElement,
2240 : aWasAlternate,
2241 : aObserver,
2242 0 : nsnull);
2243 0 : NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
2244 :
2245 0 : if (!mPostedEvents.AppendElement(evt)) {
2246 0 : return NS_ERROR_OUT_OF_MEMORY;
2247 : }
2248 :
2249 0 : nsresult rv = NS_DispatchToCurrentThread(evt);
2250 0 : if (NS_FAILED(rv)) {
2251 0 : NS_WARNING("failed to dispatch stylesheet load event");
2252 0 : mPostedEvents.RemoveElement(evt);
2253 : } else {
2254 : // We'll unblock onload when we handle the event.
2255 0 : if (mDocument) {
2256 0 : mDocument->BlockOnload();
2257 : }
2258 :
2259 : // We want to notify the observer for this data.
2260 0 : evt->mMustNotify = true;
2261 0 : evt->mSheetAlreadyComplete = true;
2262 :
2263 : // If we get to this code, aSheet loaded correctly at some point, so
2264 : // we can just use NS_OK for the status. Note that we do this here
2265 : // and not from inside our SheetComplete so that we don't end up
2266 : // running the load event async.
2267 0 : evt->ScheduleLoadEventIfNeeded(NS_OK);
2268 : }
2269 :
2270 0 : return rv;
2271 : }
2272 :
2273 : void
2274 0 : Loader::HandleLoadEvent(SheetLoadData* aEvent)
2275 : {
2276 : // XXXbz can't assert this yet.... May not have an observer because
2277 : // we're unblocking the parser
2278 : // NS_ASSERTION(aEvent->mObserver, "Must have observer");
2279 0 : NS_ASSERTION(aEvent->mSheet, "Must have sheet");
2280 :
2281 : // Very important: this needs to come before the SheetComplete call
2282 : // below, so that HasPendingLoads() will test true as needed under
2283 : // notifications we send from that SheetComplete call.
2284 0 : mPostedEvents.RemoveElement(aEvent);
2285 :
2286 0 : if (!aEvent->mIsCancelled) {
2287 : // SheetComplete will call Release(), so give it a reference to do
2288 : // that with.
2289 0 : NS_ADDREF(aEvent);
2290 0 : SheetComplete(aEvent, NS_OK);
2291 : }
2292 :
2293 0 : if (mDocument) {
2294 0 : mDocument->UnblockOnload(true);
2295 : }
2296 0 : }
2297 :
2298 : static PLDHashOperator
2299 0 : StopLoadingSheetCallback(URIAndPrincipalHashKey* aKey,
2300 : SheetLoadData*& aData,
2301 : void* aClosure)
2302 : {
2303 0 : NS_PRECONDITION(aData, "Must have a data!");
2304 0 : NS_PRECONDITION(aClosure, "Must have a loader");
2305 :
2306 0 : aData->mIsLoading = false; // we will handle the removal right here
2307 0 : aData->mIsCancelled = true;
2308 :
2309 0 : static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
2310 :
2311 0 : return PL_DHASH_REMOVE;
2312 : }
2313 :
2314 : nsresult
2315 0 : Loader::Stop()
2316 : {
2317 : PRUint32 pendingCount =
2318 0 : mPendingDatas.IsInitialized() ? mPendingDatas.Count() : 0;
2319 : PRUint32 loadingCount =
2320 0 : mLoadingDatas.IsInitialized() ? mLoadingDatas.Count() : 0;
2321 0 : LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length());
2322 :
2323 0 : if (pendingCount) {
2324 0 : mPendingDatas.Enumerate(StopLoadingSheetCallback, &arr);
2325 : }
2326 0 : if (loadingCount) {
2327 0 : mLoadingDatas.Enumerate(StopLoadingSheetCallback, &arr);
2328 : }
2329 :
2330 : PRUint32 i;
2331 0 : for (i = 0; i < mPostedEvents.Length(); ++i) {
2332 0 : SheetLoadData* data = mPostedEvents[i];
2333 0 : data->mIsCancelled = true;
2334 0 : if (arr.AppendElement(data)) {
2335 : // SheetComplete() calls Release(), so give this an extra ref.
2336 0 : NS_ADDREF(data);
2337 : }
2338 : #ifdef DEBUG
2339 : else {
2340 : NS_NOTREACHED("We preallocated this memory... shouldn't really fail, "
2341 0 : "except we never check that preallocation succeeds.");
2342 : }
2343 : #endif
2344 : }
2345 0 : mPostedEvents.Clear();
2346 :
2347 0 : mDatasToNotifyOn += arr.Length();
2348 0 : for (i = 0; i < arr.Length(); ++i) {
2349 0 : --mDatasToNotifyOn;
2350 0 : SheetComplete(arr[i], NS_BINDING_ABORTED);
2351 : }
2352 0 : return NS_OK;
2353 : }
2354 :
2355 : bool
2356 0 : Loader::HasPendingLoads()
2357 : {
2358 : return
2359 0 : (mLoadingDatas.IsInitialized() && mLoadingDatas.Count() != 0) ||
2360 0 : (mPendingDatas.IsInitialized() && mPendingDatas.Count() != 0) ||
2361 0 : mPostedEvents.Length() != 0 ||
2362 0 : mDatasToNotifyOn != 0;
2363 : }
2364 :
2365 : nsresult
2366 0 : Loader::AddObserver(nsICSSLoaderObserver* aObserver)
2367 : {
2368 0 : NS_PRECONDITION(aObserver, "Must have observer");
2369 0 : if (mObservers.AppendElementUnlessExists(aObserver)) {
2370 0 : return NS_OK;
2371 : }
2372 :
2373 0 : return NS_ERROR_OUT_OF_MEMORY;
2374 : }
2375 :
2376 : void
2377 0 : Loader::RemoveObserver(nsICSSLoaderObserver* aObserver)
2378 : {
2379 0 : mObservers.RemoveElement(aObserver);
2380 0 : }
2381 :
2382 : static PLDHashOperator
2383 0 : CollectLoadDatas(URIAndPrincipalHashKey *aKey,
2384 : SheetLoadData* &aData,
2385 : void* aClosure)
2386 : {
2387 0 : static_cast<Loader::LoadDataArray*>(aClosure)->AppendElement(aData);
2388 0 : return PL_DHASH_REMOVE;
2389 : }
2390 :
2391 : void
2392 0 : Loader::StartAlternateLoads()
2393 : {
2394 0 : NS_PRECONDITION(mPendingDatas.IsInitialized(), "Don't call me!");
2395 0 : LoadDataArray arr(mPendingDatas.Count());
2396 0 : mPendingDatas.Enumerate(CollectLoadDatas, &arr);
2397 :
2398 0 : mDatasToNotifyOn += arr.Length();
2399 0 : for (PRUint32 i = 0; i < arr.Length(); ++i) {
2400 0 : --mDatasToNotifyOn;
2401 0 : LoadSheet(arr[i], eSheetNeedsParser);
2402 : }
2403 0 : }
2404 :
2405 : } // namespace css
2406 4392 : } // namespace mozilla
|