Issue #1691 - Part 8: Fix --enable-debug builds and continue dynamic module import changes. https://bugzilla.mozilla.org/show_bug.cgi?id=1342012 Support import from timeout handlers by associating the initiating script with the compiled JSScript. Fix error message that covers all import() failures that don't throw a JS exception. https://bugzilla.mozilla.org/show_bug.cgi?id=1331662 Partial - Replace nsJSUtils::EvaluateString calls by ExecutionContext scopes. Left EvaluateString() in nsXBLProtoImplField.cpp until ExecutionContext errors can be fixed.

(cherry picked from commit 22fcfc77971a9bb204df664c474681f4dcf54211)
This commit is contained in:
Brian Smith 2023-04-21 17:35:04 -05:00 • committed by roytam1
commit 1109559a5d
20 changed files with 166 additions and 24 deletions

View file

@ -224,6 +224,7 @@
#include "mozilla/dom/PrimitiveConversions.h"
#include "mozilla/dom/WindowBinding.h"
#include "nsITabChild.h"
#include "mozilla/dom/ModuleScript.h"
#include "mozilla/dom/MediaQueryList.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/NavigatorBinding.h"
@ -12937,9 +12938,6 @@ nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
RefPtr<Function> callback = handler->GetCallback();
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);
@ -12950,9 +12948,22 @@ nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
AutoEntryScript aes(this, reason, true);
JS::CompileOptions options(aes.cx());
options.setFileAndLine(filename, lineNo).setVersion(JSVERSION_DEFAULT);
options.setNoScriptRval(true);
JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
nsresult rv =
nsJSUtils::EvaluateString(aes.cx(), script, global, options);
nsresult rv;
{
nsJSUtils::ExecutionContext exec(aes.cx(), global);
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) {
abortIntervalHandler = true;
}

View file

@ -14,6 +14,7 @@
namespace mozilla {
namespace dom {
class Function;
class LoadedScript;
} // namespace dom
} // namespace mozilla
@ -44,6 +45,9 @@ public:
// 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

