diff options
author | Moonchild <moonchild@palemoon.org> | 2020-06-30 11:32:07 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2020-07-10 18:28:50 +0000 |
commit | 0633844f46858135ee62d396829c6292492ca117 (patch) | |
tree | 051f8c4a50ab5c955f5d4e70e4de0ea50151758e /dom/base | |
parent | d0126b96cd6527e6dc89530b333fb0c196aba30d (diff) | |
download | UXP-0633844f46858135ee62d396829c6292492ca117.tar UXP-0633844f46858135ee62d396829c6292492ca117.tar.gz UXP-0633844f46858135ee62d396829c6292492ca117.tar.lz UXP-0633844f46858135ee62d396829c6292492ca117.tar.xz UXP-0633844f46858135ee62d396829c6292492ca117.zip |
Issue #1603 - Part 1: Reorganize ScriptLoader/ScriptElement
- Moves scripting parts of DOM into 'dom/script'
- Renames nsScript{Loader/Element} to Script{Loader/Element}
- Adjusts all callers
Diffstat (limited to 'dom/base')
-rw-r--r-- | dom/base/ImportManager.cpp | 8 | ||||
-rw-r--r-- | dom/base/ImportManager.h | 8 | ||||
-rw-r--r-- | dom/base/Location.cpp | 2 | ||||
-rw-r--r-- | dom/base/ScriptSettings.cpp | 839 | ||||
-rw-r--r-- | dom/base/ScriptSettings.h | 465 | ||||
-rwxr-xr-x | dom/base/moz.build | 7 | ||||
-rw-r--r-- | dom/base/nsContentPermissionHelper.cpp | 3 | ||||
-rw-r--r-- | dom/base/nsContentSink.cpp | 2 | ||||
-rw-r--r-- | dom/base/nsContentSink.h | 7 | ||||
-rw-r--r-- | dom/base/nsDocument.cpp | 4 | ||||
-rw-r--r-- | dom/base/nsDocument.h | 6 | ||||
-rw-r--r-- | dom/base/nsFrameMessageManager.cpp | 8 | ||||
-rw-r--r-- | dom/base/nsGlobalWindow.cpp | 2 | ||||
-rw-r--r-- | dom/base/nsIDocument.h | 4 | ||||
-rw-r--r-- | dom/base/nsIScriptElement.h | 329 | ||||
-rw-r--r-- | dom/base/nsIScriptLoaderObserver.idl | 47 | ||||
-rw-r--r-- | dom/base/nsInProcessTabChildGlobal.cpp | 2 | ||||
-rw-r--r-- | dom/base/nsJSEnvironment.cpp | 2 | ||||
-rw-r--r-- | dom/base/nsScriptElement.cpp | 150 | ||||
-rw-r--r-- | dom/base/nsScriptElement.h | 52 | ||||
-rw-r--r-- | dom/base/nsScriptLoader.cpp | 3061 | ||||
-rw-r--r-- | dom/base/nsScriptLoader.h | 717 |
22 files changed, 30 insertions, 5695 deletions
diff --git a/dom/base/ImportManager.cpp b/dom/base/ImportManager.cpp index d0e514b59..1f4d376b3 100644 --- a/dom/base/ImportManager.cpp +++ b/dom/base/ImportManager.cpp @@ -6,6 +6,7 @@ #include "ImportManager.h" +#include "mozilla/dom/ScriptLoader.h" #include "mozilla/EventListenerManager.h" #include "HTMLLinkElement.h" #include "nsContentPolicyUtils.h" @@ -18,7 +19,6 @@ #include "nsIDOMEvent.h" #include "nsIPrincipal.h" #include "nsIScriptObjectPrincipal.h" -#include "nsScriptLoader.h" #include "nsNetUtil.h" //----------------------------------------------------------------------------- @@ -156,7 +156,7 @@ ImportLoader::Updater::UpdateMainReferrer(uint32_t aNewIdx) // Our nearest predecessor has changed. So let's add the ScriptLoader to the // new one if there is any. And remove it from the old one. RefPtr<ImportManager> manager = mLoader->Manager(); - nsScriptLoader* loader = mLoader->mDocument->ScriptLoader(); + ScriptLoader* loader = mLoader->mDocument->ScriptLoader(); ImportLoader*& pred = mLoader->mBlockingPredecessor; ImportLoader* newPred = manager->GetNearestPredecessor(newMainReferrer); if (pred) { @@ -339,7 +339,7 @@ ImportLoader::DispatchEventIfFinished(nsINode* aNode) } void -ImportLoader::AddBlockedScriptLoader(nsScriptLoader* aScriptLoader) +ImportLoader::AddBlockedScriptLoader(ScriptLoader* aScriptLoader) { if (mBlockedScriptLoaders.Contains(aScriptLoader)) { return; @@ -352,7 +352,7 @@ ImportLoader::AddBlockedScriptLoader(nsScriptLoader* aScriptLoader) } bool -ImportLoader::RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader) +ImportLoader::RemoveBlockedScriptLoader(ScriptLoader* aScriptLoader) { aScriptLoader->RemoveParserBlockingScriptExecutionBlocker(); return mBlockedScriptLoaders.RemoveElement(aScriptLoader); diff --git a/dom/base/ImportManager.h b/dom/base/ImportManager.h index 258d4691c..ccc00125a 100644 --- a/dom/base/ImportManager.h +++ b/dom/base/ImportManager.h @@ -45,8 +45,8 @@ #include "nsIStreamListener.h" #include "nsIWeakReferenceUtils.h" #include "nsRefPtrHashtable.h" -#include "nsScriptLoader.h" #include "nsURIHashKey.h" +#include "mozilla/dom/ScriptLoader.h" class nsIDocument; class nsIPrincipal; @@ -184,8 +184,8 @@ public: // and wait for that to run its scripts. We keep track of all the // ScriptRunners that are waiting for this import. NOTE: updating // the main referrer might change this list. - void AddBlockedScriptLoader(nsScriptLoader* aScriptLoader); - bool RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader); + void AddBlockedScriptLoader(ScriptLoader* aScriptLoader); + bool RemoveBlockedScriptLoader(ScriptLoader* aScriptLoader); void SetBlockingPredecessor(ImportLoader* aLoader); private: @@ -230,7 +230,7 @@ private: // List of pending ScriptLoaders that are waiting for this import // to finish. - nsTArray<RefPtr<nsScriptLoader>> mBlockedScriptLoaders; + nsTArray<RefPtr<ScriptLoader>> mBlockedScriptLoaders; // There is always exactly one referrer link that is flagged as // the main referrer the primary link. This is the one that is diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp index 1483c32f9..308e9a4ff 100644 --- a/dom/base/Location.cpp +++ b/dom/base/Location.cpp @@ -32,9 +32,9 @@ #include "mozilla/Likely.h" #include "nsCycleCollectionParticipant.h" #include "nsNullPrincipal.h" -#include "ScriptSettings.h" #include "mozilla/Unused.h" #include "mozilla/dom/LocationBinding.h" +#include "mozilla/dom/ScriptSettings.h" namespace mozilla { namespace dom { diff --git a/dom/base/ScriptSettings.cpp b/dom/base/ScriptSettings.cpp deleted file mode 100644 index 92ab221c9..000000000 --- a/dom/base/ScriptSettings.cpp +++ /dev/null @@ -1,839 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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 "mozilla/dom/ScriptSettings.h" -#include "mozilla/ThreadLocal.h" -#include "mozilla/Assertions.h" -#include "mozilla/CycleCollectedJSContext.h" - -#include "jsapi.h" -#include "xpcpublic.h" -#include "nsIGlobalObject.h" -#include "nsIDocShell.h" -#include "nsIScriptGlobalObject.h" -#include "nsIScriptContext.h" -#include "nsContentUtils.h" -#include "nsGlobalWindow.h" -#include "nsPIDOMWindow.h" -#include "nsTArray.h" -#include "nsJSUtils.h" -#include "nsDOMJSUtils.h" -#include "WorkerPrivate.h" - -namespace mozilla { -namespace dom { - -static MOZ_THREAD_LOCAL(ScriptSettingsStackEntry*) sScriptSettingsTLS; -static bool sScriptSettingsTLSInitialized; - -class ScriptSettingsStack { -public: - static ScriptSettingsStackEntry* Top() { - return sScriptSettingsTLS.get(); - } - - static void Push(ScriptSettingsStackEntry *aEntry) { - MOZ_ASSERT(!aEntry->mOlder); - // Whenever JSAPI use is disabled, the next stack entry pushed must - // not be an AutoIncumbentScript. - MOZ_ASSERT_IF(!Top() || Top()->NoJSAPI(), - !aEntry->IsIncumbentScript()); - // Whenever the top entry is not an incumbent canidate, the next stack entry - // pushed must not be an AutoIncumbentScript. - MOZ_ASSERT_IF(Top() && !Top()->IsIncumbentCandidate(), - !aEntry->IsIncumbentScript()); - - aEntry->mOlder = Top(); - sScriptSettingsTLS.set(aEntry); - } - - static void Pop(ScriptSettingsStackEntry *aEntry) { - MOZ_ASSERT(aEntry == Top()); - sScriptSettingsTLS.set(aEntry->mOlder); - } - - static nsIGlobalObject* IncumbentGlobal() { - ScriptSettingsStackEntry *entry = Top(); - while (entry) { - if (entry->IsIncumbentCandidate()) { - return entry->mGlobalObject; - } - entry = entry->mOlder; - } - return nullptr; - } - - static ScriptSettingsStackEntry* EntryPoint() { - ScriptSettingsStackEntry *entry = Top(); - while (entry) { - if (entry->IsEntryCandidate()) { - return entry; - } - entry = entry->mOlder; - } - return nullptr; - } - - static nsIGlobalObject* EntryGlobal() { - ScriptSettingsStackEntry *entry = EntryPoint(); - if (!entry) { - return nullptr; - } - return entry->mGlobalObject; - } - -#ifdef DEBUG - static ScriptSettingsStackEntry* TopNonIncumbentScript() { - ScriptSettingsStackEntry *entry = Top(); - while (entry) { - if (!entry->IsIncumbentScript()) { - return entry; - } - entry = entry->mOlder; - } - return nullptr; - } -#endif // DEBUG - -}; - -static unsigned long gRunToCompletionListeners = 0; - -void -UseEntryScriptProfiling() -{ - MOZ_ASSERT(NS_IsMainThread()); - ++gRunToCompletionListeners; -} - -void -UnuseEntryScriptProfiling() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(gRunToCompletionListeners > 0); - --gRunToCompletionListeners; -} - -void -InitScriptSettings() -{ - bool success = sScriptSettingsTLS.init(); - if (!success) { - MOZ_CRASH(); - } - - sScriptSettingsTLS.set(nullptr); - sScriptSettingsTLSInitialized = true; -} - -void -DestroyScriptSettings() -{ - MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr); -} - -bool -ScriptSettingsInitialized() -{ - return sScriptSettingsTLSInitialized; -} - -ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, - Type aType) - : mGlobalObject(aGlobal) - , mType(aType) - , mOlder(nullptr) -{ - MOZ_ASSERT_IF(IsIncumbentCandidate() && !NoJSAPI(), mGlobalObject); - MOZ_ASSERT(!mGlobalObject || mGlobalObject->GetGlobalJSObject(), - "Must have an actual JS global for the duration on the stack"); - MOZ_ASSERT(!mGlobalObject || - JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()), - "No outer windows allowed"); -} - -ScriptSettingsStackEntry::~ScriptSettingsStackEntry() -{ - // We must have an actual JS global for the entire time this is on the stack. - MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject()); -} - -// If the entry or incumbent global ends up being something that the subject -// principal doesn't subsume, we don't want to use it. This never happens on -// the web, but can happen with asymmetric privilege relationships (i.e. -// nsExpandedPrincipal and System Principal). -// -// The most correct thing to use instead would be the topmost global on the -// callstack whose principal is subsumed by the subject principal. But that's -// hard to compute, so we just substitute the global of the current -// compartment. In practice, this is fine. -// -// Note that in particular things like: -// -// |SpecialPowers.wrap(crossOriginWindow).eval(open())| -// -// trigger this case. Although both the entry global and the current global -// have normal principals, the use of Gecko-specific System-Principaled JS -// puts the code from two different origins on the callstack at once, which -// doesn't happen normally on the web. -static nsIGlobalObject* -ClampToSubject(nsIGlobalObject* aGlobalOrNull) -{ - if (!aGlobalOrNull || !NS_IsMainThread()) { - return aGlobalOrNull; - } - - nsIPrincipal* globalPrin = aGlobalOrNull->PrincipalOrNull(); - NS_ENSURE_TRUE(globalPrin, GetCurrentGlobal()); - if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->SubsumesConsideringDomain(globalPrin)) { - return GetCurrentGlobal(); - } - - return aGlobalOrNull; -} - -nsIGlobalObject* -GetEntryGlobal() -{ - return ClampToSubject(ScriptSettingsStack::EntryGlobal()); -} - -nsIDocument* -GetEntryDocument() -{ - nsIGlobalObject* global = GetEntryGlobal(); - nsCOMPtr<nsPIDOMWindowInner> entryWin = do_QueryInterface(global); - - // If our entry global isn't a window, see if it's an addon scope associated - // with a window. If it is, the caller almost certainly wants that rather - // than null. - if (!entryWin && global) { - if (auto* win = xpc::AddonWindowOrNull(global->GetGlobalJSObject())) { - entryWin = win->AsInner(); - } - } - - return entryWin ? entryWin->GetExtantDoc() : nullptr; -} - -nsIGlobalObject* -GetIncumbentGlobal() -{ - // We need the current JSContext in order to check the JS for - // scripted frames that may have appeared since anyone last - // manipulated the stack. If it's null, that means that there - // must be no entry global on the stack, and therefore no incumbent - // global either. - JSContext *cx = nsContentUtils::GetCurrentJSContextForThread(); - if (!cx) { - MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr); - return nullptr; - } - - // See what the JS engine has to say. If we've got a scripted caller - // override in place, the JS engine will lie to us and pretend that - // there's nothing on the JS stack, which will cause us to check the - // incumbent script stack below. - if (JSObject *global = JS::GetScriptedCallerGlobal(cx)) { - return ClampToSubject(xpc::NativeGlobal(global)); - } - - // Ok, nothing from the JS engine. Let's use whatever's on the - // explicit stack. - return ClampToSubject(ScriptSettingsStack::IncumbentGlobal()); -} - -nsIGlobalObject* -GetCurrentGlobal() -{ - JSContext *cx = nsContentUtils::GetCurrentJSContextForThread(); - if (!cx) { - return nullptr; - } - - JSObject *global = JS::CurrentGlobalOrNull(cx); - if (!global) { - return nullptr; - } - - return xpc::NativeGlobal(global); -} - -nsIPrincipal* -GetWebIDLCallerPrincipal() -{ - MOZ_ASSERT(NS_IsMainThread()); - ScriptSettingsStackEntry *entry = ScriptSettingsStack::EntryPoint(); - - // If we have an entry point that is not NoJSAPI, we know it must be an - // AutoEntryScript. - if (!entry || entry->NoJSAPI()) { - return nullptr; - } - AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry); - - return aes->mWebIDLCallerPrincipal; -} - -bool -IsJSAPIActive() -{ - ScriptSettingsStackEntry* topEntry = ScriptSettingsStack::Top(); - return topEntry && !topEntry->NoJSAPI(); -} - -namespace danger { -JSContext* -GetJSContext() -{ - return CycleCollectedJSContext::Get()->Context(); -} -} // namespace danger - -JS::RootingContext* -RootingCx() -{ - return CycleCollectedJSContext::Get()->RootingCx(); -} - -AutoJSAPI::AutoJSAPI() - : ScriptSettingsStackEntry(nullptr, eJSAPI) - , mCx(nullptr) - , mIsMainThread(false) // For lack of anything better -{ -} - -AutoJSAPI::~AutoJSAPI() -{ - if (!mCx) { - // No need to do anything here: we never managed to Init, so can't have an - // exception on our (nonexistent) JSContext. We also don't need to restore - // any state on it. Finally, we never made it to pushing outselves onto the - // ScriptSettingsStack, so shouldn't pop. - MOZ_ASSERT(ScriptSettingsStack::Top() != this); - return; - } - - ReportException(); - - if (mOldWarningReporter.isSome()) { - JS::SetWarningReporter(cx(), mOldWarningReporter.value()); - } - - // Leave the request before popping. - if (mIsMainThread) { - mAutoRequest.reset(); - } - - ScriptSettingsStack::Pop(this); -} - -void -WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep); - -void -AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal, - JSContext* aCx, bool aIsMainThread) -{ - MOZ_ASSERT(aCx); - MOZ_ASSERT(aCx == danger::GetJSContext()); - MOZ_ASSERT(aIsMainThread == NS_IsMainThread()); - MOZ_ASSERT(bool(aGlobalObject) == bool(aGlobal)); - MOZ_ASSERT_IF(aGlobalObject, aGlobalObject->GetGlobalJSObject() == aGlobal); -#ifdef DEBUG - bool haveException = JS_IsExceptionPending(aCx); -#endif // DEBUG - - mCx = aCx; - mIsMainThread = aIsMainThread; - mGlobalObject = aGlobalObject; - if (aIsMainThread) { - // We _could_ just unconditionally emplace mAutoRequest here. It's just not - // needed on worker threads, and we're hoping to kill it on the main thread - // too. - mAutoRequest.emplace(mCx); - } - if (aGlobal) { - JS::ExposeObjectToActiveJS(aGlobal); - } - mAutoNullableCompartment.emplace(mCx, aGlobal); - - ScriptSettingsStack::Push(this); - - mOldWarningReporter.emplace(JS::GetWarningReporter(aCx)); - - JS::SetWarningReporter(aCx, WarningOnlyErrorReporter); - -#ifdef DEBUG - if (haveException) { - JS::Rooted<JS::Value> exn(aCx); - JS_GetPendingException(aCx, &exn); - - JS_ClearPendingException(aCx); - if (exn.isObject()) { - JS::Rooted<JSObject*> exnObj(aCx, &exn.toObject()); - - nsAutoJSString stack, filename, name, message; - int32_t line; - - JS::Rooted<JS::Value> tmp(aCx); - if (!JS_GetProperty(aCx, exnObj, "filename", &tmp)) { - JS_ClearPendingException(aCx); - } - if (tmp.isUndefined()) { - if (!JS_GetProperty(aCx, exnObj, "fileName", &tmp)) { - JS_ClearPendingException(aCx); - } - } - - if (!filename.init(aCx, tmp)) { - JS_ClearPendingException(aCx); - } - - if (!JS_GetProperty(aCx, exnObj, "stack", &tmp) || - !stack.init(aCx, tmp)) { - JS_ClearPendingException(aCx); - } - - if (!JS_GetProperty(aCx, exnObj, "name", &tmp) || - !name.init(aCx, tmp)) { - JS_ClearPendingException(aCx); - } - - if (!JS_GetProperty(aCx, exnObj, "message", &tmp) || - !message.init(aCx, tmp)) { - JS_ClearPendingException(aCx); - } - - if (!JS_GetProperty(aCx, exnObj, "lineNumber", &tmp) || - !JS::ToInt32(aCx, tmp, &line)) { - JS_ClearPendingException(aCx); - line = 0; - } - - printf_stderr("PREEXISTING EXCEPTION OBJECT: '%s: %s'\n%s:%d\n%s\n", - NS_ConvertUTF16toUTF8(name).get(), - NS_ConvertUTF16toUTF8(message).get(), - NS_ConvertUTF16toUTF8(filename).get(), line, - NS_ConvertUTF16toUTF8(stack).get()); - } else { - // It's a primitive... not much we can do other than stringify it. - nsAutoJSString exnStr; - if (!exnStr.init(aCx, exn)) { - JS_ClearPendingException(aCx); - } - - printf_stderr("PREEXISTING EXCEPTION PRIMITIVE: %s\n", - NS_ConvertUTF16toUTF8(exnStr).get()); - } - MOZ_ASSERT(false, "We had an exception; we should not have"); - } -#endif // DEBUG -} - -AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject, - bool aIsMainThread, - Type aType) - : ScriptSettingsStackEntry(aGlobalObject, aType) - , mIsMainThread(aIsMainThread) -{ - MOZ_ASSERT(aGlobalObject); - MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global"); - MOZ_ASSERT(aIsMainThread == NS_IsMainThread()); - - InitInternal(aGlobalObject, aGlobalObject->GetGlobalJSObject(), - danger::GetJSContext(), aIsMainThread); -} - -void -AutoJSAPI::Init() -{ - MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once"); - - InitInternal(/* aGlobalObject */ nullptr, /* aGlobal */ nullptr, - danger::GetJSContext(), NS_IsMainThread()); -} - -bool -AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx) -{ - MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once"); - MOZ_ASSERT(aCx); - - if (NS_WARN_IF(!aGlobalObject)) { - return false; - } - - JSObject* global = aGlobalObject->GetGlobalJSObject(); - if (NS_WARN_IF(!global)) { - return false; - } - - InitInternal(aGlobalObject, global, aCx, NS_IsMainThread()); - return true; -} - -bool -AutoJSAPI::Init(nsIGlobalObject* aGlobalObject) -{ - return Init(aGlobalObject, danger::GetJSContext()); -} - -bool -AutoJSAPI::Init(JSObject* aObject) -{ - nsIGlobalObject* global = nullptr; - if (aObject) - global = xpc::NativeGlobal(aObject); - if (global) - return Init(global); - else - return false; -} - -bool -AutoJSAPI::Init(nsPIDOMWindowInner* aWindow, JSContext* aCx) -{ - return Init(nsGlobalWindow::Cast(aWindow), aCx); -} - -bool -AutoJSAPI::Init(nsPIDOMWindowInner* aWindow) -{ - return Init(nsGlobalWindow::Cast(aWindow)); -} - -bool -AutoJSAPI::Init(nsGlobalWindow* aWindow, JSContext* aCx) -{ - return Init(static_cast<nsIGlobalObject*>(aWindow), aCx); -} - -bool -AutoJSAPI::Init(nsGlobalWindow* aWindow) -{ - return Init(static_cast<nsIGlobalObject*>(aWindow)); -} - -// Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning -// reports to the JSErrorReporter as soon as they are generated. These go -// directly to the console, so we can handle them easily here. -// -// Eventually, SpiderMonkey will have a special-purpose callback for warnings -// only. -void -WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep) -{ - MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags)); - if (!NS_IsMainThread()) { - // Reporting a warning on workers is a bit complicated because we have to - // climb our parent chain until we get to the main thread. So go ahead and - // just go through the worker ReportError codepath here. - // - // That said, it feels like we should be able to short-circuit things a bit - // here by posting an appropriate runnable to the main thread directly... - // Worth looking into sometime. - workers::WorkerPrivate* worker = workers::GetWorkerPrivateFromContext(aCx); - MOZ_ASSERT(worker); - - worker->ReportError(aCx, JS::ConstUTF8CharsZ(), aRep); - return; - } - - RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); - nsGlobalWindow* win = xpc::CurrentWindowOrNull(aCx); - if (!win) { - // We run addons in a separate privileged compartment, but if we're in an - // addon compartment we should log warnings to the console of the associated - // DOM Window. - win = xpc::AddonWindowOrNull(JS::CurrentGlobalOrNull(aCx)); - } - xpcReport->Init(aRep, nullptr, nsContentUtils::IsCallerChrome(), - win ? win->AsInner()->WindowID() : 0); - xpcReport->LogToConsole(); -} - -void -AutoJSAPI::ReportException() -{ - if (!HasException()) { - return; - } - - // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null - // compartment when the destructor is called. However, the JS engine - // requires us to be in a compartment when we fetch the pending exception. - // In this case, we enter the privileged junk scope and don't dispatch any - // error events. - JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx())); - if (!errorGlobal) { - if (mIsMainThread) { - errorGlobal = xpc::PrivilegedJunkScope(); - } else { - errorGlobal = workers::GetCurrentThreadWorkerGlobal(); - } - } - JSAutoCompartment ac(cx(), errorGlobal); - JS::Rooted<JS::Value> exn(cx()); - js::ErrorReport jsReport(cx()); - if (StealException(&exn) && - jsReport.init(cx(), exn, js::ErrorReport::WithSideEffects)) { - if (mIsMainThread) { - RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); - - RefPtr<nsGlobalWindow> win = xpc::WindowGlobalOrNull(errorGlobal); - if (!win) { - // We run addons in a separate privileged compartment, but they still - // expect to trigger the onerror handler of their associated DOM Window. - win = xpc::AddonWindowOrNull(errorGlobal); - } - nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr; - xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(), - nsContentUtils::IsCallerChrome(), - inner ? inner->WindowID() : 0); - if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) { - JS::RootingContext* rcx = JS::RootingContext::get(cx()); - DispatchScriptErrorEvent(inner, rcx, xpcReport, exn); - } else { - JS::Rooted<JSObject*> stack(cx(), - xpc::FindExceptionStackForConsoleReport(inner, exn)); - xpcReport->LogToConsoleWithStack(stack); - } - } else { - // On a worker, we just use the worker error reporting mechanism and don't - // bother with xpc::ErrorReport. This will ensure that all the right - // events (which are a lot more complicated than in the window case) get - // fired. - workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - MOZ_ASSERT(worker->GetJSContext() == cx()); - // Before invoking ReportError, put the exception back on the context, - // because it may want to put it in its error events and has no other way - // to get hold of it. After we invoke ReportError, clear the exception on - // cx(), just in case ReportError didn't. - JS_SetPendingException(cx(), exn); - worker->ReportError(cx(), jsReport.toStringResult(), jsReport.report()); - ClearException(); - } - } else { - NS_WARNING("OOMed while acquiring uncaught exception from JSAPI"); - ClearException(); - } -} - -bool -AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal) -{ - MOZ_ASSERT_IF(mIsMainThread, IsStackTop()); - MOZ_ASSERT(HasException()); - MOZ_ASSERT(js::GetContextCompartment(cx())); - if (!JS_GetPendingException(cx(), aVal)) { - return false; - } - return true; -} - -bool -AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal) -{ - if (!PeekException(aVal)) { - return false; - } - JS_ClearPendingException(cx()); - return true; -} - -#ifdef DEBUG -bool -AutoJSAPI::IsStackTop() const -{ - return ScriptSettingsStack::TopNonIncumbentScript() == this; -} -#endif // DEBUG - -AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject, - const char *aReason, - bool aIsMainThread) - : AutoJSAPI(aGlobalObject, aIsMainThread, eEntryScript) - , mWebIDLCallerPrincipal(nullptr) -{ - MOZ_ASSERT(aGlobalObject); - - if (aIsMainThread && gRunToCompletionListeners > 0) { - mDocShellEntryMonitor.emplace(cx(), aReason); - } -} - -AutoEntryScript::AutoEntryScript(JSObject* aObject, - const char *aReason, - bool aIsMainThread) - : AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread) -{ -} - -AutoEntryScript::~AutoEntryScript() -{ - // GC when we pop a script entry point. This is a useful heuristic that helps - // us out on certain (flawed) benchmarks like sunspider, because it lets us - // avoid GCing during the timing loop. - JS_MaybeGC(cx()); -} - -AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx, - const char* aReason) - : JS::dbg::AutoEntryMonitor(aCx) - , mReason(aReason) -{ -} - -void -AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFunction, - JSScript* aScript, JS::Handle<JS::Value> aAsyncStack, - const char* aAsyncCause) -{ - JS::Rooted<JSFunction*> rootedFunction(aCx); - if (aFunction) { - rootedFunction = aFunction; - } - JS::Rooted<JSScript*> rootedScript(aCx); - if (aScript) { - rootedScript = aScript; - } - - nsCOMPtr<nsPIDOMWindowInner> window = - do_QueryInterface(xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx))); - if (!window || !window->GetDocShell() || - !window->GetDocShell()->GetRecordProfileTimelineMarkers()) { - return; - } - - nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell(); - nsString filename; - uint32_t lineNumber = 0; - - js::AutoStableStringChars functionName(aCx); - if (rootedFunction) { - JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction)); - if (displayId) { - if (!functionName.initTwoByte(aCx, displayId)) { - JS_ClearPendingException(aCx); - return; - } - } - } - - if (!rootedScript) { - rootedScript = JS_GetFunctionScript(aCx, rootedFunction); - } - if (rootedScript) { - filename = NS_ConvertUTF8toUTF16(JS_GetScriptFilename(rootedScript)); - lineNumber = JS_GetScriptBaseLineNumber(aCx, rootedScript); - } - - if (!filename.IsEmpty() || functionName.isTwoByte()) { - const char16_t* functionNameChars = functionName.isTwoByte() ? - functionName.twoByteChars() : nullptr; - - docShellForJSRunToCompletion->NotifyJSRunToCompletionStart(mReason, - functionNameChars, - filename.BeginReading(), - lineNumber, aAsyncStack, - aAsyncCause); - } -} - -void -AutoEntryScript::DocshellEntryMonitor::Exit(JSContext* aCx) -{ - nsCOMPtr<nsPIDOMWindowInner> window = - do_QueryInterface(xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx))); - // Not really worth checking GetRecordProfileTimelineMarkers here. - if (window && window->GetDocShell()) { - nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell(); - docShellForJSRunToCompletion->NotifyJSRunToCompletionStop(); - } -} - -AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject) - : ScriptSettingsStackEntry(aGlobalObject, eIncumbentScript) - , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread()) -{ - ScriptSettingsStack::Push(this); -} - -AutoIncumbentScript::~AutoIncumbentScript() -{ - ScriptSettingsStack::Pop(this); -} - -AutoNoJSAPI::AutoNoJSAPI() - : ScriptSettingsStackEntry(nullptr, eNoJSAPI) -{ - ScriptSettingsStack::Push(this); -} - -AutoNoJSAPI::~AutoNoJSAPI() -{ - ScriptSettingsStack::Pop(this); -} - -} // namespace dom - -AutoJSContext::AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) - : mCx(nullptr) -{ - JS::AutoSuppressGCAnalysis nogc; - MOZ_ASSERT(!mCx, "mCx should not be initialized!"); - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - - if (dom::IsJSAPIActive()) { - mCx = dom::danger::GetJSContext(); - } else { - mJSAPI.Init(); - mCx = mJSAPI.cx(); - } -} - -AutoJSContext::operator JSContext*() const -{ - return mCx; -} - -AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) - : AutoJSAPI() -{ - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - - DebugOnly<bool> ok = Init(xpc::UnprivilegedJunkScope()); - MOZ_ASSERT(ok, - "This is quite odd. We should have crashed in the " - "xpc::NativeGlobal() call if xpc::UnprivilegedJunkScope() " - "returned null, and inited correctly otherwise!"); -} - -AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) - : AutoJSAPI() -{ - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - - Init(); -} - -void -AutoSlowOperation::CheckForInterrupt() -{ - // JS_CheckForInterrupt expects us to be in a compartment. - JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope()); - JS_CheckForInterrupt(cx()); -} - -} // namespace mozilla diff --git a/dom/base/ScriptSettings.h b/dom/base/ScriptSettings.h deleted file mode 100644 index 05e62f55e..000000000 --- a/dom/base/ScriptSettings.h +++ /dev/null @@ -1,465 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -/* Utilities for managing the script settings object stack defined in webapps */ - -#ifndef mozilla_dom_ScriptSettings_h -#define mozilla_dom_ScriptSettings_h - -#include "MainThreadUtils.h" -#include "nsIGlobalObject.h" -#include "nsIPrincipal.h" - -#include "mozilla/Maybe.h" - -#include "jsapi.h" -#include "js/Debug.h" - -class nsPIDOMWindowInner; -class nsGlobalWindow; -class nsIScriptContext; -class nsIDocument; -class nsIDocShell; - -namespace mozilla { -namespace dom { - -/* - * System-wide setup/teardown routines. Init and Destroy should be invoked - * once each, at startup and shutdown (respectively). - */ -void InitScriptSettings(); -void DestroyScriptSettings(); -bool ScriptSettingsInitialized(); - -/* - * Static helpers in ScriptSettings which track the number of listeners - * of Javascript RunToCompletion events. These should be used by the code in - * nsDocShell::SetRecordProfileTimelineMarkers to indicate to script - * settings that script run-to-completion needs to be monitored. - * SHOULD BE CALLED ONLY BY MAIN THREAD. - */ -void UseEntryScriptProfiling(); -void UnuseEntryScriptProfiling(); - -// To implement a web-compatible browser, it is often necessary to obtain the -// global object that is "associated" with the currently-running code. This -// process is made more complicated by the fact that, historically, different -// algorithms have operated with different definitions of the "associated" -// global. -// -// HTML5 formalizes this into two concepts: the "incumbent global" and the -// "entry global". The incumbent global corresponds to the global of the -// current script being executed, whereas the entry global corresponds to the -// global of the script where the current JS execution began. -// -// There is also a potentially-distinct third global that is determined by the -// current compartment. This roughly corresponds with the notion of Realms in -// ECMAScript. -// -// Suppose some event triggers an event listener in window |A|, which invokes a -// scripted function in window |B|, which invokes the |window.location.href| -// setter in window |C|. The entry global would be |A|, the incumbent global -// would be |B|, and the current compartment would be that of |C|. -// -// In general, it's best to use to use the most-closely-associated global -// unless the spec says to do otherwise. In 95% of the cases, the global of -// the current compartment (GetCurrentGlobal()) is the right thing. For -// example, WebIDL constructors (new C.XMLHttpRequest()) are initialized with -// the global of the current compartment (i.e. |C|). -// -// The incumbent global is very similar, but differs in a few edge cases. For -// example, if window |B| does |C.location.href = "..."|, the incumbent global -// used for the navigation algorithm is B, because no script from |C| was ever run. -// -// The entry global is used for various things like computing base URIs, mostly -// for historical reasons. -// -// Note that all of these functions return bonafide global objects. This means -// that, for Windows, they always return the inner. - -// Returns the global associated with the top-most Candidate Entry Point on -// the Script Settings Stack. See the HTML spec. This may be null. -nsIGlobalObject* GetEntryGlobal(); - -// If the entry global is a window, returns its extant document. Otherwise, -// returns null. -nsIDocument* GetEntryDocument(); - -// Returns the global associated with the top-most entry of the the Script -// Settings Stack. See the HTML spec. This may be null. -nsIGlobalObject* GetIncumbentGlobal(); - -// Returns the global associated with the current compartment. This may be null. -nsIGlobalObject* GetCurrentGlobal(); - -// JS-implemented WebIDL presents an interesting situation with respect to the -// subject principal. A regular C++-implemented API can simply examine the -// compartment of the most-recently-executed script, and use that to infer the -// responsible party. However, JS-implemented APIs are run with system -// principal, and thus clobber the subject principal of the script that -// invoked the API. So we have to do some extra work to keep track of this -// information. -// -// We therefore implement the following behavior: -// * Each Script Settings Object has an optional WebIDL Caller Principal field. -// This defaults to null. -// * When we push an Entry Point in preparation to run a JS-implemented WebIDL -// callback, we grab the subject principal at the time of invocation, and -// store that as the WebIDL Caller Principal. -// * When non-null, callers can query this principal from script via an API on -// Components.utils. -nsIPrincipal* GetWebIDLCallerPrincipal(); - -// This may be used by callers that know that their incumbent global is non- -// null (i.e. they know there have been no System Caller pushes since the -// inner-most script execution). -inline JSObject& IncumbentJSGlobal() -{ - return *GetIncumbentGlobal()->GetGlobalJSObject(); -} - -// Returns whether JSAPI is active right now. If it is not, working with a -// JSContext you grab from somewhere random is not OK and you should be doing -// AutoJSAPI or AutoEntryScript to get yourself a properly set up JSContext. -bool IsJSAPIActive(); - -namespace danger { - -// Get the JSContext for this thread. This is in the "danger" namespace because -// we generally want people using AutoJSAPI instead, unless they really know -// what they're doing. -JSContext* GetJSContext(); - -} // namespace danger - -JS::RootingContext* RootingCx(); - -class ScriptSettingsStack; -class ScriptSettingsStackEntry { - friend class ScriptSettingsStack; - -public: - ~ScriptSettingsStackEntry(); - - bool NoJSAPI() const { return mType == eNoJSAPI; } - bool IsEntryCandidate() const { - return mType == eEntryScript || mType == eNoJSAPI; - } - bool IsIncumbentCandidate() { return mType != eJSAPI; } - bool IsIncumbentScript() { return mType == eIncumbentScript; } - -protected: - enum Type { - eEntryScript, - eIncumbentScript, - eJSAPI, - eNoJSAPI - }; - - ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, - Type aEntryType); - - nsCOMPtr<nsIGlobalObject> mGlobalObject; - Type mType; - -private: - ScriptSettingsStackEntry *mOlder; -}; - -/* - * For any interaction with JSAPI, an AutoJSAPI (or one of its subclasses) - * must be on the stack. - * - * This base class should be instantiated as-is when the caller wants to use - * JSAPI but doesn't expect to run script. The caller must then call one of its - * Init functions before being able to access the JSContext through cx(). - * Its current duties are as-follows (see individual Init comments for details): - * - * * Grabbing an appropriate JSContext, and, on the main thread, pushing it onto - * the JSContext stack. - * * Entering an initial (possibly null) compartment, to ensure that the - * previously entered compartment for that JSContext is not used by mistake. - * * Reporting any exceptions left on the JSRuntime, unless the caller steals - * or silences them. - * * On main thread, entering a JSAutoRequest. - * - * Additionally, the following duties are planned, but not yet implemented: - * - * * De-poisoning the JSRuntime to allow manipulation of JSAPI. This requires - * implementing the poisoning first. For now, this de-poisoning - * effectively corresponds to having a non-null cx on the stack. - * - * In situations where the consumer expects to run script, AutoEntryScript - * should be used, which does additional manipulation of the script settings - * stack. In bug 991758, we'll add hard invariants to SpiderMonkey, such that - * any attempt to run script without an AutoEntryScript on the stack will - * fail. This prevents system code from accidentally triggering script - * execution at inopportune moments via surreptitious getters and proxies. - */ -class MOZ_STACK_CLASS AutoJSAPI : protected ScriptSettingsStackEntry { -public: - // Trivial constructor. One of the Init functions must be called before - // accessing the JSContext through cx(). - AutoJSAPI(); - - ~AutoJSAPI(); - - // This uses the SafeJSContext (or worker equivalent), and enters a null - // compartment, so that the consumer is forced to select a compartment to - // enter before manipulating objects. - // - // This variant will ensure that any errors reported by this AutoJSAPI as it - // comes off the stack will not fire error events or be associated with any - // particular web-visible global. - void Init(); - - // This uses the SafeJSContext (or worker equivalent), and enters the - // compartment of aGlobalObject. - // If aGlobalObject or its associated JS global are null then it returns - // false and use of cx() will cause an assertion. - // - // If aGlobalObject represents a web-visible global, errors reported by this - // AutoJSAPI as it comes off the stack will fire the relevant error events and - // show up in the corresponding web console. - MOZ_MUST_USE bool Init(nsIGlobalObject* aGlobalObject); - - // This is a helper that grabs the native global associated with aObject and - // invokes the above Init() with that. - MOZ_MUST_USE bool Init(JSObject* aObject); - - // Unsurprisingly, this uses aCx and enters the compartment of aGlobalObject. - // If aGlobalObject or its associated JS global are null then it returns - // false and use of cx() will cause an assertion. - // If aCx is null it will cause an assertion. - // - // If aGlobalObject represents a web-visible global, errors reported by this - // AutoJSAPI as it comes off the stack will fire the relevant error events and - // show up in the corresponding web console. - MOZ_MUST_USE bool Init(nsIGlobalObject* aGlobalObject, JSContext* aCx); - - // Convenience functions to take an nsPIDOMWindow* or nsGlobalWindow*, - // when it is more easily available than an nsIGlobalObject. - MOZ_MUST_USE bool Init(nsPIDOMWindowInner* aWindow); - MOZ_MUST_USE bool Init(nsPIDOMWindowInner* aWindow, JSContext* aCx); - - MOZ_MUST_USE bool Init(nsGlobalWindow* aWindow); - MOZ_MUST_USE bool Init(nsGlobalWindow* aWindow, JSContext* aCx); - - JSContext* cx() const { - MOZ_ASSERT(mCx, "Must call Init before using an AutoJSAPI"); - MOZ_ASSERT(IsStackTop()); - return mCx; - } - -#ifdef DEBUG - bool IsStackTop() const; -#endif - - // If HasException, report it. Otherwise, a no-op. - void ReportException(); - - bool HasException() const { - MOZ_ASSERT(IsStackTop()); - return JS_IsExceptionPending(cx()); - }; - - // Transfers ownership of the current exception from the JS engine to the - // caller. Callers must ensure that HasException() is true, and that cx() - // is in a non-null compartment. - // - // Note that this fails if and only if we OOM while wrapping the exception - // into the current compartment. - MOZ_MUST_USE bool StealException(JS::MutableHandle<JS::Value> aVal); - - // Peek the current exception from the JS engine, without stealing it. - // Callers must ensure that HasException() is true, and that cx() is in a - // non-null compartment. - // - // Note that this fails if and only if we OOM while wrapping the exception - // into the current compartment. - MOZ_MUST_USE bool PeekException(JS::MutableHandle<JS::Value> aVal); - - void ClearException() { - MOZ_ASSERT(IsStackTop()); - JS_ClearPendingException(cx()); - } - -protected: - // Protected constructor for subclasses. This constructor initialises the - // AutoJSAPI, so Init must NOT be called on subclasses that use this. - AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, Type aType); - -private: - mozilla::Maybe<JSAutoRequest> mAutoRequest; - mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment; - JSContext *mCx; - - // Whether we're mainthread or not; set when we're initialized. - bool mIsMainThread; - Maybe<JS::WarningReporter> mOldWarningReporter; - - void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal, - JSContext* aCx, bool aIsMainThread); - - AutoJSAPI(const AutoJSAPI&) = delete; - AutoJSAPI& operator= (const AutoJSAPI&) = delete; -}; - -/* - * A class that represents a new script entry point. - * - * |aReason| should be a statically-allocated C string naming the reason we're - * invoking JavaScript code: "setTimeout", "event", and so on. The devtools use - * these strings to label JS execution in timeline and profiling displays. - */ -class MOZ_STACK_CLASS AutoEntryScript : public AutoJSAPI { -public: - AutoEntryScript(nsIGlobalObject* aGlobalObject, - const char *aReason, - bool aIsMainThread = NS_IsMainThread()); - - AutoEntryScript(JSObject* aObject, // Any object from the relevant global - const char *aReason, - bool aIsMainThread = NS_IsMainThread()); - - ~AutoEntryScript(); - - void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) { - mWebIDLCallerPrincipal = aPrincipal; - } - -private: - // A subclass of AutoEntryMonitor that notifies the docshell. - class DocshellEntryMonitor final : public JS::dbg::AutoEntryMonitor - { - public: - DocshellEntryMonitor(JSContext* aCx, const char* aReason); - - // Please note that |aAsyncCause| here is owned by the caller, and its - // lifetime must outlive the lifetime of the DocshellEntryMonitor object. - // In practice, |aAsyncCause| is identical to |aReason| passed into - // the AutoEntryScript constructor, so the lifetime requirements are - // trivially satisfied by |aReason| being a statically allocated string. - void Entry(JSContext* aCx, JSFunction* aFunction, - JS::Handle<JS::Value> aAsyncStack, - const char* aAsyncCause) override - { - Entry(aCx, aFunction, nullptr, aAsyncStack, aAsyncCause); - } - - void Entry(JSContext* aCx, JSScript* aScript, - JS::Handle<JS::Value> aAsyncStack, - const char* aAsyncCause) override - { - Entry(aCx, nullptr, aScript, aAsyncStack, aAsyncCause); - } - - void Exit(JSContext* aCx) override; - - private: - void Entry(JSContext* aCx, JSFunction* aFunction, JSScript* aScript, - JS::Handle<JS::Value> aAsyncStack, - const char* aAsyncCause); - - const char* mReason; - }; - - // It's safe to make this a weak pointer, since it's the subject principal - // when we go on the stack, so can't go away until after we're gone. In - // particular, this is only used from the CallSetup constructor, and only in - // the aIsJSImplementedWebIDL case. And in that case, the subject principal - // is the principal of the callee function that is part of the CallArgs just a - // bit up the stack, and which will outlive us. So we know the principal - // can't go away until then either. - nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal; - friend nsIPrincipal* GetWebIDLCallerPrincipal(); - - Maybe<DocshellEntryMonitor> mDocShellEntryMonitor; -}; - -/* - * A class that can be used to force a particular incumbent script on the stack. - */ -class AutoIncumbentScript : protected ScriptSettingsStackEntry { -public: - explicit AutoIncumbentScript(nsIGlobalObject* aGlobalObject); - ~AutoIncumbentScript(); - -private: - JS::AutoHideScriptedCaller mCallerOverride; -}; - -/* - * A class to put the JS engine in an unusable state. The subject principal - * will become System, the information on the script settings stack is - * rendered inaccessible, and JSAPI may not be manipulated until the class is - * either popped or an AutoJSAPI instance is subsequently pushed. - * - * This class may not be instantiated if an exception is pending. - */ -class AutoNoJSAPI : protected ScriptSettingsStackEntry { -public: - explicit AutoNoJSAPI(); - ~AutoNoJSAPI(); -}; - -} // namespace dom - -/** - * Use AutoJSContext when you need a JS context on the stack but don't have one - * passed as a parameter. AutoJSContext will take care of finding the most - * appropriate JS context and release it when leaving the stack. - */ -class MOZ_RAII AutoJSContext { -public: - explicit AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); - operator JSContext*() const; - -protected: - JSContext* mCx; - dom::AutoJSAPI mJSAPI; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -/** - * AutoSafeJSContext is similar to AutoJSContext but will only return the safe - * JS context. That means it will never call nsContentUtils::GetCurrentJSContext(). - * - * Note - This is deprecated. Please use AutoJSAPI instead. - */ -class MOZ_RAII AutoSafeJSContext : public dom::AutoJSAPI { -public: - explicit AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); - operator JSContext*() const - { - return cx(); - } - -private: - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -/** - * Use AutoSlowOperation when native side calls many JS callbacks in a row - * and slow script dialog should be activated if too much time is spent going - * through those callbacks. - * AutoSlowOperation puts a JSAutoRequest on the stack so that we don't continue - * to reset the watchdog and CheckForInterrupt can be then used to check whether - * JS execution should be interrupted. - */ -class MOZ_RAII AutoSlowOperation : public dom::AutoJSAPI -{ -public: - explicit AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); - void CheckForInterrupt(); -private: - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -} // namespace mozilla - -#endif // mozilla_dom_ScriptSettings_h diff --git a/dom/base/moz.build b/dom/base/moz.build index 89f1785ca..aadafe412 100755 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -26,7 +26,6 @@ XPIDL_SOURCES += [ 'nsIObjectLoadingContent.idl', 'nsIRemoteWindowContext.idl', 'nsIScriptChannel.idl', - 'nsIScriptLoaderObserver.idl', 'nsISelection.idl', 'nsISelectionController.idl', 'nsISelectionDisplay.idl', @@ -96,7 +95,6 @@ EXPORTS += [ 'nsINode.h', 'nsINodeList.h', 'nsIScriptContext.h', - 'nsIScriptElement.h', 'nsIScriptGlobalObject.h', 'nsIScriptNameSpaceManager.h', 'nsIScriptObjectPrincipal.h', @@ -117,7 +115,6 @@ EXPORTS += [ 'nsRange.h', 'nsReferencedElement.h', 'nsSandboxFlags.h', - 'nsScriptLoader.h', 'nsStructuredCloneContainer.h', 'nsStubAnimationObserver.h', 'nsStubDocumentObserver.h', @@ -208,7 +205,6 @@ EXPORTS.mozilla.dom += [ 'ResponsiveImageSelector.h', 'SameProcessMessageQueue.h', 'ScreenOrientation.h', - 'ScriptSettings.h', 'ShadowRoot.h', 'SimpleTreeIterator.h', 'StructuredCloneHolder.h', @@ -328,8 +324,6 @@ SOURCES += [ 'nsRange.cpp', 'nsReferencedElement.cpp', 'nsScreen.cpp', - 'nsScriptElement.cpp', - 'nsScriptLoader.cpp', 'nsScriptNameSpaceManager.cpp', 'nsStructuredCloneContainer.cpp', 'nsStubAnimationObserver.cpp', @@ -356,7 +350,6 @@ SOURCES += [ 'ResponsiveImageSelector.cpp', 'SameProcessMessageQueue.cpp', 'ScreenOrientation.cpp', - 'ScriptSettings.cpp', 'ShadowRoot.cpp', 'StructuredCloneHolder.cpp', 'StyleSheetList.cpp', diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissionHelper.cpp index c57fc6233..eaaec2a41 100644 --- a/dom/base/nsContentPermissionHelper.cpp +++ b/dom/base/nsContentPermissionHelper.cpp @@ -29,9 +29,8 @@ #include "nsIDocument.h" #include "nsIDOMEvent.h" #include "nsWeakPtr.h" -#include "ScriptSettings.h" -using mozilla::Unused; // <snicker> +using mozilla::Unused; using namespace mozilla::dom; using namespace mozilla; diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 490f0ec17..1e6465a1b 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -10,7 +10,6 @@ */ #include "nsContentSink.h" -#include "nsScriptLoader.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "mozilla/css/Loader.h" @@ -49,6 +48,7 @@ #include "nsHTMLDNSPrefetch.h" #include "nsIObserverService.h" #include "mozilla/Preferences.h" +#include "mozilla/dom/ScriptLoader.h" #include "nsParserConstants.h" #include "nsSandboxFlags.h" diff --git a/dom/base/nsContentSink.h b/dom/base/nsContentSink.h index b1a758874..2d914a8d7 100644 --- a/dom/base/nsContentSink.h +++ b/dom/base/nsContentSink.h @@ -36,13 +36,16 @@ class nsIAtom; class nsIChannel; class nsIContent; class nsNodeInfoManager; -class nsScriptLoader; class nsIApplicationCache; namespace mozilla { namespace css { class Loader; } // namespace css + +namespace dom { +class ScriptLoader; +} // namespace dom } // namespace mozilla #ifdef DEBUG @@ -273,7 +276,7 @@ protected: nsCOMPtr<nsIDocShell> mDocShell; RefPtr<mozilla::css::Loader> mCSSLoader; RefPtr<nsNodeInfoManager> mNodeInfoManager; - RefPtr<nsScriptLoader> mScriptLoader; + RefPtr<mozilla::dom::ScriptLoader> mScriptLoader; // back off timer notification after count int32_t mBackoffCount; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 81e2783a4..d0634b0f9 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1880,7 +1880,7 @@ nsDocument::Init() mScopeObject = do_GetWeakReference(global); MOZ_ASSERT(mScopeObject); - mScriptLoader = new nsScriptLoader(this); + mScriptLoader = new dom::ScriptLoader(this); mozilla::HoldJSObjects(this); @@ -4685,7 +4685,7 @@ nsDocument::GetWindowInternal() const return win; } -nsScriptLoader* +ScriptLoader* nsDocument::ScriptLoader() { return mScriptLoader; diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 8ea4993f0..a319ad13e 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -31,7 +31,6 @@ #include "nsJSThingHashtable.h" #include "nsIScriptObjectPrincipal.h" #include "nsIURI.h" -#include "nsScriptLoader.h" #include "nsIRadioGroupContainer.h" #include "nsILayoutHistoryState.h" #include "nsIRequest.h" @@ -60,6 +59,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/PendingAnimationTracker.h" #include "mozilla/dom/DOMImplementation.h" +#include "mozilla/dom/ScriptLoader.h" #include "mozilla/dom/StyleSheetList.h" #include "nsDataHashtable.h" #include "mozilla/TimeStamp.h" @@ -674,7 +674,7 @@ public: /** * Get the script loader for this document */ - virtual nsScriptLoader* ScriptLoader() override; + virtual mozilla::dom::ScriptLoader* ScriptLoader() override; /** * Add/Remove an element to the document's id and name hashes @@ -1417,7 +1417,7 @@ public: RefPtr<mozilla::EventListenerManager> mListenerManager; RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets; RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList; - RefPtr<nsScriptLoader> mScriptLoader; + RefPtr<mozilla::dom::ScriptLoader> mScriptLoader; nsDocHeaderData* mHeaderData; /* mIdentifierMap works as follows for IDs: * 1) Attribute changes affect the table immediately (removing and adding diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index bba4232aa..331931f19 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -19,7 +19,6 @@ #include "nsJSUtils.h" #include "nsJSPrincipals.h" #include "nsNetUtil.h" -#include "nsScriptLoader.h" #include "nsFrameLoader.h" #include "nsIXULRuntime.h" #include "nsIScriptError.h" @@ -38,6 +37,7 @@ #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/ProcessGlobal.h" #include "mozilla/dom/SameProcessMessageQueue.h" +#include "mozilla/dom/ScriptLoader.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/dom/ipc/BlobParent.h" @@ -1786,9 +1786,9 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript( if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) { return; } - nsScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail, - EmptyString(), nullptr, - dataStringBuf, dataStringLength); + ScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail, + EmptyString(), nullptr, + dataStringBuf, dataStringLength); } JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength, diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index dd1fe4586..d696d826b 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -62,7 +62,7 @@ #include "nsReadableUtils.h" #include "nsDOMClassInfo.h" #include "nsJSEnvironment.h" -#include "ScriptSettings.h" +#include "mozilla/dom/ScriptSettings.h" #include "mozilla/Preferences.h" #include "mozilla/Likely.h" #include "mozilla/Sprintf.h" diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index fdaee39ca..506acc7e4 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -91,7 +91,6 @@ class nsIVariant; class nsViewManager; class nsPresContext; class nsRange; -class nsScriptLoader; class nsSMILAnimationController; class nsTextNode; class nsWindowSizes; @@ -152,6 +151,7 @@ enum class OrientationType : uint32_t; class ProcessingInstruction; class Promise; class Selection; +class ScriptLoader; class StyleSheetList; class SVGDocument; class SVGSVGElement; @@ -1290,7 +1290,7 @@ public: /** * Get the script loader for this document */ - virtual nsScriptLoader* ScriptLoader() = 0; + virtual mozilla::dom::ScriptLoader* ScriptLoader() = 0; /** * Add/Remove an element to the document's id and name hashes diff --git a/dom/base/nsIScriptElement.h b/dom/base/nsIScriptElement.h deleted file mode 100644 index 470d51c94..000000000 --- a/dom/base/nsIScriptElement.h +++ /dev/null @@ -1,329 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef nsIScriptElement_h___ -#define nsIScriptElement_h___ - -#include "nsISupports.h" -#include "nsIURI.h" -#include "nsCOMPtr.h" -#include "nsIScriptLoaderObserver.h" -#include "nsWeakPtr.h" -#include "nsIParser.h" -#include "nsContentCreatorFunctions.h" -#include "nsIDOMHTMLScriptElement.h" -#include "mozilla/CORSMode.h" - -#define NS_ISCRIPTELEMENT_IID \ -{ 0xe60fca9b, 0x1b96, 0x4e4e, \ - { 0xa9, 0xb4, 0xdc, 0x98, 0x4f, 0x88, 0x3f, 0x9c } } - -/** - * Internal interface implemented by script elements - */ -class nsIScriptElement : public nsIScriptLoaderObserver { -public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTELEMENT_IID) - - explicit nsIScriptElement(mozilla::dom::FromParser aFromParser) - : mLineNumber(1), - mAlreadyStarted(false), - mMalformed(false), - mDoneAddingChildren(aFromParser == mozilla::dom::NOT_FROM_PARSER || - aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT), - mForceAsync(aFromParser == mozilla::dom::NOT_FROM_PARSER || - aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT), - mFrozen(false), - mDefer(false), - mAsync(false), - mExternal(false), - mParserCreated(aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT ? - mozilla::dom::NOT_FROM_PARSER : aFromParser), - // Fragment parser-created scripts (if executable) - // behave like script-created scripts. - mCreatorParser(nullptr) - { - } - - /** - * Content type identifying the scripting language. Can be empty, in - * which case javascript will be assumed. - * Return false if type attribute is not found. - */ - virtual bool GetScriptType(nsAString& type) = 0; - - /** - * Location of script source text. Can return null, in which case - * this is assumed to be an inline script element. - */ - nsIURI* GetScriptURI() - { - NS_PRECONDITION(mFrozen, "Not ready for this call yet!"); - return mUri; - } - - /** - * Script source text for inline script elements. - */ - virtual void GetScriptText(nsAString& text) = 0; - - virtual void GetScriptCharset(nsAString& charset) = 0; - - /** - * Freezes the return values of GetScriptDeferred(), GetScriptAsync() and - * GetScriptURI() so that subsequent modifications to the attributes don't - * change execution behavior. - */ - virtual void FreezeUriAsyncDefer() = 0; - - /** - * Is the script deferred. Currently only supported by HTML scripts. - */ - bool GetScriptDeferred() - { - NS_PRECONDITION(mFrozen, "Not ready for this call yet!"); - return mDefer; - } - - /** - * Is the script async. Currently only supported by HTML scripts. - */ - bool GetScriptAsync() - { - NS_PRECONDITION(mFrozen, "Not ready for this call yet!"); - return mAsync; - } - - /** - * Is the script an external script? - */ - bool GetScriptExternal() - { - NS_PRECONDITION(mFrozen, "Not ready for this call yet!"); - return mExternal; - } - - /** - * Returns how the element was created. - */ - mozilla::dom::FromParser GetParserCreated() - { - return mParserCreated; - } - - void SetScriptLineNumber(uint32_t aLineNumber) - { - mLineNumber = aLineNumber; - } - uint32_t GetScriptLineNumber() - { - return mLineNumber; - } - - void SetIsMalformed() - { - mMalformed = true; - } - bool IsMalformed() - { - return mMalformed; - } - - void PreventExecution() - { - mAlreadyStarted = true; - } - - void LoseParserInsertedness() - { - mFrozen = false; - mUri = nullptr; - mCreatorParser = nullptr; - mParserCreated = mozilla::dom::NOT_FROM_PARSER; - bool async = false; - nsCOMPtr<nsIDOMHTMLScriptElement> htmlScript = do_QueryInterface(this); - if (htmlScript) { - htmlScript->GetAsync(&async); - } - mForceAsync = !async; - } - - void SetCreatorParser(nsIParser* aParser) - { - mCreatorParser = do_GetWeakReference(aParser); - } - - /** - * Unblocks the creator parser - */ - void UnblockParser() - { - nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser); - if (parser) { - parser->UnblockParser(); - } - } - - /** - * Attempts to resume parsing asynchronously - */ - void ContinueParserAsync() - { - nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser); - if (parser) { - parser->ContinueInterruptedParsingAsync(); - } - } - - /** - * Informs the creator parser that the evaluation of this script is starting - */ - void BeginEvaluating() - { - nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser); - if (parser) { - parser->PushDefinedInsertionPoint(); - } - } - - /** - * Informs the creator parser that the evaluation of this script is ending - */ - void EndEvaluating() - { - nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser); - if (parser) { - parser->PopDefinedInsertionPoint(); - } - } - - /** - * Retrieves a pointer to the creator parser if this has one or null if not - */ - already_AddRefed<nsIParser> GetCreatorParser() - { - nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser); - return parser.forget(); - } - - /** - * This method is called when the parser finishes creating the script - * element's children, if any are present. - * - * @return whether the parser will be blocked while this script is being - * loaded - */ - bool AttemptToExecute() - { - mDoneAddingChildren = true; - bool block = MaybeProcessScript(); - if (!mAlreadyStarted) { - // Need to lose parser-insertedness here to allow another script to cause - // execution later. - LoseParserInsertedness(); - } - return block; - } - - /** - * Get the CORS mode of the script element - */ - virtual mozilla::CORSMode GetCORSMode() const - { - /* Default to no CORS */ - return mozilla::CORS_NONE; - } - - /** - * Fire an error event - */ - virtual nsresult FireErrorEvent() = 0; - -protected: - /** - * Processes the script if it's in the document-tree and links to or - * contains a script. Once it has been evaluated there is no way to make it - * reevaluate the script, you'll have to create a new element. This also means - * that when adding a src attribute to an element that already contains an - * inline script, the script referenced by the src attribute will not be - * loaded. - * - * In order to be able to use multiple childNodes, or to use the - * fallback mechanism of using both inline script and linked script you have - * to add all attributes and childNodes before adding the element to the - * document-tree. - * - * @return whether the parser will be blocked while this script is being - * loaded - */ - virtual bool MaybeProcessScript() = 0; - - /** - * The start line number of the script. - */ - uint32_t mLineNumber; - - /** - * The "already started" flag per HTML5. - */ - bool mAlreadyStarted; - - /** - * The script didn't have an end tag. - */ - bool mMalformed; - - /** - * False if parser-inserted but the parser hasn't triggered running yet. - */ - bool mDoneAddingChildren; - - /** - * If true, the .async property returns true instead of reflecting the - * content attribute. - */ - bool mForceAsync; - - /** - * Whether src, defer and async are frozen. - */ - bool mFrozen; - - /** - * The effective deferredness. - */ - bool mDefer; - - /** - * The effective asyncness. - */ - bool mAsync; - - /** - * The effective externalness. A script can be external with mUri being null - * if the src attribute contained an invalid URL string. - */ - bool mExternal; - - /** - * Whether this element was parser-created. - */ - mozilla::dom::FromParser mParserCreated; - - /** - * The effective src (or null if no src). - */ - nsCOMPtr<nsIURI> mUri; - - /** - * The creator parser of a non-defer, non-async parser-inserted script. - */ - nsWeakPtr mCreatorParser; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptElement, NS_ISCRIPTELEMENT_IID) - -#endif // nsIScriptElement_h___ diff --git a/dom/base/nsIScriptLoaderObserver.idl b/dom/base/nsIScriptLoaderObserver.idl deleted file mode 100644 index ed7196525..000000000 --- a/dom/base/nsIScriptLoaderObserver.idl +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; 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 "nsISupports.idl" - -interface nsIScriptElement; -interface nsIURI; - -[scriptable, uuid(7b787204-76fb-4764-96f1-fb7a666db4f4)] -interface nsIScriptLoaderObserver : nsISupports { - - /** - * The script is available for evaluation. For inline scripts, this - * method will be called synchronously. For externally loaded scripts, - * this method will be called when the load completes. - * - * @param aResult A result code representing the result of loading - * a script. If this is a failure code, script evaluation - * will not occur. - * @param aElement The element being processed. - * @param aIsInline Is this an inline script or externally loaded? - * @param aURI What is the URI of the script (the document URI if - * it is inline). - * @param aLineNo At what line does the script appear (generally 1 - * if it is a loaded script). - */ - void scriptAvailable(in nsresult aResult, - in nsIScriptElement aElement, - in boolean aIsInline, - in nsIURI aURI, - in int32_t aLineNo); - - /** - * The script has been evaluated. - * - * @param aResult A result code representing the success or failure of - * the script evaluation. - * @param aElement The element being processed. - * @param aIsInline Is this an inline script or externally loaded? - */ - void scriptEvaluated(in nsresult aResult, - in nsIScriptElement aElement, - in boolean aIsInline); - -}; diff --git a/dom/base/nsInProcessTabChildGlobal.cpp b/dom/base/nsInProcessTabChildGlobal.cpp index 10ccf4aec..2e129f9f0 100644 --- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -11,13 +11,13 @@ #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsComponentManagerUtils.h" -#include "nsScriptLoader.h" #include "nsFrameLoader.h" #include "xpcpublic.h" #include "nsIMozBrowserFrame.h" #include "nsDOMClassInfoID.h" #include "mozilla/EventDispatcher.h" #include "mozilla/dom/SameProcessMessageQueue.h" +#include "mozilla/dom/ScriptLoader.h" using namespace mozilla; using namespace mozilla::dom; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index efea3ee40..605b1917f 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -37,7 +37,6 @@ #include "nsXPCOMCIDInternal.h" #include "nsIXULRuntime.h" #include "nsTextFormatter.h" -#include "ScriptSettings.h" #include "xpcpublic.h" @@ -55,6 +54,7 @@ #include "mozilla/dom/DOMException.h" #include "mozilla/dom/DOMExceptionBinding.h" #include "mozilla/dom/ErrorEvent.h" +#include "mozilla/dom/ScriptSettings.h" #include "nsAXPCNativeCallContext.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/Telemetry.h" diff --git a/dom/base/nsScriptElement.cpp b/dom/base/nsScriptElement.cpp deleted file mode 100644 index ebeb18f81..000000000 --- a/dom/base/nsScriptElement.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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 "nsScriptElement.h" -#include "mozilla/BasicEvents.h" -#include "mozilla/EventDispatcher.h" -#include "mozilla/dom/Element.h" -#include "nsContentUtils.h" -#include "nsPresContext.h" -#include "nsScriptLoader.h" -#include "nsIParser.h" -#include "nsGkAtoms.h" -#include "nsContentSink.h" - -using namespace mozilla; -using namespace mozilla::dom; - -NS_IMETHODIMP -nsScriptElement::ScriptAvailable(nsresult aResult, - nsIScriptElement *aElement, - bool aIsInline, - nsIURI *aURI, - int32_t aLineNo) -{ - if (!aIsInline && NS_FAILED(aResult)) { - nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser); - if (parser) { - parser->PushDefinedInsertionPoint(); - } - nsresult rv = FireErrorEvent(); - if (parser) { - parser->PopDefinedInsertionPoint(); - } - return rv; - } - return NS_OK; -} - -/* virtual */ nsresult -nsScriptElement::FireErrorEvent() -{ - nsCOMPtr<nsIContent> cont = - do_QueryInterface((nsIScriptElement*) this); - - return nsContentUtils::DispatchTrustedEvent(cont->OwnerDoc(), - cont, - NS_LITERAL_STRING("error"), - false /* bubbles */, - false /* cancelable */); -} - -NS_IMETHODIMP -nsScriptElement::ScriptEvaluated(nsresult aResult, - nsIScriptElement *aElement, - bool aIsInline) -{ - nsresult rv = NS_OK; - if (!aIsInline) { - nsCOMPtr<nsIContent> cont = - do_QueryInterface((nsIScriptElement*) this); - - RefPtr<nsPresContext> presContext = - nsContentUtils::GetContextForContent(cont); - - nsEventStatus status = nsEventStatus_eIgnore; - EventMessage message = NS_SUCCEEDED(aResult) ? eLoad : eLoadError; - WidgetEvent event(true, message); - // Load event doesn't bubble. - event.mFlags.mBubbles = (message != eLoad); - - EventDispatcher::Dispatch(cont, presContext, &event, nullptr, &status); - } - - return rv; -} - -void -nsScriptElement::CharacterDataChanged(nsIDocument *aDocument, - nsIContent* aContent, - CharacterDataChangeInfo* aInfo) -{ - MaybeProcessScript(); -} - -void -nsScriptElement::AttributeChanged(nsIDocument* aDocument, - Element* aElement, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType, - const nsAttrValue* aOldValue) -{ - MaybeProcessScript(); -} - -void -nsScriptElement::ContentAppended(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aFirstNewContent, - int32_t aNewIndexInContainer) -{ - MaybeProcessScript(); -} - -void -nsScriptElement::ContentInserted(nsIDocument *aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer) -{ - MaybeProcessScript(); -} - -bool -nsScriptElement::MaybeProcessScript() -{ - nsCOMPtr<nsIContent> cont = - do_QueryInterface((nsIScriptElement*) this); - - NS_ASSERTION(cont->DebugGetSlots()->mMutationObservers.Contains(this), - "You forgot to add self as observer"); - - if (mAlreadyStarted || !mDoneAddingChildren || - !cont->GetComposedDoc() || mMalformed || !HasScriptContent()) { - return false; - } - - FreezeUriAsyncDefer(); - - mAlreadyStarted = true; - - nsIDocument* ownerDoc = cont->OwnerDoc(); - nsCOMPtr<nsIParser> parser = ((nsIScriptElement*) this)->GetCreatorParser(); - if (parser) { - nsCOMPtr<nsIContentSink> sink = parser->GetContentSink(); - if (sink) { - nsCOMPtr<nsIDocument> parserDoc = do_QueryInterface(sink->GetTarget()); - if (ownerDoc != parserDoc) { - // Willful violation of HTML5 as of 2010-12-01 - return false; - } - } - } - - RefPtr<nsScriptLoader> loader = ownerDoc->ScriptLoader(); - return loader->ProcessScriptElement(this); -} diff --git a/dom/base/nsScriptElement.h b/dom/base/nsScriptElement.h deleted file mode 100644 index 4a2a584ac..000000000 --- a/dom/base/nsScriptElement.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef nsScriptElement_h -#define nsScriptElement_h - -#include "mozilla/Attributes.h" -#include "nsIScriptLoaderObserver.h" -#include "nsIScriptElement.h" -#include "nsStubMutationObserver.h" - -/** - * Baseclass useful for script elements (such as <xhtml:script> and - * <svg:script>). Currently the class assumes that only the 'src' - * attribute and the children of the class affect what script to execute. - */ - -class nsScriptElement : public nsIScriptElement, - public nsStubMutationObserver -{ -public: - // nsIScriptLoaderObserver - NS_DECL_NSISCRIPTLOADEROBSERVER - - // nsIMutationObserver - NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED - NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED - - explicit nsScriptElement(mozilla::dom::FromParser aFromParser) - : nsIScriptElement(aFromParser) - { - } - - virtual nsresult FireErrorEvent() override; - -protected: - // Internal methods - - /** - * Check if this element contains any script, linked or inline - */ - virtual bool HasScriptContent() = 0; - - virtual bool MaybeProcessScript() override; -}; - -#endif // nsScriptElement_h diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp deleted file mode 100644 index 25482fe7b..000000000 --- a/dom/base/nsScriptLoader.cpp +++ /dev/null @@ -1,3061 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -/* - * A class that handles loading and evaluation of <script> elements. - */ - -#include "nsScriptLoader.h" - -#include "prsystem.h" -#include "jsapi.h" -#include "jsfriendapi.h" -#include "xpcpublic.h" -#include "nsCycleCollectionParticipant.h" -#include "nsIContent.h" -#include "nsJSUtils.h" -#include "mozilla/dom/ScriptSettings.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/SRILogHelper.h" -#include "nsGkAtoms.h" -#include "nsNetUtil.h" -#include "nsIScriptGlobalObject.h" -#include "nsIScriptContext.h" -#include "nsIScriptSecurityManager.h" -#include "nsIPrincipal.h" -#include "nsJSPrincipals.h" -#include "nsContentPolicyUtils.h" -#include "nsIHttpChannel.h" -#include "nsIHttpChannelInternal.h" -#include "nsIClassOfService.h" -#include "nsITimedChannel.h" -#include "nsIScriptElement.h" -#include "nsIDOMHTMLScriptElement.h" -#include "nsIDocShell.h" -#include "nsContentUtils.h" -#include "nsUnicharUtils.h" -#include "nsAutoPtr.h" -#include "nsIXPConnect.h" -#include "nsError.h" -#include "nsThreadUtils.h" -#include "nsDocShellCID.h" -#include "nsIContentSecurityPolicy.h" -#include "mozilla/Logging.h" -#include "nsCRT.h" -#include "nsContentCreatorFunctions.h" -#include "nsProxyRelease.h" -#include "nsSandboxFlags.h" -#include "nsContentTypeParser.h" -#include "nsINetworkPredictor.h" -#include "ImportManager.h" -#include "mozilla/dom/EncodingUtils.h" -#include "mozilla/ConsoleReportCollector.h" - -#include "mozilla/Attributes.h" -#include "mozilla/Unused.h" -#include "nsIScriptError.h" - -using namespace mozilla; -using namespace mozilla::dom; - -using JS::SourceBufferHolder; - -static LazyLogModule gCspPRLog("CSP"); - -void -ImplCycleCollectionUnlink(nsScriptLoadRequestList& aField); - -void -ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, - nsScriptLoadRequestList& aField, - const char* aName, - uint32_t aFlags = 0); - -////////////////////////////////////////////////////////////// -// nsScriptLoadRequest -////////////////////////////////////////////////////////////// - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptLoadRequest) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptLoadRequest) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptLoadRequest) - -NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptLoadRequest) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptLoadRequest) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptLoadRequest) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -nsScriptLoadRequest::~nsScriptLoadRequest() -{ - js_free(mScriptTextBuf); - - // We should always clean up any off-thread script parsing resources. - MOZ_ASSERT(!mOffThreadToken); - - // But play it safe in release builds and try to clean them up here - // as a fail safe. - MaybeCancelOffThreadScript(); -} - -void -nsScriptLoadRequest::SetReady() -{ - MOZ_ASSERT(mProgress != Progress::Ready); - mProgress = Progress::Ready; -} - -void -nsScriptLoadRequest::Cancel() -{ - MaybeCancelOffThreadScript(); - mIsCanceled = true; -} - -void -nsScriptLoadRequest::MaybeCancelOffThreadScript() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mOffThreadToken) { - return; - } - - JSContext* cx = danger::GetJSContext(); - JS::CancelOffThreadScript(cx, mOffThreadToken); - mOffThreadToken = nullptr; -} - -////////////////////////////////////////////////////////////// -// nsModuleLoadRequest -////////////////////////////////////////////////////////////// - -// A load request for a module, created for every top level module script and -// every module import. Load request can share an nsModuleScript if there are -// multiple imports of the same module. - -class nsModuleLoadRequest final : public nsScriptLoadRequest -{ - ~nsModuleLoadRequest() {} - - nsModuleLoadRequest(const nsModuleLoadRequest& aOther) = delete; - nsModuleLoadRequest(nsModuleLoadRequest&& aOther) = delete; - -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsModuleLoadRequest, nsScriptLoadRequest) - - nsModuleLoadRequest(nsIScriptElement* aElement, - uint32_t aVersion, - CORSMode aCORSMode, - const SRIMetadata& aIntegrity, - nsScriptLoader* aLoader); - - bool IsTopLevel() const { - return mIsTopLevel; - } - - void SetReady() override; - void Cancel() override; - - void ModuleLoaded(); - void DependenciesLoaded(); - void LoadFailed(); - - // Is this a request for a top level module script or an import? - bool mIsTopLevel; - - // The base URL used for resolving relative module imports. - nsCOMPtr<nsIURI> mBaseURL; - - // Pointer to the script loader, used to trigger actions when the module load - // finishes. - RefPtr<nsScriptLoader> mLoader; - - // The importing module, or nullptr for top level module scripts. Used to - // implement the ancestor list checked when fetching module dependencies. - RefPtr<nsModuleLoadRequest> mParent; - - // Set to a module script object after a successful load or nullptr on - // failure. - RefPtr<nsModuleScript> mModuleScript; - - // A promise that is completed on successful load of this module and all of - // its dependencies, indicating that the module is ready for instantiation and - // evaluation. - MozPromiseHolder<GenericPromise> mReady; - - // Array of imported modules. - nsTArray<RefPtr<nsModuleLoadRequest>> mImports; -}; - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsModuleLoadRequest) -NS_INTERFACE_MAP_END_INHERITING(nsScriptLoadRequest) - -NS_IMPL_CYCLE_COLLECTION_INHERITED(nsModuleLoadRequest, nsScriptLoadRequest, - mBaseURL, - mLoader, - mParent, - mModuleScript, - mImports) - -NS_IMPL_ADDREF_INHERITED(nsModuleLoadRequest, nsScriptLoadRequest) -NS_IMPL_RELEASE_INHERITED(nsModuleLoadRequest, nsScriptLoadRequest) - -nsModuleLoadRequest::nsModuleLoadRequest(nsIScriptElement* aElement, - uint32_t aVersion, - CORSMode aCORSMode, - const SRIMetadata &aIntegrity, - nsScriptLoader* aLoader) - : nsScriptLoadRequest(nsScriptKind::Module, - aElement, - aVersion, - aCORSMode, - aIntegrity), - mIsTopLevel(true), - mLoader(aLoader) -{} - -inline nsModuleLoadRequest* -nsScriptLoadRequest::AsModuleRequest() -{ - MOZ_ASSERT(IsModuleRequest()); - return static_cast<nsModuleLoadRequest*>(this); -} - -void nsModuleLoadRequest::Cancel() -{ - nsScriptLoadRequest::Cancel(); - mModuleScript = nullptr; - mProgress = nsScriptLoadRequest::Progress::Ready; - for (size_t i = 0; i < mImports.Length(); i++) { - mImports[i]->Cancel(); - } - mReady.RejectIfExists(NS_ERROR_FAILURE, __func__); -} - -void -nsModuleLoadRequest::SetReady() -{ -#ifdef DEBUG - for (size_t i = 0; i < mImports.Length(); i++) { - MOZ_ASSERT(mImports[i]->IsReadyToRun()); - } -#endif - - nsScriptLoadRequest::SetReady(); - mReady.ResolveIfExists(true, __func__); -} - -void -nsModuleLoadRequest::ModuleLoaded() -{ - // A module that was found to be marked as fetching in the module map has now - // been loaded. - - mModuleScript = mLoader->GetFetchedModule(mURI); - mLoader->StartFetchingModuleDependencies(this); -} - -void -nsModuleLoadRequest::DependenciesLoaded() -{ - // The module and all of its dependencies have been successfully fetched and - // compiled. - - if (!mLoader->InstantiateModuleTree(this)) { - LoadFailed(); - return; - } - - SetReady(); - mLoader->ProcessLoadedModuleTree(this); - mLoader = nullptr; - mParent = nullptr; -} - -void -nsModuleLoadRequest::LoadFailed() -{ - Cancel(); - mLoader->ProcessLoadedModuleTree(this); - mLoader = nullptr; - mParent = nullptr; -} - -////////////////////////////////////////////////////////////// -// nsModuleScript -////////////////////////////////////////////////////////////// - -// A single module script. May be used to satisfy multiple load requests. - -class nsModuleScript final : public nsISupports -{ - enum InstantiationState { - Uninstantiated, - Instantiated, - Errored - }; - - RefPtr<nsScriptLoader> mLoader; - nsCOMPtr<nsIURI> mBaseURL; - JS::Heap<JSObject*> mModuleRecord; - JS::Heap<JS::Value> mException; - InstantiationState mInstantiationState; - - ~nsModuleScript(); - -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsModuleScript) - - nsModuleScript(nsScriptLoader* aLoader, - nsIURI* aBaseURL, - JS::Handle<JSObject*> aModuleRecord); - - nsScriptLoader* Loader() const { return mLoader; } - JSObject* ModuleRecord() const { return mModuleRecord; } - JS::Value Exception() const { return mException; } - nsIURI* BaseURL() const { return mBaseURL; } - - void SetInstantiationResult(JS::Handle<JS::Value> aMaybeException); - bool IsUninstantiated() const { - return mInstantiationState == Uninstantiated; - } - bool IsInstantiated() const { - return mInstantiationState == Instantiated; - } - bool InstantiationFailed() const { - return mInstantiationState == Errored; - } - - void UnlinkModuleRecord(); -}; - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsModuleScript) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_CLASS(nsModuleScript) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsModuleScript) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL) - tmp->UnlinkModuleRecord(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsModuleScript) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsModuleScript) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mException) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsModuleScript) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsModuleScript) - -nsModuleScript::nsModuleScript(nsScriptLoader *aLoader, nsIURI* aBaseURL, - JS::Handle<JSObject*> aModuleRecord) - : mLoader(aLoader), - mBaseURL(aBaseURL), - mModuleRecord(aModuleRecord), - mInstantiationState(Uninstantiated) -{ - MOZ_ASSERT(mLoader); - MOZ_ASSERT(mBaseURL); - MOZ_ASSERT(mModuleRecord); - MOZ_ASSERT(mException.isUndefined()); - - // Make module's host defined field point to this module script object. - // This is cleared in the UnlinkModuleRecord(). - JS::SetModuleHostDefinedField(mModuleRecord, JS::PrivateValue(this)); - HoldJSObjects(this); -} - -void -nsModuleScript::UnlinkModuleRecord() -{ - // Remove module's back reference to this object request if present. - if (mModuleRecord) { - MOZ_ASSERT(JS::GetModuleHostDefinedField(mModuleRecord).toPrivate() == - this); - JS::SetModuleHostDefinedField(mModuleRecord, JS::UndefinedValue()); - } - mModuleRecord = nullptr; - mException.setUndefined(); -} - -nsModuleScript::~nsModuleScript() -{ - if (mModuleRecord) { - // The object may be destroyed without being unlinked first. - UnlinkModuleRecord(); - } - DropJSObjects(this); -} - -void -nsModuleScript::SetInstantiationResult(JS::Handle<JS::Value> aMaybeException) -{ - MOZ_ASSERT(mInstantiationState == Uninstantiated); - MOZ_ASSERT(mModuleRecord); - MOZ_ASSERT(mException.isUndefined()); - - if (aMaybeException.isUndefined()) { - mInstantiationState = Instantiated; - } else { - mModuleRecord = nullptr; - mException = aMaybeException; - mInstantiationState = Errored; - } -} - -////////////////////////////////////////////////////////////// - -// nsScriptLoadRequestList -////////////////////////////////////////////////////////////// - -nsScriptLoadRequestList::~nsScriptLoadRequestList() -{ - Clear(); -} - -void -nsScriptLoadRequestList::Clear() -{ - while (!isEmpty()) { - RefPtr<nsScriptLoadRequest> first = StealFirst(); - first->Cancel(); - // And just let it go out of scope and die. - } -} - -#ifdef DEBUG -bool -nsScriptLoadRequestList::Contains(nsScriptLoadRequest* aElem) const -{ - for (const nsScriptLoadRequest* req = getFirst(); - req; req = req->getNext()) { - if (req == aElem) { - return true; - } - } - - return false; -} -#endif // DEBUG - -inline void -ImplCycleCollectionUnlink(nsScriptLoadRequestList& aField) -{ - while (!aField.isEmpty()) { - RefPtr<nsScriptLoadRequest> first = aField.StealFirst(); - } -} - -inline void -ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, - nsScriptLoadRequestList& aField, - const char* aName, - uint32_t aFlags) -{ - for (nsScriptLoadRequest* request = aField.getFirst(); - request; request = request->getNext()) - { - CycleCollectionNoteChild(aCallback, request, aName, aFlags); - } -} - -////////////////////////////////////////////////////////////// -// nsScriptLoader::PreloadInfo -////////////////////////////////////////////////////////////// - -inline void -ImplCycleCollectionUnlink(nsScriptLoader::PreloadInfo& aField) -{ - ImplCycleCollectionUnlink(aField.mRequest); -} - -inline void -ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, - nsScriptLoader::PreloadInfo& aField, - const char* aName, - uint32_t aFlags = 0) -{ - ImplCycleCollectionTraverse(aCallback, aField.mRequest, aName, aFlags); -} - -////////////////////////////////////////////////////////////// -// nsScriptLoader -////////////////////////////////////////////////////////////// - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptLoader) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION(nsScriptLoader, - mNonAsyncExternalScriptInsertedRequests, - mLoadingAsyncRequests, - mLoadedAsyncRequests, - mDeferRequests, - mXSLTRequests, - mParserBlockingRequest, - mPreloads, - mPendingChildLoaders, - mFetchedModules) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptLoader) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptLoader) - -nsScriptLoader::nsScriptLoader(nsIDocument *aDocument) - : mDocument(aDocument), - mParserBlockingBlockerCount(0), - mBlockerCount(0), - mNumberOfProcessors(0), - mEnabled(true), - mDeferEnabled(false), - mDocumentParsingDone(false), - mBlockingDOMContentLoaded(false), - mReporter(new ConsoleReportCollector()) -{ -} - -nsScriptLoader::~nsScriptLoader() -{ - mObservers.Clear(); - - if (mParserBlockingRequest) { - mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT); - } - - for (nsScriptLoadRequest* req = mXSLTRequests.getFirst(); req; - req = req->getNext()) { - req->FireScriptAvailable(NS_ERROR_ABORT); - } - - for (nsScriptLoadRequest* req = mDeferRequests.getFirst(); req; - req = req->getNext()) { - req->FireScriptAvailable(NS_ERROR_ABORT); - } - - for (nsScriptLoadRequest* req = mLoadingAsyncRequests.getFirst(); req; - req = req->getNext()) { - req->FireScriptAvailable(NS_ERROR_ABORT); - } - - for (nsScriptLoadRequest* req = mLoadedAsyncRequests.getFirst(); req; - req = req->getNext()) { - req->FireScriptAvailable(NS_ERROR_ABORT); - } - - for(nsScriptLoadRequest* req = mNonAsyncExternalScriptInsertedRequests.getFirst(); - req; - req = req->getNext()) { - req->FireScriptAvailable(NS_ERROR_ABORT); - } - - // Unblock the kids, in case any of them moved to a different document - // subtree in the meantime and therefore aren't actually going away. - for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) { - mPendingChildLoaders[j]->RemoveParserBlockingScriptExecutionBlocker(); - } -} - -// Helper method for checking if the script element is an event-handler -// This means that it has both a for-attribute and a event-attribute. -// Also, if the for-attribute has a value that matches "\s*window\s*", -// and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an -// eventhandler. (both matches are case insensitive). -// This is how IE seems to filter out a window's onload handler from a -// <script for=... event=...> element. - -static bool -IsScriptEventHandler(nsIContent* aScriptElement) -{ - if (!aScriptElement->IsHTMLElement()) { - return false; - } - - nsAutoString forAttr, eventAttr; - if (!aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_for, forAttr) || - !aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, eventAttr)) { - return false; - } - - const nsAString& for_str = - nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(forAttr); - if (!for_str.LowerCaseEqualsLiteral("window")) { - return true; - } - - // We found for="window", now check for event="onload". - const nsAString& event_str = - nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(eventAttr, false); - if (!StringBeginsWith(event_str, NS_LITERAL_STRING("onload"), - nsCaseInsensitiveStringComparator())) { - // It ain't "onload.*". - - return true; - } - - nsAutoString::const_iterator start, end; - event_str.BeginReading(start); - event_str.EndReading(end); - - start.advance(6); // advance past "onload" - - if (start != end && *start != '(' && *start != ' ') { - // We got onload followed by something other than space or - // '('. Not good enough. - - return true; - } - - return false; -} - -nsresult -nsScriptLoader::CheckContentPolicy(nsIDocument* aDocument, - nsISupports *aContext, - nsIURI *aURI, - const nsAString &aType, - bool aIsPreLoad) -{ - nsContentPolicyType contentPolicyType = aIsPreLoad - ? nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD - : nsIContentPolicy::TYPE_INTERNAL_SCRIPT; - - int16_t shouldLoad = nsIContentPolicy::ACCEPT; - nsresult rv = NS_CheckContentLoadPolicy(contentPolicyType, - aURI, - aDocument->NodePrincipal(), - aContext, - NS_LossyConvertUTF16toASCII(aType), - nullptr, //extra - &shouldLoad, - nsContentUtils::GetContentPolicy(), - nsContentUtils::GetSecurityManager()); - if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { - if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) { - return NS_ERROR_CONTENT_BLOCKED; - } - return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT; - } - - return NS_OK; -} - -bool -nsScriptLoader::ModuleScriptsEnabled() -{ - static bool sEnabledForContent = false; - static bool sCachedPref = false; - if (!sCachedPref) { - sCachedPref = true; - Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false); - } - - return nsContentUtils::IsChromeDoc(mDocument) || sEnabledForContent; -} - -bool -nsScriptLoader::ModuleMapContainsModule(nsModuleLoadRequest *aRequest) const -{ - // Returns whether we have fetched, or are currently fetching, a module script - // for the request's URL. - return mFetchingModules.Contains(aRequest->mURI) || - mFetchedModules.Contains(aRequest->mURI); -} - -bool -nsScriptLoader::IsFetchingModule(nsModuleLoadRequest *aRequest) const -{ - bool fetching = mFetchingModules.Contains(aRequest->mURI); - MOZ_ASSERT_IF(fetching, !mFetchedModules.Contains(aRequest->mURI)); - return fetching; -} - -void -nsScriptLoader::SetModuleFetchStarted(nsModuleLoadRequest *aRequest) -{ - // Update the module map to indicate that a module is currently being fetched. - - MOZ_ASSERT(aRequest->IsLoading()); - MOZ_ASSERT(!ModuleMapContainsModule(aRequest)); - mFetchingModules.Put(aRequest->mURI, nullptr); -} - -void -nsScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(nsModuleLoadRequest *aRequest, - nsresult aResult) -{ - // Update module map with the result of fetching a single module script. The - // module script pointer is nullptr on error. - - MOZ_ASSERT(!aRequest->IsReadyToRun()); - - RefPtr<GenericPromise::Private> promise; - MOZ_ALWAYS_TRUE(mFetchingModules.Get(aRequest->mURI, getter_AddRefs(promise))); - mFetchingModules.Remove(aRequest->mURI); - - RefPtr<nsModuleScript> ms(aRequest->mModuleScript); - MOZ_ASSERT(NS_SUCCEEDED(aResult) == (ms != nullptr)); - mFetchedModules.Put(aRequest->mURI, ms); - - if (promise) { - if (ms) { - promise->Resolve(true, __func__); - } else { - promise->Reject(aResult, __func__); - } - } -} - -RefPtr<GenericPromise> -nsScriptLoader::WaitForModuleFetch(nsModuleLoadRequest *aRequest) -{ - MOZ_ASSERT(ModuleMapContainsModule(aRequest)); - - RefPtr<GenericPromise::Private> promise; - if (mFetchingModules.Get(aRequest->mURI, getter_AddRefs(promise))) { - if (!promise) { - promise = new GenericPromise::Private(__func__); - mFetchingModules.Put(aRequest->mURI, promise); - } - return promise; - } - - RefPtr<nsModuleScript> ms; - MOZ_ALWAYS_TRUE(mFetchedModules.Get(aRequest->mURI, getter_AddRefs(ms))); - if (!ms) { - return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); - } - - return GenericPromise::CreateAndResolve(true, __func__); -} - -nsModuleScript* -nsScriptLoader::GetFetchedModule(nsIURI* aURL) const -{ - bool found; - nsModuleScript* ms = mFetchedModules.GetWeak(aURL, &found); - MOZ_ASSERT(found); - return ms; -} - -nsresult -nsScriptLoader::ProcessFetchedModuleSource(nsModuleLoadRequest* aRequest) -{ - MOZ_ASSERT(!aRequest->mModuleScript); - - nsresult rv = CreateModuleScript(aRequest); - SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv); - - free(aRequest->mScriptTextBuf); - aRequest->mScriptTextBuf = nullptr; - aRequest->mScriptTextLength = 0; - - if (NS_SUCCEEDED(rv)) { - StartFetchingModuleDependencies(aRequest); - } - - return rv; -} - -nsresult -nsScriptLoader::CreateModuleScript(nsModuleLoadRequest* aRequest) -{ - MOZ_ASSERT(!aRequest->mModuleScript); - MOZ_ASSERT(aRequest->mBaseURL); - - nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject(); - if (!globalObject) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext(); - if (!context) { - return NS_ERROR_FAILURE; - } - - nsAutoMicroTask mt; - AutoEntryScript aes(globalObject, "CompileModule", true); - - bool oldProcessingScriptTag = context->GetProcessingScriptTag(); - context->SetProcessingScriptTag(true); - - nsresult rv; - { - // Update our current script. - AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement); - Maybe<AutoCurrentScriptUpdater> masterScriptUpdater; - nsCOMPtr<nsIDocument> master = mDocument->MasterDocument(); - if (master != mDocument) { - masterScriptUpdater.emplace(master->ScriptLoader(), - aRequest->mElement); - } - - JSContext* cx = aes.cx(); - JS::Rooted<JSObject*> module(cx); - - if (aRequest->mWasCompiledOMT) { - module = JS::FinishOffThreadModule(cx, aRequest->mOffThreadToken); - aRequest->mOffThreadToken = nullptr; - rv = module ? NS_OK : NS_ERROR_FAILURE; - } else { - JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject()); - - JS::CompileOptions options(cx); - rv = FillCompileOptionsForRequest(aes, aRequest, global, &options); - - if (NS_SUCCEEDED(rv)) { - nsAutoString inlineData; - SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); - rv = nsJSUtils::CompileModule(cx, srcBuf, global, options, &module); - } - } - MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr)); - if (module) { - aRequest->mModuleScript = - new nsModuleScript(this, aRequest->mBaseURL, module); - } - } - - context->SetProcessingScriptTag(oldProcessingScriptTag); - - return rv; -} - -static bool -ThrowTypeError(JSContext* aCx, nsModuleScript* aScript, - const nsString& aMessage) -{ - JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord()); - JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module)); - JS::Rooted<JSString*> filename(aCx); - filename = JS_NewStringCopyZ(aCx, JS_GetScriptFilename(script)); - if (!filename) { - return false; - } - - JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyZ(aCx, aMessage.get())); - if (!message) { - return false; - } - - JS::Rooted<JS::Value> error(aCx); - if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr, - message, &error)) { - return false; - } - - JS_SetPendingException(aCx, error); - return false; -} - -static bool -HandleResolveFailure(JSContext* aCx, nsModuleScript* aScript, - const nsAString& aSpecifier) -{ - // TODO: How can we get the line number of the failed import? - - nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: ")); - message.Append(aSpecifier); - - return ThrowTypeError(aCx, aScript, message); -} - -static bool -HandleModuleNotFound(JSContext* aCx, nsModuleScript* aScript, - const nsAString& aSpecifier) -{ - // TODO: How can we get the line number of the failed import? - - nsAutoString message(NS_LITERAL_STRING("Resolved module not found in map: ")); - message.Append(aSpecifier); - - return ThrowTypeError(aCx, aScript, message); -} - -static already_AddRefed<nsIURI> -ResolveModuleSpecifier(nsModuleScript* aScript, - const nsAString& aSpecifier) -{ - // The following module specifiers are allowed by the spec: - // - a valid absolute URL - // - a valid relative URL that starts with "/", "./" or "../" - // - // Bareword module specifiers are currently disallowed as these may be given - // special meanings in the future. - - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aSpecifier); - if (NS_SUCCEEDED(rv)) { - return uri.forget(); - } - - if (rv != NS_ERROR_MALFORMED_URI) { - return nullptr; - } - - if (!StringBeginsWith(aSpecifier, NS_LITERAL_STRING("/")) && - !StringBeginsWith(aSpecifier, NS_LITERAL_STRING("./")) && - !StringBeginsWith(aSpecifier, NS_LITERAL_STRING("../"))) { - return nullptr; - } - - rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, aScript->BaseURL()); - if (NS_SUCCEEDED(rv)) { - return uri.forget(); - } - - return nullptr; -} - -static nsresult -RequestedModuleIsInAncestorList(nsModuleLoadRequest* aRequest, nsIURI* aURL, bool* aResult) -{ - const size_t ImportDepthLimit = 100; - - *aResult = false; - size_t depth = 0; - while (aRequest) { - if (depth++ == ImportDepthLimit) { - return NS_ERROR_FAILURE; - } - - bool equal; - nsresult rv = aURL->Equals(aRequest->mURI, &equal); - NS_ENSURE_SUCCESS(rv, rv); - if (equal) { - *aResult = true; - return NS_OK; - } - - aRequest = aRequest->mParent; - } - - return NS_OK; -} - -static nsresult -ResolveRequestedModules(nsModuleLoadRequest* aRequest, nsCOMArray<nsIURI> &aUrls) -{ - nsModuleScript* ms = aRequest->mModuleScript; - - AutoJSAPI jsapi; - if (!jsapi.Init(ms->ModuleRecord())) { - return NS_ERROR_FAILURE; - } - - JSContext* cx = jsapi.cx(); - JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord()); - JS::Rooted<JSObject*> specifiers(cx, JS::GetRequestedModules(cx, moduleRecord)); - - uint32_t length; - if (!JS_GetArrayLength(cx, specifiers, &length)) { - return NS_ERROR_FAILURE; - } - - JS::Rooted<JS::Value> val(cx); - for (uint32_t i = 0; i < length; i++) { - if (!JS_GetElement(cx, specifiers, i, &val)) { - return NS_ERROR_FAILURE; - } - - nsAutoJSString specifier; - if (!specifier.init(cx, val)) { - return NS_ERROR_FAILURE; - } - - // Let url be the result of resolving a module specifier given module script and requested. - nsModuleScript* ms = aRequest->mModuleScript; - nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier); - if (!uri) { - HandleResolveFailure(cx, ms, specifier); - return NS_ERROR_FAILURE; - } - - bool isAncestor; - nsresult rv = RequestedModuleIsInAncestorList(aRequest, uri, &isAncestor); - NS_ENSURE_SUCCESS(rv, rv); - if (!isAncestor) { - aUrls.AppendElement(uri.forget()); - } - } - - return NS_OK; -} - -void -nsScriptLoader::StartFetchingModuleDependencies(nsModuleLoadRequest* aRequest) -{ - MOZ_ASSERT(aRequest->mModuleScript); - MOZ_ASSERT(!aRequest->IsReadyToRun()); - aRequest->mProgress = nsModuleLoadRequest::Progress::FetchingImports; - - nsCOMArray<nsIURI> urls; - nsresult rv = ResolveRequestedModules(aRequest, urls); - if (NS_FAILED(rv)) { - aRequest->LoadFailed(); - return; - } - - if (urls.Length() == 0) { - // There are no descendents to load so this request is ready. - aRequest->DependenciesLoaded(); - return; - } - - // For each url in urls, fetch a module script tree given url, module script's - // CORS setting, and module script's settings object. - nsTArray<RefPtr<GenericPromise>> importsReady; - for (size_t i = 0; i < urls.Length(); i++) { - RefPtr<GenericPromise> childReady = - StartFetchingModuleAndDependencies(aRequest, urls[i]); - importsReady.AppendElement(childReady); - } - - // Wait for all imports to become ready. - RefPtr<GenericPromise::AllPromiseType> allReady = - GenericPromise::All(AbstractThread::GetCurrent(), importsReady); - allReady->Then(AbstractThread::GetCurrent(), __func__, aRequest, - &nsModuleLoadRequest::DependenciesLoaded, - &nsModuleLoadRequest::LoadFailed); -} - -RefPtr<GenericPromise> -nsScriptLoader::StartFetchingModuleAndDependencies(nsModuleLoadRequest* aRequest, - nsIURI* aURI) -{ - MOZ_ASSERT(aURI); - - RefPtr<nsModuleLoadRequest> childRequest = - new nsModuleLoadRequest(aRequest->mElement, aRequest->mJSVersion, - aRequest->mCORSMode, aRequest->mIntegrity, this); - - childRequest->mIsTopLevel = false; - childRequest->mURI = aURI; - childRequest->mIsInline = false; - childRequest->mReferrerPolicy = aRequest->mReferrerPolicy; - childRequest->mParent = aRequest; - - RefPtr<GenericPromise> ready = childRequest->mReady.Ensure(__func__); - - nsresult rv = StartLoad(childRequest, NS_LITERAL_STRING("module"), false); - if (NS_FAILED(rv)) { - childRequest->mReady.Reject(rv, __func__); - return ready; - } - - aRequest->mImports.AppendElement(childRequest); - return ready; -} - -bool -HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) -{ - MOZ_ASSERT(argc == 2); - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - JS::Rooted<JSObject*> module(aCx, &args[0].toObject()); - JS::Rooted<JSString*> specifier(aCx, args[1].toString()); - - // Let referencing module script be referencingModule.[[HostDefined]]. - JS::Value value = JS::GetModuleHostDefinedField(module); - auto script = static_cast<nsModuleScript*>(value.toPrivate()); - MOZ_ASSERT(script->ModuleRecord() == module); - - // Let url be the result of resolving a module specifier given referencing - // module script and specifier. If the result is failure, throw a TypeError - // exception and abort these steps. - nsAutoJSString string; - if (!string.init(aCx, specifier)) { - return false; - } - - nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string); - if (!uri) { - return HandleResolveFailure(aCx, script, string); - } - - // Let resolved module script be the value of the entry in module map whose - // key is url. If no such entry exists, throw a TypeError exception and abort - // these steps. - nsModuleScript* ms = script->Loader()->GetFetchedModule(uri); - if (!ms) { - return HandleModuleNotFound(aCx, script, string); - } - - if (ms->InstantiationFailed()) { - JS::Rooted<JS::Value> exception(aCx, ms->Exception()); - JS_SetPendingException(aCx, exception); - return false; - } - - *vp = JS::ObjectValue(*ms->ModuleRecord()); - return true; -} - -static nsresult -EnsureModuleResolveHook(JSContext* aCx) -{ - if (JS::GetModuleResolveHook(aCx)) { - return NS_OK; - } - - JS::Rooted<JSFunction*> func(aCx); - func = JS_NewFunction(aCx, HostResolveImportedModule, 2, 0, - "HostResolveImportedModule"); - if (!func) { - return NS_ERROR_FAILURE; - } - - JS::SetModuleResolveHook(aCx, func); - return NS_OK; -} - -void -nsScriptLoader::ProcessLoadedModuleTree(nsModuleLoadRequest* aRequest) -{ - if (aRequest->IsTopLevel()) { - MaybeMoveToLoadedList(aRequest); - ProcessPendingRequests(); - } - - if (aRequest->mWasCompiledOMT) { - mDocument->UnblockOnload(false); - } -} - -bool -nsScriptLoader::InstantiateModuleTree(nsModuleLoadRequest* aRequest) -{ - // Perform eager instantiation of the loaded module tree. - - MOZ_ASSERT(aRequest); - - nsModuleScript* ms = aRequest->mModuleScript; - MOZ_ASSERT(ms); - if (!ms || !ms->ModuleRecord()) { - return false; - } - - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(ms->ModuleRecord()))) { - return false; - } - - nsresult rv = EnsureModuleResolveHook(jsapi.cx()); - NS_ENSURE_SUCCESS(rv, false); - - JS::Rooted<JSObject*> module(jsapi.cx(), ms->ModuleRecord()); - bool ok = NS_SUCCEEDED(nsJSUtils::ModuleDeclarationInstantiation(jsapi.cx(), module)); - - JS::RootedValue exception(jsapi.cx()); - if (!ok) { - MOZ_ASSERT(jsapi.HasException()); - if (!jsapi.StealException(&exception)) { - return false; - } - MOZ_ASSERT(!exception.isUndefined()); - } - - // Mark this module and any uninstantiated dependencies found via depth-first - // search as instantiated and record any error. - - mozilla::Vector<nsModuleLoadRequest*, 1> requests; - if (!requests.append(aRequest)) { - return false; - } - - while (!requests.empty()) { - nsModuleLoadRequest* request = requests.popCopy(); - nsModuleScript* ms = request->mModuleScript; - if (!ms->IsUninstantiated()) { - continue; - } - - ms->SetInstantiationResult(exception); - - for (auto import : request->mImports) { - if (import->mModuleScript->IsUninstantiated() && - !requests.append(import)) - { - return false; - } - } - } - - return true; -} - -nsresult -nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, - bool aScriptFromHead) -{ - MOZ_ASSERT(aRequest->IsLoading()); - NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER); - - // If this document is sandboxed without 'allow-scripts', abort. - if (mDocument->HasScriptsBlockedBySandbox()) { - return NS_OK; - } - - if (aRequest->IsModuleRequest()) { - // Check whether the module has been fetched or is currently being fetched, - // and if so wait for it. - nsModuleLoadRequest* request = aRequest->AsModuleRequest(); - if (ModuleMapContainsModule(request)) { - WaitForModuleFetch(request) - ->Then(AbstractThread::GetCurrent(), __func__, request, - &nsModuleLoadRequest::ModuleLoaded, - &nsModuleLoadRequest::LoadFailed); - return NS_OK; - } - - // Otherwise put the URL in the module map and mark it as fetching. - SetModuleFetchStarted(request); - } - - nsContentPolicyType contentPolicyType = aRequest->IsPreload() - ? nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD - : nsIContentPolicy::TYPE_INTERNAL_SCRIPT; - nsCOMPtr<nsINode> context; - if (aRequest->mElement) { - context = do_QueryInterface(aRequest->mElement); - } - else { - context = mDocument; - } - - nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup(); - nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->MasterDocument()->GetWindow(); - NS_ENSURE_TRUE(window, NS_ERROR_NULL_POINTER); - nsIDocShell *docshell = window->GetDocShell(); - nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell)); - - nsSecurityFlags securityFlags; - if (aRequest->IsModuleRequest()) { - // According to the spec, module scripts have different behaviour to classic - // scripts and always use CORS. - securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; - if (aRequest->mCORSMode == CORS_NONE) { - securityFlags |= nsILoadInfo::SEC_COOKIES_OMIT; - } else if (aRequest->mCORSMode == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else { - MOZ_ASSERT(aRequest->mCORSMode == CORS_USE_CREDENTIALS); - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; - } - } else { - securityFlags = aRequest->mCORSMode == CORS_NONE - ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL - : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; - if (aRequest->mCORSMode == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; - } - } - securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; - - nsCOMPtr<nsIChannel> channel; - nsresult rv = NS_NewChannel(getter_AddRefs(channel), - aRequest->mURI, - context, - securityFlags, - contentPolicyType, - loadGroup, - prompter, - nsIRequest::LOAD_NORMAL | - nsIChannel::LOAD_CLASSIFY_URI); - - NS_ENSURE_SUCCESS(rv, rv); - - nsIScriptElement *script = aRequest->mElement; - nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel)); - - if (cos) { - if (aScriptFromHead && - !(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) { - // synchronous head scripts block lading of most other non js/css - // content such as images - cos->AddClassFlags(nsIClassOfService::Leader); - } else if (!(script && script->GetScriptDeferred())) { - // other scripts are neither blocked nor prioritized unless marked deferred - cos->AddClassFlags(nsIClassOfService::Unblocked); - } - } - - nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); - if (httpChannel) { - // HTTP content negotation has little value in this context. - httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), - NS_LITERAL_CSTRING("*/*"), - false); - httpChannel->SetReferrerWithPolicy(mDocument->GetDocumentURI(), - aRequest->mReferrerPolicy); - - nsCOMPtr<nsIHttpChannelInternal> internalChannel(do_QueryInterface(httpChannel)); - if (internalChannel) { - internalChannel->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString()); - } - } - - nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(docshell)); - mozilla::net::PredictorLearn(aRequest->mURI, mDocument->GetDocumentURI(), - nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadContext); - - // Set the initiator type - nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel)); - if (timedChannel) { - timedChannel->SetInitiatorType(NS_LITERAL_STRING("script")); - } - - nsAutoPtr<mozilla::dom::SRICheckDataVerifier> sriDataVerifier; - if (!aRequest->mIntegrity.IsEmpty()) { - nsAutoCString sourceUri; - if (mDocument->GetDocumentURI()) { - mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); - } - sriDataVerifier = new SRICheckDataVerifier(aRequest->mIntegrity, sourceUri, - mReporter); - } - - RefPtr<nsScriptLoadHandler> handler = - new nsScriptLoadHandler(this, aRequest, sriDataVerifier.forget()); - - nsCOMPtr<nsIIncrementalStreamLoader> loader; - rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler); - NS_ENSURE_SUCCESS(rv, rv); - - return channel->AsyncOpen2(loader); -} - -bool -nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi, - nsIURI * const &aURI) const -{ - bool same; - return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) && - same; -} - -class nsScriptRequestProcessor : public Runnable -{ -private: - RefPtr<nsScriptLoader> mLoader; - RefPtr<nsScriptLoadRequest> mRequest; -public: - nsScriptRequestProcessor(nsScriptLoader* aLoader, - nsScriptLoadRequest* aRequest) - : mLoader(aLoader) - , mRequest(aRequest) - {} - NS_IMETHOD Run() override - { - return mLoader->ProcessRequest(mRequest); - } -}; - -static inline bool -ParseTypeAttribute(const nsAString& aType, JSVersion* aVersion) -{ - MOZ_ASSERT(!aType.IsEmpty()); - MOZ_ASSERT(aVersion); - MOZ_ASSERT(*aVersion == JSVERSION_DEFAULT); - - nsContentTypeParser parser(aType); - - nsAutoString mimeType; - nsresult rv = parser.GetType(mimeType); - NS_ENSURE_SUCCESS(rv, false); - - if (!nsContentUtils::IsJavascriptMIMEType(mimeType)) { - return false; - } - - // Get the version string, and ensure the language supports it. - nsAutoString versionName; - rv = parser.GetParameter("version", versionName); - - if (NS_SUCCEEDED(rv)) { - *aVersion = nsContentUtils::ParseJavascriptVersion(versionName); - } else if (rv != NS_ERROR_INVALID_ARG) { - return false; - } - - return true; -} - -static bool -CSPAllowsInlineScript(nsIScriptElement *aElement, nsIDocument *aDocument) -{ - nsCOMPtr<nsIContentSecurityPolicy> csp; - // Note: For imports NodePrincipal and the principal of the master are - // the same. - nsresult rv = aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp)); - NS_ENSURE_SUCCESS(rv, false); - - if (!csp) { - // no CSP --> allow - return true; - } - - // query the nonce - nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement); - nsAutoString nonce; - scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce); - bool parserCreated = aElement->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER; - - // query the scripttext - nsAutoString scriptText; - aElement->GetScriptText(scriptText); - - bool allowInlineScript = false; - rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT, - nonce, parserCreated, scriptText, - aElement->GetScriptLineNumber(), - &allowInlineScript); - return allowInlineScript; -} - -nsScriptLoadRequest* -nsScriptLoader::CreateLoadRequest(nsScriptKind aKind, - nsIScriptElement* aElement, - uint32_t aVersion, CORSMode aCORSMode, - const SRIMetadata &aIntegrity) -{ - if (aKind == nsScriptKind::Classic) { - return new nsScriptLoadRequest(aKind, aElement, aVersion, aCORSMode, - aIntegrity); - } - - MOZ_ASSERT(aKind == nsScriptKind::Module); - return new nsModuleLoadRequest(aElement, aVersion, aCORSMode, aIntegrity, - this); -} - -bool -nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) -{ - // We need a document to evaluate scripts. - NS_ENSURE_TRUE(mDocument, false); - - // Check to see if scripts has been turned off. - if (!mEnabled || !mDocument->IsScriptEnabled()) { - return false; - } - - NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script"); - - nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement); - - // Step 13. Check that the script is not an eventhandler - if (IsScriptEventHandler(scriptContent)) { - return false; - } - - JSVersion version = JSVERSION_DEFAULT; - - // Check the type attribute to determine language and version. - // If type exists, it trumps the deprecated 'language=' - nsAutoString type; - bool hasType = aElement->GetScriptType(type); - - nsScriptKind scriptKind = nsScriptKind::Classic; - if (!type.IsEmpty()) { - if (ModuleScriptsEnabled() && type.LowerCaseEqualsASCII("module")) { - scriptKind = nsScriptKind::Module; - } else { - NS_ENSURE_TRUE(ParseTypeAttribute(type, &version), false); - } - } else if (!hasType) { - // no 'type=' element - // "language" is a deprecated attribute of HTML, so we check it only for - // HTML script elements. - if (scriptContent->IsHTMLElement()) { - nsAutoString language; - scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::language, language); - if (!language.IsEmpty()) { - if (!nsContentUtils::IsJavaScriptLanguage(language)) { - return false; - } - } - } - } - - // "In modern user agents that support module scripts, the script element with - // the nomodule attribute will be ignored". - // "The nomodule attribute must not be specified on module scripts (and will - // be ignored if it is)." - if (ModuleScriptsEnabled() && - scriptKind == nsScriptKind::Classic && - scriptContent->IsHTMLElement() && - scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) { - return false; - } - - // Step 15. and later in the HTML5 spec - nsresult rv = NS_OK; - RefPtr<nsScriptLoadRequest> request; - if (aElement->GetScriptExternal()) { - // external script - nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI(); - if (!scriptURI) { - // Asynchronously report the failure to create a URI object - NS_DispatchToCurrentThread( - NewRunnableMethod(aElement, - &nsIScriptElement::FireErrorEvent)); - return false; - } - - // Double-check that the preload matches what we're asked to load now. - mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy(); - CORSMode ourCORSMode = aElement->GetCORSMode(); - nsTArray<PreloadInfo>::index_type i = - mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator()); - if (i != nsTArray<PreloadInfo>::NoIndex) { - // preloaded - // note that a script-inserted script can steal a preload! - request = mPreloads[i].mRequest; - request->mElement = aElement; - nsString preloadCharset(mPreloads[i].mCharset); - mPreloads.RemoveElementAt(i); - - // Double-check that the charset the preload used is the same as - // the charset we have now. - nsAutoString elementCharset; - aElement->GetScriptCharset(elementCharset); - if (elementCharset.Equals(preloadCharset) && - ourCORSMode == request->mCORSMode && - ourRefPolicy == request->mReferrerPolicy && - scriptKind == request->mKind) { - rv = CheckContentPolicy(mDocument, aElement, request->mURI, type, false); - if (NS_FAILED(rv)) { - // probably plans have changed; even though the preload was allowed seems - // like the actual load is not; let's cancel the preload request. - request->Cancel(); - return false; - } - } else { - // Drop the preload - request = nullptr; - } - } - - if (!request) { - // no usable preload - - SRIMetadata sriMetadata; - { - nsAutoString integrity; - scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, - integrity); - if (!integrity.IsEmpty()) { - MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug, - ("nsScriptLoader::ProcessScriptElement, integrity=%s", - NS_ConvertUTF16toUTF8(integrity).get())); - nsAutoCString sourceUri; - if (mDocument->GetDocumentURI()) { - mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); - } - SRICheck::IntegrityMetadata(integrity, sourceUri, mReporter, - &sriMetadata); - } - } - - request = CreateLoadRequest(scriptKind, aElement, version, ourCORSMode, - sriMetadata); - request->mURI = scriptURI; - request->mIsInline = false; - request->mReferrerPolicy = ourRefPolicy; - - // set aScriptFromHead to false so we don't treat non preloaded scripts as - // blockers for full page load. See bug 792438. - rv = StartLoad(request, type, false); - if (NS_FAILED(rv)) { - // Asynchronously report the load failure - NS_DispatchToCurrentThread( - NewRunnableMethod(aElement, - &nsIScriptElement::FireErrorEvent)); - return false; - } - } - - // Should still be in loading stage of script. - NS_ASSERTION(!request->InCompilingStage(), - "Request should not yet be in compiling stage."); - - request->mJSVersion = version; - - if (aElement->GetScriptAsync()) { - request->mIsAsync = true; - if (request->IsReadyToRun()) { - mLoadedAsyncRequests.AppendElement(request); - // The script is available already. Run it ASAP when the event - // loop gets a chance to spin. - - // KVKV TODO: Instead of processing immediately, try off-thread-parsing - // it and only schedule a pending ProcessRequest if that fails. - ProcessPendingRequestsAsync(); - } else { - mLoadingAsyncRequests.AppendElement(request); - } - return false; - } - if (!aElement->GetParserCreated()) { - // Violate the HTML5 spec in order to make LABjs and the "order" plug-in - // for RequireJS work with their Gecko-sniffed code path. See - // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html - request->mIsNonAsyncScriptInserted = true; - mNonAsyncExternalScriptInsertedRequests.AppendElement(request); - if (request->IsReadyToRun()) { - // The script is available already. Run it ASAP when the event - // loop gets a chance to spin. - ProcessPendingRequestsAsync(); - } - return false; - } - // we now have a parser-inserted request that may or may not be still - // loading - if (aElement->GetScriptDeferred() || request->IsModuleRequest()) { - // We don't want to run this yet. - // If we come here, the script is a parser-created script and it has - // the defer attribute but not the async attribute. Since a - // a parser-inserted script is being run, we came here by the parser - // running the script, which means the parser is still alive and the - // parse is ongoing. - NS_ASSERTION(mDocument->GetCurrentContentSink() || - aElement->GetParserCreated() == FROM_PARSER_XSLT, - "Non-XSLT Defer script on a document without an active parser; bug 592366."); - AddDeferRequest(request); - return false; - } - - if (aElement->GetParserCreated() == FROM_PARSER_XSLT) { - // Need to maintain order for XSLT-inserted scripts - NS_ASSERTION(!mParserBlockingRequest, - "Parser-blocking scripts and XSLT scripts in the same doc!"); - request->mIsXSLT = true; - mXSLTRequests.AppendElement(request); - if (request->IsReadyToRun()) { - // The script is available already. Run it ASAP when the event - // loop gets a chance to spin. - ProcessPendingRequestsAsync(); - } - return true; - } - - if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) { - // The request has already been loaded and there are no pending style - // sheets. If the script comes from the network stream, cheat for - // performance reasons and avoid a trip through the event loop. - if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) { - return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK; - } - // Otherwise, we've got a document.written script, make a trip through - // the event loop to hide the preload effects from the scripts on the - // Web page. - NS_ASSERTION(!mParserBlockingRequest, - "There can be only one parser-blocking script at a time"); - NS_ASSERTION(mXSLTRequests.isEmpty(), - "Parser-blocking scripts and XSLT scripts in the same doc!"); - mParserBlockingRequest = request; - ProcessPendingRequestsAsync(); - return true; - } - - // The script hasn't loaded yet or there's a style sheet blocking it. - // The script will be run when it loads or the style sheet loads. - NS_ASSERTION(!mParserBlockingRequest, - "There can be only one parser-blocking script at a time"); - NS_ASSERTION(mXSLTRequests.isEmpty(), - "Parser-blocking scripts and XSLT scripts in the same doc!"); - mParserBlockingRequest = request; - return true; - } - - // inline script - // Is this document sandboxed without 'allow-scripts'? - if (mDocument->HasScriptsBlockedBySandbox()) { - return false; - } - - // Does CSP allow this inline script to run? - if (!CSPAllowsInlineScript(aElement, mDocument)) { - return false; - } - - // Inline scripts ignore ther CORS mode and are always CORS_NONE - request = CreateLoadRequest(scriptKind, aElement, version, CORS_NONE, - SRIMetadata()); // SRI doesn't apply - request->mJSVersion = version; - request->mIsInline = true; - request->mURI = mDocument->GetDocumentURI(); - request->mLineNo = aElement->GetScriptLineNumber(); - - if (request->IsModuleRequest()) { - nsModuleLoadRequest* modReq = request->AsModuleRequest(); - modReq->mBaseURL = mDocument->GetDocBaseURI(); - rv = CreateModuleScript(modReq); - NS_ENSURE_SUCCESS(rv, false); - StartFetchingModuleDependencies(modReq); - if (aElement->GetScriptAsync()) { - mLoadingAsyncRequests.AppendElement(request); - } else { - AddDeferRequest(request); - } - return false; - } - request->mProgress = nsScriptLoadRequest::Progress::Ready; - if (aElement->GetParserCreated() == FROM_PARSER_XSLT && - (!ReadyToExecuteParserBlockingScripts() || !mXSLTRequests.isEmpty())) { - // Need to maintain order for XSLT-inserted scripts - NS_ASSERTION(!mParserBlockingRequest, - "Parser-blocking scripts and XSLT scripts in the same doc!"); - mXSLTRequests.AppendElement(request); - return true; - } - if (aElement->GetParserCreated() == NOT_FROM_PARSER) { - NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), - "A script-inserted script is inserted without an update batch?"); - nsContentUtils::AddScriptRunner(new nsScriptRequestProcessor(this, - request)); - return false; - } - if (aElement->GetParserCreated() == FROM_PARSER_NETWORK && - !ReadyToExecuteParserBlockingScripts()) { - NS_ASSERTION(!mParserBlockingRequest, - "There can be only one parser-blocking script at a time"); - mParserBlockingRequest = request; - NS_ASSERTION(mXSLTRequests.isEmpty(), - "Parser-blocking scripts and XSLT scripts in the same doc!"); - return true; - } - // We now have a document.written inline script or we have an inline script - // from the network but there is no style sheet that is blocking scripts. - // Don't check for style sheets blocking scripts in the document.write - // case to avoid style sheet network activity affecting when - // document.write returns. It's not really necessary to do this if - // there's no document.write currently on the call stack. However, - // this way matches IE more closely than checking if document.write - // is on the call stack. - NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), - "Not safe to run a parser-inserted script?"); - return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK; -} - -namespace { - -class NotifyOffThreadScriptLoadCompletedRunnable : public Runnable -{ - RefPtr<nsScriptLoadRequest> mRequest; - RefPtr<nsScriptLoader> mLoader; - void *mToken; - -public: - NotifyOffThreadScriptLoadCompletedRunnable(nsScriptLoadRequest* aRequest, - nsScriptLoader* aLoader) - : mRequest(aRequest), mLoader(aLoader), mToken(nullptr) - {} - - virtual ~NotifyOffThreadScriptLoadCompletedRunnable(); - - void SetToken(void* aToken) { - MOZ_ASSERT(aToken && !mToken); - mToken = aToken; - } - - NS_DECL_NSIRUNNABLE -}; - -} /* anonymous namespace */ - -nsresult -nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest) -{ - MOZ_ASSERT(aRequest->mProgress == nsScriptLoadRequest::Progress::Compiling); - MOZ_ASSERT(!aRequest->mWasCompiledOMT); - - aRequest->mWasCompiledOMT = true; - - if (aRequest->IsModuleRequest()) { - MOZ_ASSERT(aRequest->mOffThreadToken); - nsModuleLoadRequest* request = aRequest->AsModuleRequest(); - nsresult rv = ProcessFetchedModuleSource(request); - if (NS_FAILED(rv)) { - request->LoadFailed(); - } - return rv; - } - - aRequest->SetReady(); - - if (aRequest == mParserBlockingRequest) { - if (!ReadyToExecuteParserBlockingScripts()) { - // If not ready to execute scripts, schedule an async call to - // ProcessPendingRequests to handle it. - ProcessPendingRequestsAsync(); - return NS_OK; - } - - // Same logic as in top of ProcessPendingRequests. - mParserBlockingRequest = nullptr; - UnblockParser(aRequest); - ProcessRequest(aRequest); - mDocument->UnblockOnload(false); - ContinueParserAsync(aRequest); - return NS_OK; - } - - nsresult rv = ProcessRequest(aRequest); - mDocument->UnblockOnload(false); - return rv; -} - -NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable() -{ - if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) { - NS_ReleaseOnMainThread(mRequest.forget()); - NS_ReleaseOnMainThread(mLoader.forget()); - } -} - -NS_IMETHODIMP -NotifyOffThreadScriptLoadCompletedRunnable::Run() -{ - MOZ_ASSERT(NS_IsMainThread()); - - // We want these to be dropped on the main thread, once we return from this - // function. - RefPtr<nsScriptLoadRequest> request = mRequest.forget(); - RefPtr<nsScriptLoader> loader = mLoader.forget(); - - request->mOffThreadToken = mToken; - nsresult rv = loader->ProcessOffThreadRequest(request); - - return rv; -} - -static void -OffThreadScriptLoaderCallback(void *aToken, void *aCallbackData) -{ - RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable = - dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData)); - aRunnable->SetToken(aToken); - NS_DispatchToMainThread(aRunnable); -} - -nsresult -nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest) -{ - MOZ_ASSERT_IF(!aRequest->IsModuleRequest(), aRequest->IsReadyToRun()); - MOZ_ASSERT(!aRequest->mWasCompiledOMT); - - // Don't off-thread compile inline scripts. - if (aRequest->mIsInline) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject(); - if (!globalObject) { - return NS_ERROR_FAILURE; - } - - AutoJSAPI jsapi; - if (!jsapi.Init(globalObject)) { - return NS_ERROR_FAILURE; - } - - JSContext* cx = jsapi.cx(); - JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject()); - JS::CompileOptions options(cx); - - nsresult rv = FillCompileOptionsForRequest(jsapi, aRequest, global, &options); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) { - return NS_ERROR_FAILURE; - } - - RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable = - new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this); - - if (aRequest->IsModuleRequest()) { - if (!JS::CompileOffThreadModule(cx, options, - aRequest->mScriptTextBuf, aRequest->mScriptTextLength, - OffThreadScriptLoaderCallback, - static_cast<void*>(runnable))) { - return NS_ERROR_OUT_OF_MEMORY; - } - } else { - if (!JS::CompileOffThread(cx, options, - aRequest->mScriptTextBuf, aRequest->mScriptTextLength, - OffThreadScriptLoaderCallback, - static_cast<void*>(runnable))) { - return NS_ERROR_OUT_OF_MEMORY; - } - } - - mDocument->BlockOnload(); - aRequest->mProgress = nsScriptLoadRequest::Progress::Compiling; - - Unused << runnable.forget(); - return NS_OK; -} - -nsresult -nsScriptLoader::CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest) -{ - NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), - "Processing requests when running scripts is unsafe."); - NS_ASSERTION(!aRequest->mOffThreadToken, - "Candidate for off-thread compile is already parsed off-thread"); - NS_ASSERTION(!aRequest->InCompilingStage(), - "Candidate for off-thread compile is already in compiling stage."); - - nsresult rv = AttemptAsyncScriptCompile(aRequest); - if (NS_SUCCEEDED(rv)) { - return rv; - } - - return ProcessRequest(aRequest); -} - -SourceBufferHolder -nsScriptLoader::GetScriptSource(nsScriptLoadRequest* aRequest, nsAutoString& inlineData) -{ - // Return a SourceBufferHolder object holding the script's source text. - // |inlineData| is used to hold the text for inline objects. - - // If there's no script text, we try to get it from the element - if (aRequest->mIsInline) { - // XXX This is inefficient - GetText makes multiple - // copies. - aRequest->mElement->GetScriptText(inlineData); - return SourceBufferHolder(inlineData.get(), - inlineData.Length(), - SourceBufferHolder::NoOwnership); - } - - return SourceBufferHolder(aRequest->mScriptTextBuf, - aRequest->mScriptTextLength, - SourceBufferHolder::NoOwnership); -} - -nsresult -nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest) -{ - NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), - "Processing requests when running scripts is unsafe."); - NS_ASSERTION(aRequest->IsReadyToRun(), - "Processing a request that is not ready to run."); - - NS_ENSURE_ARG(aRequest); - - if (aRequest->IsModuleRequest() && - !aRequest->AsModuleRequest()->mModuleScript) - { - // There was an error parsing a module script. Nothing to do here. - FireScriptAvailable(NS_ERROR_FAILURE, aRequest); - return NS_OK; - } - - nsCOMPtr<nsINode> scriptElem = do_QueryInterface(aRequest->mElement); - - nsCOMPtr<nsIDocument> doc; - if (!aRequest->mIsInline) { - doc = scriptElem->OwnerDoc(); - } - - nsCOMPtr<nsIScriptElement> oldParserInsertedScript; - uint32_t parserCreated = aRequest->mElement->GetParserCreated(); - if (parserCreated) { - oldParserInsertedScript = mCurrentParserInsertedScript; - mCurrentParserInsertedScript = aRequest->mElement; - } - - aRequest->mElement->BeginEvaluating(); - - FireScriptAvailable(NS_OK, aRequest); - - // The window may have gone away by this point, in which case there's no point - // in trying to run the script. - nsCOMPtr<nsIDocument> master = mDocument->MasterDocument(); - { - // Try to perform a microtask checkpoint - nsAutoMicroTask mt; - } - - nsPIDOMWindowInner *pwin = master->GetInnerWindow(); - bool runScript = !!pwin; - if (runScript) { - nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(), - scriptElem, - NS_LITERAL_STRING("beforescriptexecute"), - true, true, &runScript); - } - - // Inner window could have gone away after firing beforescriptexecute - pwin = master->GetInnerWindow(); - if (!pwin) { - runScript = false; - } - - nsresult rv = NS_OK; - if (runScript) { - if (doc) { - doc->BeginEvaluatingExternalScript(); - } - rv = EvaluateScript(aRequest); - if (doc) { - doc->EndEvaluatingExternalScript(); - } - - nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(), - scriptElem, - NS_LITERAL_STRING("afterscriptexecute"), - true, false); - } - - FireScriptEvaluated(rv, aRequest); - - aRequest->mElement->EndEvaluating(); - - if (parserCreated) { - mCurrentParserInsertedScript = oldParserInsertedScript; - } - - if (aRequest->mOffThreadToken) { - // The request was parsed off-main-thread, but the result of the off - // thread parse was not actually needed to process the request - // (disappearing window, some other error, ...). Finish the - // request to avoid leaks in the JS engine. - MOZ_ASSERT(!aRequest->IsModuleRequest()); - aRequest->MaybeCancelOffThreadScript(); - } - - // Free any source data. - free(aRequest->mScriptTextBuf); - aRequest->mScriptTextBuf = nullptr; - aRequest->mScriptTextLength = 0; - - return rv; -} - -void -nsScriptLoader::FireScriptAvailable(nsresult aResult, - nsScriptLoadRequest* aRequest) -{ - for (int32_t i = 0; i < mObservers.Count(); i++) { - nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i]; - obs->ScriptAvailable(aResult, aRequest->mElement, - aRequest->mIsInline, aRequest->mURI, - aRequest->mLineNo); - } - - aRequest->FireScriptAvailable(aResult); -} - -void -nsScriptLoader::FireScriptEvaluated(nsresult aResult, - nsScriptLoadRequest* aRequest) -{ - for (int32_t i = 0; i < mObservers.Count(); i++) { - nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i]; - obs->ScriptEvaluated(aResult, aRequest->mElement, - aRequest->mIsInline); - } - - aRequest->FireScriptEvaluated(aResult); -} - -already_AddRefed<nsIScriptGlobalObject> -nsScriptLoader::GetScriptGlobalObject() -{ - nsCOMPtr<nsIDocument> master = mDocument->MasterDocument(); - nsPIDOMWindowInner *pwin = master->GetInnerWindow(); - if (!pwin) { - return nullptr; - } - - nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(pwin); - NS_ASSERTION(globalObject, "windows must be global objects"); - - // and make sure we are setup for this type of script. - nsresult rv = globalObject->EnsureScriptEnvironment(); - if (NS_FAILED(rv)) { - return nullptr; - } - - return globalObject.forget(); -} - -nsresult -nsScriptLoader::FillCompileOptionsForRequest(const AutoJSAPI&jsapi, - nsScriptLoadRequest* aRequest, - JS::Handle<JSObject*> aScopeChain, - JS::CompileOptions* aOptions) -{ - // It's very important to use aRequest->mURI, not the final URI of the channel - // aRequest ended up getting script data from, as the script filename. - nsresult rv; - nsContentUtils::GetWrapperSafeScriptFilename(mDocument, aRequest->mURI, - aRequest->mURL, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool isScriptElement = !aRequest->IsModuleRequest() || - aRequest->AsModuleRequest()->IsTopLevel(); - aOptions->setIntroductionType(isScriptElement ? "scriptElement" - : "importedModule"); - aOptions->setFileAndLine(aRequest->mURL.get(), aRequest->mLineNo); - aOptions->setVersion(JSVersion(aRequest->mJSVersion)); - aOptions->setIsRunOnce(true); - // We only need the setNoScriptRval bit when compiling off-thread here, since - // otherwise nsJSUtils::EvaluateString will set it up for us. - aOptions->setNoScriptRval(true); - if (aRequest->mHasSourceMapURL) { - aOptions->setSourceMapURL(aRequest->mSourceMapURL.get()); - } - if (aRequest->mOriginPrincipal) { - nsIPrincipal* scriptPrin = nsContentUtils::ObjectPrincipal(aScopeChain); - bool subsumes = scriptPrin->Subsumes(aRequest->mOriginPrincipal); - aOptions->setMutedErrors(!subsumes); - } - - JSContext* cx = jsapi.cx(); - JS::Rooted<JS::Value> elementVal(cx); - MOZ_ASSERT(aRequest->mElement); - if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, aRequest->mElement, - &elementVal, - /* aAllowWrapping = */ true))) { - MOZ_ASSERT(elementVal.isObject()); - aOptions->setElement(&elementVal.toObject()); - } - - return NS_OK; -} - -nsresult -nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest) -{ - // We need a document to evaluate scripts. - if (!mDocument) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement)); - nsIDocument* ownerDoc = scriptContent->OwnerDoc(); - if (ownerDoc != mDocument) { - // Willful violation of HTML5 as of 2010-12-01 - return NS_ERROR_FAILURE; - } - - // Get the script-type to be used by this element. - NS_ASSERTION(scriptContent, "no content - what is default script-type?"); - - nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject(); - if (!globalObject) { - return NS_ERROR_FAILURE; - } - - // Make sure context is a strong reference since we access it after - // we've executed a script, which may cause all other references to - // the context to go away. - nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext(); - if (!context) { - return NS_ERROR_FAILURE; - } - - JSVersion version = JSVersion(aRequest->mJSVersion); - if (version == JSVERSION_UNKNOWN) { - return NS_OK; - } - - // New script entry point required, due to the "Create a script" sub-step of - // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block - nsAutoMicroTask mt; - AutoEntryScript aes(globalObject, "<script> element", true); - JS::Rooted<JSObject*> global(aes.cx(), - globalObject->GetGlobalJSObject()); - - bool oldProcessingScriptTag = context->GetProcessingScriptTag(); - context->SetProcessingScriptTag(true); - nsresult rv; - { - // Update our current script. - AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement); - Maybe<AutoCurrentScriptUpdater> masterScriptUpdater; - nsCOMPtr<nsIDocument> master = mDocument->MasterDocument(); - if (master != mDocument) { - // If this script belongs to an import document, it will be - // executed in the context of the master document. During the - // execution currentScript of the master should refer to this - // script. So let's update the mCurrentScript of the ScriptLoader - // of the master document too. - masterScriptUpdater.emplace(master->ScriptLoader(), - aRequest->mElement); - } - - if (aRequest->IsModuleRequest()) { - nsModuleLoadRequest* request = aRequest->AsModuleRequest(); - MOZ_ASSERT(request->mModuleScript); - MOZ_ASSERT(!request->mOffThreadToken); - nsModuleScript* ms = request->mModuleScript; - MOZ_ASSERT(!ms->IsUninstantiated()); - if (ms->InstantiationFailed()) { - JS::Rooted<JS::Value> exception(aes.cx(), ms->Exception()); - JS_SetPendingException(aes.cx(), exception); - rv = NS_ERROR_FAILURE; - } else { - JS::Rooted<JSObject*> module(aes.cx(), ms->ModuleRecord()); - MOZ_ASSERT(module); - rv = nsJSUtils::ModuleEvaluation(aes.cx(), module); - } - } else { - JS::CompileOptions options(aes.cx()); - rv = FillCompileOptionsForRequest(aes, aRequest, global, &options); - - if (NS_SUCCEEDED(rv)) { - nsAutoString inlineData; - SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); - rv = nsJSUtils::EvaluateString(aes.cx(), srcBuf, global, options, - aRequest->OffThreadTokenPtr()); - } - } - } - - context->SetProcessingScriptTag(oldProcessingScriptTag); - return rv; -} - -void -nsScriptLoader::ProcessPendingRequestsAsync() -{ - if (mParserBlockingRequest || - !mXSLTRequests.isEmpty() || - !mLoadedAsyncRequests.isEmpty() || - !mNonAsyncExternalScriptInsertedRequests.isEmpty() || - !mDeferRequests.isEmpty() || - !mPendingChildLoaders.IsEmpty()) { - NS_DispatchToCurrentThread(NewRunnableMethod(this, - &nsScriptLoader::ProcessPendingRequests)); - } -} - -void -nsScriptLoader::ProcessPendingRequests() -{ - RefPtr<nsScriptLoadRequest> request; - - if (mParserBlockingRequest && - mParserBlockingRequest->IsReadyToRun() && - ReadyToExecuteParserBlockingScripts()) { - request.swap(mParserBlockingRequest); - UnblockParser(request); - ProcessRequest(request); - if (request->mWasCompiledOMT) { - mDocument->UnblockOnload(false); - } - ContinueParserAsync(request); - } - - while (ReadyToExecuteParserBlockingScripts() && - !mXSLTRequests.isEmpty() && - mXSLTRequests.getFirst()->IsReadyToRun()) { - request = mXSLTRequests.StealFirst(); - ProcessRequest(request); - } - - while (ReadyToExecuteScripts() && !mLoadedAsyncRequests.isEmpty()) { - request = mLoadedAsyncRequests.StealFirst(); - if (request->IsModuleRequest()) { - ProcessRequest(request); - } else { - CompileOffThreadOrProcessRequest(request); - } - } - - while (ReadyToExecuteScripts() && - !mNonAsyncExternalScriptInsertedRequests.isEmpty() && - mNonAsyncExternalScriptInsertedRequests.getFirst()->IsReadyToRun()) { - // Violate the HTML5 spec and execute these in the insertion order in - // order to make LABjs and the "order" plug-in for RequireJS work with - // their Gecko-sniffed code path. See - // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html - request = mNonAsyncExternalScriptInsertedRequests.StealFirst(); - ProcessRequest(request); - } - - if (mDocumentParsingDone && mXSLTRequests.isEmpty()) { - while (ReadyToExecuteScripts() && - !mDeferRequests.isEmpty() && - mDeferRequests.getFirst()->IsReadyToRun()) { - request = mDeferRequests.StealFirst(); - ProcessRequest(request); - } - } - - while (!mPendingChildLoaders.IsEmpty() && - ReadyToExecuteParserBlockingScripts()) { - RefPtr<nsScriptLoader> child = mPendingChildLoaders[0]; - mPendingChildLoaders.RemoveElementAt(0); - child->RemoveParserBlockingScriptExecutionBlocker(); - } - - if (mDocumentParsingDone && mDocument && !mParserBlockingRequest && - mNonAsyncExternalScriptInsertedRequests.isEmpty() && - mXSLTRequests.isEmpty() && mDeferRequests.isEmpty() && - MaybeRemovedDeferRequests()) { - return ProcessPendingRequests(); - } - - if (mDocumentParsingDone && mDocument && - !mParserBlockingRequest && mLoadingAsyncRequests.isEmpty() && - mLoadedAsyncRequests.isEmpty() && - mNonAsyncExternalScriptInsertedRequests.isEmpty() && - mXSLTRequests.isEmpty() && mDeferRequests.isEmpty()) { - // No more pending scripts; time to unblock onload. - // OK to unblock onload synchronously here, since callers must be - // prepared for the world changing anyway. - mDocumentParsingDone = false; - mDocument->UnblockOnload(true); - } -} - -bool -nsScriptLoader::ReadyToExecuteParserBlockingScripts() -{ - // Make sure the SelfReadyToExecuteParserBlockingScripts check is first, so - // that we don't block twice on an ancestor. - if (!SelfReadyToExecuteParserBlockingScripts()) { - return false; - } - - for (nsIDocument* doc = mDocument; doc; doc = doc->GetParentDocument()) { - nsScriptLoader* ancestor = doc->ScriptLoader(); - if (!ancestor->SelfReadyToExecuteParserBlockingScripts() && - ancestor->AddPendingChildLoader(this)) { - AddParserBlockingScriptExecutionBlocker(); - return false; - } - } - - if (mDocument && !mDocument->IsMasterDocument()) { - RefPtr<ImportManager> im = mDocument->ImportManager(); - RefPtr<ImportLoader> loader = im->Find(mDocument); - MOZ_ASSERT(loader, "How can we have an import document without a loader?"); - - // The referring link that counts in the execution order calculation - // (in spec: flagged as branch) - nsCOMPtr<nsINode> referrer = loader->GetMainReferrer(); - MOZ_ASSERT(referrer, "There has to be a main referring link for each imports"); - - // Import documents are blocked by their import predecessors. We need to - // wait with script execution until all the predecessors are done. - // Technically it means we have to wait for the last one to finish, - // which is the neares one to us in the order. - RefPtr<ImportLoader> lastPred = im->GetNearestPredecessor(referrer); - if (!lastPred) { - // If there is no predecessor we can run. - return true; - } - - nsCOMPtr<nsIDocument> doc = lastPred->GetDocument(); - if (lastPred->IsBlocking() || !doc || - !doc->ScriptLoader()->SelfReadyToExecuteParserBlockingScripts()) { - // Document has not been created yet or it was created but not ready. - // Either case we are blocked by it. The ImportLoader will take care - // of blocking us, and adding the pending child loader to the blocking - // ScriptLoader when it's possible (at this point the blocking loader - // might not have created the document/ScriptLoader) - lastPred->AddBlockedScriptLoader(this); - // As more imports are parsed, this can change, let's cache what we - // blocked, so it can be later updated if needed (see: ImportLoader::Updater). - loader->SetBlockingPredecessor(lastPred); - return false; - } - } - - return true; -} - -/* static */ nsresult -nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, - uint32_t aLength, const nsAString& aHintCharset, - nsIDocument* aDocument, - char16_t*& aBufOut, size_t& aLengthOut) -{ - if (!aLength) { - aBufOut = nullptr; - aLengthOut = 0; - return NS_OK; - } - - // The encoding info precedence is as follows from high to low: - // The BOM - // HTTP Content-Type (if name recognized) - // charset attribute (if name recognized) - // The encoding of the document - - nsAutoCString charset; - - nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder; - - if (nsContentUtils::CheckForBOM(aData, aLength, charset)) { - // charset is now one of "UTF-16BE", "UTF-16BE" or "UTF-8". Those decoder - // will take care of swallowing the BOM. - unicodeDecoder = EncodingUtils::DecoderForEncoding(charset); - } - - if (!unicodeDecoder && - aChannel && - NS_SUCCEEDED(aChannel->GetContentCharset(charset)) && - EncodingUtils::FindEncodingForLabel(charset, charset)) { - unicodeDecoder = EncodingUtils::DecoderForEncoding(charset); - } - - if (!unicodeDecoder && - EncodingUtils::FindEncodingForLabel(aHintCharset, charset)) { - unicodeDecoder = EncodingUtils::DecoderForEncoding(charset); - } - - if (!unicodeDecoder && aDocument) { - charset = aDocument->GetDocumentCharacterSet(); - unicodeDecoder = EncodingUtils::DecoderForEncoding(charset); - } - - if (!unicodeDecoder) { - // Curiously, there are various callers that don't pass aDocument. The - // fallback in the old code was ISO-8859-1, which behaved like - // windows-1252. Saying windows-1252 for clarity and for compliance - // with the Encoding Standard. - unicodeDecoder = EncodingUtils::DecoderForEncoding("windows-1252"); - } - - int32_t unicodeLength = 0; - - nsresult rv = - unicodeDecoder->GetMaxLength(reinterpret_cast<const char*>(aData), - aLength, &unicodeLength); - NS_ENSURE_SUCCESS(rv, rv); - - aBufOut = static_cast<char16_t*>(js_malloc(unicodeLength * sizeof(char16_t))); - if (!aBufOut) { - aLengthOut = 0; - return NS_ERROR_OUT_OF_MEMORY; - } - aLengthOut = unicodeLength; - - rv = unicodeDecoder->Convert(reinterpret_cast<const char*>(aData), - (int32_t *) &aLength, aBufOut, - &unicodeLength); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - aLengthOut = unicodeLength; - if (NS_FAILED(rv)) { - js_free(aBufOut); - aBufOut = nullptr; - aLengthOut = 0; - } - return rv; -} - -nsresult -nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, - nsISupports* aContext, - nsresult aChannelStatus, - nsresult aSRIStatus, - mozilla::Vector<char16_t> &aString, - mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier) -{ - nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext); - NS_ASSERTION(request, "null request in stream complete handler"); - NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); - - nsCOMPtr<nsIRequest> channelRequest; - aLoader->GetRequest(getter_AddRefs(channelRequest)); - nsCOMPtr<nsIChannel> channel; - channel = do_QueryInterface(channelRequest); - - nsresult rv = NS_OK; - if (!request->mIntegrity.IsEmpty() && - NS_SUCCEEDED((rv = aSRIStatus))) { - MOZ_ASSERT(aSRIDataVerifier); - MOZ_ASSERT(mReporter); - - nsAutoCString sourceUri; - if (mDocument && mDocument->GetDocumentURI()) { - mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); - } - rv = aSRIDataVerifier->Verify(request->mIntegrity, channel, sourceUri, - mReporter); - mReporter->FlushConsoleReports(mDocument); - if (NS_FAILED(rv)) { - rv = NS_ERROR_SRI_CORRUPT; - } - } else { - nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo(); - - if (loadInfo->GetEnforceSRI()) { - MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug, - ("nsScriptLoader::OnStreamComplete, required SRI not found")); - nsCOMPtr<nsIContentSecurityPolicy> csp; - loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp)); - nsAutoCString violationURISpec; - mDocument->GetDocumentURI()->GetAsciiSpec(violationURISpec); - uint32_t lineNo = request->mElement ? request->mElement->GetScriptLineNumber() : 0; - csp->LogViolationDetails( - nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT, - NS_ConvertUTF8toUTF16(violationURISpec), - EmptyString(), lineNo, EmptyString(), EmptyString()); - rv = NS_ERROR_SRI_CORRUPT; - } - } - - if (NS_SUCCEEDED(rv)) { - rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString); - } - - if (NS_FAILED(rv)) { - /* - * Handle script not loading error because source was a tracking URL. - * We make a note of this script node by including it in a dedicated - * array of blocked tracking nodes under its parent document. - */ - if (rv == NS_ERROR_TRACKING_URI) { - nsCOMPtr<nsIContent> cont = do_QueryInterface(request->mElement); - mDocument->AddBlockedTrackingNode(cont); - } - - if (request->mIsDefer) { - MOZ_ASSERT_IF(request->IsModuleRequest(), - request->AsModuleRequest()->IsTopLevel()); - if (request->isInList()) { - RefPtr<nsScriptLoadRequest> req = mDeferRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->mIsAsync) { - MOZ_ASSERT_IF(request->IsModuleRequest(), - request->AsModuleRequest()->IsTopLevel()); - if (request->isInList()) { - RefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->mIsNonAsyncScriptInserted) { - if (request->isInList()) { - RefPtr<nsScriptLoadRequest> req = - mNonAsyncExternalScriptInsertedRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->mIsXSLT) { - if (request->isInList()) { - RefPtr<nsScriptLoadRequest> req = mXSLTRequests.Steal(request); - FireScriptAvailable(rv, req); - } - } else if (request->IsModuleRequest()) { - nsModuleLoadRequest* modReq = request->AsModuleRequest(); - MOZ_ASSERT(!modReq->IsTopLevel()); - MOZ_ASSERT(!modReq->isInList()); - modReq->Cancel(); - FireScriptAvailable(rv, request); - } else if (mParserBlockingRequest == request) { - MOZ_ASSERT(!request->isInList()); - mParserBlockingRequest = nullptr; - UnblockParser(request); - - // Ensure that we treat request->mElement as our current parser-inserted - // script while firing onerror on it. - MOZ_ASSERT(request->mElement->GetParserCreated()); - nsCOMPtr<nsIScriptElement> oldParserInsertedScript = - mCurrentParserInsertedScript; - mCurrentParserInsertedScript = request->mElement; - FireScriptAvailable(rv, request); - ContinueParserAsync(request); - mCurrentParserInsertedScript = oldParserInsertedScript; - } else { - mPreloads.RemoveElement(request, PreloadRequestComparator()); - } - } - - // Process our request and/or any pending ones - ProcessPendingRequests(); - - return NS_OK; -} - -void -nsScriptLoader::UnblockParser(nsScriptLoadRequest* aParserBlockingRequest) -{ - aParserBlockingRequest->mElement->UnblockParser(); -} - -void -nsScriptLoader::ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest) -{ - aParserBlockingRequest->mElement->ContinueParserAsync(); -} - -uint32_t -nsScriptLoader::NumberOfProcessors() -{ - if (mNumberOfProcessors > 0) - return mNumberOfProcessors; - - int32_t numProcs = PR_GetNumberOfProcessors(); - if (numProcs > 0) - mNumberOfProcessors = numProcs; - return mNumberOfProcessors; -} - -void -nsScriptLoader::MaybeMoveToLoadedList(nsScriptLoadRequest* aRequest) -{ - MOZ_ASSERT(aRequest->IsReadyToRun()); - - // If it's async, move it to the loaded list. aRequest->mIsAsync really - // _should_ be in a list, but the consequences if it's not are bad enough we - // want to avoid trying to move it if it's not. - if (aRequest->mIsAsync) { - MOZ_ASSERT(aRequest->isInList()); - if (aRequest->isInList()) { - RefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest); - mLoadedAsyncRequests.AppendElement(req); - } - } -} - -nsresult -nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, - nsIIncrementalStreamLoader* aLoader, - nsresult aStatus, - mozilla::Vector<char16_t> &aString) -{ - if (NS_FAILED(aStatus)) { - return aStatus; - } - - if (aRequest->IsCanceled()) { - return NS_BINDING_ABORTED; - } - - // If we don't have a document, then we need to abort further - // evaluation. - if (!mDocument) { - return NS_ERROR_NOT_AVAILABLE; - } - - // If the load returned an error page, then we need to abort - nsCOMPtr<nsIRequest> req; - nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); - NS_ASSERTION(req, "StreamLoader's request went away prematurely"); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(req); - if (httpChannel) { - bool requestSucceeded; - rv = httpChannel->GetRequestSucceeded(&requestSucceeded); - if (NS_SUCCEEDED(rv) && !requestSucceeded) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsAutoCString sourceMapURL; - rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("SourceMap"), sourceMapURL); - if (NS_FAILED(rv)) { - rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL); - } - if (NS_SUCCEEDED(rv)) { - aRequest->mHasSourceMapURL = true; - aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL); - } - } - - nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); - // If this load was subject to a CORS check; don't flag it with a - // separate origin principal, so that it will treat our document's - // principal as the origin principal - if (aRequest->mCORSMode == CORS_NONE) { - rv = nsContentUtils::GetSecurityManager()-> - GetChannelResultPrincipal(channel, getter_AddRefs(aRequest->mOriginPrincipal)); - NS_ENSURE_SUCCESS(rv, rv); - } - - if (!aString.empty()) { - aRequest->mScriptTextLength = aString.length(); - aRequest->mScriptTextBuf = aString.extractOrCopyRawBuffer(); - } - - // This assertion could fire errorously if we ran out of memory when - // inserting the request in the array. However it's an unlikely case - // so if you see this assertion it is likely something else that is - // wrong, especially if you see it more than once. - NS_ASSERTION(mDeferRequests.Contains(aRequest) || - mLoadingAsyncRequests.Contains(aRequest) || - mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) || - mXSLTRequests.Contains(aRequest) || - (aRequest->IsModuleRequest() && - !aRequest->AsModuleRequest()->IsTopLevel() && - !aRequest->isInList()) || - mPreloads.Contains(aRequest, PreloadRequestComparator()) || - mParserBlockingRequest, - "aRequest should be pending!"); - - if (aRequest->IsModuleRequest()) { - nsModuleLoadRequest* request = aRequest->AsModuleRequest(); - - // When loading a module, only responses with a JavaScript MIME type are - // acceptable. - nsAutoCString mimeType; - channel->GetContentType(mimeType); - NS_ConvertUTF8toUTF16 typeString(mimeType); - if (!nsContentUtils::IsJavascriptMIMEType(typeString)) { - return NS_ERROR_FAILURE; - } - - channel->GetURI(getter_AddRefs(request->mBaseURL)); - - // Attempt to compile off main thread. - rv = AttemptAsyncScriptCompile(request); - if (NS_SUCCEEDED(rv)) { - return rv; - } - - // Otherwise compile it right away and start fetching descendents. - return ProcessFetchedModuleSource(request); - } - - // The script is now loaded and ready to run. - aRequest->SetReady(); - - // If this is currently blocking the parser, attempt to compile it off-main-thread. - if (aRequest == mParserBlockingRequest && (NumberOfProcessors() > 1)) { - MOZ_ASSERT(!aRequest->IsModuleRequest()); - nsresult rv = AttemptAsyncScriptCompile(aRequest); - if (rv == NS_OK) { - MOZ_ASSERT(aRequest->mProgress == nsScriptLoadRequest::Progress::Compiling, - "Request should be off-thread compiling now."); - return NS_OK; - } - - // If off-thread compile errored, return the error. - if (rv != NS_ERROR_FAILURE) { - return rv; - } - - // If off-thread compile was rejected, continue with regular processing. - } - - MaybeMoveToLoadedList(aRequest); - - return NS_OK; -} - -void -nsScriptLoader::ParsingComplete(bool aTerminated) -{ - if (mDeferEnabled) { - // Have to check because we apparently get ParsingComplete - // without BeginDeferringScripts in some cases - mDocumentParsingDone = true; - } - mDeferEnabled = false; - if (aTerminated) { - mDeferRequests.Clear(); - mLoadingAsyncRequests.Clear(); - mLoadedAsyncRequests.Clear(); - mNonAsyncExternalScriptInsertedRequests.Clear(); - mXSLTRequests.Clear(); - if (mParserBlockingRequest) { - mParserBlockingRequest->Cancel(); - mParserBlockingRequest = nullptr; - } - } - - // Have to call this even if aTerminated so we'll correctly unblock - // onload and all. - ProcessPendingRequests(); -} - -void -nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset, - const nsAString &aType, - const nsAString &aCrossOrigin, - const nsAString& aIntegrity, - bool aScriptFromHead, - const mozilla::net::ReferrerPolicy aReferrerPolicy) -{ - NS_ENSURE_TRUE_VOID(mDocument); - // Check to see if scripts has been turned off. - if (!mEnabled || !mDocument->IsScriptEnabled()) { - return; - } - - // TODO: Preload module scripts. - if (ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) { - return; - } - - SRIMetadata sriMetadata; - if (!aIntegrity.IsEmpty()) { - MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug, - ("nsScriptLoader::PreloadURI, integrity=%s", - NS_ConvertUTF16toUTF8(aIntegrity).get())); - nsAutoCString sourceUri; - if (mDocument->GetDocumentURI()) { - mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); - } - SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata); - } - - RefPtr<nsScriptLoadRequest> request = - CreateLoadRequest(nsScriptKind::Classic, nullptr, 0, - Element::StringToCORSMode(aCrossOrigin), sriMetadata); - request->mURI = aURI; - request->mIsInline = false; - request->mReferrerPolicy = aReferrerPolicy; - - nsresult rv = StartLoad(request, aType, aScriptFromHead); - if (NS_FAILED(rv)) { - return; - } - - PreloadInfo *pi = mPreloads.AppendElement(); - pi->mRequest = request; - pi->mCharset = aCharset; -} - -void -nsScriptLoader::AddDeferRequest(nsScriptLoadRequest* aRequest) -{ - aRequest->mIsDefer = true; - mDeferRequests.AppendElement(aRequest); - if (mDeferEnabled && aRequest == mDeferRequests.getFirst() && - mDocument && !mBlockingDOMContentLoaded) { - MOZ_ASSERT(mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING); - mBlockingDOMContentLoaded = true; - mDocument->BlockDOMContentLoaded(); - } -} - -bool -nsScriptLoader::MaybeRemovedDeferRequests() -{ - if (mDeferRequests.isEmpty() && mDocument && - mBlockingDOMContentLoaded) { - mBlockingDOMContentLoaded = false; - mDocument->UnblockDOMContentLoaded(); - return true; - } - return false; -} - -////////////////////////////////////////////////////////////// -// nsScriptLoadHandler -////////////////////////////////////////////////////////////// - -nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader, - nsScriptLoadRequest *aRequest, - mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier) - : mScriptLoader(aScriptLoader), - mRequest(aRequest), - mSRIDataVerifier(aSRIDataVerifier), - mSRIStatus(NS_OK), - mDecoder(), - mBuffer() -{} - -nsScriptLoadHandler::~nsScriptLoadHandler() -{} - -NS_IMPL_ISUPPORTS(nsScriptLoadHandler, nsIIncrementalStreamLoaderObserver) - -NS_IMETHODIMP -nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, - nsISupports* aContext, - uint32_t aDataLength, - const uint8_t* aData, - uint32_t *aConsumedLength) -{ - if (mRequest->IsCanceled()) { - // If request cancelled, ignore any incoming data. - *aConsumedLength = aDataLength; - return NS_OK; - } - - if (!EnsureDecoder(aLoader, aData, aDataLength, - /* aEndOfStream = */ false)) { - return NS_OK; - } - - // Below we will/shall consume entire data chunk. - *aConsumedLength = aDataLength; - - // Decoder has already been initialized. -- trying to decode all loaded bytes. - nsresult rv = TryDecodeRawData(aData, aDataLength, - /* aEndOfStream = */ false); - NS_ENSURE_SUCCESS(rv, rv); - - // If SRI is required for this load, appending new bytes to the hash. - if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { - mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); - } - - return rv; -} - -nsresult -nsScriptLoadHandler::TryDecodeRawData(const uint8_t* aData, - uint32_t aDataLength, - bool aEndOfStream) -{ - int32_t srcLen = aDataLength; - const char* src = reinterpret_cast<const char *>(aData); - int32_t dstLen; - nsresult rv = - mDecoder->GetMaxLength(src, srcLen, &dstLen); - - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t haveRead = mBuffer.length(); - - CheckedInt<uint32_t> capacity = haveRead; - capacity += dstLen; - - if (!capacity.isValid() || !mBuffer.reserve(capacity.value())) { - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = mDecoder->Convert(src, - &srcLen, - mBuffer.begin() + haveRead, - &dstLen); - - NS_ENSURE_SUCCESS(rv, rv); - - haveRead += dstLen; - MOZ_ASSERT(haveRead <= capacity.value(), "mDecoder produced more data than expected"); - MOZ_ALWAYS_TRUE(mBuffer.resizeUninitialized(haveRead)); - - return NS_OK; -} - -bool -nsScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader, - const uint8_t* aData, - uint32_t aDataLength, - bool aEndOfStream) -{ - // Check if decoder has already been created. - if (mDecoder) { - return true; - } - - nsAutoCString charset; - - // JavaScript modules are always UTF-8. - if (mRequest->IsModuleRequest()) { - charset = "UTF-8"; - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Determine if BOM check should be done. This occurs either - // if end-of-stream has been reached, or at least 3 bytes have - // been read from input. - if (!aEndOfStream && (aDataLength < 3)) { - return false; - } - - // Do BOM detection. - if (nsContentUtils::CheckForBOM(aData, aDataLength, charset)) { - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // BOM detection failed, check content stream for charset. - nsCOMPtr<nsIRequest> req; - nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); - NS_ASSERTION(req, "StreamLoader's request went away prematurely"); - NS_ENSURE_SUCCESS(rv, false); - - nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); - - if (channel && - NS_SUCCEEDED(channel->GetContentCharset(charset)) && - EncodingUtils::FindEncodingForLabel(charset, charset)) { - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Check the hint charset from the script element or preload - // request. - nsAutoString hintCharset; - if (!mRequest->IsPreload()) { - mRequest->mElement->GetScriptCharset(hintCharset); - } else { - nsTArray<nsScriptLoader::PreloadInfo>::index_type i = - mScriptLoader->mPreloads.IndexOf(mRequest, 0, - nsScriptLoader::PreloadRequestComparator()); - - NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex, - "Incorrect preload bookkeeping"); - hintCharset = mScriptLoader->mPreloads[i].mCharset; - } - - if (EncodingUtils::FindEncodingForLabel(hintCharset, charset)) { - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Get the charset from the charset of the document. - if (mScriptLoader->mDocument) { - charset = mScriptLoader->mDocument->GetDocumentCharacterSet(); - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Curiously, there are various callers that don't pass aDocument. The - // fallback in the old code was ISO-8859-1, which behaved like - // windows-1252. Saying windows-1252 for clarity and for compliance - // with the Encoding Standard. - charset = "windows-1252"; - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; -} - -NS_IMETHODIMP -nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, - nsISupports* aContext, - nsresult aStatus, - uint32_t aDataLength, - const uint8_t* aData) -{ - if (!mRequest->IsCanceled()) { - DebugOnly<bool> encoderSet = - EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true); - MOZ_ASSERT(encoderSet); - DebugOnly<nsresult> rv = TryDecodeRawData(aData, aDataLength, - /* aEndOfStream = */ true); - - // If SRI is required for this load, appending new bytes to the hash. - if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { - mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); - } - } - - // we have to mediate and use mRequest. - return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus, - mBuffer, mSRIDataVerifier); -} diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h deleted file mode 100644 index a00239be5..000000000 --- a/dom/base/nsScriptLoader.h +++ /dev/null @@ -1,717 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -/* - * A class that handles loading and evaluation of <script> elements. - */ - -#ifndef __nsScriptLoader_h__ -#define __nsScriptLoader_h__ - -#include "nsCOMPtr.h" -#include "nsRefPtrHashtable.h" -#include "nsIUnicodeDecoder.h" -#include "nsIScriptElement.h" -#include "nsCOMArray.h" -#include "nsCycleCollectionParticipant.h" -#include "nsTArray.h" -#include "nsAutoPtr.h" -#include "nsIDocument.h" -#include "nsIIncrementalStreamLoader.h" -#include "nsURIHashKey.h" -#include "mozilla/CORSMode.h" -#include "mozilla/dom/SRIMetadata.h" -#include "mozilla/dom/SRICheck.h" -#include "mozilla/LinkedList.h" -#include "mozilla/MozPromise.h" -#include "mozilla/net/ReferrerPolicy.h" -#include "mozilla/Vector.h" - -class nsModuleLoadRequest; -class nsModuleScript; -class nsScriptLoadRequestList; -class nsIURI; - -namespace JS { - class SourceBufferHolder; -} // namespace JS - -namespace mozilla { -namespace dom { -class AutoJSAPI; -} // namespace dom -} // namespace mozilla - -////////////////////////////////////////////////////////////// -// Per-request data structure -////////////////////////////////////////////////////////////// - -enum class nsScriptKind { - Classic, - Module -}; - -class nsScriptLoadRequest : public nsISupports, - private mozilla::LinkedListElement<nsScriptLoadRequest> -{ - typedef LinkedListElement<nsScriptLoadRequest> super; - - // Allow LinkedListElement<nsScriptLoadRequest> to cast us to itself as needed. - friend class mozilla::LinkedListElement<nsScriptLoadRequest>; - friend class nsScriptLoadRequestList; - -protected: - virtual ~nsScriptLoadRequest(); - -public: - nsScriptLoadRequest(nsScriptKind aKind, - nsIScriptElement* aElement, - uint32_t aVersion, - mozilla::CORSMode aCORSMode, - const mozilla::dom::SRIMetadata &aIntegrity) - : mKind(aKind), - mElement(aElement), - mProgress(Progress::Loading), - mIsInline(true), - mHasSourceMapURL(false), - mIsDefer(false), - mIsAsync(false), - mIsNonAsyncScriptInserted(false), - mIsXSLT(false), - mIsCanceled(false), - mWasCompiledOMT(false), - mOffThreadToken(nullptr), - mScriptTextBuf(nullptr), - mScriptTextLength(0), - mJSVersion(aVersion), - mLineNo(1), - mCORSMode(aCORSMode), - mIntegrity(aIntegrity), - mReferrerPolicy(mozilla::net::RP_Default) - { - } - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(nsScriptLoadRequest) - - bool IsModuleRequest() const - { - return mKind == nsScriptKind::Module; - } - - nsModuleLoadRequest* AsModuleRequest(); - - void FireScriptAvailable(nsresult aResult) - { - mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo); - } - void FireScriptEvaluated(nsresult aResult) - { - mElement->ScriptEvaluated(aResult, mElement, mIsInline); - } - - bool IsPreload() - { - return mElement == nullptr; - } - - virtual void Cancel(); - - bool IsCanceled() const - { - return mIsCanceled; - } - - virtual void SetReady(); - - void** OffThreadTokenPtr() - { - return mOffThreadToken ? &mOffThreadToken : nullptr; - } - - enum class Progress { - Loading, - Compiling, - FetchingImports, - Ready - }; - bool IsReadyToRun() const { - return mProgress == Progress::Ready; - } - bool IsLoading() const { - return mProgress == Progress::Loading; - } - bool InCompilingStage() const { - return mProgress == Progress::Compiling || - (IsReadyToRun() && mWasCompiledOMT); - } - - void MaybeCancelOffThreadScript(); - - using super::getNext; - using super::isInList; - - const nsScriptKind mKind; - nsCOMPtr<nsIScriptElement> mElement; - Progress mProgress; // Are we still waiting for a load to complete? - bool mIsInline; // Is the script inline or loaded? - bool mHasSourceMapURL; // Does the HTTP header have a source map url? - bool mIsDefer; // True if we live in mDeferRequests. - bool mIsAsync; // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests. - bool mIsNonAsyncScriptInserted; // True if we live in mNonAsyncExternalScriptInsertedRequests - bool mIsXSLT; // True if we live in mXSLTRequests. - bool mIsCanceled; // True if we have been explicitly canceled. - bool mWasCompiledOMT; // True if the script has been compiled off main thread. - void* mOffThreadToken; // Off-thread parsing token. - nsString mSourceMapURL; // Holds source map url for loaded scripts - char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't - size_t mScriptTextLength; // use nsString so we can give ownership to jsapi. - uint32_t mJSVersion; - nsCOMPtr<nsIURI> mURI; - nsCOMPtr<nsIPrincipal> mOriginPrincipal; - nsAutoCString mURL; // Keep the URI's filename alive during off thread parsing. - int32_t mLineNo; - const mozilla::CORSMode mCORSMode; - const mozilla::dom::SRIMetadata mIntegrity; - mozilla::net::ReferrerPolicy mReferrerPolicy; -}; - -class nsScriptLoadRequestList : private mozilla::LinkedList<nsScriptLoadRequest> -{ - typedef mozilla::LinkedList<nsScriptLoadRequest> super; - -public: - ~nsScriptLoadRequestList(); - - void Clear(); - -#ifdef DEBUG - bool Contains(nsScriptLoadRequest* aElem) const; -#endif // DEBUG - - using super::getFirst; - using super::isEmpty; - - void AppendElement(nsScriptLoadRequest* aElem) - { - MOZ_ASSERT(!aElem->isInList()); - NS_ADDREF(aElem); - insertBack(aElem); - } - - MOZ_MUST_USE - already_AddRefed<nsScriptLoadRequest> Steal(nsScriptLoadRequest* aElem) - { - aElem->removeFrom(*this); - return dont_AddRef(aElem); - } - - MOZ_MUST_USE - already_AddRefed<nsScriptLoadRequest> StealFirst() - { - MOZ_ASSERT(!isEmpty()); - return Steal(getFirst()); - } - - void Remove(nsScriptLoadRequest* aElem) - { - aElem->removeFrom(*this); - NS_RELEASE(aElem); - } -}; - -////////////////////////////////////////////////////////////// -// Script loader implementation -////////////////////////////////////////////////////////////// - -class nsScriptLoader final : public nsISupports -{ - class MOZ_STACK_CLASS AutoCurrentScriptUpdater - { - public: - AutoCurrentScriptUpdater(nsScriptLoader* aScriptLoader, - nsIScriptElement* aCurrentScript) - : mOldScript(aScriptLoader->mCurrentScript) - , mScriptLoader(aScriptLoader) - { - mScriptLoader->mCurrentScript = aCurrentScript; - } - ~AutoCurrentScriptUpdater() - { - mScriptLoader->mCurrentScript.swap(mOldScript); - } - private: - nsCOMPtr<nsIScriptElement> mOldScript; - nsScriptLoader* mScriptLoader; - }; - - friend class nsModuleLoadRequest; - friend class nsScriptRequestProcessor; - friend class nsScriptLoadHandler; - friend class AutoCurrentScriptUpdater; - -public: - explicit nsScriptLoader(nsIDocument* aDocument); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(nsScriptLoader) - - /** - * The loader maintains a weak reference to the document with - * which it is initialized. This call forces the reference to - * be dropped. - */ - void DropDocumentReference() - { - mDocument = nullptr; - } - - /** - * Add an observer for all scripts loaded through this loader. - * - * @param aObserver observer for all script processing. - */ - nsresult AddObserver(nsIScriptLoaderObserver* aObserver) - { - return mObservers.AppendObject(aObserver) ? NS_OK : - NS_ERROR_OUT_OF_MEMORY; - } - - /** - * Remove an observer. - * - * @param aObserver observer to be removed - */ - void RemoveObserver(nsIScriptLoaderObserver* aObserver) - { - mObservers.RemoveObject(aObserver); - } - - /** - * Process a script element. This will include both loading the - * source of the element if it is not inline and evaluating - * the script itself. - * - * If the script is an inline script that can be executed immediately - * (i.e. there are no other scripts pending) then ScriptAvailable - * and ScriptEvaluated will be called before the function returns. - * - * If true is returned the script could not be executed immediately. - * In this case ScriptAvailable is guaranteed to be called at a later - * point (as well as possibly ScriptEvaluated). - * - * @param aElement The element representing the script to be loaded and - * evaluated. - */ - bool ProcessScriptElement(nsIScriptElement* aElement); - - /** - * Gets the currently executing script. This is useful if you want to - * generate a unique key based on the currently executing script. - */ - nsIScriptElement* GetCurrentScript() - { - return mCurrentScript; - } - - nsIScriptElement* GetCurrentParserInsertedScript() - { - return mCurrentParserInsertedScript; - } - - /** - * Whether the loader is enabled or not. - * When disabled, processing of new script elements is disabled. - * Any call to ProcessScriptElement() will return false. Note that - * this DOES NOT disable currently loading or executing scripts. - */ - bool GetEnabled() - { - return mEnabled; - } - void SetEnabled(bool aEnabled) - { - if (!mEnabled && aEnabled) { - ProcessPendingRequestsAsync(); - } - mEnabled = aEnabled; - } - - /** - * Add/remove a blocker for parser-blocking scripts (and XSLT - * scripts). Blockers will stop such scripts from executing, but not from - * loading. - */ - void AddParserBlockingScriptExecutionBlocker() - { - ++mParserBlockingBlockerCount; - } - void RemoveParserBlockingScriptExecutionBlocker() - { - if (!--mParserBlockingBlockerCount && ReadyToExecuteScripts()) { - ProcessPendingRequestsAsync(); - } - } - - /** - * Add/remove a blocker for execution of all scripts. Blockers will stop - * scripts from executing, but not from loading. - */ - void AddExecuteBlocker() - { - ++mBlockerCount; - } - void RemoveExecuteBlocker() - { - MOZ_ASSERT(mBlockerCount); - if (!--mBlockerCount) { - ProcessPendingRequestsAsync(); - } - } - - /** - * Convert the given buffer to a UTF-16 string. - * @param aChannel Channel corresponding to the data. May be null. - * @param aData The data to convert - * @param aLength Length of the data - * @param aHintCharset Hint for the character set (e.g., from a charset - * attribute). May be the empty string. - * @param aDocument Document which the data is loaded for. Must not be - * null. - * @param aBufOut [out] char16_t array allocated by ConvertToUTF16 and - * containing data converted to unicode. Caller must - * js_free() this data when no longer needed. - * @param aLengthOut [out] Length of array returned in aBufOut in number - * of char16_t code units. - */ - static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, - uint32_t aLength, - const nsAString& aHintCharset, - nsIDocument* aDocument, - char16_t*& aBufOut, size_t& aLengthOut); - - /** - * Handle the completion of a stream. This is called by the - * nsScriptLoadHandler object which observes the IncrementalStreamLoader - * loading the script. - */ - nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader, - nsISupports* aContext, - nsresult aChannelStatus, - nsresult aSRIStatus, - mozilla::Vector<char16_t> &aString, - mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier); - - /** - * Processes any pending requests that are ready for processing. - */ - void ProcessPendingRequests(); - - /** - * Starts deferring deferred scripts and puts them in the mDeferredRequests - * queue instead. - */ - void BeginDeferringScripts() - { - mDeferEnabled = true; - if (mDocument) { - mDocument->BlockOnload(); - } - } - - /** - * Notifies the script loader that parsing is done. If aTerminated is true, - * this will drop any pending scripts that haven't run yet. Otherwise, it - * will stops deferring scripts and immediately processes the - * mDeferredRequests queue. - * - * WARNING: This function will synchronously execute content scripts, so be - * prepared that the world might change around you. - */ - void ParsingComplete(bool aTerminated); - - /** - * Returns the number of pending scripts, deferred or not. - */ - uint32_t HasPendingOrCurrentScripts() - { - return mCurrentScript || mParserBlockingRequest; - } - - /** - * Adds aURI to the preload list and starts loading it. - * - * @param aURI The URI of the external script. - * @param aCharset The charset parameter for the script. - * @param aType The type parameter for the script. - * @param aCrossOrigin The crossorigin attribute for the script. - * Void if not present. - * @param aIntegrity The expect hash url, if avail, of the request - * @param aScriptFromHead Whether or not the script was a child of head - */ - virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset, - const nsAString &aType, - const nsAString &aCrossOrigin, - const nsAString& aIntegrity, - bool aScriptFromHead, - const mozilla::net::ReferrerPolicy aReferrerPolicy); - - /** - * Process a request that was deferred so that the script could be compiled - * off thread. - */ - nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest); - - bool AddPendingChildLoader(nsScriptLoader* aChild) { - return mPendingChildLoaders.AppendElement(aChild) != nullptr; - } - -private: - virtual ~nsScriptLoader(); - - nsScriptLoadRequest* CreateLoadRequest( - nsScriptKind aKind, - nsIScriptElement* aElement, - uint32_t aVersion, - mozilla::CORSMode aCORSMode, - const mozilla::dom::SRIMetadata &aIntegrity); - - /** - * Unblocks the creator parser of the parser-blocking scripts. - */ - void UnblockParser(nsScriptLoadRequest* aParserBlockingRequest); - - /** - * Asynchronously resumes the creator parser of the parser-blocking scripts. - */ - void ContinueParserAsync(nsScriptLoadRequest* aParserBlockingRequest); - - - /** - * Helper function to check the content policy for a given request. - */ - static nsresult CheckContentPolicy(nsIDocument* aDocument, - nsISupports *aContext, - nsIURI *aURI, - const nsAString &aType, - bool aIsPreLoad); - - /** - * Start a load for aRequest's URI. - */ - nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, - bool aScriptFromHead); - - /** - * Process any pending requests asynchronously (i.e. off an event) if there - * are any. Note that this is a no-op if there aren't any currently pending - * requests. - * - * This function is virtual to allow cross-library calls to SetEnabled() - */ - virtual void ProcessPendingRequestsAsync(); - - /** - * If true, the loader is ready to execute parser-blocking scripts, and so are - * all its ancestors. If the loader itself is ready but some ancestor is not, - * this function will add an execute blocker and ask the ancestor to remove it - * once it becomes ready. - */ - bool ReadyToExecuteParserBlockingScripts(); - - /** - * Return whether just this loader is ready to execute parser-blocking - * scripts. - */ - bool SelfReadyToExecuteParserBlockingScripts() - { - return ReadyToExecuteScripts() && !mParserBlockingBlockerCount; - } - - /** - * Return whether this loader is ready to execute scripts in general. - */ - bool ReadyToExecuteScripts() - { - return mEnabled && !mBlockerCount; - } - - nsresult AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest); - nsresult ProcessRequest(nsScriptLoadRequest* aRequest); - nsresult CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest); - void FireScriptAvailable(nsresult aResult, - nsScriptLoadRequest* aRequest); - void FireScriptEvaluated(nsresult aResult, - nsScriptLoadRequest* aRequest); - nsresult EvaluateScript(nsScriptLoadRequest* aRequest); - - already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject(); - nsresult FillCompileOptionsForRequest(const mozilla::dom::AutoJSAPI& jsapi, - nsScriptLoadRequest* aRequest, - JS::Handle<JSObject*> aScopeChain, - JS::CompileOptions* aOptions); - - uint32_t NumberOfProcessors(); - nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, - nsIIncrementalStreamLoader* aLoader, - nsresult aStatus, - mozilla::Vector<char16_t> &aString); - - void AddDeferRequest(nsScriptLoadRequest* aRequest); - bool MaybeRemovedDeferRequests(); - - void MaybeMoveToLoadedList(nsScriptLoadRequest* aRequest); - - JS::SourceBufferHolder GetScriptSource(nsScriptLoadRequest* aRequest, - nsAutoString& inlineData); - - bool ModuleScriptsEnabled(); - - void SetModuleFetchStarted(nsModuleLoadRequest *aRequest); - void SetModuleFetchFinishedAndResumeWaitingRequests(nsModuleLoadRequest *aRequest, - nsresult aResult); - - bool IsFetchingModule(nsModuleLoadRequest *aRequest) const; - - bool ModuleMapContainsModule(nsModuleLoadRequest *aRequest) const; - RefPtr<mozilla::GenericPromise> WaitForModuleFetch(nsModuleLoadRequest *aRequest); - nsModuleScript* GetFetchedModule(nsIURI* aURL) const; - - friend bool - HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp); - - nsresult CreateModuleScript(nsModuleLoadRequest* aRequest); - nsresult ProcessFetchedModuleSource(nsModuleLoadRequest* aRequest); - void ProcessLoadedModuleTree(nsModuleLoadRequest* aRequest); - bool InstantiateModuleTree(nsModuleLoadRequest* aRequest); - void StartFetchingModuleDependencies(nsModuleLoadRequest* aRequest); - - RefPtr<mozilla::GenericPromise> - StartFetchingModuleAndDependencies(nsModuleLoadRequest* aRequest, nsIURI* aURI); - - nsIDocument* mDocument; // [WEAK] - nsCOMArray<nsIScriptLoaderObserver> mObservers; - nsScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests; - // mLoadingAsyncRequests holds async requests while they're loading; when they - // have been loaded they are moved to mLoadedAsyncRequests. - nsScriptLoadRequestList mLoadingAsyncRequests; - nsScriptLoadRequestList mLoadedAsyncRequests; - nsScriptLoadRequestList mDeferRequests; - nsScriptLoadRequestList mXSLTRequests; - RefPtr<nsScriptLoadRequest> mParserBlockingRequest; - - // In mRequests, the additional information here is stored by the element. - struct PreloadInfo { - RefPtr<nsScriptLoadRequest> mRequest; - nsString mCharset; - }; - - friend void ImplCycleCollectionUnlink(nsScriptLoader::PreloadInfo& aField); - friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, - nsScriptLoader::PreloadInfo& aField, - const char* aName, uint32_t aFlags); - - struct PreloadRequestComparator { - bool Equals(const PreloadInfo &aPi, nsScriptLoadRequest * const &aRequest) - const - { - return aRequest == aPi.mRequest; - } - }; - struct PreloadURIComparator { - bool Equals(const PreloadInfo &aPi, nsIURI * const &aURI) const; - }; - nsTArray<PreloadInfo> mPreloads; - - nsCOMPtr<nsIScriptElement> mCurrentScript; - nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript; - nsTArray< RefPtr<nsScriptLoader> > mPendingChildLoaders; - uint32_t mParserBlockingBlockerCount; - uint32_t mBlockerCount; - uint32_t mNumberOfProcessors; - bool mEnabled; - bool mDeferEnabled; - bool mDocumentParsingDone; - bool mBlockingDOMContentLoaded; - - // Module map - nsRefPtrHashtable<nsURIHashKey, mozilla::GenericPromise::Private> mFetchingModules; - nsRefPtrHashtable<nsURIHashKey, nsModuleScript> mFetchedModules; - - nsCOMPtr<nsIConsoleReportCollector> mReporter; -}; - -class nsScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver -{ -public: - explicit nsScriptLoadHandler(nsScriptLoader* aScriptLoader, - nsScriptLoadRequest *aRequest, - mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier); - - NS_DECL_ISUPPORTS - NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER - -private: - virtual ~nsScriptLoadHandler(); - - /* - * Try to decode some raw data. - */ - nsresult TryDecodeRawData(const uint8_t* aData, uint32_t aDataLength, - bool aEndOfStream); - - /* - * Discover the charset by looking at the stream data, the script - * tag, and other indicators. Returns true if charset has been - * discovered. - */ - bool EnsureDecoder(nsIIncrementalStreamLoader *aLoader, - const uint8_t* aData, uint32_t aDataLength, - bool aEndOfStream); - - // ScriptLoader which will handle the parsed script. - RefPtr<nsScriptLoader> mScriptLoader; - - // The nsScriptLoadRequest for this load. - RefPtr<nsScriptLoadRequest> mRequest; - - // SRI data verifier. - nsAutoPtr<mozilla::dom::SRICheckDataVerifier> mSRIDataVerifier; - - // Status of SRI data operations. - nsresult mSRIStatus; - - // Unicode decoder for charset. - nsCOMPtr<nsIUnicodeDecoder> mDecoder; - - // Accumulated decoded char buffer. - mozilla::Vector<char16_t> mBuffer; -}; - -class nsAutoScriptLoaderDisabler -{ -public: - explicit nsAutoScriptLoaderDisabler(nsIDocument* aDoc) - { - mLoader = aDoc->ScriptLoader(); - mWasEnabled = mLoader->GetEnabled(); - if (mWasEnabled) { - mLoader->SetEnabled(false); - } - } - - ~nsAutoScriptLoaderDisabler() - { - if (mWasEnabled) { - mLoader->SetEnabled(true); - } - } - - bool mWasEnabled; - RefPtr<nsScriptLoader> mLoader; -}; - -#endif //__nsScriptLoader_h__ |