12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: ft=cpp tw=78 sw=2 et ts=2
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * This Original Code has been modified by IBM Corporation.
- * Modifications made by IBM described herein are Copyright (c)
- * International Business Machines Corporation, 2000. Modifications
- * to Mozilla code or documentation identified per MPL Section 3.3
- *
- * Date Modified by Description of modification
- * 04/20/2000 IBM Corp. OS/2 VisualAge build.
- */
- /* loading of CSS style sheets using the network APIs */
- #include "mozilla/ArrayUtils.h"
- #include "mozilla/LoadInfo.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/css/Loader.h"
- #include "mozilla/StyleSheetInlines.h"
- #include "nsIRunnable.h"
- #include "nsIUnicharStreamLoader.h"
- #include "nsSyncLoadService.h"
- #include "nsCOMPtr.h"
- #include "nsString.h"
- #include "nsIContent.h"
- #include "nsIDocument.h"
- #include "nsIDOMNode.h"
- #include "nsIDOMDocument.h"
- #include "nsIURI.h"
- #include "nsNetUtil.h"
- #include "nsIProtocolHandler.h"
- #include "nsContentUtils.h"
- #include "nsIScriptSecurityManager.h"
- #include "nsContentPolicyUtils.h"
- #include "nsIHttpChannel.h"
- #include "nsIHttpChannelInternal.h"
- #include "nsIClassOfService.h"
- #include "nsIScriptError.h"
- #include "nsMimeTypes.h"
- #include "nsIStyleSheetLinkingElement.h"
- #include "nsICSSLoaderObserver.h"
- #include "nsCSSParser.h"
- #include "mozilla/css/ImportRule.h"
- #include "nsThreadUtils.h"
- #include "nsGkAtoms.h"
- #include "nsIThreadInternal.h"
- #include "nsINetworkPredictor.h"
- #include "nsITimedChannel.h"
- #include "mozilla/dom/ShadowRoot.h"
- #include "mozilla/dom/URL.h"
- #include "mozilla/AsyncEventDispatcher.h"
- #include "mozilla/StyleSheet.h"
- #include "mozilla/StyleSheetInlines.h"
- #include "mozilla/ConsoleReportCollector.h"
- #ifdef MOZ_XUL
- #include "nsXULPrototypeCache.h"
- #endif
- #include "nsIMediaList.h"
- #include "nsIDOMStyleSheet.h"
- #include "nsError.h"
- #include "nsIContentSecurityPolicy.h"
- #include "mozilla/dom/SRICheck.h"
- #include "mozilla/dom/EncodingUtils.h"
- using mozilla::dom::EncodingUtils;
- using namespace mozilla::dom;
- /**
- * OVERALL ARCHITECTURE
- *
- * The CSS Loader gets requests to load various sorts of style sheets:
- * inline style from <style> elements, linked style, @import-ed child
- * sheets, non-document sheets. The loader handles the following tasks:
- * 1) Creation of the actual style sheet objects: CreateSheet()
- * 2) setting of the right media, title, enabled state, etc on the
- * sheet: PrepareSheet()
- * 3) Insertion of the sheet in the proper cascade order:
- * InsertSheetInDoc() and InsertChildSheet()
- * 4) Load of the sheet: LoadSheet() including security checks
- * 5) Parsing of the sheet: ParseSheet()
- * 6) Cleanup: SheetComplete()
- *
- * The detailed documentation for these functions is found with the
- * function implementations.
- *
- * The following helper object is used:
- * SheetLoadData -- a small class that is used to store all the
- * information needed for the loading of a sheet;
- * this class handles listening for the stream
- * loader completion and also handles charset
- * determination.
- */
- namespace mozilla {
- namespace css {
- /*********************************************
- * Data needed to properly load a stylesheet *
- *********************************************/
- static_assert(eAuthorSheetFeatures == 0 &&
- eUserSheetFeatures == 1 &&
- eAgentSheetFeatures == 2,
- "sheet parsing mode constants won't fit "
- "in SheetLoadData::mParsingMode");
- class SheetLoadData final : public nsIRunnable,
- public nsIUnicharStreamLoaderObserver,
- public nsIThreadObserver
- {
- protected:
- virtual ~SheetLoadData(void);
- public:
- // Data for loading a sheet linked from a document
- SheetLoadData(Loader* aLoader,
- const nsSubstring& aTitle,
- nsIURI* aURI,
- StyleSheet* aSheet,
- nsIStyleSheetLinkingElement* aOwningElement,
- bool aIsAlternate,
- nsICSSLoaderObserver* aObserver,
- nsIPrincipal* aLoaderPrincipal,
- nsINode* aRequestingNode);
- // Data for loading a sheet linked from an @import rule
- SheetLoadData(Loader* aLoader,
- nsIURI* aURI,
- StyleSheet* aSheet,
- SheetLoadData* aParentData,
- nsICSSLoaderObserver* aObserver,
- nsIPrincipal* aLoaderPrincipal,
- nsINode* aRequestingNode);
- // Data for loading a non-document sheet
- SheetLoadData(Loader* aLoader,
- nsIURI* aURI,
- StyleSheet* aSheet,
- bool aSyncLoad,
- bool aUseSystemPrincipal,
- const nsCString& aCharset,
- nsICSSLoaderObserver* aObserver,
- nsIPrincipal* aLoaderPrincipal,
- nsINode* aRequestingNode);
- already_AddRefed<nsIURI> GetReferrerURI();
- void ScheduleLoadEventIfNeeded(nsresult aStatus);
- NS_DECL_ISUPPORTS
- NS_DECL_NSIRUNNABLE
- NS_DECL_NSITHREADOBSERVER
- NS_DECL_NSIUNICHARSTREAMLOADEROBSERVER
- // Hold a ref to the CSSLoader so we can call back to it to let it
- // know the load finished
- RefPtr<Loader> mLoader;
- // Title needed to pull datas out of the pending datas table when
- // the preferred title is changed
- nsString mTitle;
- // Charset we decided to use for the sheet
- nsCString mCharset;
- // URI we're loading. Null for inline sheets
- nsCOMPtr<nsIURI> mURI;
- // Should be 1 for non-inline sheets.
- uint32_t mLineNumber;
- // The sheet we're loading data for
- RefPtr<StyleSheet> mSheet;
- // Linked list of datas for the same URI as us
- SheetLoadData* mNext; // strong ref
- // Load data for the sheet that @import-ed us if we were @import-ed
- // during the parse
- RefPtr<SheetLoadData> mParentData;
- // Number of sheets we @import-ed that are still loading
- uint32_t mPendingChildren;
- // mSyncLoad is true when the load needs to be synchronous -- right
- // now only for LoadSheetSync and children of sync loads.
- bool mSyncLoad : 1;
- // mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
- // LoadSheet or an @import from such a sheet. Non-document sheet loads can
- // proceed even if we have no document.
- bool mIsNonDocumentSheet : 1;
- // mIsLoading is true from the moment we are placed in the loader's
- // "loading datas" table (right after the async channel is opened)
- // to the moment we are removed from said table (due to the load
- // completing or being cancelled).
- bool mIsLoading : 1;
- // mIsCancelled is set to true when a sheet load is stopped by
- // Stop() or StopLoadingSheet() (which was removed in Bug 556446).
- // SheetLoadData::OnStreamComplete() checks this to avoid parsing
- // sheets that have been cancelled and such.
- bool mIsCancelled : 1;
- // mMustNotify is true if the load data is being loaded async and
- // the original function call that started the load has returned.
- // This applies only to observer notifications; load/error events
- // are fired for any SheetLoadData that has a non-null
- // mOwningElement.
- bool mMustNotify : 1;
- // mWasAlternate is true if the sheet was an alternate when the load data was
- // created.
- bool mWasAlternate : 1;
- // mUseSystemPrincipal is true if the system principal should be used for
- // this sheet, no matter what the channel principal is. Only true for sync
- // loads.
- bool mUseSystemPrincipal : 1;
- // If true, this SheetLoadData is being used as a way to handle
- // async observer notification for an already-complete sheet.
- bool mSheetAlreadyComplete : 1;
- // This is the element that imported the sheet. Needed to get the
- // charset set on it and to fire load/error events.
- nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
- // The observer that wishes to be notified of load completion
- nsCOMPtr<nsICSSLoaderObserver> mObserver;
- // The principal that identifies who started loading us.
- nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
- // The node that identifies who started loading us.
- nsCOMPtr<nsINode> mRequestingNode;
- // The charset to use if the transport and sheet don't indicate one.
- // May be empty. Must be empty if mOwningElement is non-null.
- nsCString mCharsetHint;
- // The status our load ended up with; this determines whether we
- // should fire error events or load events. This gets initialized
- // by ScheduleLoadEventIfNeeded, and is only used after that has
- // been called.
- MOZ_INIT_OUTSIDE_CTOR nsresult mStatus;
- private:
- void FireLoadEvent(nsIThreadInternal* aThread);
- };
- #include "mozilla/Logging.h"
- static mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
- static mozilla::LazyLogModule gSriPRLog("SRI");
- #define LOG_ERROR(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Error, args)
- #define LOG_WARN(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Warning, args)
- #define LOG_DEBUG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
- #define LOG(args) LOG_DEBUG(args)
- #define LOG_ERROR_ENABLED() MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Error)
- #define LOG_WARN_ENABLED() MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Warning)
- #define LOG_DEBUG_ENABLED() MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Debug)
- #define LOG_ENABLED() LOG_DEBUG_ENABLED()
- #define LOG_URI(format, uri) \
- PR_BEGIN_MACRO \
- NS_ASSERTION(uri, "Logging null uri"); \
- if (LOG_ENABLED()) { \
- LOG((format, uri->GetSpecOrDefault().get())); \
- } \
- PR_END_MACRO
- // And some convenience strings...
- static const char* const gStateStrings[] = {
- "eSheetStateUnknown",
- "eSheetNeedsParser",
- "eSheetPending",
- "eSheetLoading",
- "eSheetComplete"
- };
- /********************************
- * SheetLoadData implementation *
- ********************************/
- NS_IMPL_ISUPPORTS(SheetLoadData, nsIUnicharStreamLoaderObserver, nsIRunnable,
- nsIThreadObserver)
- SheetLoadData::SheetLoadData(Loader* aLoader,
- const nsSubstring& aTitle,
- nsIURI* aURI,
- StyleSheet* aSheet,
- nsIStyleSheetLinkingElement* aOwningElement,
- bool aIsAlternate,
- nsICSSLoaderObserver* aObserver,
- nsIPrincipal* aLoaderPrincipal,
- nsINode* aRequestingNode)
- : mLoader(aLoader),
- mTitle(aTitle),
- mURI(aURI),
- mLineNumber(1),
- mSheet(aSheet),
- mNext(nullptr),
- mPendingChildren(0),
- mSyncLoad(false),
- mIsNonDocumentSheet(false),
- mIsLoading(false),
- mIsCancelled(false),
- mMustNotify(false),
- mWasAlternate(aIsAlternate),
- mUseSystemPrincipal(false),
- mSheetAlreadyComplete(false),
- mOwningElement(aOwningElement),
- mObserver(aObserver),
- mLoaderPrincipal(aLoaderPrincipal),
- mRequestingNode(aRequestingNode)
- {
- NS_PRECONDITION(mLoader, "Must have a loader!");
- }
- SheetLoadData::SheetLoadData(Loader* aLoader,
- nsIURI* aURI,
- StyleSheet* aSheet,
- SheetLoadData* aParentData,
- nsICSSLoaderObserver* aObserver,
- nsIPrincipal* aLoaderPrincipal,
- nsINode* aRequestingNode)
- : mLoader(aLoader),
- mURI(aURI),
- mLineNumber(1),
- mSheet(aSheet),
- mNext(nullptr),
- mParentData(aParentData),
- mPendingChildren(0),
- mSyncLoad(false),
- mIsNonDocumentSheet(false),
- mIsLoading(false),
- mIsCancelled(false),
- mMustNotify(false),
- mWasAlternate(false),
- mUseSystemPrincipal(false),
- mSheetAlreadyComplete(false),
- mOwningElement(nullptr),
- mObserver(aObserver),
- mLoaderPrincipal(aLoaderPrincipal),
- mRequestingNode(aRequestingNode)
- {
- NS_PRECONDITION(mLoader, "Must have a loader!");
- if (mParentData) {
- mSyncLoad = mParentData->mSyncLoad;
- mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
- mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
- ++(mParentData->mPendingChildren);
- }
- NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
- "Shouldn't use system principal for async loads");
- }
- SheetLoadData::SheetLoadData(Loader* aLoader,
- nsIURI* aURI,
- StyleSheet* aSheet,
- bool aSyncLoad,
- bool aUseSystemPrincipal,
- const nsCString& aCharset,
- nsICSSLoaderObserver* aObserver,
- nsIPrincipal* aLoaderPrincipal,
- nsINode* aRequestingNode)
- : mLoader(aLoader),
- mURI(aURI),
- mLineNumber(1),
- mSheet(aSheet),
- mNext(nullptr),
- mPendingChildren(0),
- mSyncLoad(aSyncLoad),
- mIsNonDocumentSheet(true),
- mIsLoading(false),
- mIsCancelled(false),
- mMustNotify(false),
- mWasAlternate(false),
- mUseSystemPrincipal(aUseSystemPrincipal),
- mSheetAlreadyComplete(false),
- mOwningElement(nullptr),
- mObserver(aObserver),
- mLoaderPrincipal(aLoaderPrincipal),
- mRequestingNode(aRequestingNode),
- mCharsetHint(aCharset)
- {
- NS_PRECONDITION(mLoader, "Must have a loader!");
- NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad,
- "Shouldn't use system principal for async loads");
- }
- SheetLoadData::~SheetLoadData()
- {
- NS_CSS_NS_RELEASE_LIST_MEMBER(SheetLoadData, this, mNext);
- }
- NS_IMETHODIMP
- SheetLoadData::Run()
- {
- mLoader->HandleLoadEvent(this);
- return NS_OK;
- }
- NS_IMETHODIMP
- SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
- {
- return NS_OK;
- }
- NS_IMETHODIMP
- SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
- bool aMayWait)
- {
- // XXXkhuey this is insane!
- // We want to fire our load even before or after event processing,
- // whichever comes first.
- FireLoadEvent(aThread);
- return NS_OK;
- }
- NS_IMETHODIMP
- SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
- bool aEventWasProcessed)
- {
- // XXXkhuey this too!
- // We want to fire our load even before or after event processing,
- // whichever comes first.
- FireLoadEvent(aThread);
- return NS_OK;
- }
- void
- SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
- {
- // First remove ourselves as a thread observer. But we need to keep
- // ourselves alive while doing that!
- RefPtr<SheetLoadData> kungFuDeathGrip(this);
- aThread->RemoveObserver(this);
- // Now fire the event
- nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
- NS_ASSERTION(node, "How did that happen???");
- nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
- node,
- NS_SUCCEEDED(mStatus) ?
- NS_LITERAL_STRING("load") :
- NS_LITERAL_STRING("error"),
- false, false);
- // And unblock onload
- if (mLoader->mDocument) {
- mLoader->mDocument->UnblockOnload(true);
- }
- }
- void
- SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
- {
- if (!mOwningElement) {
- return;
- }
- mStatus = aStatus;
- nsCOMPtr<nsIThread> thread = do_GetMainThread();
- nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
- if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
- // Make sure to block onload here
- if (mLoader->mDocument) {
- mLoader->mDocument->BlockOnload();
- }
- }
- }
- /*********************
- * Style sheet reuse *
- *********************/
- bool
- LoaderReusableStyleSheets::FindReusableStyleSheet(nsIURI* aURL,
- RefPtr<CSSStyleSheet>& aResult)
- {
- MOZ_ASSERT(aURL);
- for (size_t i = mReusableSheets.Length(); i > 0; --i) {
- size_t index = i - 1;
- bool sameURI;
- MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI());
- nsresult rv = aURL->Equals(mReusableSheets[index]->GetOriginalURI(),
- &sameURI);
- if (!NS_FAILED(rv) && sameURI) {
- aResult = mReusableSheets[index];
- mReusableSheets.RemoveElementAt(index);
- return true;
- }
- }
- return false;
- }
- /*************************
- * Loader Implementation *
- *************************/
- Loader::Loader(StyleBackendType aType)
- : mDocument(nullptr)
- , mDatasToNotifyOn(0)
- , mCompatMode(eCompatibility_FullStandards)
- , mStyleBackendType(Some(aType))
- , mEnabled(true)
- , mReporter(new ConsoleReportCollector())
- #ifdef DEBUG
- , mSyncCallback(false)
- #endif
- {
- }
- Loader::Loader(nsIDocument* aDocument)
- : mDocument(aDocument)
- , mDatasToNotifyOn(0)
- , mCompatMode(eCompatibility_FullStandards)
- , mEnabled(true)
- , mReporter(new ConsoleReportCollector())
- #ifdef DEBUG
- , mSyncCallback(false)
- #endif
- {
- // We can just use the preferred set, since there are no sheets in the
- // document yet (if there are, how did they get there? _we_ load the sheets!)
- // and hence the selected set makes no sense at this time.
- nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mDocument);
- if (domDoc) {
- domDoc->GetPreferredStyleSheetSet(mPreferredSheet);
- }
- }
- Loader::~Loader()
- {
- NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0,
- "How did we get destroyed when there are loading data?");
- NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0,
- "How did we get destroyed when there are pending data?");
- // Note: no real need to revoke our stylesheet loaded events -- they
- // hold strong references to us, so if we're going away that means
- // they're all done.
- }
- void
- Loader::DropDocumentReference(void)
- {
- mDocument = nullptr;
- // Flush out pending datas just so we don't leak by accident. These
- // loads should short-circuit through the mDocument check in
- // LoadSheet and just end up in SheetComplete immediately
- if (mSheets) {
- StartAlternateLoads();
- }
- }
- nsresult
- Loader::SetPreferredSheet(const nsAString& aTitle)
- {
- #ifdef DEBUG
- nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
- if (doc) {
- nsAutoString currentPreferred;
- doc->GetLastStyleSheetSet(currentPreferred);
- if (DOMStringIsNull(currentPreferred)) {
- doc->GetPreferredStyleSheetSet(currentPreferred);
- }
- NS_ASSERTION(currentPreferred.Equals(aTitle),
- "Unexpected argument to SetPreferredSheet");
- }
- #endif
- mPreferredSheet = aTitle;
- // start any pending alternates that aren't alternates anymore
- if (mSheets) {
- LoadDataArray arr(mSheets->mPendingDatas.Count());
- for (auto iter = mSheets->mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
- SheetLoadData* data = iter.Data();
- MOZ_ASSERT(data, "Must have a data");
- // Note that we don't want to affect what the selected style set is, so
- // use true for aHasAlternateRel.
- if (!data->mLoader->IsAlternate(data->mTitle, true)) {
- arr.AppendElement(data);
- iter.Remove();
- }
- }
- mDatasToNotifyOn += arr.Length();
- for (uint32_t i = 0; i < arr.Length(); ++i) {
- --mDatasToNotifyOn;
- LoadSheet(arr[i], eSheetNeedsParser, false);
- }
- }
- return NS_OK;
- }
- static const char kCharsetSym[] = "@charset \"";
- static bool GetCharsetFromData(const char* aStyleSheetData,
- uint32_t aDataLength,
- nsACString& aCharset)
- {
- aCharset.Truncate();
- if (aDataLength <= sizeof(kCharsetSym) - 1)
- return false;
- if (strncmp(aStyleSheetData,
- kCharsetSym,
- sizeof(kCharsetSym) - 1)) {
- return false;
- }
- for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
- char c = aStyleSheetData[i];
- if (c == '"') {
- ++i;
- if (i < aDataLength && aStyleSheetData[i] == ';') {
- return true;
- }
- // fail
- break;
- }
- aCharset.Append(c);
- }
- // Did not see end quote or semicolon
- aCharset.Truncate();
- return false;
- }
- NS_IMETHODIMP
- SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
- nsISupports* aContext,
- nsACString const& aSegment,
- nsACString& aCharset)
- {
- NS_PRECONDITION(!mOwningElement || mCharsetHint.IsEmpty(),
- "Can't have element _and_ charset hint");
- LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
- // The precedence is (per CSS3 Syntax 2012-11-08 ED):
- // BOM
- // Channel
- // @charset rule
- // charset attribute on the referrer
- // encoding of the referrer
- // UTF-8
- aCharset.Truncate();
- if (nsContentUtils::CheckForBOM((const unsigned char*)aSegment.BeginReading(),
- aSegment.Length(),
- aCharset)) {
- // aCharset is now either "UTF-16BE", "UTF-16BE" or "UTF-8"
- // which will swallow the BOM.
- mCharset.Assign(aCharset);
- LOG((" Setting from BOM to: %s", PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- nsCOMPtr<nsIChannel> channel;
- nsAutoCString specified;
- aLoader->GetChannel(getter_AddRefs(channel));
- if (channel) {
- channel->GetContentCharset(specified);
- if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
- mCharset.Assign(aCharset);
- LOG((" Setting from HTTP to: %s", PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- }
- if (GetCharsetFromData(aSegment.BeginReading(),
- aSegment.Length(),
- specified)) {
- if (EncodingUtils::FindEncodingForLabel(specified, aCharset)) {
- // FindEncodingForLabel currently never returns UTF-16LE but will
- // probably change to never return UTF-16 instead, so check both here
- // to avoid relying on the exact behavior.
- if (aCharset.EqualsLiteral("UTF-16") ||
- aCharset.EqualsLiteral("UTF-16BE") ||
- aCharset.EqualsLiteral("UTF-16LE")) {
- // Be consistent with HTML <meta> handling in face of impossibility.
- // When the @charset rule itself evidently was not UTF-16-encoded,
- // it saying UTF-16 has to be a lie.
- aCharset.AssignLiteral("UTF-8");
- }
- mCharset.Assign(aCharset);
- LOG((" Setting from @charset rule to: %s",
- PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- }
- // Now try the charset on the <link> or processing instruction
- // that loaded us
- if (mOwningElement) {
- nsAutoString specified16;
- mOwningElement->GetCharset(specified16);
- if (EncodingUtils::FindEncodingForLabel(specified16, aCharset)) {
- mCharset.Assign(aCharset);
- LOG((" Setting from charset attribute to: %s",
- PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- }
- // In the preload case, the value of the charset attribute on <link> comes
- // in via mCharsetHint instead.
- if (EncodingUtils::FindEncodingForLabel(mCharsetHint, aCharset)) {
- mCharset.Assign(aCharset);
- LOG((" Setting from charset attribute (preload case) to: %s",
- PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- // Try charset from the parent stylesheet.
- if (mParentData) {
- aCharset = mParentData->mCharset;
- if (!aCharset.IsEmpty()) {
- mCharset.Assign(aCharset);
- LOG((" Setting from parent sheet to: %s",
- PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- }
- if (mLoader->mDocument) {
- // no useful data on charset. Try the document charset.
- aCharset = mLoader->mDocument->GetDocumentCharacterSet();
- MOZ_ASSERT(!aCharset.IsEmpty());
- mCharset.Assign(aCharset);
- LOG((" Setting from document to: %s", PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- aCharset.AssignLiteral("UTF-8");
- mCharset = aCharset;
- LOG((" Setting from default to: %s", PromiseFlatCString(aCharset).get()));
- return NS_OK;
- }
- already_AddRefed<nsIURI>
- SheetLoadData::GetReferrerURI()
- {
- nsCOMPtr<nsIURI> uri;
- if (mParentData)
- uri = mParentData->mSheet->GetSheetURI();
- if (!uri && mLoader->mDocument)
- uri = mLoader->mDocument->GetDocumentURI();
- return uri.forget();
- }
- /*
- * Here we need to check that the load did not give us an http error
- * page and check the mimetype on the channel to make sure we're not
- * loading non-text/css data in standards mode.
- */
- NS_IMETHODIMP
- SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
- nsISupports* aContext,
- nsresult aStatus,
- const nsAString& aBuffer)
- {
- LOG(("SheetLoadData::OnStreamComplete"));
- NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
- if (mIsCancelled) {
- // Just return. Don't call SheetComplete -- it's already been
- // called and calling it again will lead to an extra NS_RELEASE on
- // this data and a likely crash.
- return NS_OK;
- }
- if (!mLoader->mDocument && !mIsNonDocumentSheet) {
- // Sorry, we don't care about this load anymore
- LOG_WARN((" No document and not non-document sheet; dropping load"));
- mLoader->SheetComplete(this, NS_BINDING_ABORTED);
- return NS_OK;
- }
- if (NS_FAILED(aStatus)) {
- LOG_WARN((" Load failed: status 0x%x", aStatus));
- // Handle sheet not loading error because source was a tracking URL.
- // We make a note of this sheet node by including it in a dedicated
- // array of blocked tracking nodes under its parent document.
- //
- // Multiple sheet load instances might be tied to this request,
- // we annotate each one linked to a valid owning element (node).
- if (aStatus == NS_ERROR_TRACKING_URI) {
- nsIDocument* doc = mLoader->GetDocument();
- if (doc) {
- for (SheetLoadData* data = this; data; data = data->mNext) {
- // mOwningElement may be null but AddBlockTrackingNode can cope
- nsCOMPtr<nsIContent> content = do_QueryInterface(data->mOwningElement);
- doc->AddBlockedTrackingNode(content);
- }
- }
- }
- mLoader->SheetComplete(this, aStatus);
- return NS_OK;
- }
- nsCOMPtr<nsIChannel> channel;
- nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
- if (NS_FAILED(result)) {
- LOG_WARN((" No channel from loader"));
- mLoader->SheetComplete(this, result);
- return NS_OK;
- }
- nsCOMPtr<nsIURI> originalURI;
- channel->GetOriginalURI(getter_AddRefs(originalURI));
- // If the channel's original URI is "chrome:", we want that, since
- // the observer code in nsXULPrototypeCache depends on chrome stylesheets
- // having a chrome URI. (Whether or not chrome stylesheets come through
- // this codepath seems nondeterministic.)
- // Otherwise we want the potentially-HTTP-redirected URI.
- nsCOMPtr<nsIURI> channelURI;
- NS_GetFinalChannelURI(channel, getter_AddRefs(channelURI));
- if (!channelURI || !originalURI) {
- NS_ERROR("Someone just violated the nsIRequest contract");
- LOG_WARN((" Channel without a URI. Bad!"));
- mLoader->SheetComplete(this, NS_ERROR_UNEXPECTED);
- return NS_OK;
- }
- nsCOMPtr<nsIPrincipal> principal;
- nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
- result = NS_ERROR_NOT_AVAILABLE;
- if (secMan) { // Could be null if we already shut down
- if (mUseSystemPrincipal) {
- result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
- } else {
- result = secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
- }
- }
- if (NS_FAILED(result)) {
- LOG_WARN((" Couldn't get principal"));
- mLoader->SheetComplete(this, result);
- return NS_OK;
- }
- mSheet->SetPrincipal(principal);
- // If it's an HTTP channel, we want to make sure this is not an
- // error document we got.
- nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
- if (httpChannel) {
- bool requestSucceeded;
- result = httpChannel->GetRequestSucceeded(&requestSucceeded);
- if (NS_SUCCEEDED(result) && !requestSucceeded) {
- LOG((" Load returned an error page"));
- mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
- return NS_OK;
- }
- }
- nsAutoCString contentType;
- if (channel) {
- channel->GetContentType(contentType);
- }
- // In standards mode, a style sheet must have one of these MIME
- // types to be processed at all. In quirks mode, we accept any
- // MIME type, but only if the style sheet is same-origin with the
- // requesting document or parent sheet. See bug 524223.
- bool validType = contentType.EqualsLiteral("text/css") ||
- contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
- contentType.IsEmpty();
- if (!validType) {
- const char *errorMessage;
- uint32_t errorFlag;
- bool sameOrigin = true;
- if (mLoaderPrincipal) {
- bool subsumed;
- result = mLoaderPrincipal->Subsumes(principal, &subsumed);
- if (NS_FAILED(result) || !subsumed) {
- sameOrigin = false;
- }
- }
- if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
- errorMessage = "MimeNotCssWarn";
- errorFlag = nsIScriptError::warningFlag;
- } else {
- errorMessage = "MimeNotCss";
- errorFlag = nsIScriptError::errorFlag;
- }
- const nsAFlatString& specUTF16 =
- NS_ConvertUTF8toUTF16(channelURI->GetSpecOrDefault());
- const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
- const char16_t *strings[] = { specUTF16.get(), ctypeUTF16.get() };
- nsCOMPtr<nsIURI> referrer = GetReferrerURI();
- nsContentUtils::ReportToConsole(errorFlag,
- NS_LITERAL_CSTRING("CSS Loader"),
- mLoader->mDocument,
- nsContentUtils::eCSS_PROPERTIES,
- errorMessage,
- strings, ArrayLength(strings),
- referrer);
- if (errorFlag == nsIScriptError::errorFlag) {
- LOG_WARN((" Ignoring sheet with improper MIME type %s",
- contentType.get()));
- mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
- return NS_OK;
- }
- }
- SRIMetadata sriMetadata;
- mSheet->GetIntegrity(sriMetadata);
- if (sriMetadata.IsEmpty()) {
- nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
- if (loadInfo->GetEnforceSRI()) {
- LOG((" Load was blocked by SRI"));
- MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
- ("css::Loader::OnStreamComplete, required SRI not found"));
- mLoader->SheetComplete(this, NS_ERROR_SRI_CORRUPT);
- // log the failed load to web console
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
- nsAutoCString spec;
- mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(spec);
- // line number unknown. mRequestingNode doesn't bear this info.
- csp->LogViolationDetails(
- nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_STYLE,
- NS_ConvertUTF8toUTF16(spec), EmptyString(),
- 0, EmptyString(), EmptyString());
- return NS_OK;
- }
- } else {
- nsAutoCString sourceUri;
- if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
- mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
- }
- nsresult rv = SRICheck::VerifyIntegrity(sriMetadata, aLoader, aBuffer,
- sourceUri, mLoader->mReporter);
- mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
- if (NS_FAILED(rv)) {
- LOG((" Load was blocked by SRI"));
- MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
- ("css::Loader::OnStreamComplete, bad metadata"));
- mLoader->SheetComplete(this, NS_ERROR_SRI_CORRUPT);
- return NS_OK;
- }
- }
- // Enough to set the URIs on mSheet, since any sibling datas we have share
- // the same mInner as mSheet and will thus get the same URI.
- mSheet->SetURIs(channelURI, originalURI, channelURI);
- bool completed;
- result = mLoader->ParseSheet(aBuffer, this, completed);
- NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
- return result;
- }
- bool
- Loader::IsAlternate(const nsAString& aTitle, bool aHasAlternateRel)
- {
- // A sheet is alternate if it has a nonempty title that doesn't match the
- // currently selected style set. But if there _is_ no currently selected
- // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
- // is nonempty, we should select the style set corresponding to aTitle, since
- // that's a preferred sheet.
- if (aTitle.IsEmpty()) {
- return false;
- }
- if (!aHasAlternateRel && mDocument && mPreferredSheet.IsEmpty()) {
- // There's no preferred set yet, and we now have a sheet with a title.
- // Make that be the preferred set.
- mDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, aTitle);
- // We're definitely not an alternate
- return false;
- }
- return !aTitle.Equals(mPreferredSheet);
- }
- nsresult
- Loader::ObsoleteSheet(nsIURI* aURI)
- {
- if (!mSheets) {
- return NS_OK;
- }
- if (!aURI) {
- return NS_ERROR_INVALID_ARG;
- }
- for (auto iter = mSheets->mCompleteSheets.Iter(); !iter.Done(); iter.Next()) {
- nsIURI* sheetURI = iter.Key()->GetURI();
- bool areEqual;
- nsresult rv = sheetURI->Equals(aURI, &areEqual);
- if (NS_SUCCEEDED(rv) && areEqual) {
- iter.Remove();
- }
- }
- return NS_OK;
- }
- nsresult
- Loader::CheckContentPolicy(nsIPrincipal* aSourcePrincipal,
- nsIURI* aTargetURI,
- nsISupports* aContext,
- bool aIsPreload)
- {
- // When performing a system load (e.g. aUseSystemPrincipal = true)
- // then aSourcePrincipal == null; don't consult content policies.
- if (!aSourcePrincipal) {
- return NS_OK;
- }
- nsContentPolicyType contentPolicyType =
- aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
- : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
- int16_t shouldLoad = nsIContentPolicy::ACCEPT;
- nsresult rv = NS_CheckContentLoadPolicy(contentPolicyType,
- aTargetURI,
- aSourcePrincipal,
- aContext,
- NS_LITERAL_CSTRING("text/css"),
- nullptr, //extra param
- &shouldLoad,
- nsContentUtils::GetContentPolicy(),
- nsContentUtils::GetSecurityManager());
- if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
- return NS_ERROR_CONTENT_BLOCKED;
- }
- return NS_OK;
- }
- /**
- * CreateSheet() creates a CSSStyleSheet object for the given URI,
- * if any. If there is no URI given, we just create a new style sheet
- * object. Otherwise, we check for an existing style sheet object for
- * that uri in various caches and clone it if we find it. Cloned
- * sheets will have the title/media/enabled state of the sheet they
- * are clones off; make sure to call PrepareSheet() on the result of
- * CreateSheet().
- */
- nsresult
- Loader::CreateSheet(nsIURI* aURI,
- nsIContent* aLinkingContent,
- nsIPrincipal* aLoaderPrincipal,
- css::SheetParsingMode aParsingMode,
- CORSMode aCORSMode,
- ReferrerPolicy aReferrerPolicy,
- const nsAString& aIntegrity,
- bool aSyncLoad,
- bool aHasAlternateRel,
- const nsAString& aTitle,
- StyleSheetState& aSheetState,
- bool *aIsAlternate,
- RefPtr<StyleSheet>* aSheet)
- {
- LOG(("css::Loader::CreateSheet"));
- NS_PRECONDITION(aSheet, "Null out param!");
- if (!mSheets) {
- mSheets = new Sheets();
- }
- *aSheet = nullptr;
- aSheetState = eSheetStateUnknown;
- // Check the alternate state before doing anything else, because it
- // can mess with our hashtables.
- *aIsAlternate = IsAlternate(aTitle, aHasAlternateRel);
- // XXXheycam Cached sheets currently must be CSSStyleSheets.
- if (aURI && GetStyleBackendType() == StyleBackendType::Gecko) {
- aSheetState = eSheetComplete;
- RefPtr<StyleSheet> sheet;
- // First, the XUL cache
- #ifdef MOZ_XUL
- if (IsChromeURI(aURI)) {
- nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
- if (cache) {
- if (cache->IsEnabled()) {
- sheet = cache->GetStyleSheet(aURI);
- LOG((" From XUL cache: %p", sheet.get()));
- }
- }
- }
- #endif
- bool fromCompleteSheets = false;
- if (!sheet) {
- // Then our per-document complete sheets.
- URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
- StyleSheet* completeSheet = nullptr;
- mSheets->mCompleteSheets.Get(&key, &completeSheet);
- sheet = completeSheet;
- LOG((" From completed: %p", sheet.get()));
- fromCompleteSheets = !!sheet;
- }
- if (sheet) {
- if (sheet->IsServo()) {
- MOZ_CRASH("stylo: can't clone ServoStyleSheets yet");
- }
- // This sheet came from the XUL cache or our per-document hashtable; it
- // better be a complete sheet.
- NS_ASSERTION(sheet->AsGecko()->IsComplete(),
- "Sheet thinks it's not complete while we think it is");
- // Make sure it hasn't been modified; if it has, we can't use it
- if (sheet->AsGecko()->IsModified()) {
- LOG((" Not cloning completed sheet %p because it's been modified",
- sheet.get()));
- sheet = nullptr;
- fromCompleteSheets = false;
- }
- }
- // Then loading sheets
- if (!sheet && !aSyncLoad) {
- aSheetState = eSheetLoading;
- SheetLoadData* loadData = nullptr;
- URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
- mSheets->mLoadingDatas.Get(&key, &loadData);
- if (loadData) {
- sheet = loadData->mSheet;
- LOG((" From loading: %p", sheet.get()));
- #ifdef DEBUG
- bool debugEqual;
- NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
- (aLoaderPrincipal && loadData->mLoaderPrincipal &&
- NS_SUCCEEDED(aLoaderPrincipal->
- Equals(loadData->mLoaderPrincipal,
- &debugEqual)) && debugEqual),
- "Principals should be the same");
- #endif
- }
- // Then alternate sheets
- if (!sheet) {
- aSheetState = eSheetPending;
- loadData = nullptr;
- mSheets->mPendingDatas.Get(&key, &loadData);
- if (loadData) {
- sheet = loadData->mSheet;
- LOG((" From pending: %p", sheet.get()));
- #ifdef DEBUG
- bool debugEqual;
- NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
- (aLoaderPrincipal && loadData->mLoaderPrincipal &&
- NS_SUCCEEDED(aLoaderPrincipal->
- Equals(loadData->mLoaderPrincipal,
- &debugEqual)) && debugEqual),
- "Principals should be the same");
- #endif
- }
- }
- }
- if (sheet) {
- // The sheet we have now should be either incomplete or unmodified
- if (sheet->IsServo()) {
- MOZ_CRASH("stylo: can't clone ServoStyleSheets yet");
- }
- NS_ASSERTION(!sheet->AsGecko()->IsModified() ||
- !sheet->AsGecko()->IsComplete(),
- "Unexpected modified complete sheet");
- NS_ASSERTION(sheet->AsGecko()->IsComplete() ||
- aSheetState != eSheetComplete,
- "Sheet thinks it's not complete while we think it is");
- RefPtr<CSSStyleSheet> clonedSheet =
- sheet->AsGecko()->Clone(nullptr, nullptr, nullptr, nullptr);
- *aSheet = Move(clonedSheet);
- if (*aSheet && fromCompleteSheets &&
- !sheet->AsGecko()->GetOwnerNode() &&
- !sheet->AsGecko()->GetParentSheet()) {
- // The sheet we're cloning isn't actually referenced by
- // anyone. Replace it in the cache, so that if our CSSOM is
- // later modified we don't end up with two copies of our inner
- // hanging around.
- URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
- NS_ASSERTION((*aSheet)->AsGecko()->IsComplete(),
- "Should only be caching complete sheets");
- mSheets->mCompleteSheets.Put(&key, *aSheet);
- }
- }
- }
- if (!*aSheet) {
- aSheetState = eSheetNeedsParser;
- nsIURI *sheetURI;
- nsCOMPtr<nsIURI> baseURI;
- nsIURI* originalURI;
- if (!aURI) {
- // Inline style. Use the document's base URL so that @import in
- // the inline sheet picks up the right base.
- NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?");
- baseURI = aLinkingContent->GetBaseURI();
- sheetURI = aLinkingContent->OwnerDoc()->GetDocumentURI();
- originalURI = nullptr;
- } else {
- baseURI = aURI;
- sheetURI = aURI;
- originalURI = aURI;
- }
- SRIMetadata sriMetadata;
- if (!aIntegrity.IsEmpty()) {
- MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
- ("css::Loader::CreateSheet, integrity=%s",
- NS_ConvertUTF16toUTF8(aIntegrity).get()));
- nsAutoCString sourceUri;
- if (mDocument && mDocument->GetDocumentURI()) {
- mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
- }
- SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter,
- &sriMetadata);
- }
- if (GetStyleBackendType() == StyleBackendType::Gecko) {
- *aSheet = new CSSStyleSheet(aParsingMode, aCORSMode, aReferrerPolicy, sriMetadata);
- } else {
- *aSheet = new ServoStyleSheet(aParsingMode, aCORSMode, aReferrerPolicy, sriMetadata);
- }
- (*aSheet)->SetURIs(sheetURI, originalURI, baseURI);
- }
- NS_ASSERTION(*aSheet, "We should have a sheet by now!");
- NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
- LOG((" State: %s", gStateStrings[aSheetState]));
- return NS_OK;
- }
- /**
- * PrepareSheet() handles setting the media and title on the sheet, as
- * well as setting the enabled state based on the title and whether
- * the sheet had "alternate" in its rel.
- */
- void
- Loader::PrepareSheet(StyleSheet* aSheet,
- const nsSubstring& aTitle,
- const nsSubstring& aMediaString,
- nsMediaList* aMediaList,
- Element* aScopeElement,
- bool isAlternate,
- bool isExplicitlyEnabled)
- {
- NS_PRECONDITION(aSheet, "Must have a sheet!");
- // XXXheycam Need to set media, title, etc. on ServoStyleSheets.
- if (aSheet->IsServo()) {
- NS_WARNING("stylo: should set metadata on ServoStyleSheets. See bug 1290209.");
- return;
- }
- CSSStyleSheet* sheet = aSheet->AsGecko();
- RefPtr<nsMediaList> mediaList(aMediaList);
- if (!aMediaString.IsEmpty()) {
- NS_ASSERTION(!aMediaList,
- "must not provide both aMediaString and aMediaList");
- mediaList = new nsMediaList();
- nsCSSParser mediumParser(this);
- // We have aMediaString only when linked from link elements, style
- // elements, or PIs, so pass true.
- mediumParser.ParseMediaList(aMediaString, nullptr, 0, mediaList, true);
- }
- sheet->SetMedia(mediaList);
- sheet->SetTitle(aTitle);
- sheet->SetEnabled(!isAlternate || isExplicitlyEnabled);
- sheet->SetScopeElement(aScopeElement);
- }
- /**
- * InsertSheetInDoc handles ordering of sheets in the document. Here
- * we have two types of sheets -- those with linking elements and
- * those without. The latter are loaded by Link: headers.
- * The following constraints are observed:
- * 1) Any sheet with a linking element comes after all sheets without
- * linking elements
- * 2) Sheets without linking elements are inserted in the order in
- * which the inserting requests come in, since all of these are
- * inserted during header data processing in the content sink
- * 3) Sheets with linking elements are ordered based on document order
- * as determined by CompareDocumentPosition.
- */
- nsresult
- Loader::InsertSheetInDoc(StyleSheet* aSheet,
- nsIContent* aLinkingContent,
- nsIDocument* aDocument)
- {
- LOG(("css::Loader::InsertSheetInDoc"));
- NS_PRECONDITION(aSheet, "Nothing to insert");
- NS_PRECONDITION(aDocument, "Must have a document to insert into");
- // XXX Need to cancel pending sheet loads for this element, if any
- int32_t sheetCount = aDocument->SheetCount();
- /*
- * Start the walk at the _end_ of the list, since in the typical
- * case we'll just want to append anyway. We want to break out of
- * the loop when insertionPoint points to just before the index we
- * want to insert at. In other words, when we leave the loop
- * insertionPoint is the index of the stylesheet that immediately
- * precedes the one we're inserting.
- */
- int32_t insertionPoint;
- for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) {
- StyleSheet* curSheet = aDocument->SheetAt(insertionPoint);
- NS_ASSERTION(curSheet, "There must be a sheet here!");
- nsCOMPtr<nsINode> sheetOwner = curSheet->GetOwnerNode();
- if (sheetOwner && !aLinkingContent) {
- // Keep moving; all sheets with a sheetOwner come after all
- // sheets without a linkingNode
- continue;
- }
- if (!sheetOwner) {
- // Aha! The current sheet has no sheet owner, so we want to
- // insert after it no matter whether we have a linkingNode
- break;
- }
- NS_ASSERTION(aLinkingContent != sheetOwner,
- "Why do we still have our old sheet?");
- // Have to compare
- if (nsContentUtils::PositionIsBefore(sheetOwner, aLinkingContent)) {
- // The current sheet comes before us, and it better be the first
- // such, because now we break
- break;
- }
- }
- ++insertionPoint; // adjust the index to the spot we want to insert in
- // XXX <meta> elements do not implement nsIStyleSheetLinkingElement;
- // need to fix this for them to be ordered correctly.
- nsCOMPtr<nsIStyleSheetLinkingElement>
- linkingElement = do_QueryInterface(aLinkingContent);
- if (linkingElement) {
- linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
- }
- aDocument->BeginUpdate(UPDATE_STYLE);
- aDocument->InsertStyleSheetAt(aSheet, insertionPoint);
- aDocument->EndUpdate(UPDATE_STYLE);
- LOG((" Inserting into document at position %d", insertionPoint));
- return NS_OK;
- }
- /**
- * InsertChildSheet handles ordering of @import-ed sheet in their
- * parent sheets. Here we want to just insert based on order of the
- * @import rules that imported the sheets. In theory we can't just
- * append to the end because the CSSOM can insert @import rules. In
- * practice, we get the call to load the child sheet before the CSSOM
- * has finished inserting the @import rule, so we have no idea where
- * to put it anyway. So just append for now. (In the future if we
- * want to insert the sheet at the correct position, we'll need to
- * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
- * bug 1220506.)
- */
- nsresult
- Loader::InsertChildSheet(StyleSheet* aSheet,
- StyleSheet* aParentSheet,
- ImportRule* aParentRule)
- {
- LOG(("css::Loader::InsertChildSheet"));
- NS_PRECONDITION(aSheet, "Nothing to insert");
- NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
- NS_PRECONDITION(aParentSheet, "How did we get imported?");
- // XXXheycam The InsertChildSheet API doesn't work with ServoStyleSheets,
- // since they won't have Gecko ImportRules in them.
- if (aSheet->IsServo()) {
- return NS_ERROR_FAILURE;
- }
- // child sheets should always start out enabled, even if they got
- // cloned off of top-level sheets which were disabled
- aSheet->AsGecko()->SetEnabled(true);
- aParentSheet->AppendStyleSheet(aSheet);
- aParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet
- LOG((" Inserting into parent sheet"));
- // LOG((" Inserting into parent sheet at position %d", insertionPoint));
- return NS_OK;
- }
- /**
- * LoadSheet handles the actual load of a sheet. If the load is
- * supposed to be synchronous it just opens a channel synchronously
- * using the given uri, wraps the resulting stream in a converter
- * stream and calls ParseSheet. Otherwise it tries to look for an
- * existing load for this URI and piggyback on it. Failing all that,
- * a new load is kicked off asynchronously.
- */
- nsresult
- Loader::LoadSheet(SheetLoadData* aLoadData,
- StyleSheetState aSheetState,
- bool aIsPreload)
- {
- LOG(("css::Loader::LoadSheet"));
- NS_PRECONDITION(aLoadData, "Need a load data");
- NS_PRECONDITION(aLoadData->mURI, "Need a URI to load");
- NS_PRECONDITION(aLoadData->mSheet, "Need a sheet to load into");
- NS_PRECONDITION(aSheetState != eSheetComplete, "Why bother?");
- NS_PRECONDITION(!aLoadData->mUseSystemPrincipal || aLoadData->mSyncLoad,
- "Shouldn't use system principal for async loads");
- NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
- LOG_URI(" Load from: '%s'", aLoadData->mURI);
- nsresult rv = NS_OK;
- if (!mDocument && !aLoadData->mIsNonDocumentSheet) {
- // No point starting the load; just release all the data and such.
- LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
- SheetComplete(aLoadData, NS_BINDING_ABORTED);
- return NS_BINDING_ABORTED;
- }
- SRIMetadata sriMetadata;
- aLoadData->mSheet->GetIntegrity(sriMetadata);
- if (aLoadData->mSyncLoad) {
- LOG((" Synchronous load"));
- NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
- NS_ASSERTION(aSheetState == eSheetNeedsParser,
- "Sync loads can't reuse existing async loads");
- // Create a nsIUnicharStreamLoader instance to which we will feed
- // the data from the sync load. Do this before creating the
- // channel to make error recovery simpler.
- nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
- rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
- if (NS_FAILED(rv)) {
- LOG_ERROR((" Failed to create stream loader for sync load"));
- SheetComplete(aLoadData, rv);
- return rv;
- }
- if (mDocument) {
- mozilla::net::PredictorLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
- nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
- mDocument);
- }
- nsSecurityFlags securityFlags =
- nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
- nsILoadInfo::SEC_ALLOW_CHROME;
- nsContentPolicyType contentPolicyType =
- aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
- : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
- // Just load it
- nsCOMPtr<nsIChannel> channel;
- // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
- // a node and a principal.
- // This is because of a case where the node is the document being styled and
- // the principal is the stylesheet (perhaps from a different origin) that is
- // applying the styles.
- if (aLoadData->mRequestingNode && aLoadData->mLoaderPrincipal) {
- rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
- aLoadData->mURI,
- aLoadData->mRequestingNode,
- aLoadData->mLoaderPrincipal,
- securityFlags,
- contentPolicyType);
- }
- else {
- // either we are loading something inside a document, in which case
- // we should always have a requestingNode, or we are loading something
- // outside a document, in which case the loadingPrincipal and the
- // triggeringPrincipal should always be the systemPrincipal.
- rv = NS_NewChannel(getter_AddRefs(channel),
- aLoadData->mURI,
- nsContentUtils::GetSystemPrincipal(),
- securityFlags,
- contentPolicyType);
- }
- if (NS_FAILED(rv)) {
- LOG_ERROR((" Failed to create channel"));
- SheetComplete(aLoadData, rv);
- return rv;
- }
- nsCOMPtr<nsIInputStream> stream;
- rv = channel->Open2(getter_AddRefs(stream));
- if (NS_FAILED(rv)) {
- LOG_ERROR((" Failed to open URI synchronously"));
- SheetComplete(aLoadData, rv);
- return rv;
- }
- // Force UA sheets to be UTF-8.
- // XXX this is only necessary because the default in
- // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
- channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
- // Manually feed the streamloader the contents of the stream.
- // This will call back into OnStreamComplete
- // and thence to ParseSheet. Regardless of whether this fails,
- // SheetComplete has been called.
- return nsSyncLoadService::PushSyncStreamToListener(stream,
- streamLoader,
- channel);
- }
- SheetLoadData* existingData = nullptr;
- URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData->mURI,
- aLoadData->mLoaderPrincipal,
- aLoadData->mSheet->GetCORSMode(),
- aLoadData->mSheet->GetReferrerPolicy());
- if (aSheetState == eSheetLoading) {
- mSheets->mLoadingDatas.Get(&key, &existingData);
- NS_ASSERTION(existingData, "CreateSheet lied about the state");
- }
- else if (aSheetState == eSheetPending){
- mSheets->mPendingDatas.Get(&key, &existingData);
- NS_ASSERTION(existingData, "CreateSheet lied about the state");
- }
- if (existingData) {
- LOG((" Glomming on to existing load"));
- SheetLoadData* data = existingData;
- while (data->mNext) {
- data = data->mNext;
- }
- data->mNext = aLoadData; // transfer ownership
- if (aSheetState == eSheetPending && !aLoadData->mWasAlternate) {
- // Kick the load off; someone cares about it right away
- #ifdef DEBUG
- SheetLoadData* removedData;
- NS_ASSERTION(mSheets->mPendingDatas.Get(&key, &removedData) &&
- removedData == existingData,
- "Bad pending table.");
- #endif
- mSheets->mPendingDatas.Remove(&key);
- LOG((" Forcing load of pending data"));
- return LoadSheet(existingData, eSheetNeedsParser, aIsPreload);
- }
- // All done here; once the load completes we'll be marked complete
- // automatically
- return NS_OK;
- }
- nsCOMPtr<nsILoadGroup> loadGroup;
- if (mDocument) {
- loadGroup = mDocument->GetDocumentLoadGroup();
- // load for a document with no loadgrup indicates that something is
- // completely bogus, let's bail out early.
- if (!loadGroup) {
- LOG_ERROR((" Failed to query loadGroup from document"));
- SheetComplete(aLoadData, NS_ERROR_UNEXPECTED);
- return NS_ERROR_UNEXPECTED;
- }
- }
- #ifdef DEBUG
- mSyncCallback = true;
- #endif
- CORSMode ourCORSMode = aLoadData->mSheet->GetCORSMode();
- nsSecurityFlags securityFlags =
- ourCORSMode == CORS_NONE
- ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
- : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
- if (ourCORSMode == CORS_ANONYMOUS) {
- securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
- } else if (ourCORSMode == CORS_USE_CREDENTIALS) {
- securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
- }
- securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
- nsContentPolicyType contentPolicyType =
- aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
- : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
- nsCOMPtr<nsIChannel> channel;
- // Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
- // and a principal. This is because of a case where the node is the document
- // being styled and the principal is the stylesheet (perhaps from a different
- // origin) that is applying the styles.
- if (aLoadData->mRequestingNode && aLoadData->mLoaderPrincipal) {
- rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
- aLoadData->mURI,
- aLoadData->mRequestingNode,
- aLoadData->mLoaderPrincipal,
- securityFlags,
- contentPolicyType,
- loadGroup,
- nullptr, // aCallbacks
- nsIChannel::LOAD_NORMAL |
- nsIChannel::LOAD_CLASSIFY_URI);
- }
- else {
- // either we are loading something inside a document, in which case
- // we should always have a requestingNode, or we are loading something
- // outside a document, in which case the loadingPrincipal and the
- // triggeringPrincipal should always be the systemPrincipal.
- rv = NS_NewChannel(getter_AddRefs(channel),
- aLoadData->mURI,
- nsContentUtils::GetSystemPrincipal(),
- securityFlags,
- contentPolicyType,
- loadGroup,
- nullptr, // aCallbacks
- nsIChannel::LOAD_NORMAL |
- nsIChannel::LOAD_CLASSIFY_URI);
- }
- if (NS_FAILED(rv)) {
- #ifdef DEBUG
- mSyncCallback = false;
- #endif
- LOG_ERROR((" Failed to create channel"));
- SheetComplete(aLoadData, rv);
- return rv;
- }
- if (!aLoadData->mWasAlternate) {
- nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
- if (cos) {
- cos->AddClassFlags(nsIClassOfService::Leader);
- }
- }
- nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
- if (httpChannel) {
- nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
- if (referrerURI)
- httpChannel->SetReferrerWithPolicy(referrerURI,
- aLoadData->mSheet->GetReferrerPolicy());
- nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
- if (internalChannel) {
- internalChannel->SetIntegrityMetadata(sriMetadata.GetIntegrityString());
- }
- // Set the initiator type
- nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
- if (timedChannel) {
- if (aLoadData->mParentData) {
- timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
- } else {
- timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
- }
- }
- }
- // Now tell the channel we expect text/css data back.... We do
- // this before opening it, so it's only treated as a hint.
- channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
- // We don't have to hold on to the stream loader. The ownership
- // model is: Necko owns the stream loader, which owns the load data,
- // which owns us
- nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
- rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData);
- if (NS_FAILED(rv)) {
- #ifdef DEBUG
- mSyncCallback = false;
- #endif
- LOG_ERROR((" Failed to create stream loader"));
- SheetComplete(aLoadData, rv);
- return rv;
- }
- if (mDocument) {
- mozilla::net::PredictorLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
- nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
- mDocument);
- }
- rv = channel->AsyncOpen2(streamLoader);
- #ifdef DEBUG
- mSyncCallback = false;
- #endif
- if (NS_FAILED(rv)) {
- LOG_ERROR((" Failed to create stream loader"));
- SheetComplete(aLoadData, rv);
- return rv;
- }
- mSheets->mLoadingDatas.Put(&key, aLoadData);
- aLoadData->mIsLoading = true;
- return NS_OK;
- }
- /**
- * ParseSheet handles parsing the data stream. The main idea here is
- * to push the current load data onto the parse stack before letting
- * the CSS parser at the data stream. That lets us handle @import
- * correctly.
- */
- nsresult
- Loader::ParseSheet(const nsAString& aInput,
- SheetLoadData* aLoadData,
- bool& aCompleted)
- {
- LOG(("css::Loader::ParseSheet"));
- NS_PRECONDITION(aLoadData, "Must have load data");
- NS_PRECONDITION(aLoadData->mSheet, "Must have sheet to parse into");
- aCompleted = false;
- // Push our load data on the stack so any kids can pick it up
- mParsingDatas.AppendElement(aLoadData);
- nsIURI* sheetURI = aLoadData->mSheet->GetSheetURI();
- nsIURI* baseURI = aLoadData->mSheet->GetBaseURI();
- nsresult rv;
- if (aLoadData->mSheet->IsGecko()) {
- nsCSSParser parser(this, aLoadData->mSheet->AsGecko());
- rv = parser.ParseSheet(aInput, sheetURI, baseURI,
- aLoadData->mSheet->Principal(),
- aLoadData->mLineNumber);
- } else {
- rv =
- aLoadData->mSheet->AsServo()->ParseSheet(aInput, sheetURI, baseURI,
- aLoadData->mSheet->Principal(),
- aLoadData->mLineNumber);
- }
- mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
- if (NS_FAILED(rv)) {
- LOG_ERROR((" Low-level error in parser!"));
- SheetComplete(aLoadData, rv);
- return rv;
- }
- NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad,
- "Sync load has leftover pending children!");
- if (aLoadData->mPendingChildren == 0) {
- LOG((" No pending kids from parse"));
- aCompleted = true;
- SheetComplete(aLoadData, NS_OK);
- }
- // Otherwise, the children are holding strong refs to the data and
- // will call SheetComplete() on it when they complete.
- return NS_OK;
- }
- /**
- * SheetComplete is the do-it-all cleanup function. It removes the
- * load data from the "loading" hashtable, adds the sheet to the
- * "completed" hashtable, massages the XUL cache, handles siblings of
- * the load data (other loads for the same URI), handles unblocking
- * blocked parent loads as needed, and most importantly calls
- * NS_RELEASE on the load data to destroy the whole mess.
- */
- void
- Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
- {
- LOG(("css::Loader::SheetComplete"));
- if (aLoadData->mSheet->IsServo() && NS_FAILED(aStatus)) {
- aLoadData->mSheet->AsServo()->LoadFailed();
- }
- // 8 is probably big enough for all our common cases. It's not likely that
- // imports will nest more than 8 deep, and multiple sheets with the same URI
- // are rare.
- AutoTArray<RefPtr<SheetLoadData>, 8> datasToNotify;
- DoSheetComplete(aLoadData, aStatus, datasToNotify);
- // Now it's safe to go ahead and notify observers
- uint32_t count = datasToNotify.Length();
- mDatasToNotifyOn += count;
- for (uint32_t i = 0; i < count; ++i) {
- --mDatasToNotifyOn;
- SheetLoadData* data = datasToNotify[i];
- NS_ASSERTION(data && data->mMustNotify, "How did this data get here?");
- if (data->mObserver) {
- LOG((" Notifying observer %p for data %p. wasAlternate: %d",
- data->mObserver.get(), data, data->mWasAlternate));
- data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate,
- aStatus);
- }
- nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers);
- nsCOMPtr<nsICSSLoaderObserver> obs;
- while (iter.HasMore()) {
- obs = iter.GetNext();
- LOG((" Notifying global observer %p for data %p. wasAlternate: %d",
- obs.get(), data, data->mWasAlternate));
- obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus);
- }
- }
- if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) {
- LOG((" No more loading sheets; starting alternates"));
- StartAlternateLoads();
- }
- }
- void
- Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
- LoadDataArray& aDatasToNotify)
- {
- LOG(("css::Loader::DoSheetComplete"));
- NS_PRECONDITION(aLoadData, "Must have a load data!");
- NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
- NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
- LOG(("Load completed, status: 0x%x", aStatus));
- // Twiddle the hashtables
- if (aLoadData->mURI) {
- LOG_URI(" Finished loading: '%s'", aLoadData->mURI);
- // Remove the data from the list of loading datas
- if (aLoadData->mIsLoading) {
- URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData->mURI,
- aLoadData->mLoaderPrincipal,
- aLoadData->mSheet->GetCORSMode(),
- aLoadData->mSheet->GetReferrerPolicy());
- #ifdef DEBUG
- SheetLoadData *loadingData;
- NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) &&
- loadingData == aLoadData,
- "Bad loading table");
- #endif
- mSheets->mLoadingDatas.Remove(&key);
- aLoadData->mIsLoading = false;
- }
- }
- // Go through and deal with the whole linked list.
- SheetLoadData* data = aLoadData;
- while (data) {
- if (!data->mSheetAlreadyComplete) {
- // If mSheetAlreadyComplete, then the sheet could well be modified between
- // when we posted the async call to SheetComplete and now, since the sheet
- // was page-accessible during that whole time.
- MOZ_ASSERT(!(data->mSheet->IsGecko() &&
- data->mSheet->AsGecko()->IsModified()),
- "should not get marked modified during parsing");
- data->mSheet->SetComplete();
- data->ScheduleLoadEventIfNeeded(aStatus);
- }
- if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
- // Don't notify here so we don't trigger script. Remember the
- // info we need to notify, then do it later when it's safe.
- aDatasToNotify.AppendElement(data);
- // On append failure, just press on. We'll fail to notify the observer,
- // but not much we can do about that....
- }
- NS_ASSERTION(!data->mParentData ||
- data->mParentData->mPendingChildren != 0,
- "Broken pending child count on our parent");
- // If we have a parent, our parent is no longer being parsed, and
- // we are the last pending child, then our load completion
- // completes the parent too. Note that the parent _can_ still be
- // being parsed (eg if the child (us) failed to open the channel
- // or some such).
- if (data->mParentData &&
- --(data->mParentData->mPendingChildren) == 0 &&
- !mParsingDatas.Contains(data->mParentData)) {
- DoSheetComplete(data->mParentData, aStatus, aDatasToNotify);
- }
- data = data->mNext;
- }
- // Now that it's marked complete, put the sheet in our cache.
- // If we ever start doing this for failure aStatus, we'll need to
- // adjust the PostLoadEvent code that thinks anything already
- // complete must have loaded succesfully.
- if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
- // Pick our sheet to cache carefully. Ideally, we want to cache
- // one of the sheets that will be kept alive by a document or
- // parent sheet anyway, so that if someone then accesses it via
- // CSSOM we won't have extra clones of the inner lying around.
- if (aLoadData->mSheet->IsGecko()) {
- data = aLoadData;
- CSSStyleSheet* sheet = aLoadData->mSheet->AsGecko();
- while (data) {
- if (data->mSheet->GetParentSheet() || data->mSheet->GetOwnerNode()) {
- sheet = data->mSheet->AsGecko();
- break;
- }
- data = data->mNext;
- }
- #ifdef MOZ_XUL
- if (IsChromeURI(aLoadData->mURI)) {
- nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
- if (cache && cache->IsEnabled()) {
- if (!cache->GetStyleSheet(aLoadData->mURI)) {
- LOG((" Putting sheet in XUL prototype cache"));
- NS_ASSERTION(sheet->IsComplete(),
- "Should only be caching complete sheets");
- cache->PutStyleSheet(sheet);
- }
- }
- }
- else {
- #endif
- URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData->mURI,
- aLoadData->mLoaderPrincipal,
- aLoadData->mSheet->GetCORSMode(),
- aLoadData->mSheet->GetReferrerPolicy());
- NS_ASSERTION(sheet->IsComplete(),
- "Should only be caching complete sheets");
- mSheets->mCompleteSheets.Put(&key, sheet);
- #ifdef MOZ_XUL
- }
- #endif
- } else {
- NS_WARNING("stylo: Stylesheet caching not yet supported - see bug 1290218.");
- }
- }
- NS_RELEASE(aLoadData); // this will release parents and siblings and all that
- }
- nsresult
- Loader::LoadInlineStyle(nsIContent* aElement,
- const nsAString& aBuffer,
- uint32_t aLineNumber,
- const nsAString& aTitle,
- const nsAString& aMedia,
- Element* aScopeElement,
- nsICSSLoaderObserver* aObserver,
- bool* aCompleted,
- bool* aIsAlternate,
- bool* aIsExplicitlyEnabled)
- {
- LOG(("css::Loader::LoadInlineStyle"));
- NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
- *aCompleted = true;
- if (!mEnabled) {
- LOG_WARN((" Not enabled"));
- return NS_ERROR_NOT_AVAILABLE;
- }
- NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
- nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
- NS_ASSERTION(owningElement, "Element is not a style linking element!");
- // Since we're not planning to load a URI, no need to hand a principal to the
- // load data or to CreateSheet(). Also, OK to use CORS_NONE for the CORS
- // mode and mDocument's ReferrerPolicy.
- StyleSheetState state;
- RefPtr<StyleSheet> sheet;
- nsresult rv = CreateSheet(nullptr, aElement, nullptr, eAuthorSheetFeatures,
- CORS_NONE, mDocument->GetReferrerPolicy(),
- EmptyString(), // no inline integrity checks
- false, false, aTitle, state, aIsAlternate,
- &sheet);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ASSERTION(state == eSheetNeedsParser,
- "Inline sheets should not be cached");
- LOG((" Sheet is alternate: %d", *aIsAlternate));
- LOG((" Sheet is explicitly enabled: %d", *aIsExplicitlyEnabled));
- PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate, *aIsExplicitlyEnabled);
- if (aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
- ShadowRoot* containingShadow = aElement->GetContainingShadow();
- MOZ_ASSERT(containingShadow);
- containingShadow->InsertSheet(sheet, aElement);
- } else {
- rv = InsertSheetInDoc(sheet, aElement, mDocument);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- SheetLoadData* data = new SheetLoadData(this, aTitle, nullptr, sheet,
- owningElement, *aIsAlternate,
- aObserver, nullptr, static_cast<nsINode*>(aElement));
- // We never actually load this, so just set its principal directly
- sheet->SetPrincipal(aElement->NodePrincipal());
- NS_ADDREF(data);
- data->mLineNumber = aLineNumber;
- // Parse completion releases the load data
- rv = ParseSheet(aBuffer, data, *aCompleted);
- NS_ENSURE_SUCCESS(rv, rv);
- // If aCompleted is true, |data| may well be deleted by now.
- if (!*aCompleted) {
- data->mMustNotify = true;
- }
- return rv;
- }
- nsresult
- Loader::LoadStyleLink(nsIContent* aElement,
- nsIURI* aURL,
- const nsAString& aTitle,
- const nsAString& aMedia,
- bool aHasAlternateRel,
- CORSMode aCORSMode,
- ReferrerPolicy aReferrerPolicy,
- const nsAString& aIntegrity,
- nsICSSLoaderObserver* aObserver,
- bool* aIsAlternate,
- bool* aIsExplicitlyEnabled)
- {
- LOG(("css::Loader::LoadStyleLink"));
- NS_PRECONDITION(aURL, "Must have URL to load");
- NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
- LOG_URI(" Link uri: '%s'", aURL);
- LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle).get()));
- LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia).get()));
- LOG((" Link alternate rel: %d", aHasAlternateRel));
- if (!mEnabled) {
- LOG_WARN((" Not enabled"));
- return NS_ERROR_NOT_AVAILABLE;
- }
- NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
- nsIPrincipal* principal =
- aElement ? aElement->NodePrincipal() : mDocument->NodePrincipal();
- nsISupports* context = aElement;
- if (!context) {
- context = mDocument;
- }
- nsresult rv = CheckContentPolicy(principal, aURL, context, false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- // Don't fire the error event if our document is loaded as data. We're
- // supposed to not even try to do loads in that case... Unfortunately, we
- // implement that via nsDataDocumentContentPolicy, which doesn't have a good
- // way to communicate back to us that _it_ is the thing that blocked the
- // load.
- if (aElement && !mDocument->IsLoadedAsData()) {
- // Fire an async error event on it.
- RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
- new LoadBlockingAsyncEventDispatcher(aElement,
- NS_LITERAL_STRING("error"),
- false, false);
- loadBlockingAsyncDispatcher->PostDOMEvent();
- }
- return rv;
- }
- StyleSheetState state;
- RefPtr<StyleSheet> sheet;
- rv = CreateSheet(aURL, aElement, principal, eAuthorSheetFeatures,
- aCORSMode, aReferrerPolicy, aIntegrity, false,
- aHasAlternateRel, aTitle, state, aIsAlternate,
- &sheet);
- NS_ENSURE_SUCCESS(rv, rv);
- LOG((" Sheet is alternate: %d", *aIsAlternate));
- LOG((" Sheet is explicitly enabled: %d", *aIsExplicitlyEnabled));
- PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate, *aIsExplicitlyEnabled);
- rv = InsertSheetInDoc(sheet, aElement, mDocument);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
- if (state == eSheetComplete) {
- LOG((" Sheet already complete: 0x%p", sheet.get()));
- if (aObserver || !mObservers.IsEmpty() || owningElement) {
- rv = PostLoadEvent(aURL, sheet, aObserver, *aIsAlternate,
- owningElement);
- return rv;
- }
- return NS_OK;
- }
- // Now we need to actually load it
- nsCOMPtr<nsINode> requestingNode = do_QueryInterface(context);
- SheetLoadData* data = new SheetLoadData(this, aTitle, aURL, sheet,
- owningElement, *aIsAlternate,
- aObserver, principal, requestingNode);
- NS_ADDREF(data);
- // If we have to parse and it's an alternate non-inline, defer it unless
- // it's explicitly enabled.
- if (aURL && state == eSheetNeedsParser && mSheets->mLoadingDatas.Count() != 0 &&
- *aIsAlternate && !*aIsExplicitlyEnabled) {
- LOG((" Deferring alternate sheet load"));
- URIPrincipalReferrerPolicyAndCORSModeHashKey key(data->mURI,
- data->mLoaderPrincipal,
- data->mSheet->GetCORSMode(),
- data->mSheet->GetReferrerPolicy());
- mSheets->mPendingDatas.Put(&key, data);
- data->mMustNotify = true;
- return NS_OK;
- }
- // Load completion will free the data
- rv = LoadSheet(data, state, false);
- NS_ENSURE_SUCCESS(rv, rv);
- data->mMustNotify = true;
- return rv;
- }
- static bool
- HaveAncestorDataWithURI(SheetLoadData *aData, nsIURI *aURI)
- {
- if (!aData->mURI) {
- // Inline style; this won't have any ancestors
- MOZ_ASSERT(!aData->mParentData,
- "How does inline style have a parent?");
- return false;
- }
- bool equal;
- if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) {
- return true;
- }
- // Datas down the mNext chain have the same URI as aData, so we
- // don't have to compare to them. But they might have different
- // parents, and we have to check all of those.
- while (aData) {
- if (aData->mParentData &&
- HaveAncestorDataWithURI(aData->mParentData, aURI)) {
- return true;
- }
- aData = aData->mNext;
- }
- return false;
- }
- nsresult
- Loader::LoadChildSheet(StyleSheet* aParentSheet,
- nsIURI* aURL,
- nsMediaList* aMedia,
- ImportRule* aParentRule,
- LoaderReusableStyleSheets* aReusableSheets)
- {
- LOG(("css::Loader::LoadChildSheet"));
- NS_PRECONDITION(aURL, "Must have a URI to load");
- NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
- if (!mEnabled) {
- LOG_WARN((" Not enabled"));
- return NS_ERROR_NOT_AVAILABLE;
- }
- LOG_URI(" Child uri: '%s'", aURL);
- nsCOMPtr<nsINode> owningNode;
- // check for an associated document: if none, don't bother walking up the
- // parent sheets
- if (aParentSheet->GetAssociatedDocument()) {
- StyleSheet* topSheet = aParentSheet;
- while (StyleSheet* parent = topSheet->GetParentSheet()) {
- topSheet = parent;
- }
- owningNode = topSheet->GetOwnerNode();
- }
- nsISupports* context = owningNode;
- if (!context) {
- context = mDocument;
- }
- nsIPrincipal* principal = aParentSheet->Principal();
- nsresult rv = CheckContentPolicy(principal, aURL, context, false);
- NS_ENSURE_SUCCESS(rv, rv);
- SheetLoadData* parentData = nullptr;
- nsCOMPtr<nsICSSLoaderObserver> observer;
- int32_t count = mParsingDatas.Length();
- if (count > 0) {
- LOG((" Have a parent load"));
- parentData = mParsingDatas.ElementAt(count - 1);
- // Check for cycles
- if (HaveAncestorDataWithURI(parentData, aURL)) {
- // Houston, we have a loop, blow off this child and pretend this never
- // happened
- LOG_ERROR((" @import cycle detected, dropping load"));
- return NS_OK;
- }
- NS_ASSERTION(parentData->mSheet == aParentSheet,
- "Unexpected call to LoadChildSheet");
- } else {
- LOG((" No parent load; must be CSSOM"));
- // No parent load data, so the sheet will need to be notified when
- // we finish, if it can be, if we do the load asynchronously.
- // XXXheycam ServoStyleSheet doesn't implement nsICSSLoaderObserver yet.
- MOZ_ASSERT(aParentSheet->IsGecko(),
- "stylo: ServoStyleSheets don't support child sheet loading yet");
- observer = aParentSheet->AsGecko();
- }
- // Now that we know it's safe to load this (passes security check and not a
- // loop) do so.
- RefPtr<StyleSheet> sheet;
- RefPtr<CSSStyleSheet> reusableSheet;
- StyleSheetState state;
- if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, reusableSheet)) {
- sheet = reusableSheet;
- aParentRule->SetSheet(reusableSheet);
- state = eSheetComplete;
- } else {
- bool isAlternate;
- const nsSubstring& empty = EmptyString();
- // For now, use CORS_NONE for child sheets
- rv = CreateSheet(aURL, nullptr, principal,
- aParentSheet->ParsingMode(),
- CORS_NONE, aParentSheet->GetReferrerPolicy(),
- EmptyString(), // integrity is only checked on main sheet
- parentData ? parentData->mSyncLoad : false,
- false, empty, state, &isAlternate, &sheet);
- NS_ENSURE_SUCCESS(rv, rv);
- // For now, child sheets are not explicitly enabled (seventh argument is
- // always false here).
- PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate, false);
- }
- rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
- NS_ENSURE_SUCCESS(rv, rv);
- if (state == eSheetComplete) {
- LOG((" Sheet already complete"));
- // We're completely done. No need to notify, even, since the
- // @import rule addition/modification will trigger the right style
- // changes automatically.
- return NS_OK;
- }
- nsCOMPtr<nsINode> requestingNode = do_QueryInterface(context);
- SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
- observer, principal, requestingNode);
- NS_ADDREF(data);
- bool syncLoad = data->mSyncLoad;
- // Load completion will release the data
- rv = LoadSheet(data, state, false);
- NS_ENSURE_SUCCESS(rv, rv);
- // If syncLoad is true, |data| will be deleted by now.
- if (!syncLoad) {
- data->mMustNotify = true;
- }
- return rv;
- }
- nsresult
- Loader::LoadSheetSync(nsIURI* aURL,
- SheetParsingMode aParsingMode,
- bool aUseSystemPrincipal,
- RefPtr<StyleSheet>* aSheet)
- {
- LOG(("css::Loader::LoadSheetSync"));
- return InternalLoadNonDocumentSheet(aURL,
- false, aParsingMode, aUseSystemPrincipal,
- nullptr, EmptyCString(),
- aSheet, nullptr);
- }
- nsresult
- Loader::LoadSheet(nsIURI* aURL,
- nsIPrincipal* aOriginPrincipal,
- const nsCString& aCharset,
- nsICSSLoaderObserver* aObserver,
- RefPtr<StyleSheet>* aSheet)
- {
- LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
- NS_PRECONDITION(aSheet, "aSheet is null");
- return InternalLoadNonDocumentSheet(aURL,
- false, eAuthorSheetFeatures, false,
- aOriginPrincipal, aCharset,
- aSheet, aObserver);
- }
- nsresult
- Loader::LoadSheet(nsIURI* aURL,
- bool aIsPreload,
- nsIPrincipal* aOriginPrincipal,
- const nsCString& aCharset,
- nsICSSLoaderObserver* aObserver,
- CORSMode aCORSMode,
- ReferrerPolicy aReferrerPolicy,
- const nsAString& aIntegrity)
- {
- LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
- return InternalLoadNonDocumentSheet(aURL,
- aIsPreload, eAuthorSheetFeatures, false,
- aOriginPrincipal, aCharset,
- nullptr, aObserver,
- aCORSMode, aReferrerPolicy, aIntegrity);
- }
- nsresult
- Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
- bool aIsPreload,
- SheetParsingMode aParsingMode,
- bool aUseSystemPrincipal,
- nsIPrincipal* aOriginPrincipal,
- const nsCString& aCharset,
- RefPtr<StyleSheet>* aSheet,
- nsICSSLoaderObserver* aObserver,
- CORSMode aCORSMode,
- ReferrerPolicy aReferrerPolicy,
- const nsAString& aIntegrity)
- {
- NS_PRECONDITION(aURL, "Must have a URI to load");
- NS_PRECONDITION(aSheet || aObserver, "Sheet and observer can't both be null");
- NS_PRECONDITION(!aUseSystemPrincipal || !aObserver,
- "Shouldn't load system-principal sheets async");
- NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
- LOG_URI(" Non-document sheet uri: '%s'", aURL);
- if (aSheet) {
- *aSheet = nullptr;
- }
- if (!mEnabled) {
- LOG_WARN((" Not enabled"));
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsresult rv = CheckContentPolicy(aOriginPrincipal, aURL, mDocument, aIsPreload);
- NS_ENSURE_SUCCESS(rv, rv);
- StyleSheetState state;
- bool isAlternate;
- RefPtr<StyleSheet> sheet;
- bool syncLoad = (aObserver == nullptr);
- const nsSubstring& empty = EmptyString();
- rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aParsingMode,
- aCORSMode, aReferrerPolicy, aIntegrity, syncLoad,
- false, empty, state, &isAlternate, &sheet);
- NS_ENSURE_SUCCESS(rv, rv);
- // Sheets can only be explicitly enabled after creation and preparation, so
- // we always pass false for the initial value of the explicitly enabled flag
- // when calling PrepareSheet.
- PrepareSheet(sheet, empty, empty, nullptr, nullptr, isAlternate, false);
- if (state == eSheetComplete) {
- LOG((" Sheet already complete"));
- if (aObserver || !mObservers.IsEmpty()) {
- rv = PostLoadEvent(aURL, sheet, aObserver, false, nullptr);
- }
- if (aSheet) {
- sheet.swap(*aSheet);
- }
- return rv;
- }
- SheetLoadData* data =
- new SheetLoadData(this, aURL, sheet, syncLoad,
- aUseSystemPrincipal, aCharset, aObserver,
- aOriginPrincipal, mDocument);
- NS_ADDREF(data);
- rv = LoadSheet(data, state, aIsPreload);
- NS_ENSURE_SUCCESS(rv, rv);
- if (aSheet) {
- sheet.swap(*aSheet);
- }
- if (aObserver) {
- data->mMustNotify = true;
- }
- return rv;
- }
- nsresult
- Loader::PostLoadEvent(nsIURI* aURI,
- StyleSheet* aSheet,
- nsICSSLoaderObserver* aObserver,
- bool aWasAlternate,
- nsIStyleSheetLinkingElement* aElement)
- {
- LOG(("css::Loader::PostLoadEvent"));
- NS_PRECONDITION(aSheet, "Must have sheet");
- NS_PRECONDITION(aObserver || !mObservers.IsEmpty() || aElement,
- "Must have observer or element");
- RefPtr<SheetLoadData> evt =
- new SheetLoadData(this, EmptyString(), // title doesn't matter here
- aURI,
- aSheet,
- aElement,
- aWasAlternate,
- aObserver,
- nullptr,
- mDocument);
- NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
- if (!mPostedEvents.AppendElement(evt)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- nsresult rv = NS_DispatchToCurrentThread(evt);
- if (NS_FAILED(rv)) {
- NS_WARNING("failed to dispatch stylesheet load event");
- mPostedEvents.RemoveElement(evt);
- } else {
- // We'll unblock onload when we handle the event.
- if (mDocument) {
- mDocument->BlockOnload();
- }
- // We want to notify the observer for this data.
- evt->mMustNotify = true;
- evt->mSheetAlreadyComplete = true;
- // If we get to this code, aSheet loaded correctly at some point, so
- // we can just use NS_OK for the status. Note that we do this here
- // and not from inside our SheetComplete so that we don't end up
- // running the load event async.
- evt->ScheduleLoadEventIfNeeded(NS_OK);
- }
- return rv;
- }
- void
- Loader::HandleLoadEvent(SheetLoadData* aEvent)
- {
- // XXXbz can't assert this yet.... May not have an observer because
- // we're unblocking the parser
- // NS_ASSERTION(aEvent->mObserver, "Must have observer");
- NS_ASSERTION(aEvent->mSheet, "Must have sheet");
- // Very important: this needs to come before the SheetComplete call
- // below, so that HasPendingLoads() will test true as needed under
- // notifications we send from that SheetComplete call.
- mPostedEvents.RemoveElement(aEvent);
- if (!aEvent->mIsCancelled) {
- // SheetComplete will call Release(), so give it a reference to do
- // that with.
- NS_ADDREF(aEvent);
- SheetComplete(aEvent, NS_OK);
- }
- if (mDocument) {
- mDocument->UnblockOnload(true);
- }
- }
- static void
- StopLoadingSheets(
- nsDataHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey, SheetLoadData*>& aDatas,
- Loader::LoadDataArray& aArr)
- {
- for (auto iter = aDatas.Iter(); !iter.Done(); iter.Next()) {
- SheetLoadData* data = iter.Data();
- MOZ_ASSERT(data, "Must have a data!");
- data->mIsLoading = false; // we will handle the removal right here
- data->mIsCancelled = true;
- aArr.AppendElement(data);
- iter.Remove();
- }
- }
- nsresult
- Loader::Stop()
- {
- uint32_t pendingCount = mSheets ? mSheets->mPendingDatas.Count() : 0;
- uint32_t loadingCount = mSheets ? mSheets->mLoadingDatas.Count() : 0;
- LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length());
- if (pendingCount) {
- StopLoadingSheets(mSheets->mPendingDatas, arr);
- }
- if (loadingCount) {
- StopLoadingSheets(mSheets->mLoadingDatas, arr);
- }
- uint32_t i;
- for (i = 0; i < mPostedEvents.Length(); ++i) {
- SheetLoadData* data = mPostedEvents[i];
- data->mIsCancelled = true;
- if (arr.AppendElement(data)) {
- // SheetComplete() calls Release(), so give this an extra ref.
- NS_ADDREF(data);
- }
- #ifdef DEBUG
- else {
- NS_NOTREACHED("We preallocated this memory... shouldn't really fail, "
- "except we never check that preallocation succeeds.");
- }
- #endif
- }
- mPostedEvents.Clear();
- mDatasToNotifyOn += arr.Length();
- for (i = 0; i < arr.Length(); ++i) {
- --mDatasToNotifyOn;
- SheetComplete(arr[i], NS_BINDING_ABORTED);
- }
- return NS_OK;
- }
- bool
- Loader::HasPendingLoads()
- {
- return
- (mSheets && mSheets->mLoadingDatas.Count() != 0) ||
- (mSheets && mSheets->mPendingDatas.Count() != 0) ||
- mPostedEvents.Length() != 0 ||
- mDatasToNotifyOn != 0;
- }
- nsresult
- Loader::AddObserver(nsICSSLoaderObserver* aObserver)
- {
- NS_PRECONDITION(aObserver, "Must have observer");
- if (mObservers.AppendElementUnlessExists(aObserver)) {
- return NS_OK;
- }
- return NS_ERROR_OUT_OF_MEMORY;
- }
- void
- Loader::RemoveObserver(nsICSSLoaderObserver* aObserver)
- {
- mObservers.RemoveElement(aObserver);
- }
- void
- Loader::StartAlternateLoads()
- {
- NS_PRECONDITION(mSheets, "Don't call me!");
- LoadDataArray arr(mSheets->mPendingDatas.Count());
- for (auto iter = mSheets->mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
- arr.AppendElement(iter.Data());
- iter.Remove();
- }
- mDatasToNotifyOn += arr.Length();
- for (uint32_t i = 0; i < arr.Length(); ++i) {
- --mDatasToNotifyOn;
- LoadSheet(arr[i], eSheetNeedsParser, false);
- }
- }
- NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
- if (tmp->mSheets) {
- for (auto iter = tmp->mSheets->mCompleteSheets.Iter();
- !iter.Done();
- iter.Next()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "Sheet cache nsCSSLoader");
- if (iter.UserData()->IsGecko()) {
- CSSStyleSheet* sheet = iter.UserData()->AsGecko();
- cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, sheet));
- }
- }
- }
- nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>>::ForwardIterator
- it(tmp->mObservers);
- while (it.HasMore()) {
- ImplCycleCollectionTraverse(cb, it.GetNext(),
- "mozilla::css::Loader.mObservers");
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader)
- if (tmp->mSheets) {
- tmp->mSheets->mCompleteSheets.Clear();
- }
- tmp->mObservers.Clear();
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Loader, AddRef)
- NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Loader, Release)
- size_t
- Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
- {
- size_t n = aMallocSizeOf(this);
- if (mSheets) {
- n += mSheets->mCompleteSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
- for (auto iter = mSheets->mCompleteSheets.ConstIter();
- !iter.Done();
- iter.Next()) {
- // If aSheet has a parent, then its parent will report it so we don't
- // have to worry about it here. Likewise, if aSheet has an owning node,
- // then the document that node is in will report it.
- const StyleSheet* sheet = iter.UserData();
- n += (sheet->GetOwnerNode() || sheet->GetParentSheet())
- ? 0
- : sheet->SizeOfIncludingThis(aMallocSizeOf);
- }
- }
- n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
- // Measurement of the following members may be added later if DMD finds it is
- // worthwhile:
- // - mLoadingDatas: transient, and should be small
- // - mPendingDatas: transient, and should be small
- // - mParsingDatas: transient, and should be small
- // - mPostedEvents: transient, and should be small
- //
- // The following members aren't measured:
- // - mDocument, because it's a weak backpointer
- // - mPreferredSheet, because it can be a shared string
- return n;
- }
- StyleBackendType
- Loader::GetStyleBackendType() const
- {
- MOZ_ASSERT(mStyleBackendType || mDocument,
- "you must construct a Loader with a document or set a "
- "StyleBackendType on it before calling GetStyleBackendType");
- if (mStyleBackendType) {
- return *mStyleBackendType;
- }
- return mDocument->GetStyleBackendType();
- }
- } // namespace css
- } // namespace mozilla
|