@ -10,6 +10,7 @@
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/ModuleScript.h"
#include "nsAXPCNativeCallContext.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
@ -44,6 +45,7 @@ public:
nsTArray<JS::Heap<JS::Value>>&& aArguments,
ErrorResult& aError);
nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow* aWindow,
LoadedScript* aInitiatingScript,
const nsAString& aExpression, bool* aAllowEval,
ErrorResult& aError);
nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
@ -77,6 +79,10 @@ public:
*aColumn = mColumn;
}
virtual LoadedScript* GetInitiatingScript() override {
return mInitiatingScript;
}
virtual void MarkForCC() override
{
if (mFunction) {
@ -104,6 +110,9 @@ private:
// 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;
};
@ -112,6 +121,8 @@ private:
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)
@ -151,6 +162,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)
@ -243,12 +257,14 @@ nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
nsGlobalWindow *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,
@ -352,9 +368,11 @@ already_AddRefed<nsIScriptTimeoutHandler>
NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
const nsAString& aExpression, ErrorResult& aError)
{
LoadedScript* script = ScriptLoader::GetActiveScript(aCx);
bool allowEval = false;
RefPtr<nsJSScriptTimeoutHandler> handler =
new nsJSScriptTimeoutHandler(aCx, aWindow, aExpression, &allowEval, aError);
new nsJSScriptTimeoutHandler(aCx, aWindow, script, aExpression, &allowEval, aError);
if (aError.Failed() || !allowEval) {
return nullptr;
}

View file

@ -142,6 +142,7 @@ nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
, mRv(NS_OK)
, mSkip(false)
, mCoerceToString(false)
, mEncodeBytecode(false)
#ifdef DEBUG
, mWantsReturnValue(false)
, mExpectScopeChain(false)
@ -150,7 +151,6 @@ nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
{
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
MOZ_ASSERT(mRetValue.isUndefined());
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aGlobal) == aGlobal);
@ -207,7 +207,7 @@ nsJSUtils::ExecutionContext::JoinCompile(void** aOffThreadToken)
return mRv;
}
if (!StartIncrementalEncoding(mCx, mScript)) {
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
@ -246,7 +246,7 @@ nsJSUtils::ExecutionContext::Compile(JS::CompileOptions& aCompileOptions,
return mRv;
}
if (!StartIncrementalEncoding(mCx, mScript)) {
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
@ -340,6 +340,19 @@ nsresult nsJSUtils::ExecutionContext::ExecScript() {
return NS_OK;
}
static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
if (!aValue.isObject()) {
return false;
}
JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrap(&aValue.toObject()));
if (!obj) {
return false;
}
return JS::IsPromiseObject(obj);
}
nsresult
nsJSUtils::ExecutionContext::ExecScript(JS::MutableHandle<JS::Value> aRetValue)
{
@ -359,6 +372,15 @@ nsJSUtils::ExecutionContext::ExecScript(JS::MutableHandle<JS::Value> aRetValue)
#ifdef DEBUG
mWantsReturnValue = false;
#endif
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.
aRetValue.setUndefined();
}
if (mCoerceToString && !aRetValue.isUndefined()) {
JSString* str = JS::ToString(mCx, aRetValue);
if (!str) {

View file

@ -90,6 +90,9 @@ public:
// Should the result be serialized before being returned.
bool mCoerceToString;
// Encode the bytecode before it is being executed.
bool mEncodeBytecode;
#ifdef DEBUG
// Should we set the return value.
bool mWantsReturnValue;

View file

@ -275,10 +275,14 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
JS::CompileOptions options(cx);
options.setFileAndLine(mURL.get(), 1)
.setVersion(JSVERSION_DEFAULT);
nsJSUtils::EvaluateOptions evalOptions(cx);
evalOptions.setCoerceToString(true);
rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
globalJSObject, options, evalOptions, &v);
{
nsJSUtils::ExecutionContext exec(cx, globalJSObject);
exec.SetCoerceToString(true);
exec.Compile(options, NS_ConvertUTF8toUTF16(script));
rv = exec.ExecScript(&v);
}
js::AssertSameCompartment(cx, v);
if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
return NS_ERROR_MALFORMED_URI;

View file

@ -1388,14 +1388,23 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
options.setFileAndLine(spec, 0)
.setVersion(JSVERSION_DEFAULT);
JS::Rooted<JS::Value> rval(cx);
nsJSUtils::EvaluateOptions evalOptions(cx);
JS::AutoObjectVector scopeChain(cx);
if (obj != js::GetGlobalForObjectCrossCompartment(obj) &&
!evalOptions.scopeChain.append(obj)) {
!scopeChain.append(obj)) {
return false;
}
obj = js::GetGlobalForObjectCrossCompartment(obj);
nsresult rv = nsJSUtils::EvaluateString(cx, utf16script, obj, options,
evalOptions, &rval);
nsresult rv = NS_OK;
{
nsJSUtils::ExecutionContext exec(cx, obj);
exec.SetScopeChain(scopeChain);
exec.Compile(options, utf16script);
rv = exec.ExecScript(&rval);
}
if (!JS_WrapValue(cx, &rval)) {
return false;
}
return NS_SUCCEEDED(rv) &&
(!result || JSValToNPVariant(npp, cx, rval, result));

View file

@ -1072,7 +1072,7 @@ void ScriptLoader::FinishDynamicImport(JSContext* aCx,
if (NS_FAILED(aResult)) {
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_IMPORT_SCRIPT_NOT_FOUND);
JSMSG_DYNAMIC_IMPORT_FAILED);
}
JS::Rooted<JS::Value> referencingScript(aCx,
@ -2374,6 +2374,15 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
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::ProcessPendingRequestsAsync()
{

View file

@ -34,6 +34,7 @@ namespace mozilla {
namespace dom {
class AutoJSAPI;
class LoadedScript;
class ModuleLoadRequest;
class ModuleScript;
class ScriptLoadRequestList;
@ -586,6 +587,12 @@ public:
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:

View file

@ -12,6 +12,7 @@ XPIDL_MODULE = 'dom'
EXPORTS += ['nsIScriptElement.h']
EXPORTS.mozilla.dom += [
'ModuleScript.h',
'ScriptElement.h',
'ScriptLoader.h',
'ScriptSettings.h',

View file

@ -202,8 +202,6 @@ public:
compileOptions.setVersion(JSVERSION_DEFAULT);
compileOptions.setIsRunOnce(true);
// We only need the setNoScriptRval bit when compiling off-thread here,
// since otherwise nsJSUtils::EvaluateString will set it up for us.
compileOptions.setNoScriptRval(true);
JS::Rooted<JS::Value> unused(cx);

View file

@ -434,6 +434,7 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
JS::CompileOptions options(cx);
options.setFileAndLine(uriSpec.get(), mLineNumber)
.setVersion(JSVERSION_LATEST);
#if 1
nsJSUtils::EvaluateOptions evalOptions(cx);
if (!nsJSUtils::GetScopeChainForElement(cx, boundElement,
evalOptions.scopeChain)) {
@ -442,6 +443,21 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
rv = nsJSUtils::EvaluateString(cx, nsDependentString(mFieldText,
mFieldTextLength),
scopeObject, options, evalOptions, &result);
#endif
#if 0
JS::AutoObjectVector scopeChain(cx);
if (!nsJSUtils::GetScopeChainForElement(cx, boundElement, scopeChain)) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = NS_OK;
{
nsJSUtils::ExecutionContext exec(cx, scopeObject);
exec.SetScopeChain(scopeChain);
exec.Compile(options, nsDependentString(mFieldText, mFieldTextLength));
rv = exec.ExecScript(&result);
}
#endif
if (NS_FAILED(rv)) {
return rv;
}

View file

@ -5220,7 +5220,7 @@ template <class ParseHandler>
inline typename ParseHandler::Node
Parser<ParseHandler>::importDeclarationOrImportExpr(YieldHandling yieldHandling)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_IMPORT));
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IMPORT));
TokenKind tt;
if (!tokenStream.peekToken(&tt))
@ -10480,7 +10480,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::importExpr(YieldHandling yieldHandling)
{
MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_IMPORT));
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IMPORT));
Node importHolder = handler.newPosHolder(pos());
if (!importHolder)

