1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
- #include "ScriptLoader.h"
- #include "nsIChannel.h"
- #include "nsIContentPolicy.h"
- #include "nsIContentSecurityPolicy.h"
- #include "nsIDocShell.h"
- #include "nsIDOMDocument.h"
- #include "nsIHttpChannel.h"
- #include "nsIHttpChannelInternal.h"
- #include "nsIInputStreamPump.h"
- #include "nsIIOService.h"
- #include "nsIProtocolHandler.h"
- #include "nsIScriptError.h"
- #include "nsIScriptSecurityManager.h"
- #include "nsIStreamLoader.h"
- #include "nsIStreamListenerTee.h"
- #include "nsIThreadRetargetableRequest.h"
- #include "nsIURI.h"
- #include "jsapi.h"
- #include "jsfriendapi.h"
- #include "nsError.h"
- #include "nsContentPolicyUtils.h"
- #include "nsContentUtils.h"
- #include "nsDocShellCID.h"
- #include "nsISupportsPrimitives.h"
- #include "nsNetUtil.h"
- #include "nsIPipe.h"
- #include "nsIOutputStream.h"
- #include "nsPrintfCString.h"
- #include "nsString.h"
- #include "nsStreamUtils.h"
- #include "nsTArray.h"
- #include "nsThreadUtils.h"
- #include "nsXPCOM.h"
- #include "xpcpublic.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/LoadContext.h"
- #include "mozilla/Maybe.h"
- #include "mozilla/ipc/BackgroundUtils.h"
- #include "mozilla/dom/CacheBinding.h"
- #include "mozilla/dom/cache/CacheTypes.h"
- #include "mozilla/dom/cache/Cache.h"
- #include "mozilla/dom/cache/CacheStorage.h"
- #include "mozilla/dom/ChannelInfo.h"
- #include "mozilla/dom/Exceptions.h"
- #include "mozilla/dom/InternalResponse.h"
- #include "mozilla/dom/nsCSPService.h"
- #include "mozilla/dom/nsCSPUtils.h"
- #include "mozilla/dom/Promise.h"
- #include "mozilla/dom/PromiseNativeHandler.h"
- #include "mozilla/dom/Response.h"
- #include "mozilla/dom/ScriptLoader.h"
- #include "mozilla/dom/ScriptSettings.h"
- #include "mozilla/dom/SRILogHelper.h"
- #include "mozilla/UniquePtr.h"
- #include "Principal.h"
- #include "WorkerHolder.h"
- #include "WorkerPrivate.h"
- #include "WorkerRunnable.h"
- #include "WorkerScope.h"
- #define MAX_CONCURRENT_SCRIPTS 1000
- USING_WORKERS_NAMESPACE
- using namespace mozilla;
- using namespace mozilla::dom;
- using mozilla::dom::cache::Cache;
- using mozilla::dom::cache::CacheStorage;
- using mozilla::ipc::PrincipalInfo;
- namespace {
- nsIURI*
- GetBaseURI(bool aIsMainScript, WorkerPrivate* aWorkerPrivate)
- {
- MOZ_ASSERT(aWorkerPrivate);
- nsIURI* baseURI;
- WorkerPrivate* parentWorker = aWorkerPrivate->GetParent();
- if (aIsMainScript) {
- if (parentWorker) {
- baseURI = parentWorker->GetBaseURI();
- NS_ASSERTION(baseURI, "Should have been set already!");
- }
- else {
- // May be null.
- baseURI = aWorkerPrivate->GetBaseURI();
- }
- }
- else {
- baseURI = aWorkerPrivate->GetBaseURI();
- NS_ASSERTION(baseURI, "Should have been set already!");
- }
- return baseURI;
- }
- nsresult
- ChannelFromScriptURL(nsIPrincipal* principal,
- nsIURI* baseURI,
- nsIDocument* parentDoc,
- nsILoadGroup* loadGroup,
- nsIIOService* ios,
- nsIScriptSecurityManager* secMan,
- const nsAString& aScriptURL,
- bool aIsMainScript,
- WorkerScriptType aWorkerScriptType,
- nsContentPolicyType aContentPolicyType,
- nsLoadFlags aLoadFlags,
- bool aDefaultURIEncoding,
- nsIChannel** aChannel)
- {
- AssertIsOnMainThread();
- nsresult rv;
- nsCOMPtr<nsIURI> uri;
- if (aDefaultURIEncoding) {
- rv = NS_NewURI(getter_AddRefs(uri), aScriptURL, nullptr, baseURI);
- } else {
- rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
- aScriptURL, parentDoc,
- baseURI);
- }
- if (NS_FAILED(rv)) {
- return NS_ERROR_DOM_SYNTAX_ERR;
- }
- // If we have the document, use it. Unfortunately, for dedicated workers
- // 'parentDoc' ends up being the parent document, which is not the document
- // that we want to use. So make sure to avoid using 'parentDoc' in that
- // situation.
- if (parentDoc && parentDoc->NodePrincipal() != principal) {
- parentDoc = nullptr;
- }
- aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
- uint32_t secFlags = aIsMainScript ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
- : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
- if (aWorkerScriptType == DebuggerScript) {
- // A DebuggerScript needs to be a local resource like chrome: or resource:
- bool isUIResource = false;
- rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE,
- &isUIResource);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!isUIResource) {
- return NS_ERROR_DOM_SECURITY_ERR;
- }
- secFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
- }
- // Note: this is for backwards compatibility and goes against spec.
- // We should find a better solution.
- bool isData = false;
- if (aIsMainScript && NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData) {
- secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
- }
- nsCOMPtr<nsIChannel> channel;
- // If we have the document, use it. Unfortunately, for dedicated workers
- // 'parentDoc' ends up being the parent document, which is not the document
- // that we want to use. So make sure to avoid using 'parentDoc' in that
- // situation.
- if (parentDoc && parentDoc->NodePrincipal() == principal) {
- rv = NS_NewChannel(getter_AddRefs(channel),
- uri,
- parentDoc,
- secFlags,
- aContentPolicyType,
- loadGroup,
- nullptr, // aCallbacks
- aLoadFlags,
- ios);
- } else {
- // We must have a loadGroup with a load context for the principal to
- // traverse the channel correctly.
- MOZ_ASSERT(loadGroup);
- MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal));
- rv = NS_NewChannel(getter_AddRefs(channel),
- uri,
- principal,
- secFlags,
- aContentPolicyType,
- loadGroup,
- nullptr, // aCallbacks
- aLoadFlags,
- ios);
- }
- NS_ENSURE_SUCCESS(rv, rv);
- if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
- rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
- httpChannel, mozilla::net::RP_Default);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- channel.forget(aChannel);
- return rv;
- }
- struct ScriptLoadInfo
- {
- ScriptLoadInfo()
- : mScriptTextBuf(nullptr)
- , mScriptTextLength(0)
- , mLoadResult(NS_ERROR_NOT_INITIALIZED)
- , mLoadingFinished(false)
- , mExecutionScheduled(false)
- , mExecutionResult(false)
- , mCacheStatus(Uncached)
- { }
- ~ScriptLoadInfo()
- {
- if (mScriptTextBuf) {
- js_free(mScriptTextBuf);
- }
- }
- bool
- ReadyToExecute()
- {
- return !mChannel && NS_SUCCEEDED(mLoadResult) && !mExecutionScheduled;
- }
- nsString mURL;
- // This full URL string is populated only if this object is used in a
- // ServiceWorker.
- nsString mFullURL;
- // This promise is set only when the script is for a ServiceWorker but
- // it's not in the cache yet. The promise is resolved when the full body is
- // stored into the cache. mCachePromise will be set to nullptr after
- // resolution.
- RefPtr<Promise> mCachePromise;
- // The reader stream the cache entry should be filled from, for those cases
- // when we're going to have an mCachePromise.
- nsCOMPtr<nsIInputStream> mCacheReadStream;
- nsCOMPtr<nsIChannel> mChannel;
- char16_t* mScriptTextBuf;
- size_t mScriptTextLength;
- nsresult mLoadResult;
- bool mLoadingFinished;
- bool mExecutionScheduled;
- bool mExecutionResult;
- enum CacheStatus {
- // By default a normal script is just loaded from the network. But for
- // ServiceWorkers, we have to check if the cache contains the script and
- // load it from the cache.
- Uncached,
- WritingToCache,
- ReadingFromCache,
- // This script has been loaded from the ServiceWorker cache.
- Cached,
- // This script must be stored in the ServiceWorker cache.
- ToBeCached,
- // Something went wrong or the worker went away.
- Cancel
- };
- CacheStatus mCacheStatus;
- Maybe<bool> mMutedErrorFlag;
- bool Finished() const
- {
- return mLoadingFinished && !mCachePromise && !mChannel;
- }
- };
- class ScriptLoaderRunnable;
- class ScriptExecutorRunnable final : public MainThreadWorkerSyncRunnable
- {
- ScriptLoaderRunnable& mScriptLoader;
- bool mIsWorkerScript;
- uint32_t mFirstIndex;
- uint32_t mLastIndex;
- public:
- ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
- nsIEventTarget* aSyncLoopTarget,
- bool aIsWorkerScript,
- uint32_t aFirstIndex,
- uint32_t aLastIndex);
- private:
- ~ScriptExecutorRunnable()
- { }
- virtual bool
- IsDebuggerRunnable() const override;
- virtual bool
- PreRun(WorkerPrivate* aWorkerPrivate) override;
- virtual bool
- WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
- virtual void
- PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
- override;
- nsresult
- Cancel() override;
- void
- ShutdownScriptLoader(JSContext* aCx,
- WorkerPrivate* aWorkerPrivate,
- bool aResult,
- bool aMutedError);
- void LogExceptionToConsole(JSContext* aCx,
- WorkerPrivate* WorkerPrivate);
- };
- class CacheScriptLoader;
- class CacheCreator final : public PromiseNativeHandler
- {
- public:
- NS_DECL_ISUPPORTS
- explicit CacheCreator(WorkerPrivate* aWorkerPrivate)
- : mCacheName(aWorkerPrivate->ServiceWorkerCacheName())
- , mOriginAttributes(aWorkerPrivate->GetOriginAttributes())
- {
- MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
- MOZ_ASSERT(aWorkerPrivate->LoadScriptAsPartOfLoadingServiceWorkerScript());
- AssertIsOnMainThread();
- }
- void
- AddLoader(CacheScriptLoader* aLoader)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(!mCacheStorage);
- mLoaders.AppendElement(aLoader);
- }
- virtual void
- ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
- virtual void
- RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
- // Try to load from cache with aPrincipal used for cache access.
- nsresult
- Load(nsIPrincipal* aPrincipal);
- Cache*
- Cache_() const
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(mCache);
- return mCache;
- }
- nsIGlobalObject*
- Global() const
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(mSandboxGlobalObject);
- return mSandboxGlobalObject;
- }
- void
- DeleteCache();
- private:
- ~CacheCreator()
- {
- }
- nsresult
- CreateCacheStorage(nsIPrincipal* aPrincipal);
- void
- FailLoaders(nsresult aRv);
- RefPtr<Cache> mCache;
- RefPtr<CacheStorage> mCacheStorage;
- nsCOMPtr<nsIGlobalObject> mSandboxGlobalObject;
- nsTArray<RefPtr<CacheScriptLoader>> mLoaders;
- nsString mCacheName;
- PrincipalOriginAttributes mOriginAttributes;
- };
- NS_IMPL_ISUPPORTS0(CacheCreator)
- class CacheScriptLoader final : public PromiseNativeHandler
- , public nsIStreamLoaderObserver
- {
- public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSISTREAMLOADEROBSERVER
- CacheScriptLoader(WorkerPrivate* aWorkerPrivate, ScriptLoadInfo& aLoadInfo,
- uint32_t aIndex, bool aIsWorkerScript,
- ScriptLoaderRunnable* aRunnable)
- : mLoadInfo(aLoadInfo)
- , mIndex(aIndex)
- , mRunnable(aRunnable)
- , mIsWorkerScript(aIsWorkerScript)
- , mFailed(false)
- {
- MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
- mBaseURI = GetBaseURI(mIsWorkerScript, aWorkerPrivate);
- AssertIsOnMainThread();
- }
- void
- Fail(nsresult aRv);
- void
- Load(Cache* aCache);
- virtual void
- ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
- virtual void
- RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
- private:
- ~CacheScriptLoader()
- {
- AssertIsOnMainThread();
- }
- ScriptLoadInfo& mLoadInfo;
- uint32_t mIndex;
- RefPtr<ScriptLoaderRunnable> mRunnable;
- bool mIsWorkerScript;
- bool mFailed;
- nsCOMPtr<nsIInputStreamPump> mPump;
- nsCOMPtr<nsIURI> mBaseURI;
- mozilla::dom::ChannelInfo mChannelInfo;
- UniquePtr<PrincipalInfo> mPrincipalInfo;
- };
- NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
- class CachePromiseHandler final : public PromiseNativeHandler
- {
- public:
- NS_DECL_ISUPPORTS
- CachePromiseHandler(ScriptLoaderRunnable* aRunnable,
- ScriptLoadInfo& aLoadInfo,
- uint32_t aIndex)
- : mRunnable(aRunnable)
- , mLoadInfo(aLoadInfo)
- , mIndex(aIndex)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(mRunnable);
- }
- virtual void
- ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
- virtual void
- RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
- private:
- ~CachePromiseHandler()
- {
- AssertIsOnMainThread();
- }
- RefPtr<ScriptLoaderRunnable> mRunnable;
- ScriptLoadInfo& mLoadInfo;
- uint32_t mIndex;
- };
- NS_IMPL_ISUPPORTS0(CachePromiseHandler)
- class LoaderListener final : public nsIStreamLoaderObserver
- , public nsIRequestObserver
- {
- public:
- NS_DECL_ISUPPORTS
- LoaderListener(ScriptLoaderRunnable* aRunnable, uint32_t aIndex)
- : mRunnable(aRunnable)
- , mIndex(aIndex)
- {
- MOZ_ASSERT(mRunnable);
- }
- NS_IMETHOD
- OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
- nsresult aStatus, uint32_t aStringLen,
- const uint8_t* aString) override;
- NS_IMETHOD
- OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override;
- NS_IMETHOD
- OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
- nsresult aStatusCode) override
- {
- // Nothing to do here!
- return NS_OK;
- }
- private:
- ~LoaderListener() {}
- RefPtr<ScriptLoaderRunnable> mRunnable;
- uint32_t mIndex;
- };
- NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
- class ScriptLoaderHolder;
- class ScriptLoaderRunnable final : public nsIRunnable
- {
- friend class ScriptExecutorRunnable;
- friend class ScriptLoaderHolder;
- friend class CachePromiseHandler;
- friend class CacheScriptLoader;
- friend class LoaderListener;
- WorkerPrivate* mWorkerPrivate;
- nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
- nsTArray<ScriptLoadInfo> mLoadInfos;
- RefPtr<CacheCreator> mCacheCreator;
- bool mIsMainScript;
- WorkerScriptType mWorkerScriptType;
- bool mCanceled;
- bool mCanceledMainThread;
- ErrorResult& mRv;
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
- nsIEventTarget* aSyncLoopTarget,
- nsTArray<ScriptLoadInfo>& aLoadInfos,
- bool aIsMainScript,
- WorkerScriptType aWorkerScriptType,
- ErrorResult& aRv)
- : mWorkerPrivate(aWorkerPrivate), mSyncLoopTarget(aSyncLoopTarget),
- mIsMainScript(aIsMainScript), mWorkerScriptType(aWorkerScriptType),
- mCanceled(false), mCanceledMainThread(false), mRv(aRv)
- {
- aWorkerPrivate->AssertIsOnWorkerThread();
- MOZ_ASSERT(aSyncLoopTarget);
- MOZ_ASSERT_IF(aIsMainScript, aLoadInfos.Length() == 1);
- mLoadInfos.SwapElements(aLoadInfos);
- }
- private:
- ~ScriptLoaderRunnable()
- { }
- NS_IMETHOD
- Run() override
- {
- AssertIsOnMainThread();
- nsresult rv = RunInternal();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- CancelMainThread(rv);
- }
- return NS_OK;
- }
- void
- LoadingFinished(uint32_t aIndex, nsresult aRv)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(aIndex < mLoadInfos.Length());
- ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
- loadInfo.mLoadResult = aRv;
- MOZ_ASSERT(!loadInfo.mLoadingFinished);
- loadInfo.mLoadingFinished = true;
- MaybeExecuteFinishedScripts(aIndex);
- }
- void
- MaybeExecuteFinishedScripts(uint32_t aIndex)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(aIndex < mLoadInfos.Length());
- ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
- // We execute the last step if we don't have a pending operation with the
- // cache and the loading is completed.
- if (loadInfo.Finished()) {
- ExecuteFinishedScripts();
- }
- }
- nsresult
- OnStreamComplete(nsIStreamLoader* aLoader, uint32_t aIndex,
- nsresult aStatus, uint32_t aStringLen,
- const uint8_t* aString)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(aIndex < mLoadInfos.Length());
- nsresult rv = OnStreamCompleteInternal(aLoader, aStatus, aStringLen,
- aString, mLoadInfos[aIndex]);
- LoadingFinished(aIndex, rv);
- return NS_OK;
- }
- nsresult
- OnStartRequest(nsIRequest* aRequest, uint32_t aIndex)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(aIndex < mLoadInfos.Length());
- // If one load info cancels or hits an error, it can race with the start
- // callback coming from another load info.
- if (mCanceledMainThread || !mCacheCreator) {
- aRequest->Cancel(NS_ERROR_FAILURE);
- return NS_ERROR_FAILURE;
- }
- ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
- MOZ_ASSERT(channel == loadInfo.mChannel);
- // We synthesize the result code, but its never exposed to content.
- RefPtr<mozilla::dom::InternalResponse> ir =
- new mozilla::dom::InternalResponse(200, NS_LITERAL_CSTRING("OK"));
- ir->SetBody(loadInfo.mCacheReadStream, InternalResponse::UNKNOWN_BODY_SIZE);
- // Drop our reference to the stream now that we've passed it along, so it
- // doesn't hang around once the cache is done with it and keep data alive.
- loadInfo.mCacheReadStream = nullptr;
- // Set the channel info of the channel on the response so that it's
- // saved in the cache.
- ir->InitChannelInfo(channel);
- // Save the principal of the channel since its URI encodes the script URI
- // rather than the ServiceWorkerRegistrationInfo URI.
- nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
- NS_ASSERTION(ssm, "Should never be null!");
- nsCOMPtr<nsIPrincipal> channelPrincipal;
- nsresult rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- channel->Cancel(rv);
- return rv;
- }
- UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
- rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- channel->Cancel(rv);
- return rv;
- }
- ir->SetPrincipalInfo(Move(principalInfo));
- RefPtr<mozilla::dom::Response> response =
- new mozilla::dom::Response(mCacheCreator->Global(), ir, nullptr);
- mozilla::dom::RequestOrUSVString request;
- MOZ_ASSERT(!loadInfo.mFullURL.IsEmpty());
- request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
- loadInfo.mFullURL.Length());
- ErrorResult error;
- RefPtr<Promise> cachePromise =
- mCacheCreator->Cache_()->Put(request, *response, error);
- if (NS_WARN_IF(error.Failed())) {
- nsresult rv = error.StealNSResult();
- channel->Cancel(rv);
- return rv;
- }
- RefPtr<CachePromiseHandler> promiseHandler =
- new CachePromiseHandler(this, loadInfo, aIndex);
- cachePromise->AppendNativeHandler(promiseHandler);
- loadInfo.mCachePromise.swap(cachePromise);
- loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache;
- return NS_OK;
- }
- bool
- Notify(Status aStatus)
- {
- mWorkerPrivate->AssertIsOnWorkerThread();
- if (aStatus >= Terminating && !mCanceled) {
- mCanceled = true;
- MOZ_ALWAYS_SUCCEEDS(
- NS_DispatchToMainThread(NewRunnableMethod(this,
- &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted)));
- }
- return true;
- }
- bool
- IsMainWorkerScript() const
- {
- return mIsMainScript && mWorkerScriptType == WorkerScript;
- }
- void
- CancelMainThreadWithBindingAborted()
- {
- CancelMainThread(NS_BINDING_ABORTED);
- }
- void
- CancelMainThread(nsresult aCancelResult)
- {
- AssertIsOnMainThread();
- if (mCanceledMainThread) {
- return;
- }
- mCanceledMainThread = true;
- if (mCacheCreator) {
- MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
- DeleteCache();
- }
- // Cancel all the channels that were already opened.
- for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
- ScriptLoadInfo& loadInfo = mLoadInfos[index];
- // If promise or channel is non-null, their failures will lead to
- // LoadingFinished being called.
- bool callLoadingFinished = true;
- if (loadInfo.mCachePromise) {
- MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
- loadInfo.mCachePromise->MaybeReject(aCancelResult);
- loadInfo.mCachePromise = nullptr;
- callLoadingFinished = false;
- }
- if (loadInfo.mChannel) {
- if (NS_SUCCEEDED(loadInfo.mChannel->Cancel(aCancelResult))) {
- callLoadingFinished = false;
- } else {
- NS_WARNING("Failed to cancel channel!");
- }
- }
- if (callLoadingFinished && !loadInfo.Finished()) {
- LoadingFinished(index, aCancelResult);
- }
- }
- ExecuteFinishedScripts();
- }
- void
- DeleteCache()
- {
- AssertIsOnMainThread();
- if (!mCacheCreator) {
- return;
- }
- mCacheCreator->DeleteCache();
- mCacheCreator = nullptr;
- }
- nsresult
- RunInternal()
- {
- AssertIsOnMainThread();
- if (IsMainWorkerScript() && mWorkerPrivate->IsServiceWorker()) {
- mWorkerPrivate->SetLoadingWorkerScript(true);
- }
- if (!mWorkerPrivate->IsServiceWorker() ||
- !mWorkerPrivate->LoadScriptAsPartOfLoadingServiceWorkerScript()) {
- for (uint32_t index = 0, len = mLoadInfos.Length(); index < len;
- ++index) {
- nsresult rv = LoadScript(index);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- LoadingFinished(index, rv);
- return rv;
- }
- }
- return NS_OK;
- }
- MOZ_ASSERT(!mCacheCreator);
- mCacheCreator = new CacheCreator(mWorkerPrivate);
- for (uint32_t index = 0, len = mLoadInfos.Length(); index < len; ++index) {
- RefPtr<CacheScriptLoader> loader =
- new CacheScriptLoader(mWorkerPrivate, mLoadInfos[index], index,
- IsMainWorkerScript(), this);
- mCacheCreator->AddLoader(loader);
- }
- // The worker may have a null principal on first load, but in that case its
- // parent definitely will have one.
- nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
- if (!principal) {
- WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
- MOZ_ASSERT(parentWorker, "Must have a parent!");
- principal = parentWorker->GetPrincipal();
- }
- nsresult rv = mCacheCreator->Load(principal);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- nsresult
- LoadScript(uint32_t aIndex)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(aIndex < mLoadInfos.Length());
- WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
- // Figure out which principal to use.
- nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
- nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
- if (!principal) {
- NS_ASSERTION(parentWorker, "Must have a principal!");
- NS_ASSERTION(mIsMainScript, "Must have a principal for importScripts!");
- principal = parentWorker->GetPrincipal();
- loadGroup = parentWorker->GetLoadGroup();
- }
- NS_ASSERTION(principal, "This should never be null here!");
- MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal));
- // Figure out our base URI.
- nsCOMPtr<nsIURI> baseURI = GetBaseURI(mIsMainScript, mWorkerPrivate);
- // May be null.
- nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
- nsCOMPtr<nsIChannel> channel;
- if (IsMainWorkerScript()) {
- // May be null.
- channel = mWorkerPrivate->ForgetWorkerChannel();
- }
- nsCOMPtr<nsIIOService> ios(do_GetIOService());
- nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
- NS_ASSERTION(secMan, "This should never be null!");
- ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
- nsresult& rv = loadInfo.mLoadResult;
- nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
- // Get the top-level worker.
- WorkerPrivate* topWorkerPrivate = mWorkerPrivate;
- WorkerPrivate* parent = topWorkerPrivate->GetParent();
- while (parent) {
- topWorkerPrivate = parent;
- parent = topWorkerPrivate->GetParent();
- }
- // If the top-level worker is a dedicated worker and has a window, and the
- // window has a docshell, the caching behavior of this worker should match
- // that of that docshell.
- if (topWorkerPrivate->IsDedicatedWorker()) {
- nsCOMPtr<nsPIDOMWindowInner> window = topWorkerPrivate->GetWindow();
- if (window) {
- nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
- if (docShell) {
- nsresult rv = docShell->GetDefaultLoadFlags(&loadFlags);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- }
- // If we are loading a script for a ServiceWorker then we must not
- // try to intercept it. If the interception matches the current
- // ServiceWorker's scope then we could deadlock the load.
- if (mWorkerPrivate->IsServiceWorker()) {
- loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
- }
- if (!channel) {
- // Only top level workers' main script use the document charset for the
- // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
- bool useDefaultEncoding = !(!parentWorker && IsMainWorkerScript());
- rv = ChannelFromScriptURL(principal, baseURI, parentDoc, loadGroup, ios,
- secMan, loadInfo.mURL, IsMainWorkerScript(),
- mWorkerScriptType,
- mWorkerPrivate->ContentPolicyType(), loadFlags,
- useDefaultEncoding,
- getter_AddRefs(channel));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- // We need to know which index we're on in OnStreamComplete so we know
- // where to put the result.
- RefPtr<LoaderListener> listener = new LoaderListener(this, aIndex);
- // We don't care about progress so just use the simple stream loader for
- // OnStreamComplete notification only.
- nsCOMPtr<nsIStreamLoader> loader;
- rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) {
- rv = channel->AsyncOpen2(loader);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- } else {
- nsCOMPtr<nsIOutputStream> writer;
- // In case we return early.
- loadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
- rv = NS_NewPipe(getter_AddRefs(loadInfo.mCacheReadStream),
- getter_AddRefs(writer), 0,
- UINT32_MAX, // unlimited size to avoid writer WOULD_BLOCK case
- true, false); // non-blocking reader, blocking writer
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIStreamListenerTee> tee =
- do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID);
- rv = tee->Init(loader, writer, listener);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsresult rv = channel->AsyncOpen2(tee);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- loadInfo.mChannel.swap(channel);
- return NS_OK;
- }
- nsresult
- OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsresult aStatus,
- uint32_t aStringLen, const uint8_t* aString,
- ScriptLoadInfo& aLoadInfo)
- {
- AssertIsOnMainThread();
- if (!aLoadInfo.mChannel) {
- return NS_BINDING_ABORTED;
- }
- aLoadInfo.mChannel = nullptr;
- if (NS_FAILED(aStatus)) {
- return aStatus;
- }
- NS_ASSERTION(aString, "This should never be null!");
- nsCOMPtr<nsIRequest> request;
- nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
- MOZ_ASSERT(channel);
- nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
- NS_ASSERTION(ssm, "Should never be null!");
- nsCOMPtr<nsIPrincipal> channelPrincipal;
- rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
- if (!principal) {
- WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
- MOZ_ASSERT(parentWorker, "Must have a parent!");
- principal = parentWorker->GetPrincipal();
- }
- // We don't mute the main worker script becase we've already done
- // same-origin checks on them so we should be able to see their errors.
- // Note that for data: url, where we allow it through the same-origin check
- // but then give it a different origin.
- aLoadInfo.mMutedErrorFlag.emplace(IsMainWorkerScript()
- ? false
- : !principal->Subsumes(channelPrincipal));
- // Make sure we're not seeing the result of a 404 or something by checking
- // the 'requestSucceeded' attribute on the http channel.
- nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
- nsAutoCString tCspHeaderValue, tCspROHeaderValue, tRPHeaderCValue;
- if (httpChannel) {
- bool requestSucceeded;
- rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!requestSucceeded) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- httpChannel->GetResponseHeader(
- NS_LITERAL_CSTRING("content-security-policy"),
- tCspHeaderValue);
- httpChannel->GetResponseHeader(
- NS_LITERAL_CSTRING("content-security-policy-report-only"),
- tCspROHeaderValue);
- httpChannel->GetResponseHeader(
- NS_LITERAL_CSTRING("referrer-policy"),
- tRPHeaderCValue);
- }
- // May be null.
- nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
- // Use the regular ScriptLoader for this grunt work! Should be just fine
- // because we're running on the main thread.
- // Unlike <script> tags, Worker scripts are always decoded as UTF-8,
- // per spec. So we explicitly pass in the charset hint.
- rv = ScriptLoader::ConvertToUTF16(aLoadInfo.mChannel, aString, aStringLen,
- NS_LITERAL_STRING("UTF-8"), parentDoc,
- aLoadInfo.mScriptTextBuf,
- aLoadInfo.mScriptTextLength);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!aLoadInfo.mScriptTextLength && !aLoadInfo.mScriptTextBuf) {
- nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
- NS_LITERAL_CSTRING("DOM"), parentDoc,
- nsContentUtils::eDOM_PROPERTIES,
- "EmptyWorkerSourceWarning");
- } else if (!aLoadInfo.mScriptTextBuf) {
- return NS_ERROR_FAILURE;
- }
- // Figure out what we actually loaded.
- nsCOMPtr<nsIURI> finalURI;
- rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
- NS_ENSURE_SUCCESS(rv, rv);
- if (principal->Subsumes(channelPrincipal)) {
- nsCString filename;
- rv = finalURI->GetSpec(filename);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!filename.IsEmpty()) {
- // This will help callers figure out what their script url resolved to in
- // case of errors.
- aLoadInfo.mURL.Assign(NS_ConvertUTF8toUTF16(filename));
- }
- }
- nsCOMPtr<nsILoadInfo> chanLoadInfo = channel->GetLoadInfo();
- if (chanLoadInfo && chanLoadInfo->GetEnforceSRI()) {
- // importScripts() and the Worker constructor do not support integrity metadata
- // (or any fetch options). Until then, we can just block.
- // If we ever have those data in the future, we'll have to the check to
- // by using the SRICheck module
- MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
- ("Scriptloader::Load, SRI required but not supported in workers"));
- nsCOMPtr<nsIContentSecurityPolicy> wcsp;
- chanLoadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(wcsp));
- MOZ_ASSERT(wcsp, "We sould have a CSP for the worker here");
- if (wcsp) {
- wcsp->LogViolationDetails(
- nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
- aLoadInfo.mURL, EmptyString(), 0, EmptyString(), EmptyString());
- }
- return NS_ERROR_SRI_CORRUPT;
- }
- // Update the principal of the worker and its base URI if we just loaded the
- // worker's primary script.
- if (IsMainWorkerScript()) {
- // Take care of the base URI first.
- mWorkerPrivate->SetBaseURI(finalURI);
- // Store the channel info if needed.
- mWorkerPrivate->InitChannelInfo(channel);
- // Now to figure out which principal to give this worker.
- WorkerPrivate* parent = mWorkerPrivate->GetParent();
- NS_ASSERTION(mWorkerPrivate->GetPrincipal() || parent,
- "Must have one of these!");
- nsCOMPtr<nsIPrincipal> loadPrincipal = mWorkerPrivate->GetPrincipal() ?
- mWorkerPrivate->GetPrincipal() :
- parent->GetPrincipal();
- nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
- NS_ASSERTION(ssm, "Should never be null!");
- nsCOMPtr<nsIPrincipal> channelPrincipal;
- rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsILoadGroup> channelLoadGroup;
- rv = channel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
- NS_ENSURE_SUCCESS(rv, rv);
- MOZ_ASSERT(channelLoadGroup);
- // If the load principal is the system principal then the channel
- // principal must also be the system principal (we do not allow chrome
- // code to create workers with non-chrome scripts, and if we ever decide
- // to change this we need to make sure we don't always set
- // mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
- // this channel principal must be same origin with the load principal (we
- // check again here in case redirects changed the location of the script).
- if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
- if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
- // See if this is a resource URI. Since JSMs usually come from
- // resource:// URIs we're currently considering all URIs with the
- // URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
- bool isResource;
- rv = NS_URIChainHasFlags(finalURI,
- nsIProtocolHandler::URI_IS_UI_RESOURCE,
- &isResource);
- NS_ENSURE_SUCCESS(rv, rv);
- if (isResource) {
- // Assign the system principal to the resource:// worker only if it
- // was loaded from code using the system principal.
- channelPrincipal = loadPrincipal;
- } else {
- return NS_ERROR_DOM_BAD_URI;
- }
- }
- }
- // The principal can change, but it should still match the original
- // load group's appId and browser element flag.
- MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup, channelPrincipal));
- mWorkerPrivate->SetPrincipal(channelPrincipal, channelLoadGroup);
- // We did inherit CSP in bug 1223647. If we do not already have a CSP, we
- // should get it from the HTTP headers on the worker script.
- if (!mWorkerPrivate->GetCSP() && CSPService::sCSPEnabled) {
- NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
- NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
- nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
- MOZ_ASSERT(principal, "Should not be null");
- nsCOMPtr<nsIContentSecurityPolicy> csp;
- rv = principal->EnsureCSP(nullptr, getter_AddRefs(csp));
- if (csp) {
- // If there's a CSP header, apply it.
- if (!cspHeaderValue.IsEmpty()) {
- rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // If there's a report-only CSP header, apply it.
- if (!cspROHeaderValue.IsEmpty()) {
- rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Set evalAllowed, default value is set in GetAllowsEval
- bool evalAllowed = false;
- bool reportEvalViolations = false;
- rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
- NS_ENSURE_SUCCESS(rv, rv);
- mWorkerPrivate->SetCSP(csp);
- mWorkerPrivate->SetEvalAllowed(evalAllowed);
- mWorkerPrivate->SetReportCSPViolations(reportEvalViolations);
- // Set ReferrerPolicy, default value is set in GetReferrerPolicy
- bool hasReferrerPolicy = false;
- uint32_t rp = mozilla::net::RP_Default;
- rv = csp->GetReferrerPolicy(&rp, &hasReferrerPolicy);
- NS_ENSURE_SUCCESS(rv, rv);
- if (hasReferrerPolicy) { //FIXME bug 1307366: move RP out of CSP code
- mWorkerPrivate->SetReferrerPolicy(static_cast<net::ReferrerPolicy>(rp));
- }
- }
- }
- if (parent) {
- // XHR Params Allowed
- mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
- }
- }
- NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
- // If there's a Referrer-Policy header, apply it.
- if (!tRPHeaderValue.IsEmpty()) {
- net::ReferrerPolicy policy =
- nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
- if (policy != net::RP_Unset) {
- mWorkerPrivate->SetReferrerPolicy(policy);
- }
- }
- return NS_OK;
- }
- void
- DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
- uint32_t aStringLen,
- const mozilla::dom::ChannelInfo& aChannelInfo,
- UniquePtr<PrincipalInfo> aPrincipalInfo)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(aIndex < mLoadInfos.Length());
- ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
- MOZ_ASSERT(loadInfo.mCacheStatus == ScriptLoadInfo::Cached);
- nsCOMPtr<nsIPrincipal> responsePrincipal =
- PrincipalInfoToPrincipal(*aPrincipalInfo);
- nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
- if (!principal) {
- WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
- MOZ_ASSERT(parentWorker, "Must have a parent!");
- principal = parentWorker->GetPrincipal();
- }
- loadInfo.mMutedErrorFlag.emplace(!principal->Subsumes(responsePrincipal));
- // May be null.
- nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
- MOZ_ASSERT(!loadInfo.mScriptTextBuf);
- nsresult rv =
- ScriptLoader::ConvertToUTF16(nullptr, aString, aStringLen,
- NS_LITERAL_STRING("UTF-8"), parentDoc,
- loadInfo.mScriptTextBuf,
- loadInfo.mScriptTextLength);
- if (NS_SUCCEEDED(rv) && IsMainWorkerScript()) {
- nsCOMPtr<nsIURI> finalURI;
- rv = NS_NewURI(getter_AddRefs(finalURI), loadInfo.mFullURL, nullptr, nullptr);
- if (NS_SUCCEEDED(rv)) {
- mWorkerPrivate->SetBaseURI(finalURI);
- }
- mozilla::DebugOnly<nsIPrincipal*> principal = mWorkerPrivate->GetPrincipal();
- MOZ_ASSERT(principal);
- nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
- MOZ_ASSERT(loadGroup);
- mozilla::DebugOnly<bool> equal = false;
- MOZ_ASSERT(responsePrincipal && NS_SUCCEEDED(responsePrincipal->Equals(principal, &equal)));
- MOZ_ASSERT(equal);
- mWorkerPrivate->InitChannelInfo(aChannelInfo);
- mWorkerPrivate->SetPrincipal(responsePrincipal, loadGroup);
- }
- if (NS_SUCCEEDED(rv)) {
- DataReceived();
- }
- LoadingFinished(aIndex, rv);
- }
- void
- DataReceived()
- {
- if (IsMainWorkerScript()) {
- WorkerPrivate* parent = mWorkerPrivate->GetParent();
- if (parent) {
- // XHR Params Allowed
- mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
- // Set Eval and ContentSecurityPolicy
- mWorkerPrivate->SetCSP(parent->GetCSP());
- mWorkerPrivate->SetEvalAllowed(parent->IsEvalAllowed());
- }
- }
- }
- void
- ExecuteFinishedScripts()
- {
- AssertIsOnMainThread();
- if (IsMainWorkerScript()) {
- mWorkerPrivate->WorkerScriptLoaded();
- }
- uint32_t firstIndex = UINT32_MAX;
- uint32_t lastIndex = UINT32_MAX;
- // Find firstIndex based on whether mExecutionScheduled is unset.
- for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
- if (!mLoadInfos[index].mExecutionScheduled) {
- firstIndex = index;
- break;
- }
- }
- // Find lastIndex based on whether mChannel is set, and update
- // mExecutionScheduled on the ones we're about to schedule.
- if (firstIndex != UINT32_MAX) {
- for (uint32_t index = firstIndex; index < mLoadInfos.Length(); index++) {
- ScriptLoadInfo& loadInfo = mLoadInfos[index];
- if (!loadInfo.Finished()) {
- break;
- }
- // We can execute this one.
- loadInfo.mExecutionScheduled = true;
- lastIndex = index;
- }
- }
- // This is the last index, we can unused things before the exection of the
- // script and the stopping of the sync loop.
- if (lastIndex == mLoadInfos.Length() - 1) {
- mCacheCreator = nullptr;
- }
- if (firstIndex != UINT32_MAX && lastIndex != UINT32_MAX) {
- RefPtr<ScriptExecutorRunnable> runnable =
- new ScriptExecutorRunnable(*this, mSyncLoopTarget, IsMainWorkerScript(),
- firstIndex, lastIndex);
- if (!runnable->Dispatch()) {
- MOZ_ASSERT(false, "This should never fail!");
- }
- }
- }
- };
- NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable)
- class MOZ_STACK_CLASS ScriptLoaderHolder final : public WorkerHolder
- {
- // Raw pointer because this holder object follows the mRunnable life-time.
- ScriptLoaderRunnable* mRunnable;
- public:
- explicit ScriptLoaderHolder(ScriptLoaderRunnable* aRunnable)
- : mRunnable(aRunnable)
- {
- MOZ_ASSERT(aRunnable);
- }
- virtual bool
- Notify(Status aStatus) override
- {
- mRunnable->Notify(aStatus);
- return true;
- }
- };
- NS_IMETHODIMP
- LoaderListener::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
- nsresult aStatus, uint32_t aStringLen,
- const uint8_t* aString)
- {
- return mRunnable->OnStreamComplete(aLoader, mIndex, aStatus, aStringLen, aString);
- }
- NS_IMETHODIMP
- LoaderListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
- {
- return mRunnable->OnStartRequest(aRequest, mIndex);
- }
- void
- CachePromiseHandler::ResolvedCallback(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
- {
- AssertIsOnMainThread();
- // May already have been canceled by CacheScriptLoader::Fail from
- // CancelMainThread.
- MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
- mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
- MOZ_ASSERT_IF(mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel, !mLoadInfo.mCachePromise);
- if (mLoadInfo.mCachePromise) {
- mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
- mLoadInfo.mCachePromise = nullptr;
- mRunnable->MaybeExecuteFinishedScripts(mIndex);
- }
- }
- void
- CachePromiseHandler::RejectedCallback(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
- {
- AssertIsOnMainThread();
- // May already have been canceled by CacheScriptLoader::Fail from
- // CancelMainThread.
- MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
- mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
- mLoadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
- mLoadInfo.mCachePromise = nullptr;
- // This will delete the cache object and will call LoadingFinished() with an
- // error for each ongoing operation.
- mRunnable->DeleteCache();
- }
- nsresult
- CacheCreator::CreateCacheStorage(nsIPrincipal* aPrincipal)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(!mCacheStorage);
- MOZ_ASSERT(aPrincipal);
- nsIXPConnect* xpc = nsContentUtils::XPConnect();
- MOZ_ASSERT(xpc, "This should never be null!");
- mozilla::AutoSafeJSContext cx;
- JS::Rooted<JSObject*> sandbox(cx);
- nsresult rv = xpc->CreateSandbox(cx, aPrincipal, sandbox.address());
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- mSandboxGlobalObject = xpc::NativeGlobal(sandbox);
- if (NS_WARN_IF(!mSandboxGlobalObject)) {
- return NS_ERROR_FAILURE;
- }
- // If we're in private browsing mode, don't even try to create the
- // CacheStorage. Instead, just fail immediately to terminate the
- // ServiceWorker load.
- if (NS_WARN_IF(mOriginAttributes.mPrivateBrowsingId > 0)) {
- return NS_ERROR_DOM_SECURITY_ERR;
- }
- // Create a CacheStorage bypassing its trusted origin checks. The
- // ServiceWorker has already performed its own checks before getting
- // to this point.
- ErrorResult error;
- mCacheStorage =
- CacheStorage::CreateOnMainThread(mozilla::dom::cache::CHROME_ONLY_NAMESPACE,
- mSandboxGlobalObject,
- aPrincipal,
- false, /* privateBrowsing can't be true here */
- true /* force trusted origin */,
- error);
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- return NS_OK;
- }
- nsresult
- CacheCreator::Load(nsIPrincipal* aPrincipal)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(!mLoaders.IsEmpty());
- nsresult rv = CreateCacheStorage(aPrincipal);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- ErrorResult error;
- MOZ_ASSERT(!mCacheName.IsEmpty());
- RefPtr<Promise> promise = mCacheStorage->Open(mCacheName, error);
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- promise->AppendNativeHandler(this);
- return NS_OK;
- }
- void
- CacheCreator::FailLoaders(nsresult aRv)
- {
- AssertIsOnMainThread();
- // Fail() can call LoadingFinished() which may call ExecuteFinishedScripts()
- // which sets mCacheCreator to null, so hold a ref.
- RefPtr<CacheCreator> kungfuDeathGrip = this;
- for (uint32_t i = 0, len = mLoaders.Length(); i < len; ++i) {
- mLoaders[i]->Fail(aRv);
- }
- mLoaders.Clear();
- }
- void
- CacheCreator::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
- {
- AssertIsOnMainThread();
- FailLoaders(NS_ERROR_FAILURE);
- }
- void
- CacheCreator::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
- {
- AssertIsOnMainThread();
- if (!aValue.isObject()) {
- FailLoaders(NS_ERROR_FAILURE);
- return;
- }
- JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
- Cache* cache = nullptr;
- nsresult rv = UNWRAP_OBJECT(Cache, &obj, cache);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- FailLoaders(NS_ERROR_FAILURE);
- return;
- }
- mCache = cache;
- MOZ_DIAGNOSTIC_ASSERT(mCache);
- // If the worker is canceled, CancelMainThread() will have cleared the
- // loaders via DeleteCache().
- for (uint32_t i = 0, len = mLoaders.Length(); i < len; ++i) {
- MOZ_DIAGNOSTIC_ASSERT(mLoaders[i]);
- mLoaders[i]->Load(cache);
- }
- }
- void
- CacheCreator::DeleteCache()
- {
- AssertIsOnMainThread();
- // This is called when the load is canceled which can occur before
- // mCacheStorage is initialized.
- if (mCacheStorage) {
- // It's safe to do this while Cache::Match() and Cache::Put() calls are
- // running.
- IgnoredErrorResult rv;
- RefPtr<Promise> promise = mCacheStorage->Delete(mCacheName, rv);
- // We don't care to know the result of the promise object.
- }
- // Always call this here to ensure the loaders array is cleared.
- FailLoaders(NS_ERROR_FAILURE);
- }
- void
- CacheScriptLoader::Fail(nsresult aRv)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(NS_FAILED(aRv));
- if (mFailed) {
- return;
- }
- mFailed = true;
- if (mPump) {
- MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
- mPump->Cancel(aRv);
- mPump = nullptr;
- }
- mLoadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
- // Stop if the load was aborted on the main thread.
- // Can't use Finished() because mCachePromise may still be true.
- if (mLoadInfo.mLoadingFinished) {
- MOZ_ASSERT(!mLoadInfo.mChannel);
- MOZ_ASSERT_IF(mLoadInfo.mCachePromise,
- mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
- mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
- return;
- }
- mRunnable->LoadingFinished(mIndex, aRv);
- }
- void
- CacheScriptLoader::Load(Cache* aCache)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(aCache);
- nsCOMPtr<nsIURI> uri;
- nsresult rv = NS_NewURI(getter_AddRefs(uri), mLoadInfo.mURL, nullptr,
- mBaseURI);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- Fail(rv);
- return;
- }
- nsAutoCString spec;
- rv = uri->GetSpec(spec);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- Fail(rv);
- return;
- }
- MOZ_ASSERT(mLoadInfo.mFullURL.IsEmpty());
- CopyUTF8toUTF16(spec, mLoadInfo.mFullURL);
- mozilla::dom::RequestOrUSVString request;
- request.SetAsUSVString().Rebind(mLoadInfo.mFullURL.Data(),
- mLoadInfo.mFullURL.Length());
- mozilla::dom::CacheQueryOptions params;
- ErrorResult error;
- RefPtr<Promise> promise = aCache->Match(request, params, error);
- if (NS_WARN_IF(error.Failed())) {
- Fail(error.StealNSResult());
- return;
- }
- promise->AppendNativeHandler(this);
- }
- void
- CacheScriptLoader::RejectedCallback(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
- {
- AssertIsOnMainThread();
- MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
- Fail(NS_ERROR_FAILURE);
- }
- void
- CacheScriptLoader::ResolvedCallback(JSContext* aCx,
- JS::Handle<JS::Value> aValue)
- {
- AssertIsOnMainThread();
- // If we have already called 'Fail', we should not proceed.
- if (mFailed) {
- return;
- }
- MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
- nsresult rv;
- if (aValue.isUndefined()) {
- mLoadInfo.mCacheStatus = ScriptLoadInfo::ToBeCached;
- rv = mRunnable->LoadScript(mIndex);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- Fail(rv);
- }
- return;
- }
- MOZ_ASSERT(aValue.isObject());
- JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
- mozilla::dom::Response* response = nullptr;
- rv = UNWRAP_OBJECT(Response, &obj, response);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- Fail(rv);
- return;
- }
- nsCOMPtr<nsIInputStream> inputStream;
- response->GetBody(getter_AddRefs(inputStream));
- mChannelInfo = response->GetChannelInfo();
- const UniquePtr<PrincipalInfo>& pInfo = response->GetPrincipalInfo();
- if (pInfo) {
- mPrincipalInfo = mozilla::MakeUnique<PrincipalInfo>(*pInfo);
- }
- if (!inputStream) {
- mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
- mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo,
- Move(mPrincipalInfo));
- return;
- }
- MOZ_ASSERT(!mPump);
- rv = NS_NewInputStreamPump(getter_AddRefs(mPump), inputStream);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- Fail(rv);
- return;
- }
- nsCOMPtr<nsIStreamLoader> loader;
- rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- Fail(rv);
- return;
- }
- rv = mPump->AsyncRead(loader, nullptr);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- mPump = nullptr;
- Fail(rv);
- return;
- }
- nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(mPump);
- if (rr) {
- nsCOMPtr<nsIEventTarget> sts =
- do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
- rv = rr->RetargetDeliveryTo(sts);
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
- }
- }
- mLoadInfo.mCacheStatus = ScriptLoadInfo::ReadingFromCache;
- }
- NS_IMETHODIMP
- CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
- nsresult aStatus, uint32_t aStringLen,
- const uint8_t* aString)
- {
- AssertIsOnMainThread();
- mPump = nullptr;
- if (NS_FAILED(aStatus)) {
- MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache ||
- mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
- Fail(aStatus);
- return NS_OK;
- }
- MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
- mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
- MOZ_ASSERT(mPrincipalInfo);
- mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo,
- Move(mPrincipalInfo));
- return NS_OK;
- }
- class ChannelGetterRunnable final : public WorkerMainThreadRunnable
- {
- const nsAString& mScriptURL;
- nsIChannel** mChannel;
- nsresult mResult;
- public:
- ChannelGetterRunnable(WorkerPrivate* aParentWorker,
- const nsAString& aScriptURL,
- nsIChannel** aChannel)
- : WorkerMainThreadRunnable(aParentWorker,
- NS_LITERAL_CSTRING("ScriptLoader :: ChannelGetter"))
- , mScriptURL(aScriptURL)
- , mChannel(aChannel)
- , mResult(NS_ERROR_FAILURE)
- {
- MOZ_ASSERT(aParentWorker);
- aParentWorker->AssertIsOnWorkerThread();
- }
- virtual bool
- MainThreadRun() override
- {
- AssertIsOnMainThread();
- nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
- MOZ_ASSERT(principal);
- // Figure out our base URI.
- nsCOMPtr<nsIURI> baseURI = mWorkerPrivate->GetBaseURI();
- MOZ_ASSERT(baseURI);
- // May be null.
- nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
- nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
- nsCOMPtr<nsIChannel> channel;
- mResult =
- scriptloader::ChannelFromScriptURLMainThread(principal, baseURI,
- parentDoc, loadGroup,
- mScriptURL,
- // Nested workers are always dedicated.
- nsIContentPolicy::TYPE_INTERNAL_WORKER,
- // Nested workers use default uri encoding.
- true,
- getter_AddRefs(channel));
- if (NS_SUCCEEDED(mResult)) {
- channel.forget(mChannel);
- }
- return true;
- }
- nsresult
- GetResult() const
- {
- return mResult;
- }
- private:
- virtual ~ChannelGetterRunnable()
- { }
- };
- ScriptExecutorRunnable::ScriptExecutorRunnable(
- ScriptLoaderRunnable& aScriptLoader,
- nsIEventTarget* aSyncLoopTarget,
- bool aIsWorkerScript,
- uint32_t aFirstIndex,
- uint32_t aLastIndex)
- : MainThreadWorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncLoopTarget),
- mScriptLoader(aScriptLoader), mIsWorkerScript(aIsWorkerScript),
- mFirstIndex(aFirstIndex), mLastIndex(aLastIndex)
- {
- MOZ_ASSERT(aFirstIndex <= aLastIndex);
- MOZ_ASSERT(aLastIndex < aScriptLoader.mLoadInfos.Length());
- }
- bool
- ScriptExecutorRunnable::IsDebuggerRunnable() const
- {
- // ScriptExecutorRunnable is used to execute both worker and debugger scripts.
- // In the latter case, the runnable needs to be dispatched to the debugger
- // queue.
- return mScriptLoader.mWorkerScriptType == DebuggerScript;
- }
- bool
- ScriptExecutorRunnable::PreRun(WorkerPrivate* aWorkerPrivate)
- {
- aWorkerPrivate->AssertIsOnWorkerThread();
- if (!mIsWorkerScript) {
- return true;
- }
- if (!aWorkerPrivate->GetJSContext()) {
- return false;
- }
- MOZ_ASSERT(mFirstIndex == 0);
- MOZ_ASSERT(!mScriptLoader.mRv.Failed());
- AutoJSAPI jsapi;
- jsapi.Init();
- WorkerGlobalScope* globalScope =
- aWorkerPrivate->GetOrCreateGlobalScope(jsapi.cx());
- if (NS_WARN_IF(!globalScope)) {
- NS_WARNING("Failed to make global!");
- // There's no way to report the exception on jsapi right now, because there
- // is no way to even enter a compartment on this thread anymore. Just clear
- // the exception. We'll report some sort of error to our caller in
- // ShutdownScriptLoader, but it will get squelched for the same reason we're
- // squelching here: all the error reporting machinery relies on being able
- // to enter a compartment to report the error.
- jsapi.ClearException();
- return false;
- }
- return true;
- }
- bool
- ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
- {
- aWorkerPrivate->AssertIsOnWorkerThread();
- nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
- // Don't run if something else has already failed.
- for (uint32_t index = 0; index < mFirstIndex; index++) {
- ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
- NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
- NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
- if (!loadInfo.mExecutionResult) {
- return true;
- }
- }
- // If nothing else has failed, our ErrorResult better not be a failure either.
- MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
- // Slightly icky action at a distance, but there's no better place to stash
- // this value, really.
- JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
- MOZ_ASSERT(global);
- for (uint32_t index = mFirstIndex; index <= mLastIndex; index++) {
- ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
- NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
- NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
- NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
- MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
- mScriptLoader.mRv.MightThrowJSException();
- if (NS_FAILED(loadInfo.mLoadResult)) {
- scriptloader::ReportLoadError(mScriptLoader.mRv,
- loadInfo.mLoadResult, loadInfo.mURL);
- // Top level scripts only!
- if (mIsWorkerScript) {
- aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
- }
- return true;
- }
- NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
- JS::CompileOptions options(aCx);
- options.setFileAndLine(filename.get(), 1)
- .setNoScriptRval(true);
- if (mScriptLoader.mWorkerScriptType == DebuggerScript) {
- options.setVersion(JSVERSION_LATEST);
- }
- MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
- options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
- JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
- loadInfo.mScriptTextLength,
- JS::SourceBufferHolder::GiveOwnership);
- loadInfo.mScriptTextBuf = nullptr;
- loadInfo.mScriptTextLength = 0;
- // Our ErrorResult still shouldn't be a failure.
- MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
- JS::Rooted<JS::Value> unused(aCx);
- if (!JS::Evaluate(aCx, options, srcBuf, &unused)) {
- mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
- return true;
- }
- loadInfo.mExecutionResult = true;
- }
- return true;
- }
- void
- ScriptExecutorRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
- bool aRunResult)
- {
- aWorkerPrivate->AssertIsOnWorkerThread();
- MOZ_ASSERT(!JS_IsExceptionPending(aCx), "Who left an exception on there?");
- nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
- if (mLastIndex == loadInfos.Length() - 1) {
- // All done. If anything failed then return false.
- bool result = true;
- bool mutedError = false;
- for (uint32_t index = 0; index < loadInfos.Length(); index++) {
- if (!loadInfos[index].mExecutionResult) {
- mutedError = loadInfos[index].mMutedErrorFlag.valueOr(true);
- result = false;
- break;
- }
- }
- // The only way we can get here with "result" false but without
- // mScriptLoader.mRv being a failure is if we're loading the main worker
- // script and GetOrCreateGlobalScope() fails. In that case we would have
- // returned false from WorkerRun, so assert that.
- MOZ_ASSERT_IF(!result && !mScriptLoader.mRv.Failed(),
- !aRunResult);
- ShutdownScriptLoader(aCx, aWorkerPrivate, result, mutedError);
- }
- }
- nsresult
- ScriptExecutorRunnable::Cancel()
- {
- if (mLastIndex == mScriptLoader.mLoadInfos.Length() - 1) {
- ShutdownScriptLoader(mWorkerPrivate->GetJSContext(), mWorkerPrivate,
- false, false);
- }
- return MainThreadWorkerSyncRunnable::Cancel();
- }
- void
- ScriptExecutorRunnable::ShutdownScriptLoader(JSContext* aCx,
- WorkerPrivate* aWorkerPrivate,
- bool aResult,
- bool aMutedError)
- {
- aWorkerPrivate->AssertIsOnWorkerThread();
- MOZ_ASSERT(mLastIndex == mScriptLoader.mLoadInfos.Length() - 1);
- if (mIsWorkerScript && aWorkerPrivate->IsServiceWorker()) {
- aWorkerPrivate->SetLoadingWorkerScript(false);
- }
- if (!aResult) {
- // At this point there are two possibilities:
- //
- // 1) mScriptLoader.mRv.Failed(). In that case we just want to leave it
- // as-is, except if it has a JS exception and we need to mute JS
- // exceptions. In that case, we log the exception without firing any
- // events and then replace it on the ErrorResult with a NetworkError,
- // per spec.
- //
- // 2) mScriptLoader.mRv succeeded. As far as I can tell, this can only
- // happen when loading the main worker script and
- // GetOrCreateGlobalScope() fails or if ScriptExecutorRunnable::Cancel
- // got called. Does it matter what we throw in this case? I'm not
- // sure...
- if (mScriptLoader.mRv.Failed()) {
- if (aMutedError && mScriptLoader.mRv.IsJSException()) {
- LogExceptionToConsole(aCx, aWorkerPrivate);
- mScriptLoader.mRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
- }
- } else {
- mScriptLoader.mRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
- }
- }
- aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, aResult);
- }
- void
- ScriptExecutorRunnable::LogExceptionToConsole(JSContext* aCx,
- WorkerPrivate* aWorkerPrivate)
- {
- aWorkerPrivate->AssertIsOnWorkerThread();
- MOZ_ASSERT(mScriptLoader.mRv.IsJSException());
- JS::Rooted<JS::Value> exn(aCx);
- if (!ToJSValue(aCx, mScriptLoader.mRv, &exn)) {
- return;
- }
- // Now the exception state should all be in exn.
- MOZ_ASSERT(!JS_IsExceptionPending(aCx));
- MOZ_ASSERT(!mScriptLoader.mRv.Failed());
- js::ErrorReport report(aCx);
- if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
- JS_ClearPendingException(aCx);
- return;
- }
- RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
- xpcReport->Init(report.report(), report.toStringResult().c_str(),
- aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
- RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
- NS_DispatchToMainThread(r);
- }
- void
- LoadAllScripts(WorkerPrivate* aWorkerPrivate,
- nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsMainScript,
- WorkerScriptType aWorkerScriptType, ErrorResult& aRv)
- {
- aWorkerPrivate->AssertIsOnWorkerThread();
- NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
- AutoSyncLoopHolder syncLoop(aWorkerPrivate, Terminating);
- nsCOMPtr<nsIEventTarget> syncLoopTarget = syncLoop.GetEventTarget();
- if (!syncLoopTarget) {
- aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
- return;
- }
- RefPtr<ScriptLoaderRunnable> loader =
- new ScriptLoaderRunnable(aWorkerPrivate, syncLoopTarget, aLoadInfos,
- aIsMainScript, aWorkerScriptType, aRv);
- NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
- ScriptLoaderHolder workerHolder(loader);
- if (NS_WARN_IF(!workerHolder.HoldWorker(aWorkerPrivate, Terminating))) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- if (NS_FAILED(NS_DispatchToMainThread(loader))) {
- NS_ERROR("Failed to dispatch!");
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- syncLoop.Run();
- }
- } /* anonymous namespace */
- BEGIN_WORKERS_NAMESPACE
- namespace scriptloader {
- nsresult
- ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
- nsIURI* aBaseURI,
- nsIDocument* aParentDoc,
- nsILoadGroup* aLoadGroup,
- const nsAString& aScriptURL,
- nsContentPolicyType aContentPolicyType,
- bool aDefaultURIEncoding,
- nsIChannel** aChannel)
- {
- AssertIsOnMainThread();
- nsCOMPtr<nsIIOService> ios(do_GetIOService());
- nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
- NS_ASSERTION(secMan, "This should never be null!");
- return ChannelFromScriptURL(aPrincipal, aBaseURI, aParentDoc, aLoadGroup,
- ios, secMan, aScriptURL, true, WorkerScript,
- aContentPolicyType, nsIRequest::LOAD_NORMAL,
- aDefaultURIEncoding, aChannel);
- }
- nsresult
- ChannelFromScriptURLWorkerThread(JSContext* aCx,
- WorkerPrivate* aParent,
- const nsAString& aScriptURL,
- nsIChannel** aChannel)
- {
- aParent->AssertIsOnWorkerThread();
- RefPtr<ChannelGetterRunnable> getter =
- new ChannelGetterRunnable(aParent, aScriptURL, aChannel);
- ErrorResult rv;
- getter->Dispatch(Terminating, rv);
- if (rv.Failed()) {
- NS_ERROR("Failed to dispatch!");
- return rv.StealNSResult();
- }
- return getter->GetResult();
- }
- void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult,
- const nsAString& aScriptURL)
- {
- MOZ_ASSERT(!aRv.Failed());
- switch (aLoadResult) {
- case NS_ERROR_FILE_NOT_FOUND:
- case NS_ERROR_NOT_AVAILABLE:
- aLoadResult = NS_ERROR_DOM_NETWORK_ERR;
- break;
- case NS_ERROR_MALFORMED_URI:
- aLoadResult = NS_ERROR_DOM_SYNTAX_ERR;
- break;
- case NS_BINDING_ABORTED:
- // Note: we used to pretend like we didn't set an exception for
- // NS_BINDING_ABORTED, but then ShutdownScriptLoader did it anyway. The
- // other callsite, in WorkerPrivate::Constructor, never passed in
- // NS_BINDING_ABORTED. So just throw it directly here. Consumers will
- // deal as needed. But note that we do NOT want to ThrowDOMException()
- // for this case, because that will make it impossible for consumers to
- // realize that our error was NS_BINDING_ABORTED.
- aRv.Throw(aLoadResult);
- return;
- case NS_ERROR_DOM_SECURITY_ERR:
- case NS_ERROR_DOM_SYNTAX_ERR:
- break;
- case NS_ERROR_DOM_BAD_URI:
- // This is actually a security error.
- aLoadResult = NS_ERROR_DOM_SECURITY_ERR;
- break;
- default:
- // For lack of anything better, go ahead and throw a NetworkError here.
- // We don't want to throw a JS exception, because for toplevel script
- // loads that would get squelched.
- aRv.ThrowDOMException(NS_ERROR_DOM_NETWORK_ERR,
- nsPrintfCString("Failed to load worker script at %s (nsresult = 0x%x)",
- NS_ConvertUTF16toUTF8(aScriptURL).get(),
- aLoadResult));
- return;
- }
- aRv.ThrowDOMException(aLoadResult,
- NS_LITERAL_CSTRING("Failed to load worker script at \"") +
- NS_ConvertUTF16toUTF8(aScriptURL) +
- NS_LITERAL_CSTRING("\""));
- }
- void
- LoadMainScript(WorkerPrivate* aWorkerPrivate,
- const nsAString& aScriptURL,
- WorkerScriptType aWorkerScriptType,
- ErrorResult& aRv)
- {
- nsTArray<ScriptLoadInfo> loadInfos;
- ScriptLoadInfo* info = loadInfos.AppendElement();
- info->mURL = aScriptURL;
- LoadAllScripts(aWorkerPrivate, loadInfos, true, aWorkerScriptType, aRv);
- }
- void
- Load(WorkerPrivate* aWorkerPrivate,
- const nsTArray<nsString>& aScriptURLs, WorkerScriptType aWorkerScriptType,
- ErrorResult& aRv)
- {
- const uint32_t urlCount = aScriptURLs.Length();
- if (!urlCount) {
- return;
- }
- if (urlCount > MAX_CONCURRENT_SCRIPTS) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
- nsTArray<ScriptLoadInfo> loadInfos;
- loadInfos.SetLength(urlCount);
- for (uint32_t index = 0; index < urlCount; index++) {
- loadInfos[index].mURL = aScriptURLs[index];
- }
- LoadAllScripts(aWorkerPrivate, loadInfos, false, aWorkerScriptType, aRv);
- }
- } // namespace scriptloader
- END_WORKERS_NAMESPACE
|