Merge mozilla-inbound to mozilla-central. a=merge

This commit is contained in:
Daniel Varga 2019-01-03 18:22:07 +02:00
commit 6475e7a21d
54 changed files with 1334 additions and 703 deletions

View File

@ -234,6 +234,7 @@
#include "mozilla/dom/PrimitiveConversions.h"
#include "mozilla/dom/WindowBinding.h"
#include "nsITabChild.h"
#include "mozilla/dom/LoadedScript.h"
#include "mozilla/dom/MediaQueryList.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/NavigatorBinding.h"
@ -5983,8 +5984,6 @@ bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
if (!callback) {
// Evaluate the timeout expression.
const nsAString& script = handler->GetHandlerText();
const char* filename = nullptr;
uint32_t lineNo = 0, dummyColumn = 0;
handler->GetLocation(&filename, &lineNo, &dummyColumn);
@ -6001,7 +6000,16 @@ bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
nsresult rv;
{
nsJSUtils::ExecutionContext exec(aes.cx(), global);
rv = exec.CompileAndExec(options, script);
rv = exec.Compile(options, handler->GetHandlerText());
if (rv == NS_OK) {
LoadedScript* initiatingScript = handler->GetInitiatingScript();
if (initiatingScript) {
initiatingScript->AssociateWithScript(exec.GetScript());
}
rv = exec.ExecScript();
}
}
if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {

View File

@ -14,6 +14,7 @@
namespace mozilla {
namespace dom {
class Function;
class LoadedScript;
} // namespace dom
} // namespace mozilla
@ -46,6 +47,9 @@ class nsIScriptTimeoutHandler : public nsITimeoutHandler {
// If we have a Function, get the arguments for passing to it.
virtual const nsTArray<JS::Value>& GetArgs() = 0;
// If we have an expression, get the initiating script.
virtual mozilla::dom::LoadedScript* GetInitiatingScript() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptTimeoutHandler,

View File

@ -11,6 +11,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/dom/CSPEvalChecker.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/LoadedScript.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
@ -39,6 +40,7 @@ class nsJSScriptTimeoutHandler final : public nsIScriptTimeoutHandler {
nsTArray<JS::Heap<JS::Value>>&& aArguments,
ErrorResult& aError);
nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindowInner* aWindow,
LoadedScript* aInitiatingScript,
const nsAString& aExpression, bool* aAllowEval,
ErrorResult& aError);
nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
@ -63,6 +65,10 @@ class nsJSScriptTimeoutHandler final : public nsIScriptTimeoutHandler {
*aColumn = mColumn;
}
virtual LoadedScript* GetInitiatingScript() override {
return mInitiatingScript;
}
virtual void MarkForCC() override {
if (mFunction) {
mFunction->MarkForCC();
@ -88,6 +94,9 @@ class nsJSScriptTimeoutHandler final : public nsIScriptTimeoutHandler {
// it should be used, else use mExpr.
nsString mExpr;
RefPtr<Function> mFunction;
// Initiating script for use when evaluating mExpr on the main thread.
RefPtr<LoadedScript> mInitiatingScript;
};
// nsJSScriptTimeoutHandler
@ -95,8 +104,11 @@ class nsJSScriptTimeoutHandler final : public nsIScriptTimeoutHandler {
NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFunction)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInitiatingScript)
tmp->ReleaseJSObjects();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
nsAutoCString name("nsJSScriptTimeoutHandler");
@ -134,6 +146,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
if (tmp->mFunction) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFunction)
}
if (tmp->mInitiatingScript) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInitiatingScript)
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
@ -167,12 +182,14 @@ nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(
Init(aCx, std::move(aArguments));
}
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
nsGlobalWindowInner* aWindow,
const nsAString& aExpression,
bool* aAllowEval,
ErrorResult& aError)
: mLineNo(0), mColumn(0), mExpr(aExpression) {
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(
JSContext* aCx, nsGlobalWindowInner* aWindow,
LoadedScript* aInitiatingScript, const nsAString& aExpression,
bool* aAllowEval, ErrorResult& aError)
: mLineNo(0),
mColumn(0),
mExpr(aExpression),
mInitiatingScript(aInitiatingScript) {
if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
// This window was already closed, or never properly initialized,
// don't let a timer be scheduled on such a window.
@ -260,9 +277,11 @@ already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(
already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(
JSContext* aCx, nsGlobalWindowInner* aWindow, const nsAString& aExpression,
ErrorResult& aError) {
LoadedScript* script = ScriptLoader::GetActiveScript(aCx);
bool allowEval = false;
RefPtr<nsJSScriptTimeoutHandler> handler = new nsJSScriptTimeoutHandler(
aCx, aWindow, aExpression, &allowEval, aError);
aCx, aWindow, script, aExpression, &allowEval, aError);
if (aError.Failed() || !allowEval) {
return nullptr;
}

View File

@ -124,6 +124,7 @@ nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
mRealm(aCx, aGlobal),
mRetValue(aCx),
mScopeChain(aCx),
mScript(aCx),
mRv(NS_OK),
mSkip(false),
mCoerceToString(false),
@ -131,7 +132,8 @@ nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
#ifdef DEBUG
,
mWantsReturnValue(false),
mExpectScopeChain(false)
mExpectScopeChain(false),
mScriptUsed(false)
#endif
{
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
@ -174,30 +176,24 @@ void nsJSUtils::ExecutionContext::SetScopeChain(
}
}
nsresult nsJSUtils::ExecutionContext::JoinAndExec(
JS::OffThreadToken** aOffThreadToken,
JS::MutableHandle<JSScript*> aScript) {
nsresult nsJSUtils::ExecutionContext::JoinCompile(
JS::OffThreadToken** aOffThreadToken) {
if (mSkip) {
return mRv;
}
MOZ_ASSERT(!mWantsReturnValue);
MOZ_ASSERT(!mExpectScopeChain);
aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
MOZ_ASSERT(!mScript);
mScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken));
*aOffThreadToken = nullptr; // Mark the token as having been finished.
if (!aScript) {
if (!mScript) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
@ -206,9 +202,8 @@ nsresult nsJSUtils::ExecutionContext::JoinAndExec(
return NS_OK;
}
nsresult nsJSUtils::ExecutionContext::CompileAndExec(
JS::CompileOptions& aCompileOptions, JS::SourceText<char16_t>& aSrcBuf,
JS::MutableHandle<JSScript*> aScript) {
nsresult nsJSUtils::ExecutionContext::Compile(
JS::CompileOptions& aCompileOptions, JS::SourceText<char16_t>& aSrcBuf) {
if (mSkip) {
return mRv;
}
@ -219,29 +214,23 @@ nsresult nsJSUtils::ExecutionContext::CompileAndExec(
mWantsReturnValue = !aCompileOptions.noScriptRval;
#endif
MOZ_ASSERT(!mScript);
bool compiled = true;
if (mScopeChain.length() == 0) {
compiled = JS::Compile(mCx, aCompileOptions, aSrcBuf, aScript);
compiled = JS::Compile(mCx, aCompileOptions, aSrcBuf, &mScript);
} else {
compiled =
JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf, aScript);
compiled = JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf,
&mScript);
}
MOZ_ASSERT_IF(compiled, aScript);
MOZ_ASSERT_IF(compiled, mScript);
if (!compiled) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
MOZ_ASSERT(!mCoerceToString || mWantsReturnValue);
if (!JS_ExecuteScript(mCx, mScopeChain, aScript, &mRetValue)) {
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
@ -250,10 +239,8 @@ nsresult nsJSUtils::ExecutionContext::CompileAndExec(
return NS_OK;
}
nsresult nsJSUtils::ExecutionContext::CompileAndExec(
nsresult nsJSUtils::ExecutionContext::Compile(
JS::CompileOptions& aCompileOptions, const nsAString& aScript) {
MOZ_ASSERT(!mEncodeBytecode,
"A JSScript is needed for calling FinishIncrementalEncoding");
if (mSkip) {
return mRv;
}
@ -267,23 +254,19 @@ nsresult nsJSUtils::ExecutionContext::CompileAndExec(
return mRv;
}
JS::Rooted<JSScript*> script(mCx);
return CompileAndExec(aCompileOptions, srcBuf, &script);
return Compile(aCompileOptions, srcBuf);
}
nsresult nsJSUtils::ExecutionContext::DecodeAndExec(
nsresult nsJSUtils::ExecutionContext::Decode(
JS::CompileOptions& aCompileOptions, mozilla::Vector<uint8_t>& aBytecodeBuf,
size_t aBytecodeIndex) {
MOZ_ASSERT(!mEncodeBytecode,
"A JSScript is needed for calling FinishIncrementalEncoding");
if (mSkip) {
return mRv;
}
MOZ_ASSERT(!mWantsReturnValue);
JS::Rooted<JSScript*> script(mCx);
JS::TranscodeResult tr =
JS::DecodeScript(mCx, aBytecodeBuf, &script, aBytecodeIndex);
JS::DecodeScript(mCx, aBytecodeBuf, &mScript, aBytecodeIndex);
// These errors are external parameters which should be handled before the
// decoding phase, and which are the only reasons why you might want to
// fallback on decoding failures.
@ -295,16 +278,10 @@ nsresult nsJSUtils::ExecutionContext::DecodeAndExec(
return mRv;
}
if (!JS_ExecuteScript(mCx, mScopeChain, script)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return mRv;
}
nsresult nsJSUtils::ExecutionContext::DecodeJoinAndExec(
nsresult nsJSUtils::ExecutionContext::JoinDecode(
JS::OffThreadToken** aOffThreadToken) {
if (mSkip) {
return mRv;
@ -312,10 +289,9 @@ nsresult nsJSUtils::ExecutionContext::DecodeJoinAndExec(
MOZ_ASSERT(!mWantsReturnValue);
MOZ_ASSERT(!mExpectScopeChain);
JS::Rooted<JSScript*> script(mCx);
script.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken));
mScript.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken));
*aOffThreadToken = nullptr; // Mark the token as having been finished.
if (!script || !JS_ExecuteScript(mCx, mScopeChain, script)) {
if (!mScript) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
@ -324,9 +300,8 @@ nsresult nsJSUtils::ExecutionContext::DecodeJoinAndExec(
return NS_OK;
}
nsresult nsJSUtils::ExecutionContext::DecodeBinASTJoinAndExec(
JS::OffThreadToken** aOffThreadToken,
JS::MutableHandle<JSScript*> aScript) {
nsresult nsJSUtils::ExecutionContext::JoinDecodeBinAST(
JS::OffThreadToken** aOffThreadToken) {
#ifdef JS_BUILD_BINAST
if (mSkip) {
return mRv;
@ -335,22 +310,16 @@ nsresult nsJSUtils::ExecutionContext::DecodeBinASTJoinAndExec(
MOZ_ASSERT(!mWantsReturnValue);
MOZ_ASSERT(!mExpectScopeChain);
aScript.set(JS::FinishOffThreadBinASTDecode(mCx, *aOffThreadToken));
mScript.set(JS::FinishOffThreadBinASTDecode(mCx, *aOffThreadToken));
*aOffThreadToken = nullptr; // Mark the token as having been finished.
if (!aScript) {
if (!mScript) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
@ -362,9 +331,8 @@ nsresult nsJSUtils::ExecutionContext::DecodeBinASTJoinAndExec(
#endif
}
nsresult nsJSUtils::ExecutionContext::DecodeBinASTAndExec(
JS::CompileOptions& aCompileOptions, const uint8_t* aBuf, size_t aLength,
JS::MutableHandle<JSScript*> aScript) {
nsresult nsJSUtils::ExecutionContext::DecodeBinAST(
JS::CompileOptions& aCompileOptions, const uint8_t* aBuf, size_t aLength) {
#ifdef JS_BUILD_BINAST
MOZ_ASSERT(mScopeChain.length() == 0,
"BinAST decoding is not supported in non-syntactic scopes");
@ -379,22 +347,15 @@ nsresult nsJSUtils::ExecutionContext::DecodeBinASTAndExec(
mWantsReturnValue = !aCompileOptions.noScriptRval;
#endif
aScript.set(JS::DecodeBinAST(mCx, aCompileOptions, aBuf, aLength));
mScript.set(JS::DecodeBinAST(mCx, aCompileOptions, aBuf, aLength));
if (!aScript) {
if (!mScript) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
MOZ_ASSERT(!mCoerceToString || mWantsReturnValue);
if (!JS_ExecuteScript(mCx, mScopeChain, aScript, &mRetValue)) {
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
@ -406,6 +367,32 @@ nsresult nsJSUtils::ExecutionContext::DecodeBinASTAndExec(
#endif
}
JSScript* nsJSUtils::ExecutionContext::GetScript() {
#ifdef DEBUG
MOZ_ASSERT(!mSkip);
MOZ_ASSERT(mScript);
mScriptUsed = true;
#endif
return mScript;
}
nsresult nsJSUtils::ExecutionContext::ExecScript() {
if (mSkip) {
return mRv;
}
MOZ_ASSERT(mScript);
if (!JS_ExecuteScript(mCx, mScopeChain, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return NS_OK;
}
static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
if (!aValue.isObject()) {
return false;
@ -419,43 +406,45 @@ static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
return JS::IsPromiseObject(obj);
}
nsresult nsJSUtils::ExecutionContext::ExtractReturnValue(
nsresult nsJSUtils::ExecutionContext::ExecScript(
JS::MutableHandle<JS::Value> aRetValue) {
MOZ_ASSERT(aRetValue.isUndefined());
if (mSkip) {
// Repeat earlier result, as NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW are not
// failures cases.
#ifdef DEBUG
mWantsReturnValue = false;
#endif
aRetValue.setUndefined();
return mRv;
}
MOZ_ASSERT(mScript);
MOZ_ASSERT(mWantsReturnValue);
if (!JS_ExecuteScript(mCx, mScopeChain, mScript, aRetValue)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
#ifdef DEBUG
mWantsReturnValue = false;
#endif
if (mCoerceToString && IsPromiseValue(mCx, mRetValue)) {
if (mCoerceToString && IsPromiseValue(mCx, aRetValue)) {
// We're a javascript: url and we should treat Promise return values as
// undefined.
//
// Once bug 1477821 is fixed this code might be able to go away, or will
// become enshrined in the spec, depending.
mRetValue.setUndefined();
aRetValue.setUndefined();
}
if (mCoerceToString && !mRetValue.isUndefined()) {
JSString* str = JS::ToString(mCx, mRetValue);
if (mCoerceToString && !aRetValue.isUndefined()) {
JSString* str = JS::ToString(mCx, aRetValue);
if (!str) {
// ToString can be a function call, so an exception can be raised while
// executing the function.
mSkip = true;
return EvaluationExceptionToNSResult(mCx);
}
mRetValue.set(JS::StringValue(str));
aRetValue.set(JS::StringValue(str));
}
aRetValue.set(mRetValue);
return NS_OK;
}
@ -465,7 +454,6 @@ nsresult nsJSUtils::CompileModule(JSContext* aCx,
JS::CompileOptions& aCompileOptions,
JS::MutableHandle<JSObject*> aModule) {
AUTO_PROFILER_LABEL("nsJSUtils::CompileModule", JS);
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
MOZ_ASSERT(aSrcBuf.get());
MOZ_ASSERT(JS_IsGlobalObject(aEvaluationGlobal));

View File

@ -81,6 +81,9 @@ class nsJSUtils {
// Scope chain in which the execution takes place.
JS::AutoObjectVector mScopeChain;
// The compiled script.
JS::Rooted<JSScript*> mScript;
// returned value forwarded when we have to interupt the execution eagerly
// with mSkip.
nsresult mRv;
@ -100,6 +103,8 @@ class nsJSUtils {
bool mWantsReturnValue;
bool mExpectScopeChain;
bool mScriptUsed;
#endif
public:
@ -111,8 +116,12 @@ class nsJSUtils {
ExecutionContext(ExecutionContext&&) = delete;
~ExecutionContext() {
// This flag is resetted, when the returned value is extracted.
MOZ_ASSERT(!mWantsReturnValue);
// This flag is reset when the returned value is extracted.
MOZ_ASSERT_IF(!mSkip, !mWantsReturnValue);
// If encoding was started we expect the script to have been
// used when ending the encoding.
MOZ_ASSERT_IF(mEncodeBytecode && mScript && mRv == NS_OK, mScriptUsed);
}
// The returned value would be converted to a string if the
@ -134,54 +143,53 @@ class nsJSUtils {
// Set the scope chain in which the code should be executed.
void SetScopeChain(const JS::AutoObjectVector& aScopeChain);
// Copy the returned value in the mutable handle argument, in case of a
// After getting a notification that an off-thread compilation terminated,
// this function will take the result of the parser and move it to the main
// thread.
MOZ_MUST_USE nsresult JoinCompile(JS::OffThreadToken** aOffThreadToken);
// Compile a script contained in a SourceText.
nsresult Compile(JS::CompileOptions& aCompileOptions,
JS::SourceText<char16_t>& aSrcBuf);
// Compile a script contained in a string.
nsresult Compile(JS::CompileOptions& aCompileOptions,
const nsAString& aScript);
// Decode a script contained in a buffer.
nsresult Decode(JS::CompileOptions& aCompileOptions,
mozilla::Vector<uint8_t>& aBytecodeBuf,
size_t aBytecodeIndex);
// After getting a notification that an off-thread decoding terminated, this
// function will get the result of the decoder and move it to the main
// thread.
nsresult JoinDecode(JS::OffThreadToken** aOffThreadToken);
nsresult JoinDecodeBinAST(JS::OffThreadToken** aOffThreadToken);
// Decode a BinAST encoded script contained in a buffer.
nsresult DecodeBinAST(JS::CompileOptions& aCompileOptions,
const uint8_t* aBuf, size_t aLength);
// Get a successfully compiled script.
JSScript* GetScript();
// Execute the compiled script and ignore the return value.
MOZ_MUST_USE nsresult ExecScript();
// Execute the compiled script a get the return value.
//
// Copy the returned value into the mutable handle argument. In case of a
// evaluation failure either during the execution or the conversion of the
// result to a string, the nsresult would be set to the corresponding result
// code, and the mutable handle argument would remain unchanged.
// result to a string, the nsresult is be set to the corresponding result
// code and the mutable handle argument remains unchanged.
//
// The value returned in the mutable handle argument is part of the
// compartment given as argument to the ExecutionContext constructor. If the
// caller is in a different compartment, then the out-param value should be
// wrapped by calling |JS_WrapValue|.
MOZ_MUST_USE nsresult
ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue);
// After getting a notification that an off-thread compilation terminated,
// this function will take the result of the parser by moving it to the main
// thread before starting the execution of the script.
//
// The compiled script would be returned in the |aScript| out-param.
MOZ_MUST_USE nsresult JoinAndExec(JS::OffThreadToken** aOffThreadToken,
JS::MutableHandle<JSScript*> aScript);
// Compile a script contained in a SourceText, and execute it.
nsresult CompileAndExec(JS::CompileOptions& aCompileOptions,
JS::SourceText<char16_t>& aSrcBuf,
JS::MutableHandle<JSScript*> aScript);
// Compile a script contained in a string, and execute it.
nsresult CompileAndExec(JS::CompileOptions& aCompileOptions,
const nsAString& aScript);
// Decode a script contained in a buffer, and execute it.
MOZ_MUST_USE nsresult DecodeAndExec(JS::CompileOptions& aCompileOptions,
mozilla::Vector<uint8_t>& aBytecodeBuf,
size_t aBytecodeIndex);
// After getting a notification that an off-thread decoding terminated, this
// function will get the result of the decoder by moving it to the main
// thread before starting the execution of the script.
MOZ_MUST_USE nsresult
DecodeJoinAndExec(JS::OffThreadToken** aOffThreadToken);
MOZ_MUST_USE nsresult
DecodeBinASTJoinAndExec(JS::OffThreadToken** aOffThreadToken,
JS::MutableHandle<JSScript*> aScript);
// Decode a BinAST encoded script contained in a buffer, and execute it.
nsresult DecodeBinASTAndExec(JS::CompileOptions& aCompileOptions,
const uint8_t* aBuf, size_t aLength,
JS::MutableHandle<JSScript*> aScript);
MOZ_MUST_USE nsresult ExecScript(JS::MutableHandle<JS::Value> aRetValue);
};
static nsresult CompileModule(JSContext* aCx,

View File

@ -22,7 +22,9 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/EventTargetBinding.h"
#include "mozilla/dom/LoadedScript.h"
#include "mozilla/dom/PopupBlocker.h"
#include "mozilla/dom/ScriptLoader.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/TimelineConsumers.h"
@ -990,6 +992,15 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
NS_ENSURE_SUCCESS(result, result);
NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
JS::Rooted<JSFunction*> func(cx, JS_GetObjectFunction(handler));
MOZ_ASSERT(func);
JS::Rooted<JSScript*> jsScript(cx, JS_GetFunctionScript(cx, func));
MOZ_ASSERT(jsScript);
RefPtr<LoadedScript> loaderScript = ScriptLoader::GetActiveScript(cx);
if (loaderScript) {
loaderScript->AssociateWithScript(jsScript);
}
MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx));
JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx));

View File

@ -254,8 +254,8 @@ nsresult nsJSThunk::EvaluateScript(
{
nsJSUtils::ExecutionContext exec(cx, globalJSObject);
exec.SetCoerceToString(true);
exec.CompileAndExec(options, NS_ConvertUTF8toUTF16(script));
rv = exec.ExtractReturnValue(&v);
exec.Compile(options, NS_ConvertUTF8toUTF16(script));
rv = exec.ExecScript(&v);
}
js::AssertSameCompartment(cx, v);

View File

@ -998,8 +998,8 @@ bool _evaluate(NPP npp, NPObject *npobj, NPString *script, NPVariant *result) {
{
nsJSUtils::ExecutionContext exec(cx, obj);
exec.SetScopeChain(scopeChain);
exec.CompileAndExec(options, utf16script);
rv = exec.ExtractReturnValue(&rval);
exec.Compile(options, utf16script);
rv = exec.ExecScript(&rval);
}
if (!JS_WrapValue(cx, &rval)) {

184
dom/script/LoadedScript.cpp Normal file
View File

@ -0,0 +1,184 @@
/* -*- 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 "LoadedScript.h"
#include "mozilla/HoldDropJSObjects.h"
#include "jsfriendapi.h"
#include "ScriptLoader.h"
namespace mozilla {
namespace dom {
//////////////////////////////////////////////////////////////
// LoadedScript
//////////////////////////////////////////////////////////////
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadedScript)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_CLASS(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadedScript)
NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadedScript)
LoadedScript::LoadedScript(ScriptKind aKind, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL)
: mKind(aKind),
mFetchOptions(aFetchOptions),
mBaseURL(aBaseURL) {
MOZ_ASSERT(mFetchOptions);
MOZ_ASSERT(mBaseURL);
}
LoadedScript::~LoadedScript() { DropJSObjects(this); }
void LoadedScript::AssociateWithScript(JSScript* aScript) {
// Set a JSScript's private value to point to this object and
// increment our reference count. This is decremented by
// HostFinalizeTopLevelScript() below when the JSScript dies.
MOZ_ASSERT(JS::GetScriptPrivate(aScript).isUndefined());
JS::SetScriptPrivate(aScript, JS::PrivateValue(this));
AddRef();
}
void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate) {
// Decrement the reference count of a LoadedScript object that is
// pointed to by a dying JSScript. The reference count was
// originally incremented by AssociateWithScript() above.
auto script = static_cast<LoadedScript*>(aPrivate.toPrivate());
#ifdef DEBUG
if (script->IsModuleScript()) {
JSObject* module = script->AsModuleScript()->mModuleRecord.unbarrieredGet();
MOZ_ASSERT_IF(module, JS::GetModulePrivate(module) == aPrivate);
}
#endif
script->Release();
}
//////////////////////////////////////////////////////////////
// ClassicScript
//////////////////////////////////////////////////////////////
ClassicScript::ClassicScript(ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL)
: LoadedScript(ScriptKind::eClassic, aFetchOptions, aBaseURL) {}
//////////////////////////////////////////////////////////////
// ModuleScript
//////////////////////////////////////////////////////////////
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleScript)
NS_INTERFACE_MAP_END_INHERITING(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleScript, LoadedScript)
tmp->UnlinkModuleRecord();
tmp->mParseError.setUndefined();
tmp->mErrorToRethrow.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript, LoadedScript)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleScript, LoadedScript)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ADDREF_INHERITED(ModuleScript, LoadedScript)
NS_IMPL_RELEASE_INHERITED(ModuleScript, LoadedScript)
ModuleScript::ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
: LoadedScript(ScriptKind::eModule, aFetchOptions, aBaseURL),
mSourceElementAssociated(false) {
MOZ_ASSERT(!ModuleRecord());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
}
void ModuleScript::UnlinkModuleRecord() {
// Remove the module record's pointer to this object if present and
// decrement our reference count. The reference is added by
// SetModuleRecord() below.
if (mModuleRecord) {
MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).toPrivate() == this);
JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue());
mModuleRecord = nullptr;
Release();
}
}
ModuleScript::~ModuleScript() {
// The object may be destroyed without being unlinked first.
UnlinkModuleRecord();
}
void ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) {
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasParseError());
MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasErrorToRethrow());
mModuleRecord = aModuleRecord;
// Make module's host defined field point to this object and
// increment our reference count. This is decremented by
// UnlinkModuleRecord() above.
MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).isUndefined());
JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
HoldJSObjects(this);
AddRef();
}
void ModuleScript::SetParseError(const JS::Value& aError) {
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
UnlinkModuleRecord();
mParseError = aError;
HoldJSObjects(this);
}
void ModuleScript::SetErrorToRethrow(const JS::Value& aError) {
MOZ_ASSERT(!aError.isUndefined());
// This is only called after SetModuleRecord() or SetParseError() so we don't
// need to call HoldJSObjects() here.
MOZ_ASSERT(ModuleRecord() || HasParseError());
mErrorToRethrow = aError;
}
void ModuleScript::SetSourceElementAssociated() {
MOZ_ASSERT(ModuleRecord());
MOZ_ASSERT(!mSourceElementAssociated);
mSourceElementAssociated = true;
}
} // namespace dom
} // namespace mozilla

108
dom/script/LoadedScript.h Normal file
View File

@ -0,0 +1,108 @@
/* -*- 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 mozilla_dom_LoadedScript_h
#define mozilla_dom_LoadedScript_h
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "jsapi.h"
#include "ScriptLoadRequest.h"
class nsIURI;
namespace mozilla {
namespace dom {
class ScriptLoader;
void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate);
class ClassicScript;
class ModuleScript;
class LoadedScript : public nsISupports {
ScriptKind mKind;
RefPtr<ScriptFetchOptions> mFetchOptions;
nsCOMPtr<nsIURI> mBaseURL;
protected:
LoadedScript(ScriptKind aKind, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL);
virtual ~LoadedScript();
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(LoadedScript)
bool IsModuleScript() const { return mKind == ScriptKind::eModule; }
inline ClassicScript* AsClassicScript();
inline ModuleScript* AsModuleScript();
ScriptFetchOptions* FetchOptions() const { return mFetchOptions; }
nsIURI* BaseURL() const { return mBaseURL; }
void AssociateWithScript(JSScript* aScript);
};
class ClassicScript final : public LoadedScript {
~ClassicScript() = default;
public:
ClassicScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
};
// A single module script. May be used to satisfy multiple load requests.
class ModuleScript final : public LoadedScript {
JS::Heap<JSObject*> mModuleRecord;
JS::Heap<JS::Value> mParseError;
JS::Heap<JS::Value> mErrorToRethrow;
bool mSourceElementAssociated;
~ModuleScript();
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleScript,
LoadedScript)
ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetParseError(const JS::Value& aError);
void SetErrorToRethrow(const JS::Value& aError);
void SetSourceElementAssociated();
JSObject* ModuleRecord() const { return mModuleRecord; }
JS::Value ParseError() const { return mParseError; }
JS::Value ErrorToRethrow() const { return mErrorToRethrow; }
bool HasParseError() const { return !mParseError.isUndefined(); }
bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); }
bool SourceElementAssociated() const { return mSourceElementAssociated; }
void UnlinkModuleRecord();
friend void HostFinalizeTopLevelScript(JSFreeOp*, const JS::Value&);
};
ClassicScript* LoadedScript::AsClassicScript() {
MOZ_ASSERT(!IsModuleScript());
return static_cast<ClassicScript*>(this);
}
ModuleScript* LoadedScript::AsModuleScript() {
MOZ_ASSERT(IsModuleScript());
return static_cast<ModuleScript*>(this);
}
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_LoadedScript_h

View File

@ -5,7 +5,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ModuleLoadRequest.h"
#include "ModuleScript.h"
#include "mozilla/HoldDropJSObjects.h"
#include "LoadedScript.h"
#include "ScriptLoader.h"
namespace mozilla {
@ -18,35 +21,91 @@ namespace dom {
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoadRequest)
NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest,
mBaseURL, mLoader, mModuleScript, mImports)
NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader, mModuleScript, mImports)
tmp->ClearDynamicImport();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader, mModuleScript, mImports)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicReferencingPrivate)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicSpecifier)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicPromise)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI,
ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity,
nsIURI* aReferrer, ScriptLoader* aLoader)
static VisitedURLSet* NewVisitedSetForTopLevelImport(nsIURI* aURI) {
auto set = new VisitedURLSet();
set->PutEntry(aURI);
return set;
}
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateTopLevel(
nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity, nsIURI* aReferrer, ScriptLoader* aLoader) {
return new ModuleLoadRequest(aURI, aFetchOptions, aIntegrity, aReferrer,
true, /* is top level */
false, /* is dynamic import */
aLoader, NewVisitedSetForTopLevelImport(aURI));
}
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateStaticImport(
nsIURI* aURI, ModuleLoadRequest* aParent) {
auto request =
new ModuleLoadRequest(aURI, aParent->mFetchOptions, SRIMetadata(),
aParent->mURI, false, /* is top level */
false, /* is dynamic import */
aParent->mLoader, aParent->mVisitedSet);
request->mIsInline = false;
request->mScriptMode = aParent->mScriptMode;
return request;
}
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateDynamicImport(
nsIURI* aURI, ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
ScriptLoader* aLoader, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise) {
MOZ_ASSERT(aSpecifier);
MOZ_ASSERT(aPromise);
auto request = new ModuleLoadRequest(
aURI, aFetchOptions, SRIMetadata(), aBaseURL, true, /* is top level */
true, /* is dynamic import */
aLoader, NewVisitedSetForTopLevelImport(aURI));
request->mIsInline = false;
request->mScriptMode = ScriptMode::eAsync;
request->mDynamicReferencingPrivate = aReferencingPrivate;
request->mDynamicSpecifier = aSpecifier;
request->mDynamicPromise = aPromise;
HoldJSObjects(request);
return request;
}
ModuleLoadRequest::ModuleLoadRequest(
nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity, nsIURI* aReferrer, bool aIsTopLevel,
bool aIsDynamicImport, ScriptLoader* aLoader, VisitedURLSet* aVisitedSet)
: ScriptLoadRequest(ScriptKind::eModule, aURI, aFetchOptions, aIntegrity,
aReferrer),
mIsTopLevel(true),
mIsTopLevel(aIsTopLevel),
mIsDynamicImport(aIsDynamicImport),
mLoader(aLoader),
mVisitedSet(new VisitedURLSet()) {
mVisitedSet->PutEntry(aURI);
}
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI, ModuleLoadRequest* aParent)
: ScriptLoadRequest(ScriptKind::eModule, aURI, aParent->mFetchOptions,
SRIMetadata(), aParent->mURI),
mIsTopLevel(false),
mLoader(aParent->mLoader),
mVisitedSet(aParent->mVisitedSet) {
MOZ_ASSERT(mVisitedSet->Contains(aURI));
mIsInline = false;
mScriptMode = aParent->mScriptMode;
}
mVisitedSet(aVisitedSet) {}
void ModuleLoadRequest::Cancel() {
ScriptLoadRequest::Cancel();
@ -138,5 +197,11 @@ void ModuleLoadRequest::LoadFinished() {
mLoader = nullptr;
}
void ModuleLoadRequest::ClearDynamicImport() {
mDynamicReferencingPrivate = JS::UndefinedValue();
mDynamicSpecifier = nullptr;
mDynamicPromise = nullptr;
}
} // namespace dom
} // namespace mozilla

View File

@ -36,22 +36,40 @@ class ModuleLoadRequest final : public ScriptLoadRequest {
ModuleLoadRequest(const ModuleLoadRequest& aOther) = delete;
ModuleLoadRequest(ModuleLoadRequest&& aOther) = delete;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
// Create a top-level module load request.
ModuleLoadRequest(nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity, nsIURI* aReferrer,
ScriptLoader* aLoader);
bool aIsTopLevel, bool aIsDynamicImport,
ScriptLoader* aLoader, VisitedURLSet* aVisitedSet);
// Create a module load request for an imported module.
ModuleLoadRequest(nsIURI* aURI, ModuleLoadRequest* aParent);
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleLoadRequest,
ScriptLoadRequest)
// Create a top-level module load request.
static ModuleLoadRequest* CreateTopLevel(nsIURI* aURI,
ScriptFetchOptions* aFetchOptions,
const SRIMetadata& aIntegrity,
nsIURI* aReferrer,
ScriptLoader* aLoader);
// Create a module load request for a static module import.
static ModuleLoadRequest* CreateStaticImport(nsIURI* aURI,
ModuleLoadRequest* aParent);
// Create a module load request for dynamic module import.
static ModuleLoadRequest* CreateDynamicImport(
nsIURI* aURI, ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
ScriptLoader* aLoader, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise);
bool IsTopLevel() const override { return mIsTopLevel; }
bool IsDynamicImport() const { return mIsDynamicImport; }
void SetReady() override;
void Cancel() override;
void ClearDynamicImport();
void ModuleLoaded();
void ModuleErrored();
@ -66,8 +84,8 @@ class ModuleLoadRequest final : public ScriptLoadRequest {
// Is this a request for a top level module script or an import?
const bool mIsTopLevel;
// The base URL used for resolving relative module imports.
nsCOMPtr<nsIURI> mBaseURL;
// Is this the top level request for a dynamic module import?
const bool mIsDynamicImport;
// Pointer to the script loader, used to trigger actions when the module load
// finishes.
@ -88,6 +106,11 @@ class ModuleLoadRequest final : public ScriptLoadRequest {
// Set of module URLs visited while fetching the module graph this request is
// part of.
RefPtr<VisitedURLSet> mVisitedSet;
// For dynamic imports, the details to pass to FinishDynamicImport.
JS::Heap<JS::Value> mDynamicReferencingPrivate;
JS::Heap<JSString*> mDynamicSpecifier;
JS::Heap<JSObject*> mDynamicPromise;
};
} // namespace dom

View File

@ -1,108 +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 "ModuleScript.h"
#include "mozilla/HoldDropJSObjects.h"
#include "ScriptLoader.h"
namespace mozilla {
namespace dom {
// A single module script. May be used to satisfy multiple load requests.
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleScript)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
tmp->UnlinkModuleRecord();
tmp->mParseError.setUndefined();
tmp->mErrorToRethrow.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript)
ModuleScript::ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL)
: mLoader(aLoader), mBaseURL(aBaseURL), mSourceElementAssociated(false) {
MOZ_ASSERT(mLoader);
MOZ_ASSERT(mBaseURL);
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
}
void ModuleScript::UnlinkModuleRecord() {
// Remove module's back reference to this object request if present.
if (mModuleRecord) {
MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).toPrivate() == this);
JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue());
mModuleRecord = nullptr;
}
}
ModuleScript::~ModuleScript() {
// The object may be destroyed without being unlinked first.
UnlinkModuleRecord();
DropJSObjects(this);
}
void ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) {
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
mModuleRecord = aModuleRecord;
// Make module's host defined field point to this module script object.
// This is cleared in the UnlinkModuleRecord().
JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
HoldJSObjects(this);
}
void ModuleScript::SetParseError(const JS::Value& aError) {
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
UnlinkModuleRecord();
mParseError = aError;
HoldJSObjects(this);
}
void ModuleScript::SetErrorToRethrow(const JS::Value& aError) {
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasErrorToRethrow());
// This is only called after SetModuleRecord() or SetParseError() so we don't
// need to call HoldJSObjects() here.
MOZ_ASSERT(mModuleRecord || HasParseError());
mErrorToRethrow = aError;
}
void ModuleScript::SetSourceElementAssociated() {
MOZ_ASSERT(mModuleRecord);
MOZ_ASSERT(!mSourceElementAssociated);
mSourceElementAssociated = true;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,57 +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 mozilla_dom_ModuleScript_h
#define mozilla_dom_ModuleScript_h
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "jsapi.h"
class nsIURI;
namespace mozilla {
namespace dom {
class ScriptLoader;
class ModuleScript final : public nsISupports {
RefPtr<ScriptLoader> mLoader;
nsCOMPtr<nsIURI> mBaseURL;
JS::Heap<JSObject*> mModuleRecord;
JS::Heap<JS::Value> mParseError;
JS::Heap<JS::Value> mErrorToRethrow;
bool mSourceElementAssociated;
~ModuleScript();
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript)
ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetParseError(const JS::Value& aError);
void SetErrorToRethrow(const JS::Value& aError);
void SetSourceElementAssociated();
ScriptLoader* Loader() const { return mLoader; }
JSObject* ModuleRecord() const { return mModuleRecord; }
nsIURI* BaseURL() const { return mBaseURL; }
JS::Value ParseError() const { return mParseError; }
JS::Value ErrorToRethrow() const { return mErrorToRethrow; }
bool HasParseError() const { return !mParseError.isUndefined(); }
bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); }
bool SourceElementAssociated() const { return mSourceElementAssociated; }
void UnlinkModuleRecord();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ModuleScript_h

View File

@ -4,7 +4,7 @@
* 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 "ModuleLoadRequest.h"
#include "ScriptLoadRequest.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/Unused.h"
@ -30,6 +30,7 @@ ScriptFetchOptions::ScriptFetchOptions(
nsIScriptElement* aElement, nsIPrincipal* aTriggeringPrincipal)
: mCORSMode(aCORSMode),
mReferrerPolicy(aReferrerPolicy),
mIsPreload(false),
mElement(aElement),
mTriggeringPrincipal(aTriggeringPrincipal) {
MOZ_ASSERT(mTriggeringPrincipal);
@ -50,14 +51,13 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions, mCacheInfo)
tmp->mScript = nullptr;
tmp->DropBytecodeCacheReferences();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheInfo)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mCacheInfo)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ScriptLoadRequest)
@ -105,6 +105,8 @@ ScriptLoadRequest::~ScriptLoadRequest() {
if (mScript) {
DropBytecodeCacheReferences();
}
DropJSObjects(this);
}
void ScriptLoadRequest::SetReady() {
@ -207,6 +209,12 @@ void ScriptLoadRequest::ClearScriptSource() {
}
}
void ScriptLoadRequest::SetScript(JSScript* aScript) {
MOZ_ASSERT(!mScript);
mScript = aScript;
HoldJSObjects(this);
}
//////////////////////////////////////////////////////////////
// ScriptLoadRequestList
//////////////////////////////////////////////////////////////

View File

@ -50,6 +50,7 @@ class ScriptFetchOptions {
const mozilla::CORSMode mCORSMode;
const mozilla::net::ReferrerPolicy mReferrerPolicy;
bool mIsPreload;
nsCOMPtr<nsIScriptElement> mElement;
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
};
@ -91,7 +92,10 @@ class ScriptLoadRequest
Element()->ScriptEvaluated(aResult, Element(), mIsInline);
}
bool IsPreload() { return Element() == nullptr; }
bool IsPreload() const {
MOZ_ASSERT_IF(mFetchOptions->mIsPreload, !Element());
return mFetchOptions->mIsPreload;
}
virtual void Cancel();
@ -199,17 +203,36 @@ class ScriptLoadRequest
return mFetchOptions->mTriggeringPrincipal;
}
void SetElement(nsIScriptElement* aElement) {
// Called when a preload request is later used for an actual request.
// Make this request a preload (speculative) request.
void SetIsPreloadRequest() {
MOZ_ASSERT(!Element());
MOZ_ASSERT(!IsPreload());
mFetchOptions->mIsPreload = true;
}
// Make a preload request into an actual load request for the given element.
void SetIsLoadRequest(nsIScriptElement* aElement) {
MOZ_ASSERT(aElement);
MOZ_ASSERT(!Element());
MOZ_ASSERT(IsPreload());
mFetchOptions->mElement = aElement;
mFetchOptions->mIsPreload = false;
}
FromParser GetParserCreated() const {
nsIScriptElement* element = Element();
if (!element) {
return NOT_FROM_PARSER;
}
return element->GetParserCreated();
}
bool ShouldAcceptBinASTEncoding() const;
void ClearScriptSource();
void SetScript(JSScript* aScript);
void MaybeCancelOffThreadScript();
void DropBytecodeCacheReferences();
@ -271,6 +294,9 @@ class ScriptLoadRequest
// Holds the Cache information, which is used to register the bytecode
// on the cache entry, such that we can load it the next time.
nsCOMPtr<nsICacheInfoChannel> mCacheInfo;
// The base URL used for resolving relative module imports.
nsCOMPtr<nsIURI> mBaseURL;
};
class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> {

View File

@ -8,8 +8,8 @@
#include "ScriptLoadHandler.h"
#include "ScriptLoadRequest.h"
#include "ScriptTrace.h"
#include "LoadedScript.h"
#include "ModuleLoadRequest.h"
#include "ModuleScript.h"
#include "prsystem.h"
#include "jsapi.h"
@ -17,6 +17,7 @@
#include "js/CompilationAndEvaluation.h"
#include "js/MemoryFunctions.h"
#include "js/OffThreadScriptCompilation.h"
#include "js/Realm.h"
#include "js/SourceText.h"
#include "js/Utility.h"
#include "xpcpublic.h"
@ -29,6 +30,7 @@
#include "mozilla/dom/SRILogHelper.h"
#include "nsGkAtoms.h"
#include "nsNetUtil.h"
#include "nsGlobalWindowInner.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptContext.h"
#include "nsIScriptSecurityManager.h"
@ -113,9 +115,9 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION(ScriptLoader, mNonAsyncExternalScriptInsertedRequests,
mLoadingAsyncRequests, mLoadedAsyncRequests,
mDeferRequests, mXSLTRequests, mParserBlockingRequest,
mBytecodeEncodingQueue, mPreloads,
mPendingChildLoaders, mFetchedModules)
mDeferRequests, mXSLTRequests, mDynamicImportRequests,
mParserBlockingRequest, mBytecodeEncodingQueue,
mPreloads, mPendingChildLoaders, mFetchedModules)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader)
@ -133,6 +135,7 @@ ScriptLoader::ScriptLoader(nsIDocument* aDocument)
mGiveUpEncoding(false),
mReporter(new ConsoleReportCollector()) {
LOG(("ScriptLoader::ScriptLoader %p", this));
EnsureModuleHooksInitialized();
}
ScriptLoader::~ScriptLoader() {
@ -164,6 +167,11 @@ ScriptLoader::~ScriptLoader() {
req->FireScriptAvailable(NS_ERROR_ABORT);
}
for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req;
req = req->getNext()) {
FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
}
for (ScriptLoadRequest* req =
mNonAsyncExternalScriptInsertedRequests.getFirst();
req; req = req->getNext()) {
@ -204,8 +212,6 @@ static void CollectScriptTelemetry(ScriptLoadRequest* aRequest) {
if (aRequest->IsLoadingSource()) {
if (aRequest->mIsInline) {
AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline);
nsAutoString inlineData;
aRequest->Element()->GetScriptText(inlineData);
} else if (aRequest->IsTextSource()) {
AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::SourceFallback);
}
@ -471,7 +477,7 @@ nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) {
MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
RefPtr<ModuleScript> moduleScript =
new ModuleScript(this, aRequest->mBaseURL);
new ModuleScript(aRequest->mFetchOptions, aRequest->mBaseURL);
aRequest->mModuleScript = moduleScript;
if (!module) {
@ -541,7 +547,7 @@ static nsresult HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
}
static already_AddRefed<nsIURI> ResolveModuleSpecifier(
ModuleScript* aScript, const nsAString& aSpecifier) {
ScriptLoader* aLoader, LoadedScript* 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 "../"
@ -565,7 +571,15 @@ static already_AddRefed<nsIURI> ResolveModuleSpecifier(
return nullptr;
}
rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, aScript->BaseURL());
// Get the document's base URL if we don't have a referencing script here.
nsCOMPtr<nsIURI> baseURL;
if (aScript) {
baseURL = aScript->BaseURL();
} else {
baseURL = aLoader->GetDocument()->GetDocBaseURI();
}
rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, baseURL);
if (NS_SUCCEEDED(rv)) {
return uri.forget();
}
@ -609,7 +623,8 @@ static nsresult ResolveRequestedModules(ModuleLoadRequest* aRequest,
// Let url be the result of resolving a module specifier given module script
// and requested.
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
nsCOMPtr<nsIURI> uri =
ResolveModuleSpecifier(aRequest->mLoader, ms, specifier);
if (!uri) {
uint32_t lineNumber = 0;
uint32_t columnNumber = 0;
@ -689,7 +704,8 @@ RefPtr<GenericPromise> ScriptLoader::StartFetchingModuleAndDependencies(
ModuleLoadRequest* aParent, nsIURI* aURI) {
MOZ_ASSERT(aURI);
RefPtr<ModuleLoadRequest> childRequest = new ModuleLoadRequest(aURI, aParent);
RefPtr<ModuleLoadRequest> childRequest =
ModuleLoadRequest::CreateStaticImport(aURI, aParent);
aParent->mImports.AppendElement(childRequest);
@ -720,23 +736,81 @@ RefPtr<GenericPromise> ScriptLoader::StartFetchingModuleAndDependencies(
return ready;
}
static ScriptLoader* GetCurrentScriptLoader(JSContext* aCx) {
JSObject* object = JS::CurrentGlobalOrNull(aCx);
if (!object) {
return nullptr;
}
nsIGlobalObject* global = xpc::NativeGlobal(object);
if (!global) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(win);
if (!innerWindow) {
return nullptr;
}
nsIDocument* document = innerWindow->GetDocument();
if (!document) {
return nullptr;
}
ScriptLoader* loader = document->ScriptLoader();
if (!loader) {
return nullptr;
}
return loader;
}
static LoadedScript* GetLoadedScriptOrNull(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate) {
if (aReferencingPrivate.isUndefined()) {
return nullptr;
}
auto script = static_cast<LoadedScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT_IF(
script->IsModuleScript(),
JS::GetModulePrivate(script->AsModuleScript()->ModuleRecord()) ==
aReferencingPrivate);
return script;
}
// 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier)
JSObject* HostResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier) {
// Let referencing module script be referencingModule.[[HostDefined]].
auto script = static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
aReferencingPrivate);
JS::Rooted<JSObject*> module(aCx);
ScriptLoader::ResolveImportedModule(aCx, aReferencingPrivate, aSpecifier,
&module);
return module;
}
/* static */ void ScriptLoader::ResolveImportedModule(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier, JS::MutableHandle<JSObject*> aModuleOut) {
MOZ_ASSERT(!aModuleOut);
RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aCx, aReferencingPrivate));
// Let url be the result of resolving a module specifier given referencing
// module script and specifier.
nsAutoJSString string;
if (!string.init(aCx, aSpecifier)) {
return nullptr;
return;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string);
RefPtr<ScriptLoader> loader = GetCurrentScriptLoader(aCx);
if (!loader) {
return;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(loader, script, string);
// This cannot fail because resolving a module specifier must have been
// previously successful with these same two arguments.
@ -744,19 +818,20 @@ JSObject* HostResolveImportedModule(JSContext* aCx,
// Let resolved module script be moduleMap[url]. (This entry must exist for us
// to have gotten to this point.)
ModuleScript* ms = script->Loader()->GetFetchedModule(uri);
ModuleScript* ms = loader->GetFetchedModule(uri);
MOZ_ASSERT(ms, "Resolved module not found in module map");
MOZ_ASSERT(!ms->HasParseError());
MOZ_ASSERT(ms->ModuleRecord());
return ms->ModuleRecord();
aModuleOut.set(ms->ModuleRecord());
}
bool HostPopulateImportMeta(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSObject*> aMetaObject) {
auto script = static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
RefPtr<ModuleScript> script =
static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT(script->IsModuleScript());
MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
aReferencingPrivate);
@ -774,14 +849,128 @@ bool HostPopulateImportMeta(JSContext* aCx,
JSPROP_ENUMERATE);
}
static void EnsureModuleResolveHook(JSContext* aCx) {
JSRuntime* rt = JS_GetRuntime(aCx);
bool HostImportModuleDynamically(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier,
JS::Handle<JSObject*> aPromise) {
RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aCx, aReferencingPrivate));
// Attempt to resolve the module specifier.
nsAutoJSString string;
if (!string.init(aCx, aSpecifier)) {
return false;
}
RefPtr<ScriptLoader> loader = GetCurrentScriptLoader(aCx);
if (!loader) {
return false;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(loader, script, string);
if (!uri) {
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_BAD_MODULE_SPECIFIER, string.get());
return false;
}
// Create a new top-level load request.
ScriptFetchOptions* options;
nsIURI* baseURL;
if (script) {
options = script->FetchOptions();
baseURL = script->BaseURL();
} else {
// We don't have a referencing script so fall back on using
// options from the document. This can happen when the user
// triggers an inline event handler, as there is no active script
// there.
nsIDocument* document = loader->GetDocument();
options = new ScriptFetchOptions(mozilla::CORS_NONE,
document->GetReferrerPolicy(), nullptr,
document->NodePrincipal());
baseURL = document->GetDocBaseURI();
}
RefPtr<ModuleLoadRequest> request = ModuleLoadRequest::CreateDynamicImport(
uri, options, baseURL, loader, aReferencingPrivate, aSpecifier, aPromise);
loader->StartDynamicImport(request);
return true;
}
void ScriptLoader::StartDynamicImport(ModuleLoadRequest* aRequest) {
LOG(("ScriptLoadRequest (%p): Start dynamic import", aRequest));
mDynamicImportRequests.AppendElement(aRequest);
nsresult rv = StartLoad(aRequest);
if (NS_FAILED(rv)) {
FinishDynamicImport(aRequest, rv);
}
}
void ScriptLoader::FinishDynamicImport(ModuleLoadRequest* aRequest,
nsresult aResult) {
AutoJSAPI jsapi;
MOZ_ALWAYS_TRUE(jsapi.Init(aRequest->mDynamicPromise));
FinishDynamicImport(jsapi.cx(), aRequest, aResult);
}
void ScriptLoader::FinishDynamicImport(JSContext* aCx,
ModuleLoadRequest* aRequest,
nsresult aResult) {
LOG(("ScriptLoadRequest (%p): Finish dynamic import %x %d", aRequest,
unsigned(aResult), JS_IsExceptionPending(aCx)));
// Complete the dynamic import, report failures indicated by aResult or as a
// pending exception on the context.
if (NS_FAILED(aResult)) {
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_DYNAMIC_IMPORT_FAILED);
}
JS::Rooted<JS::Value> referencingScript(aCx,
aRequest->mDynamicReferencingPrivate);
JS::Rooted<JSString*> specifier(aCx, aRequest->mDynamicSpecifier);
JS::Rooted<JSObject*> promise(aCx, aRequest->mDynamicPromise);
JS::FinishDynamicModuleImport(aCx, referencingScript, specifier, promise);
// FinishDynamicModuleImport clears any pending exception.
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
aRequest->ClearDynamicImport();
}
static void DynamicImportPrefChangedCallback(const char* aPrefName,
void* aClosure) {
bool enabled = Preferences::GetBool(aPrefName);
JS::ModuleDynamicImportHook hook =
enabled ? HostImportModuleDynamically : nullptr;
AutoJSAPI jsapi;
jsapi.Init();
JSRuntime* rt = JS_GetRuntime(jsapi.cx());
JS::SetModuleDynamicImportHook(rt, hook);
}
void ScriptLoader::EnsureModuleHooksInitialized() {
AutoJSAPI jsapi;
jsapi.Init();
JSRuntime* rt = JS_GetRuntime(jsapi.cx());
if (JS::GetModuleResolveHook(rt)) {
return;
}
JS::SetModuleResolveHook(rt, HostResolveImportedModule);
JS::SetModuleMetadataHook(rt, HostPopulateImportMeta);
JS::SetScriptPrivateFinalizeHook(rt, HostFinalizeTopLevelScript);
Preferences::RegisterCallbackAndCall(DynamicImportPrefChangedCallback,
"javascript.options.dynamicImport",
(void*)nullptr);
}
void ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest) {
@ -815,18 +1004,34 @@ class ScriptRequestProcessor : public Runnable {
: Runnable("dom::ScriptRequestProcessor"),
mLoader(aLoader),
mRequest(aRequest) {}
NS_IMETHOD Run() override { return mLoader->ProcessRequest(mRequest); }
NS_IMETHOD Run() override {
if (mRequest->IsModuleRequest() &&
mRequest->AsModuleRequest()->IsDynamicImport()) {
mLoader->ProcessDynamicImport(mRequest->AsModuleRequest());
return NS_OK;
}
return mLoader->ProcessRequest(mRequest);
}
};
void ScriptLoader::RunScriptWhenSafe(ScriptLoadRequest* aRequest) {
auto runnable = new ScriptRequestProcessor(this, aRequest);
nsContentUtils::AddScriptRunner(runnable);
}
void ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) {
MOZ_ASSERT(aRequest->IsReadyToRun());
if (aRequest->IsTopLevel()) {
if (aRequest->mIsInline &&
aRequest->Element()->GetParserCreated() == NOT_FROM_PARSER) {
if (aRequest->IsDynamicImport()) {
MOZ_ASSERT(aRequest->isInList());
RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
RunScriptWhenSafe(req);
} else if (aRequest->mIsInline &&
aRequest->GetParserCreated() == NOT_FROM_PARSER) {
MOZ_ASSERT(!aRequest->isInList());
nsContentUtils::AddScriptRunner(
new ScriptRequestProcessor(this, aRequest));
RunScriptWhenSafe(aRequest);
} else {
MaybeMoveToLoadedList(aRequest);
ProcessPendingRequests();
@ -884,8 +1089,6 @@ bool ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) {
return false;
}
EnsureModuleResolveHook(jsapi.cx());
JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord());
bool ok = NS_SUCCEEDED(nsJSUtils::ModuleInstantiate(jsapi.cx(), module));
@ -924,10 +1127,12 @@ nsresult ScriptLoader::AssociateSourceElementsForModuleTree(
JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
MOZ_ASSERT(module);
nsresult rv =
nsJSUtils::InitModuleSourceElement(aCx, module, aRequest->Element());
NS_ENSURE_SUCCESS(rv, rv);
moduleScript->SetSourceElementAssociated();
nsIScriptElement* element = aRequest->Element();
if (element) {
nsresult rv = nsJSUtils::InitModuleSourceElement(aCx, module, element);
NS_ENSURE_SUCCESS(rv, rv);
moduleScript->SetSourceElementAssociated();
}
// The script is now ready to be exposed to the debugger.
JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(module));
@ -1210,7 +1415,8 @@ ScriptLoadRequest* ScriptLoader::CreateLoadRequest(
}
MOZ_ASSERT(aKind == ScriptKind::eModule);
return new ModuleLoadRequest(aURI, fetchOptions, aIntegrity, referrer, this);
return ModuleLoadRequest::CreateTopLevel(aURI, fetchOptions, aIntegrity,
referrer, this);
}
bool ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement) {
@ -1505,10 +1711,10 @@ bool ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
LOG(("ScriptLoadRequest (%p): Created request for inline script",
request.get()));
request->mBaseURL = mDocument->GetDocBaseURI();
if (request->IsModuleRequest()) {
ModuleLoadRequest* modReq = request->AsModuleRequest();
modReq->mBaseURL = mDocument->GetDocBaseURI();
if (aElement->GetParserCreated() != NOT_FROM_PARSER) {
if (aElement->GetScriptAsync()) {
AddAsyncRequest(modReq);
@ -1538,7 +1744,7 @@ bool ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
NS_ASSERTION(
!nsContentUtils::IsSafeToRunScript(),
"A script-inserted script is inserted without an update batch?");
nsContentUtils::AddScriptRunner(new ScriptRequestProcessor(this, request));
RunScriptWhenSafe(request);
return false;
}
if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
@ -1576,7 +1782,7 @@ ScriptLoadRequest* ScriptLoader::LookupPreloadRequest(
// Found preloaded request. Note that a script-inserted script can steal a
// preload!
RefPtr<ScriptLoadRequest> request = mPreloads[i].mRequest;
request->SetElement(aElement);
request->SetIsLoadRequest(aElement);
nsString preloadCharset(mPreloads[i].mCharset);
mPreloads.RemoveElementAt(i);
@ -1915,8 +2121,7 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
if (aRequest->IsModuleRequest()) {
ModuleLoadRequest* request = aRequest->AsModuleRequest();
if (request->mModuleScript &&
!request->mModuleScript->HasErrorToRethrow()) {
if (request->mModuleScript) {
if (!InstantiateModuleTree(request)) {
request->mModuleScript = nullptr;
}
@ -1939,7 +2144,7 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
}
nsCOMPtr<nsIScriptElement> oldParserInsertedScript;
uint32_t parserCreated = aRequest->Element()->GetParserCreated();
uint32_t parserCreated = aRequest->GetParserCreated();
if (parserCreated) {
oldParserInsertedScript = mCurrentParserInsertedScript;
mCurrentParserInsertedScript = aRequest->Element();
@ -2018,6 +2223,25 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
return rv;
}
void ScriptLoader::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
if (aRequest->mModuleScript) {
if (!InstantiateModuleTree(aRequest)) {
aRequest->mModuleScript = nullptr;
}
}
nsresult rv = NS_ERROR_FAILURE;
if (aRequest->mModuleScript) {
rv = EvaluateScript(aRequest);
}
if (NS_FAILED(rv)) {
FinishDynamicImport(aRequest, rv);
}
return;
}
void ScriptLoader::FireScriptAvailable(nsresult aResult,
ScriptLoadRequest* aRequest) {
for (int32_t i = 0; i < mObservers.Count(); i++) {
@ -2062,7 +2286,7 @@ already_AddRefed<nsIScriptGlobalObject> ScriptLoader::GetScriptGlobalObject() {
}
nsresult ScriptLoader::FillCompileOptionsForRequest(
const AutoJSAPI& jsapi, ScriptLoadRequest* aRequest,
const mozilla::dom::AutoJSAPI& jsapi, ScriptLoadRequest* 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.
@ -2203,6 +2427,32 @@ nsresult ScriptLoader::FillCompileOptionsForRequest(
return true;
}
class MOZ_RAII AutoSetProcessingScriptTag {
nsCOMPtr<nsIScriptContext> mContext;
bool mOldTag;
public:
explicit AutoSetProcessingScriptTag(nsIScriptContext* aContext)
: mContext(aContext), mOldTag(mContext->GetProcessingScriptTag()) {
mContext->SetProcessingScriptTag(true);
}
~AutoSetProcessingScriptTag() { mContext->SetProcessingScriptTag(mOldTag); }
};
static nsresult ExecuteCompiledScript(JSContext* aCx,
ScriptLoadRequest* aRequest,
nsJSUtils::ExecutionContext& aExec) {
JS::Rooted<JSScript*> script(aCx, aExec.GetScript());
// Create a ClassicScript object and associate it with the JSScript.
RefPtr<ClassicScript> classicScript = new ClassicScript(
aRequest->mFetchOptions, aRequest->mBaseURL);
classicScript->AssociateWithScript(script);
return aExec.ExecScript();
}
nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
using namespace mozilla::Telemetry;
MOZ_ASSERT(aRequest->IsReadyToRun());
@ -2212,16 +2462,18 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->Element()));
nsIDocument* ownerDoc = scriptContent->OwnerDoc();
if (ownerDoc != mDocument) {
// Willful violation of HTML5 as of 2010-12-01
return NS_ERROR_FAILURE;
bool isDynamicImport = aRequest->IsModuleRequest() &&
aRequest->AsModuleRequest()->IsDynamicImport();
if (!isDynamicImport) {
nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->Element()));
MOZ_ASSERT(scriptContent);
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;
@ -2242,8 +2494,8 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
bool oldProcessingScriptTag = context->GetProcessingScriptTag();
context->SetProcessingScriptTag(true);
AutoSetProcessingScriptTag setProcessingScriptTag(context);
nsresult rv;
{
if (aRequest->IsModuleRequest()) {
@ -2255,8 +2507,6 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
// currentScript is set to null for modules.
AutoCurrentScriptUpdater scriptUpdater(this, nullptr);
EnsureModuleResolveHook(cx);
ModuleLoadRequest* request = aRequest->AsModuleRequest();
MOZ_ASSERT(request->mModuleScript);
MOZ_ASSERT(!request->mOffThreadToken);
@ -2267,7 +2517,13 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
aRequest));
JS::Rooted<JS::Value> error(cx, moduleScript->ErrorToRethrow());
JS_SetPendingException(cx, error);
return NS_OK; // An error is reported by AutoEntryScript.
// For a dynamic import, the promise is rejected. Otherwise an error is
// either reported by AutoEntryScript.
if (request->IsDynamicImport()) {
FinishDynamicImport(cx, request, NS_OK);
}
return NS_OK;
}
JS::Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());
@ -2280,9 +2536,16 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
rv = nsJSUtils::ModuleEvaluate(cx, module);
MOZ_ASSERT(NS_FAILED(rv) == aes.HasException());
if (NS_FAILED(rv)) {
LOG(("ScriptLoadRequest (%p): evaluation failed", aRequest));
rv = NS_OK; // An error is reported by AutoEntryScript.
// For a dynamic import, the promise is rejected. Otherwise an error is
// either reported by AutoEntryScript.
rv = NS_OK;
}
if (request->IsDynamicImport()) {
FinishDynamicImport(cx, request, rv);
}
aRequest->mCacheInfo = nullptr;
@ -2300,13 +2563,18 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
if (aRequest->mOffThreadToken) {
LOG(("ScriptLoadRequest (%p): Decode Bytecode & Join and Execute",
aRequest));
rv = exec.DecodeJoinAndExec(&aRequest->mOffThreadToken);
rv = exec.JoinDecode(&aRequest->mOffThreadToken);
} else {
LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute",
aRequest));
rv = exec.DecodeAndExec(options, aRequest->mScriptBytecode,
aRequest->mBytecodeOffset);
rv = exec.Decode(options, aRequest->mScriptBytecode,
aRequest->mBytecodeOffset);
}
if (rv == NS_OK) {
rv = ExecuteCompiledScript(cx, aRequest, exec);
}
// We do not expect to be saving anything when we already have some
// bytecode.
MOZ_ASSERT(!aRequest->mCacheInfo);
@ -2326,36 +2594,39 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
"Execute",
aRequest));
if (aRequest->IsBinASTSource()) {
rv = exec.DecodeBinASTJoinAndExec(&aRequest->mOffThreadToken,
&script);
rv = exec.JoinDecodeBinAST(&aRequest->mOffThreadToken);
} else {
MOZ_ASSERT(aRequest->IsTextSource());
rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
rv = exec.JoinCompile(&aRequest->mOffThreadToken);
}
} else {
// Main thread parsing (inline and small scripts)
LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
if (aRequest->IsBinASTSource()) {
rv = exec.DecodeBinASTAndExec(
options, aRequest->ScriptBinASTData().begin(),
aRequest->ScriptBinASTData().length(), &script);
rv = exec.DecodeBinAST(options,
aRequest->ScriptBinASTData().begin(),
aRequest->ScriptBinASTData().length());
} else {
MOZ_ASSERT(aRequest->IsTextSource());
auto srcBuf = GetScriptSource(cx, aRequest);
if (srcBuf) {
rv = exec.CompileAndExec(options, *srcBuf, &script);
rv = exec.Compile(options, *srcBuf);
} else {
rv = NS_ERROR_OUT_OF_MEMORY;
}
}
}
if (rv == NS_OK) {
script = exec.GetScript();
rv = ExecuteCompiledScript(cx, aRequest, exec);
}
}
// Queue the current script load request to later save the bytecode.
if (script && encodeBytecode) {
aRequest->mScript = script;
HoldJSObjects(aRequest);
aRequest->SetScript(script);
TRACE_FOR_TEST(aRequest->Element(), "scriptloader_encode");
MOZ_ASSERT(aRequest->mBytecodeOffset ==
aRequest->mScriptBytecode.length());
@ -2379,10 +2650,18 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
MaybeTriggerBytecodeEncoding();
}
context->SetProcessingScriptTag(oldProcessingScriptTag);
return rv;
}
/* static */ LoadedScript* ScriptLoader::GetActiveScript(JSContext* aCx) {
JS::Value value = JS::GetScriptedCallerPrivate(aCx);
if (value.isUndefined()) {
return nullptr;
}
return static_cast<LoadedScript*>(value.toPrivate());
}
void ScriptLoader::RegisterForBytecodeEncoding(ScriptLoadRequest* aRequest) {
MOZ_ASSERT(aRequest->mCacheInfo);
MOZ_ASSERT(aRequest->mScript);
@ -2567,7 +2846,8 @@ bool ScriptLoader::HasPendingRequests() {
return mParserBlockingRequest || !mXSLTRequests.isEmpty() ||
!mLoadedAsyncRequests.isEmpty() ||
!mNonAsyncExternalScriptInsertedRequests.isEmpty() ||
!mDeferRequests.isEmpty() || !mPendingChildLoaders.IsEmpty();
!mDeferRequests.isEmpty() || !mDynamicImportRequests.isEmpty() ||
!mPendingChildLoaders.IsEmpty();
}
void ScriptLoader::ProcessPendingRequestsAsync() {
@ -2909,7 +3189,7 @@ void ScriptLoader::ReportErrorToConsole(ScriptLoadRequest* aRequest,
nsresult aResult) const {
MOZ_ASSERT(aRequest);
if (!aRequest->Element()) {
if (aRequest->IsPreload()) {
return;
}
@ -2929,8 +3209,9 @@ void ScriptLoader::ReportErrorToConsole(ScriptLoadRequest* aRequest,
NS_ConvertUTF8toUTF16 url(aRequest->mURI->GetSpecOrDefault());
const char16_t* params[] = {url.get()};
uint32_t lineNo = aRequest->Element()->GetScriptLineNumber();
uint32_t columnNo = aRequest->Element()->GetScriptColumnNumber();
nsIScriptElement* element = aRequest->Element();
uint32_t lineNo = element ? element->GetScriptLineNumber() : 0;
uint32_t columnNo = element ? element->GetScriptColumnNumber() : 0;
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Script Loader"),
@ -2980,12 +3261,31 @@ void ScriptLoader::HandleLoadError(ScriptLoadRequest* aRequest,
RefPtr<ScriptLoadRequest> req = mXSLTRequests.Steal(aRequest);
FireScriptAvailable(aResult, req);
}
} else if (aRequest->IsModuleRequest() && !aRequest->IsPreload()) {
} else if (aRequest->IsPreload()) {
if (aRequest->IsModuleRequest()) {
aRequest->Cancel();
}
if (aRequest->IsTopLevel()) {
MOZ_ALWAYS_TRUE(
mPreloads.RemoveElement(aRequest, PreloadRequestComparator()));
}
MOZ_ASSERT(!aRequest->isInList());
AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::LoadError);
} else if (aRequest->IsModuleRequest()) {
ModuleLoadRequest* modReq = aRequest->AsModuleRequest();
MOZ_ASSERT(!modReq->IsTopLevel());
MOZ_ASSERT(!modReq->isInList());
modReq->Cancel();
// A single error is fired for the top level module.
if (modReq->IsDynamicImport()) {
MOZ_ASSERT(modReq->IsTopLevel());
if (aRequest->isInList()) {
RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
modReq->Cancel();
FinishDynamicImport(modReq, aResult);
}
} else {
MOZ_ASSERT(!modReq->IsTopLevel());
MOZ_ASSERT(!modReq->isInList());
modReq->Cancel();
// The error is handled for the top level module.
}
} else if (mParserBlockingRequest == aRequest) {
MOZ_ASSERT(!aRequest->isInList());
mParserBlockingRequest = nullptr;
@ -3000,16 +3300,6 @@ void ScriptLoader::HandleLoadError(ScriptLoadRequest* aRequest,
FireScriptAvailable(aResult, aRequest);
ContinueParserAsync(aRequest);
mCurrentParserInsertedScript = oldParserInsertedScript;
} else if (aRequest->IsPreload()) {
if (aRequest->IsModuleRequest()) {
aRequest->Cancel();
}
if (aRequest->IsTopLevel()) {
MOZ_ALWAYS_TRUE(
mPreloads.RemoveElement(aRequest, PreloadRequestComparator()));
}
MOZ_ASSERT(!aRequest->isInList());
AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::LoadError);
} else {
// This happens for blocking requests cancelled by ParsingComplete().
MOZ_ASSERT(aRequest->IsCanceled());
@ -3110,13 +3400,26 @@ nsresult ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest,
mLoadingAsyncRequests.Contains(aRequest) ||
mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) ||
mXSLTRequests.Contains(aRequest) ||
mDynamicImportRequests.Contains(aRequest) ||
(aRequest->IsModuleRequest() &&
!aRequest->AsModuleRequest()->IsTopLevel() &&
!aRequest->isInList()) ||
mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
mParserBlockingRequest,
mParserBlockingRequest == aRequest,
"aRequest should be pending!");
nsCOMPtr<nsIURI> uri;
rv = channel->GetOriginalURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
// Fixup moz-extension: and resource: URIs, because the channel URI will
// point to file:, which won't be allowed to load.
if (uri && IsInternalURIScheme(uri)) {
aRequest->mBaseURL = uri;
} else {
channel->GetURI(getter_AddRefs(aRequest->mBaseURL));
}
if (aRequest->IsModuleRequest()) {
MOZ_ASSERT(aRequest->IsSource());
ModuleLoadRequest* request = aRequest->AsModuleRequest();
@ -3130,18 +3433,6 @@ nsresult ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest,
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> uri;
rv = channel->GetOriginalURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
// Fixup moz-extension: and resource: URIs, because the channel URI will
// point to file:, which won't be allowed to load.
if (uri && IsInternalURIScheme(uri)) {
request->mBaseURL = uri;
} else {
channel->GetURI(getter_AddRefs(request->mBaseURL));
}
// Attempt to compile off main thread.
bool couldCompile = false;
rv = AttemptAsyncScriptCompile(request, &couldCompile);
@ -3191,6 +3482,13 @@ void ScriptLoader::ParsingComplete(bool aTerminated) {
mLoadedAsyncRequests.Clear();
mNonAsyncExternalScriptInsertedRequests.Clear();
mXSLTRequests.Clear();
for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req;
req = req->getNext()) {
req->Cancel();
FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
}
if (mParserBlockingRequest) {
mParserBlockingRequest->Cancel();
mParserBlockingRequest = nullptr;
@ -3245,6 +3543,7 @@ void ScriptLoader::PreloadURI(
request->mIsInline = false;
request->mScriptFromHead = aScriptFromHead;
request->SetScriptMode(aDefer, aAsync);
request->SetIsPreloadRequest();
if (LOG_ENABLED()) {
nsAutoCString url;

View File

@ -42,6 +42,7 @@ namespace mozilla {
namespace dom {
class AutoJSAPI;
class LoadedScript;
class ModuleLoadRequest;
class ModuleScript;
class ScriptLoadHandler;
@ -310,9 +311,41 @@ class ScriptLoader final : public nsISupports {
*/
void Destroy() { GiveUpBytecodeEncoding(); }
/**
* Implement the HostResolveImportedModule abstract operation.
*
* Resolve a module specifier string and look this up in the module
* map, returning the result. This is only called for previously
* loaded modules and always succeeds.
*
* @param aReferencingPrivate A JS::Value which is either undefined
* or contains a LoadedScript private pointer.
* @param aSpecifier The module specifier.
* @param aModuleOut This is set to the module found.
*/
static void ResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier,
JS::MutableHandle<JSObject*> aModuleOut);
void StartDynamicImport(ModuleLoadRequest* aRequest);
void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
nsresult aResult);
/*
* Get the currently active script. This is used as the initiating script when
* executing timeout handler scripts.
*/
static LoadedScript* GetActiveScript(JSContext* aCx);
nsIDocument* GetDocument() const { return mDocument; }
private:
virtual ~ScriptLoader();
void EnsureModuleHooksInitialized();
ScriptLoadRequest* CreateLoadRequest(
ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
nsIPrincipal* aTriggeringPrincipal, mozilla::CORSMode aCORSMode,
@ -419,6 +452,7 @@ class ScriptLoader final : public nsISupports {
nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
bool* aCouldCompileOut);
nsresult ProcessRequest(ScriptLoadRequest* aRequest);
void ProcessDynamicImport(ModuleLoadRequest* aRequest);
nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
void FireScriptAvailable(nsresult aResult, ScriptLoadRequest* aRequest);
void FireScriptEvaluated(nsresult aResult, ScriptLoadRequest* aRequest);
@ -500,6 +534,8 @@ class ScriptLoader final : public nsISupports {
nsresult AssociateSourceElementsForModuleTree(JSContext* aCx,
ModuleLoadRequest* aRequest);
void RunScriptWhenSafe(ScriptLoadRequest* aRequest);
nsIDocument* mDocument; // [WEAK]
nsCOMArray<nsIScriptLoaderObserver> mObservers;
ScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests;
@ -509,6 +545,7 @@ class ScriptLoader final : public nsISupports {
ScriptLoadRequestList mLoadedAsyncRequests;
ScriptLoadRequestList mDeferRequests;
ScriptLoadRequestList mXSLTRequests;
ScriptLoadRequestList mDynamicImportRequests;
RefPtr<ScriptLoadRequest> mParserBlockingRequest;
// List of script load request that are holding a buffer which has to be saved
@ -560,6 +597,7 @@ class ScriptLoader final : public nsISupports {
nsCOMPtr<nsIConsoleReportCollector> mReporter;
// Logging
public:
static LazyLogModule gCspPRLog;
static LazyLogModule gScriptLoaderLog;
};

View File

@ -18,6 +18,7 @@ EXPORTS += [
]
EXPORTS.mozilla.dom += [
'LoadedScript.h',
'ScriptElement.h',
'ScriptLoader.h',
'ScriptLoadRequest.h',
@ -25,8 +26,8 @@ EXPORTS.mozilla.dom += [
]
UNIFIED_SOURCES += [
'LoadedScript.cpp',
'ModuleLoadRequest.cpp',
'ModuleScript.cpp',
'ScriptElement.cpp',
'ScriptLoader.cpp',
'ScriptLoadHandler.cpp',

View File

@ -415,9 +415,8 @@ nsresult nsXBLProtoImplField::InstallField(
{
nsJSUtils::ExecutionContext exec(cx, scopeObject);
exec.SetScopeChain(scopeChain);
exec.CompileAndExec(options,
nsDependentString(mFieldText, mFieldTextLength));
rv = exec.ExtractReturnValue(&result);
exec.Compile(options, nsDependentString(mFieldText, mFieldTextLength));
rv = exec.ExecScript(&result);
}
if (NS_FAILED(rv)) {

View File

@ -625,6 +625,11 @@ struct ImplicitEdgeHolderType<JSScript*> {
typedef JSScript* Type;
};
template <>
struct ImplicitEdgeHolderType<LazyScript*> {
typedef LazyScript* Type;
};
void GCMarker::markEphemeronValues(gc::Cell* markedCell,
WeakEntryVector& values) {
DebugOnly<size_t> initialLen = values.length();
@ -838,6 +843,7 @@ void GCMarker::traverse(JSString* thing) {
template <>
void GCMarker::traverse(LazyScript* thing) {
markAndScan(thing);
markImplicitEdges(thing);
}
template <>
void GCMarker::traverse(Shape* thing) {

View File

@ -0,0 +1,13 @@
function checkGetOffsetsCoverage() {
var g = newGlobal();
var dbg = Debugger(g);
var topLevel;
dbg.onNewScript = function(s) {
topLevel = s;
};
g.eval(`import(() => 1)`);
topLevel.getChildScripts();
}
checkGetOffsetsCoverage();
gczeal(14, 10);
Object.defineProperty(this, "fuzzutils", {});

View File

@ -619,7 +619,8 @@ MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found f
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
MSG_DEF(JSMSG_NO_DYNAMIC_IMPORT, 0, JSEXN_SYNTAXERR, "dynamic module import is not implemented")
MSG_DEF(JSMSG_IMPORT_SCRIPT_NOT_FOUND, 0, JSEXN_TYPEERR, "can't find referencing script for dynamic module import")
MSG_DEF(JSMSG_DYNAMIC_IMPORT_FAILED, 0, JSEXN_TYPEERR, "error loading dynamically imported module")
MSG_DEF(JSMSG_BAD_MODULE_SPECIFIER, 1, JSEXN_TYPEERR, "error resolving module specifier '{0}'")
// Promise
MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")

View File

@ -3740,7 +3740,7 @@ JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module,
}
JS_PUBLIC_API JS::Value JS::GetModulePrivate(JSObject* module) {
return module->as<ModuleObject>().scriptSourceObject()->unwrappedPrivate();
return module->as<ModuleObject>().scriptSourceObject()->canonicalPrivate();
}
JS_PUBLIC_API void JS::SetScriptPrivate(JSScript* script,
@ -3749,7 +3749,31 @@ JS_PUBLIC_API void JS::SetScriptPrivate(JSScript* script,
}
JS_PUBLIC_API JS::Value JS::GetScriptPrivate(JSScript* script) {
return script->sourceObject()->unwrappedPrivate();
return script->sourceObject()->canonicalPrivate();
}
JS_PUBLIC_API JS::Value JS::GetScriptedCallerPrivate(JSContext* cx) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
NonBuiltinFrameIter iter(cx, cx->realm()->principals());
if (iter.done() || !iter.hasScript()) {
return UndefinedValue();
}
return FindScriptOrModulePrivateForScript(iter.script());
}
JS_PUBLIC_API JS::ScriptPrivateFinalizeHook JS::GetScriptPrivateFinalizeHook(
JSRuntime* rt) {
AssertHeapIsIdle();
return rt->scriptPrivateFinalizeHook;
}
JS_PUBLIC_API void JS::SetScriptPrivateFinalizeHook(
JSRuntime* rt, JS::ScriptPrivateFinalizeHook func) {
AssertHeapIsIdle();
rt->scriptPrivateFinalizeHook = func;
}
JS_PUBLIC_API bool JS::ModuleInstantiate(JSContext* cx,

View File

@ -3104,13 +3104,17 @@ using ModuleDynamicImportHook = bool (*)(JSContext* cx,
HandleObject promise);
/**
* Get the HostResolveImportedModule hook for the runtime.
* Get the HostImportModuleDynamically hook for the runtime.
*/
extern JS_PUBLIC_API ModuleDynamicImportHook
GetModuleDynamicImportHook(JSRuntime* rt);
/**
* Set the HostResolveImportedModule hook for the runtime to the given function.
* Set the HostImportModuleDynamically hook for the runtime to the given
* function.
*
* If this hook is not set (or set to nullptr) then the JS engine will throw an
* exception if dynamic module import is attempted.
*/
extern JS_PUBLIC_API void SetModuleDynamicImportHook(
JSRuntime* rt, ModuleDynamicImportHook func);
@ -3152,6 +3156,32 @@ extern JS_PUBLIC_API void SetScriptPrivate(JSScript* script,
*/
extern JS_PUBLIC_API JS::Value GetScriptPrivate(JSScript* script);
/*
* Return the private value associated with currently executing script or
* module, or undefined if there is no such script.
*/
extern JS_PUBLIC_API JS::Value GetScriptedCallerPrivate(JSContext* cx);
/**
* A hook that's called whenever a script or module which has a private value
* set with SetScriptPrivate() or SetModulePrivate() is finalized. This can be
* used to clean up the private state. The private value is passed as an
* argument.
*/
using ScriptPrivateFinalizeHook = void (*)(JSFreeOp*, const JS::Value&);
/**
* Get the script private finalize hook for the runtime.
*/
extern JS_PUBLIC_API ScriptPrivateFinalizeHook
GetScriptPrivateFinalizeHook(JSRuntime* rt);
/**
* Set the script private finalize hook for the runtime to the given function.
*/
extern JS_PUBLIC_API void SetScriptPrivateFinalizeHook(
JSRuntime* rt, ScriptPrivateFinalizeHook func);
/*
* Perform the ModuleInstantiate operation on the given source text module
* record.

View File

@ -1404,6 +1404,14 @@ JS_FRIEND_API void js::LogDtor(void* self, const char* type, uint32_t sz) {
}
}
JS_FRIEND_API JS::Value js::MaybeGetScriptPrivate(JSObject* object) {
if (!object->is<ScriptSourceObject>()) {
return UndefinedValue();
}
return object->as<ScriptSourceObject>().canonicalPrivate();
}
JS_FRIEND_API uint64_t js::GetGCHeapUsageForObjectZone(JSObject* obj) {
return obj->zone()->usage.gcBytes();
}

View File

@ -106,6 +106,22 @@ extern JS_FRIEND_API bool JS_IsDeadWrapper(JSObject* obj);
extern JS_FRIEND_API JSObject* JS_NewDeadWrapper(
JSContext* cx, JSObject* origObject = nullptr);
namespace js {
/**
* Get the script private value associated with an object, if any.
*
* The private value is set with SetScriptPrivate() or SetModulePrivate() and is
* internally stored on the relevant ScriptSourceObject.
*
* This is used by the cycle collector to trace through
* ScriptSourceObjects. This allows private values to contain an nsISupports
* pointer and hence support references to cycle collected C++ objects.
*/
JS_FRIEND_API JS::Value MaybeGetScriptPrivate(JSObject* object);
} // namespace js
/*
* Used by the cycle collector to trace through a shape or object group and
* all cycle-participating data it reaches, using bounded stack space.

View File

@ -3365,15 +3365,17 @@ ModuleObject* js::GetModuleObjectForScript(JSScript* script) {
}
Value js::FindScriptOrModulePrivateForScript(JSScript* script) {
while (script) {
ScriptSourceObject* sso = script->sourceObject();
Value value = sso->unwrappedPrivate();
MOZ_ASSERT(script);
ScriptSourceObject* sso = script->sourceObject();
while (sso) {
Value value = sso->canonicalPrivate();
if (!value.isUndefined()) {
return value;
}
MOZ_ASSERT(sso->unwrappedIntroductionScript() != script);
script = sso->unwrappedIntroductionScript();
ScriptSourceObject* parent = sso->unwrappedIntroductionSourceObject();
MOZ_ASSERT(parent != sso);
sso = parent;
}
return UndefinedValue();

View File

@ -1317,6 +1317,16 @@ void ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj) {
MOZ_ASSERT(fop->onMainThread());
ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
sso->source()->decref();
Value value = sso->canonicalPrivate();
if (!value.isUndefined()) {
// The embedding may need to dispose of its private data.
JS::AutoSuppressGCAnalysis suppressGC;
if (JS::ScriptPrivateFinalizeHook hook =
fop->runtime()->scriptPrivateFinalizeHook) {
hook(fop, value);
}
}
}
void ScriptSourceObject::trace(JSTracer* trc, JSObject* obj) {
@ -1372,6 +1382,7 @@ ScriptSourceObject* ScriptSourceObject::createInternal(JSContext* cx,
obj->initReservedSlot(ELEMENT_SLOT, MagicValue(JS_GENERIC_MAGIC));
obj->initReservedSlot(ELEMENT_PROPERTY_SLOT, MagicValue(JS_GENERIC_MAGIC));
obj->initReservedSlot(INTRODUCTION_SCRIPT_SLOT, MagicValue(JS_GENERIC_MAGIC));
obj->initReservedSlot(INTRODUCTION_SOURCE_OBJECT_SLOT, MagicValue(JS_GENERIC_MAGIC));
return obj;
}
@ -1410,6 +1421,8 @@ ScriptSourceObject* ScriptSourceObject::unwrappedCanonical() const {
source->getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic(JS_GENERIC_MAGIC));
MOZ_ASSERT(source->getReservedSlot(INTRODUCTION_SCRIPT_SLOT)
.isMagic(JS_GENERIC_MAGIC));
MOZ_ASSERT(source->getReservedSlot(INTRODUCTION_SOURCE_OBJECT_SLOT)
.isMagic(JS_GENERIC_MAGIC));
RootedObject element(cx, options.element());
RootedString elementAttributeName(cx, options.elementAttributeName());
@ -1420,14 +1433,20 @@ ScriptSourceObject* ScriptSourceObject::unwrappedCanonical() const {
// There is no equivalent of cross-compartment wrappers for scripts. If the
// introduction script and ScriptSourceObject are in different compartments,
// we would be creating a cross-compartment script reference, which is
// forbidden. In that case, simply don't bother to retain the introduction
// script.
Value introductionScript = UndefinedValue();
if (options.introductionScript() &&
options.introductionScript()->compartment() == cx->compartment()) {
introductionScript.setPrivateGCThing(options.introductionScript());
// forbidden. We can still store a CCW to the script source object though.
RootedValue introdutionScript(cx);
RootedValue introdutionSource(cx);
if (options.introductionScript()) {
if (options.introductionScript()->compartment() == cx->compartment()) {
introdutionScript.setPrivateGCThing(options.introductionScript());
}
introdutionSource.setObject(*options.introductionScript()->sourceObject());
if (!cx->compartment()->wrap(cx, &introdutionSource)) {
return false;
}
}
source->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, introductionScript);
source->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, introdutionScript);
source->setReservedSlot(INTRODUCTION_SOURCE_OBJECT_SLOT, introdutionSource);
return true;
}

View File

@ -1170,14 +1170,24 @@ class ScriptSourceObject : public NativeObject {
}
return value.toGCThing()->as<JSScript>();
}
ScriptSourceObject* unwrappedIntroductionSourceObject() const {
Value value =
unwrappedCanonical()->getReservedSlot(INTRODUCTION_SOURCE_OBJECT_SLOT);
if (value.isUndefined()) {
return nullptr;
}
return &UncheckedUnwrap(&value.toObject())->as<ScriptSourceObject>();
}
void setPrivate(const Value& value) {
MOZ_ASSERT(isCanonical());
setReservedSlot(PRIVATE_SLOT, value);
}
Value unwrappedPrivate() const {
return unwrappedCanonical()->getReservedSlot(PRIVATE_SLOT);
Value canonicalPrivate() const {
Value value = getReservedSlot(PRIVATE_SLOT);
MOZ_ASSERT_IF(!isCanonical(), value.isUndefined());
return value;
}
private:
@ -1187,6 +1197,7 @@ class ScriptSourceObject : public NativeObject {
ELEMENT_SLOT,
ELEMENT_PROPERTY_SLOT,
INTRODUCTION_SCRIPT_SLOT,
INTRODUCTION_SOURCE_OBJECT_SLOT,
PRIVATE_SLOT,
RESERVED_SLOTS
};

View File

@ -163,7 +163,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
wasmInstances(mutexid::WasmRuntimeInstances),
moduleResolveHook(),
moduleMetadataHook(),
moduleDynamicImportHook() {
moduleDynamicImportHook(),
scriptPrivateFinalizeHook() {
JS_COUNT_CTOR(JSRuntime);
liveRuntimesCount++;

View File

@ -975,6 +975,9 @@ struct JSRuntime : public js::MallocProvider<JSRuntime> {
// module import and can accessed by off-thread parsing.
mozilla::Atomic<JS::ModuleDynamicImportHook> moduleDynamicImportHook;
// A hook called on script finalization.
js::MainThreadData<JS::ScriptPrivateFinalizeHook> scriptPrivateFinalizeHook;
public:
#if defined(JS_BUILD_BINAST)
js::BinaryASTSupport& binast() { return binast_; }

View File

@ -1584,6 +1584,9 @@ pref("javascript.options.streams", true);
// BigInt API
pref("javascript.options.bigint", false);
// Dynamic module import.
pref("javascript.options.dynamicImport", false);
// advanced prefs
pref("advanced.mailftp", false);
pref("image.animation_mode", "normal");

View File

@ -1 +1,2 @@
lsan-allowed: [Init, nsHostResolver::ResolveHost]
prefs: [javascript.options.dynamicImport:true, security.csp.experimentalEnabled:true]

View File

@ -1,4 +0,0 @@
[dynamic-imports-fetch-error.sub.html]
[import(): error cases occuring during fetching]
expected: FAIL
bug: 1342012

View File

@ -1,3 +0,0 @@
[dynamic-imports-script-error.html]
[import(): error cases caused by the imported module script]
expected: FAIL

View File

@ -1,7 +0,0 @@
[dynamic-imports.html]
[Basic dynamic imports]
expected: FAIL
[Dynamic imports should resolve module.]
expected: FAIL

View File

@ -1,5 +0,0 @@
[inline-event-handler.html]
expected: ERROR
[dynamic import should work when triggered from inline event handlers]
expected: TIMEOUT

View File

@ -1,10 +0,0 @@
[propagate-nonce-external-classic.html]
[Untitled]
expected: FAIL
[propagate-nonce-external-classic]
expected: FAIL
[Dynamically imported module should eval when imported from script w/ a valid nonce.]
expected: FAIL

View File

@ -1,10 +0,0 @@
[propagate-nonce-external-module.html]
[Untitled]
expected: FAIL
[propagate-nonce-external-module]
expected: FAIL
[Dynamically imported module should eval when imported from script w/ a valid nonce.]
expected: FAIL

View File

@ -1,10 +0,0 @@
[propagate-nonce-inline-classic.html]
[Untitled]
expected: FAIL
[propagate-nonce-inline-classic]
expected: FAIL
[Dynamically imported module should eval when imported from script w/ a valid nonce.]
expected: FAIL

View File

@ -1,10 +0,0 @@
[propagate-nonce-inline-module.html]
[Untitled]
expected: FAIL
[propagate-nonce-inline-module]
expected: FAIL
[Dynamically imported module should eval when imported from script w/ a valid nonce.]
expected: FAIL

View File

@ -1,17 +0,0 @@
[string-compilation-base-url-external-classic.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
[eval should successfully import]
expected: NOTRUN
[Function should successfully import]
expected: NOTRUN
[reflected-inline-event-handlers should successfully import]
expected: NOTRUN
[inline-event-handlers-UA-code should successfully import]
expected: NOTRUN

View File

@ -1,17 +0,0 @@
[string-compilation-base-url-external-module.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
[eval should successfully import]
expected: NOTRUN
[Function should successfully import]
expected: NOTRUN
[reflected-inline-event-handlers should successfully import]
expected: NOTRUN
[inline-event-handlers-UA-code should successfully import]
expected: NOTRUN

View File

@ -1,17 +0,0 @@
[string-compilation-base-url-inline-classic.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
[eval should successfully import]
expected: NOTRUN
[the Function constructor should successfully import]
expected: NOTRUN
[reflected inline event handlers should successfully import]
expected: NOTRUN
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN

View File

@ -1,17 +0,0 @@
[string-compilation-base-url-inline-module.html]
expected: ERROR
[setTimeout should successfully import]
expected: TIMEOUT
[eval should successfully import]
expected: NOTRUN
[the Function constructor should successfully import]
expected: NOTRUN
[reflected inline event handlers should successfully import]
expected: NOTRUN
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN

View File

@ -1,17 +0,0 @@
[string-compilation-classic.html]
expected: ERROR
[eval should successfully import]
expected: FAIL
[setTimeout should successfully import]
expected: TIMEOUT
[the Function constructor should successfully import]
expected: NOTRUN
[reflected inline event handlers should successfully import]
expected: NOTRUN
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN

View File

@ -1,17 +0,0 @@
[string-compilation-module.html]
expected: ERROR
[eval should successfully import]
expected: FAIL
[setTimeout should successfully import]
expected: TIMEOUT
[the Function constructor should successfully import]
expected: NOTRUN
[reflected inline event handlers should successfully import]
expected: NOTRUN
[inline event handlers triggered via UA code should successfully import]
expected: NOTRUN

View File

@ -1,20 +1,4 @@
[string-compilation-nonce-classic.html]
expected: ERROR
[setTimeout must inherit the nonce from the triggering script, thus execute]
expected: TIMEOUT
[direct eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[indirect eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[the Function constructor must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[reflected inline event handlers must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[inline event handlers triggered via UA code must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL

View File

@ -1,20 +1,3 @@
[string-compilation-nonce-module.html]
expected: ERROR
[setTimeout must inherit the nonce from the triggering script, thus execute]
expected: TIMEOUT
[direct eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[indirect eval must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[the Function constructor must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[reflected inline event handlers must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
[inline event handlers triggered via UA code must inherit the nonce from the triggering script, thus execute]
expected: NOTRUN
expected: FAIL

View File

@ -1,13 +0,0 @@
[string-compilation-of-promise-result.html]
[Evaled the script via eval, successful import]
expected: FAIL
[Evaled the script via eval, failed import]
expected: FAIL
[Evaled the script via Function, successful import]
expected: FAIL
[Evaled the script via Function, failed import]
expected: FAIL

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="dummy"></div>
</body>
</html>

View File

@ -0,0 +1,58 @@
<!doctype html>
<meta charset=utf-8>
<title>Check import() works when active script is in another document</title>
<link rel="author" title="Jon Coppeard" href="mailto:jcoppeard@mozilla.com">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<iframe id="frame" src="resources/empty-iframe.html"></iframe>
<script>
function startTest() {
const otherWindow = document.getElementById("frame").contentWindow;
const otherDiv = otherWindow.document.getElementById("dummy");
function createTestPromise() {
return new Promise((resolve, reject) => {
otherWindow.continueTest = resolve;
otherWindow.errorTest = reject;
});
}
const evaluators = {
eval: otherWindow.eval,
setTimeout: otherWindow.setTimeout,
"the Function constructor"(x) {
otherWindow.Function(x)();
},
"reflected inline event handlers"(x) {
otherDiv.setAttribute("onclick", x);
otherDiv.onclick();
},
"inline event handlers triggered by JS"(x) {
otherDiv.setAttribute("onclick", x);
otherDiv.click(); // different from .**on**click()
}
};
for (const [label, evaluator] of Object.entries(evaluators)) {
promise_test(t => {
t.add_cleanup(() => {
otherDiv.removeAttribute("onclick");
delete otherWindow.evaluated_imports_a;
});
const promise = createTestPromise();
evaluator(`import('../imports-a.js?label=${label}').then(window.continueTest, window.errorTest);`);
return promise.then(module => {
assert_true(otherWindow.evaluated_imports_a, "The module must have been evaluated");
assert_equals(module.A.from, "imports-a.js", "The module namespace object must be correct");
});
}, label + " should successfully import");
};
}
</script>
<body onLoad="startTest()"></body>

View File

@ -75,6 +75,7 @@
#include "mozilla/dom/ScriptSettings.h"
#include "js/Debug.h"
#include "js/GCAPI.h"
#include "jsfriendapi.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionNoteRootCallback.h"
#include "nsCycleCollectionParticipant.h"
@ -627,28 +628,36 @@ void CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(
// Nothing else to do!
return;
}
// XXX This test does seem fragile, we should probably whitelist classes
// that do hold a strong reference, but that might not be possible.
else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
} else {
const DOMJSClass* domClass = GetDOMClass(aObj);
if (domClass) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
// It's possible that our object is an unforgeable holder object, in
// which case it doesn't actually have a C++ DOM object associated with
// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
if (domClass->mDOMObjectIsISupports) {
aCb.NoteXPCOMChild(
UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
} else if (domClass->mParticipant) {
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
domClass->mParticipant);
}
return;
}
const DOMJSClass* domClass = GetDOMClass(aObj);
if (domClass) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
// It's possible that our object is an unforgeable holder object, in
// which case it doesn't actually have a C++ DOM object associated with
// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
if (domClass->mDOMObjectIsISupports) {
aCb.NoteXPCOMChild(
UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
} else if (domClass->mParticipant) {
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
domClass->mParticipant);
}
return;
}
JS::Value value = js::MaybeGetScriptPrivate(aObj);
if (!value.isUndefined()) {
aCb.NoteXPCOMChild(static_cast<nsISupports*>(value.toPrivate()));
}
}