View file

@ -592,7 +592,7 @@ 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

View file

@ -366,6 +366,12 @@ js::AssertSameCompartment(JSContext* cx, JSObject* obj)
assertSameCompartment(cx, obj);
}
JS_FRIEND_API(void)
js::AssertSameCompartment(JSContext* cx, JS::HandleValue v)
{
assertSameCompartment(cx, v);
}
#ifdef DEBUG
JS_FRIEND_API(void)
js::AssertSameCompartment(JSObject* objA, JSObject* objB)

View file

@ -659,6 +659,9 @@ GetPrototypeNoProxy(JSObject* obj);
JS_FRIEND_API(void)
AssertSameCompartment(JSContext* cx, JSObject* obj);
JS_FRIEND_API(void)
AssertSameCompartment(JSContext* cx, JS::HandleValue v);
#ifdef JS_DEBUG
JS_FRIEND_API(void)
AssertSameCompartment(JSObject* objA, JSObject* objB);

View file

@ -1401,7 +1401,7 @@ const Class ScriptSourceObject::class_ = {
};
ScriptSourceObject*
ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
ScriptSourceObject::createInternal(ExclusiveContext* cx, ScriptSource* source, HandleObject canonical)
{
RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr));
if (!object)
@ -1411,6 +1411,12 @@ ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
source->incref(); // The matching decref is in ScriptSourceObject::finalize.
sourceObject->initReservedSlot(SOURCE_SLOT, PrivateValue(source));
if (canonical) {
sourceObject->initReservedSlot(CANONICAL_SLOT, ObjectValue(*canonical));
} else {
sourceObject->initReservedSlot(CANONICAL_SLOT, ObjectValue(*sourceObject));
}
// The remaining slots should eventually be populated by a call to
// initFromOptions. Poison them until that point.
sourceObject->initReservedSlot(ELEMENT_SLOT, MagicValue(JS_GENERIC_MAGIC));
@ -1420,6 +1426,20 @@ ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
return sourceObject;
}
ScriptSourceObject*
ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
{
return createInternal(cx, source, nullptr);
}
ScriptSourceObject* ScriptSourceObject::unwrappedCanonical() const
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromAnyThread()));
JSObject* obj = &getReservedSlot(CANONICAL_SLOT).toObject();
return &UncheckedUnwrap(obj)->as<ScriptSourceObject>();
}
/* static */ bool
ScriptSourceObject::initFromOptions(JSContext* cx, HandleScriptSource source,
const ReadOnlyCompileOptions& options)

View file

@ -637,6 +637,14 @@ class ScriptSourceObject : public NativeObject
{
static const ClassOps classOps_;
static ScriptSourceObject* createInternal(ExclusiveContext* cx, ScriptSource* source,
HandleObject canonical);
bool isCanonical() const {
return &getReservedSlot(CANONICAL_SLOT).toObject() == this;
}
ScriptSourceObject* unwrappedCanonical() const;
public:
static const Class class_;
@ -684,6 +692,7 @@ class ScriptSourceObject : public NativeObject
private:
enum {
SOURCE_SLOT = 0,
CANONICAL_SLOT,
ELEMENT_SLOT,
ELEMENT_PROPERTY_SLOT,
INTRODUCTION_SCRIPT_SLOT,

View file

@ -6,6 +6,7 @@
#include "MDNSResponderReply.h"
#include "mozilla/EndianUtils.h"
#include "private/pprio.h"
#include "nsSocketTransportService2.h"
namespace mozilla {
namespace net {

View file

@ -8,6 +8,7 @@
#include "nsICancelable.h"
#include "nsXULAppAPI.h"
#include "private/pprio.h"
#include "MainThreadUtils.h"
namespace mozilla {
namespace net {