Backed out 3 changesets (bug 1763586) for causing mochitest-chrome failures on test_Debugger.Source.prototype.element.html.

Backed out changeset f6b6ccfce3d1 (bug 1763586)
Backed out changeset ca0157c919a9 (bug 1763586)
Backed out changeset e0294a6f9f9f (bug 1763586)
This commit is contained in:
Iulian Moraru 2022-04-13 21:53:34 +03:00
parent 54f7c29a1d
commit 2844dc6b1f
24 changed files with 300 additions and 63 deletions

View File

@ -1240,7 +1240,7 @@ nsresult EventListenerManager::CompileEventHandlerInternal(
aElement->OwnerDoc()->NodePrincipal());
RefPtr<JS::loader::EventScript> eventScript =
new JS::loader::EventScript(fetchOptions, uri);
new JS::loader::EventScript(fetchOptions, uri, aElement);
JS::CompileOptions options(cx);
// Use line 0 to make the function body starts from line 1.

View File

@ -242,7 +242,8 @@ already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateTopLevel(
already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateStaticImport(
nsIURI* aURI, ModuleLoadRequest* aParent) {
RefPtr<ScriptLoadContext> newContext = new ScriptLoadContext();
RefPtr<ScriptLoadContext> newContext =
new ScriptLoadContext(aParent->GetLoadContext()->mElement);
newContext->mIsInline = false;
// Propagated Parent values. TODO: allow child modules to use root module's
// script mode.
@ -264,13 +265,15 @@ already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateDynamicImport(
MOZ_ASSERT(aSpecifier);
MOZ_ASSERT(aPromise);
RefPtr<ScriptFetchOptions> options = nullptr;
RefPtr<ScriptFetchOptions> options;
nsIURI* baseURL = nullptr;
RefPtr<ScriptLoadContext> context = new ScriptLoadContext();
RefPtr<ScriptLoadContext> context;
if (aMaybeActiveScript) {
options = aMaybeActiveScript->GetFetchOptions();
baseURL = aMaybeActiveScript->BaseURL();
nsCOMPtr<Element> element = aMaybeActiveScript->GetScriptElement();
context = new ScriptLoadContext(element);
} else {
// We don't have a referencing script so fall back on using
// options from the document. This can happen when the user
@ -283,9 +286,10 @@ already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateDynamicImport(
BasePrincipal::Cast(principal)->ContentScriptAddonPolicy());
MOZ_ASSERT_IF(GetKind() == Normal, principal == document->NodePrincipal());
options = new ScriptFetchOptions(
mozilla::CORS_NONE, document->GetReferrerPolicy(), principal, nullptr);
options = new ScriptFetchOptions(mozilla::CORS_NONE,
document->GetReferrerPolicy(), principal);
baseURL = document->GetDocBaseURI();
context = new ScriptLoadContext(nullptr);
}
context->mIsInline = false;

View File

@ -38,7 +38,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadContext)
NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoadBlockedDocument, mRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoadBlockedDocument, mRequest, mElement)
if (Runnable* runnable = tmp->mRunnable.exchange(nullptr)) {
runnable->Release();
}
@ -46,13 +46,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDocument, mRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDocument, mRequest, mElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ScriptLoadContext)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
ScriptLoadContext::ScriptLoadContext()
ScriptLoadContext::ScriptLoadContext(Element* aElement)
: mScriptMode(ScriptMode::eBlocking),
mScriptFromHead(false),
mIsInline(true),
@ -67,6 +67,7 @@ ScriptLoadContext::ScriptLoadContext()
mRunnable(nullptr),
mLineNo(1),
mIsPreload(false),
mElement(aElement),
mRequest(nullptr),
mUnreportedPreloadError(NS_OK) {}
@ -179,8 +180,12 @@ bool ScriptLoadContext::CompileStarted() const {
}
nsIScriptElement* ScriptLoadContext::GetScriptElement() const {
nsCOMPtr<nsIScriptElement> scriptElement =
do_QueryInterface(mRequest->mFetchOptions->mElement);
if (mRequest->IsModuleRequest() && !mRequest->IsTopLevel()) {
JS::loader::ModuleLoadRequest* root =
mRequest->AsModuleRequest()->GetRootModule();
return root->GetLoadContext()->GetScriptElement();
}
nsCOMPtr<nsIScriptElement> scriptElement = do_QueryInterface(mElement);
return scriptElement;
}
@ -188,9 +193,8 @@ void ScriptLoadContext::SetIsLoadRequest(nsIScriptElement* aElement) {
MOZ_ASSERT(aElement);
MOZ_ASSERT(!GetScriptElement());
MOZ_ASSERT(IsPreload());
// We are not tracking our own element, and are relying on the one in
// FetchOptions.
mRequest->mFetchOptions->mElement = do_QueryInterface(aElement);
// TODO: How to allow both to access fetch options
mElement = do_QueryInterface(aElement);
mIsPreload = false;
}

View File

@ -84,7 +84,7 @@ class ScriptLoadContext : public PreloaderBase {
virtual ~ScriptLoadContext();
public:
explicit ScriptLoadContext();
explicit ScriptLoadContext(Element* aElement);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadContext)
@ -187,6 +187,7 @@ class ScriptLoadContext : public PreloaderBase {
// Set on scripts and top level modules.
bool mIsPreload;
nsCOMPtr<Element> mElement;
RefPtr<JS::loader::ScriptLoadRequest> mRequest;

View File

@ -817,14 +817,13 @@ already_AddRefed<ScriptLoadRequest> ScriptLoader::CreateLoadRequest(
const SRIMetadata& aIntegrity, ReferrerPolicy aReferrerPolicy) {
nsIURI* referrer = mDocument->GetDocumentURIAsReferrer();
nsCOMPtr<Element> domElement = do_QueryInterface(aElement);
RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
aCORSMode, aReferrerPolicy, aTriggeringPrincipal, domElement);
RefPtr<ScriptLoadContext> context = new ScriptLoadContext();
RefPtr<ScriptFetchOptions> fetchOptions =
new ScriptFetchOptions(aCORSMode, aReferrerPolicy, aTriggeringPrincipal);
RefPtr<ScriptLoadContext> context = new ScriptLoadContext(domElement);
if (aKind == ScriptKind::eClassic) {
RefPtr<ScriptLoadRequest> aRequest =
new ScriptLoadRequest(aKind, aURI, fetchOptions, aIntegrity, referrer,
new ScriptLoadContext());
RefPtr<ScriptLoadRequest> aRequest = new ScriptLoadRequest(
aKind, aURI, fetchOptions, aIntegrity, referrer, context);
return aRequest.forget();
}
@ -2268,7 +2267,8 @@ nsresult ScriptLoader::EvaluateScript(nsIGlobalObject* aGlobalObject,
// Create a ClassicScript object and associate it with the JSScript.
RefPtr<ClassicScript> classicScript =
new ClassicScript(aRequest->mFetchOptions, aRequest->mBaseURL);
new ClassicScript(aRequest->mFetchOptions, aRequest->mBaseURL,
aRequest->GetLoadContext()->mElement);
JS::RootedValue classicScriptValue(cx, JS::PrivateValue(classicScript));
JS::CompileOptions options(cx);

View File

@ -51,6 +51,34 @@
namespace mozilla {
namespace dom {
JSObject* SourceElementCallback(JSContext* aCx, JS::HandleValue aPrivateValue) {
// NOTE: The result of this is only used by DevTools for matching sources, so
// it is safe to silently ignore any errors and return nullptr for them.
JS::loader::LoadedScript* script =
static_cast<JS::loader::LoadedScript*>(aPrivateValue.toPrivate());
JS::Rooted<JS::Value> elementValue(aCx);
{
nsCOMPtr<Element> domElement = script->GetScriptElement();
if (!domElement) {
return nullptr;
}
JSObject* globalObject =
domElement->OwnerDoc()->GetScopeObject()->GetGlobalJSObject();
JSAutoRealm ar(aCx, globalObject);
nsresult rv = nsContentUtils::WrapNative(aCx, domElement, &elementValue,
/* aAllowWrapping = */ true);
if (NS_FAILED(rv)) {
return nullptr;
}
}
return &elementValue.toObject();
}
static MOZ_THREAD_LOCAL(ScriptSettingsStackEntry*) sScriptSettingsTLS;
class ScriptSettingsStack {
@ -312,6 +340,7 @@ void AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
mOldWarningReporter.emplace(JS::GetWarningReporter(aCx));
JS::SetWarningReporter(aCx, WarningOnlyErrorReporter);
JS::SetSourceElementCallback(aCx, SourceElementCallback);
#ifdef DEBUG
if (haveException) {

View File

@ -7,6 +7,7 @@
#include "LoadedScript.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/dom/Element.h"
#include "jsfriendapi.h"
#include "js/Modules.h" // JS::{Get,Set}ModulePrivate
@ -25,10 +26,11 @@ 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(mElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(LoadedScript)
@ -38,8 +40,11 @@ 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) {
nsIURI* aBaseURL, mozilla::dom::Element* aElement)
: mKind(aKind),
mFetchOptions(aFetchOptions),
mBaseURL(aBaseURL),
mElement(aElement) {
MOZ_ASSERT(mFetchOptions);
MOZ_ASSERT(mBaseURL);
}
@ -89,16 +94,17 @@ void HostReleaseTopLevelScript(const JS::Value& aPrivate) {
// EventScript
//////////////////////////////////////////////////////////////
EventScript::EventScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
: LoadedScript(ScriptKind::eEvent, aFetchOptions, aBaseURL) {}
EventScript::EventScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
mozilla::dom::Element* aElement)
: LoadedScript(ScriptKind::eEvent, aFetchOptions, aBaseURL, aElement) {}
//////////////////////////////////////////////////////////////
// ClassicScript
//////////////////////////////////////////////////////////////
ClassicScript::ClassicScript(ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL)
: LoadedScript(ScriptKind::eClassic, aFetchOptions, aBaseURL) {}
nsIURI* aBaseURL, mozilla::dom::Element* aElement)
: LoadedScript(ScriptKind::eClassic, aFetchOptions, aBaseURL, aElement) {}
//////////////////////////////////////////////////////////////
// ModuleScript
@ -127,8 +133,9 @@ 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),
ModuleScript::ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
mozilla::dom::Element* aElement)
: LoadedScript(ScriptKind::eModule, aFetchOptions, aBaseURL, aElement),
mDebuggerDataInitialized(false) {
MOZ_ASSERT(!ModuleRecord());
MOZ_ASSERT(!HasParseError());

View File

@ -27,10 +27,11 @@ class LoadedScript : public nsISupports {
ScriptKind mKind;
RefPtr<ScriptFetchOptions> mFetchOptions;
nsCOMPtr<nsIURI> mBaseURL;
RefPtr<mozilla::dom::Element> mElement;
protected:
LoadedScript(ScriptKind aKind, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL);
nsIURI* aBaseURL, mozilla::dom::Element* aElement);
virtual ~LoadedScript();
@ -48,6 +49,9 @@ class LoadedScript : public nsISupports {
// Used to propagate Fetch Options to child modules
ScriptFetchOptions* GetFetchOptions() const { return mFetchOptions; }
// Used by the Debugger to get the associated Script Element
mozilla::dom::Element* GetScriptElement() const { return mElement; }
nsIURI* BaseURL() const { return mBaseURL; }
void AssociateWithScript(JSScript* aScript);
@ -57,14 +61,16 @@ class ClassicScript final : public LoadedScript {
~ClassicScript() = default;
public:
ClassicScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
ClassicScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
mozilla::dom::Element* aElement);
};
class EventScript final : public LoadedScript {
~EventScript() = default;
public:
EventScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
EventScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
mozilla::dom::Element* aElement);
};
// A single module script. May be used to satisfy multiple load requests.
@ -82,7 +88,8 @@ class ModuleScript final : public LoadedScript {
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleScript,
LoadedScript)
ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
mozilla::dom::Element* aElement);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetParseError(const JS::Value& aError);

View File

@ -491,7 +491,8 @@ nsresult ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest* aRequest) {
}
RefPtr<ModuleScript> moduleScript =
new ModuleScript(aRequest->mFetchOptions, aRequest->mBaseURL);
new ModuleScript(aRequest->mFetchOptions, aRequest->mBaseURL,
aRequest->mLoadContext->mElement);
aRequest->mModuleScript = moduleScript;
if (!module) {

View File

@ -32,18 +32,17 @@ namespace JS::loader {
// ScriptFetchOptions
//////////////////////////////////////////////////////////////
NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mTriggeringPrincipal, mElement)
NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mTriggeringPrincipal)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ScriptFetchOptions, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ScriptFetchOptions, Release)
ScriptFetchOptions::ScriptFetchOptions(
mozilla::CORSMode aCORSMode, mozilla::dom::ReferrerPolicy aReferrerPolicy,
nsIPrincipal* aTriggeringPrincipal, mozilla::dom::Element* aElement)
nsIPrincipal* aTriggeringPrincipal)
: mCORSMode(aCORSMode),
mReferrerPolicy(aReferrerPolicy),
mTriggeringPrincipal(aTriggeringPrincipal),
mElement(aElement) {
mTriggeringPrincipal(aTriggeringPrincipal) {
MOZ_ASSERT(mTriggeringPrincipal);
}

View File

@ -53,16 +53,10 @@ class ScriptLoadRequestList;
* with the exception of the following properties:
* cryptographic nonce
* The cryptographic nonce metadata used for the initial fetch and for
* fetching any imported modules. As this is populated by a DOM element,
* this is implemented via mozilla::dom::Element as the field
* mElement. The default value is an empty string, and is indicated
* when this field is a nullptr. Nonce is not represented on the dom
* side as per bug 1374612.
* fetching any imported modules. This is handled by the principal.
* parser metadata
* The parser metadata used for the initial fetch and for fetching any
* imported modules. This is populated from a mozilla::dom::Element and is
* handled by the field mElement. The default value is an empty string,
* and is indicated when this field is a nullptr.
* imported modules. This is not implemented.
* integrity metadata
* The integrity metadata used for the initial fetch. This is
* implemented in ScriptLoadRequest, as it changes for every
@ -83,8 +77,7 @@ class ScriptFetchOptions {
ScriptFetchOptions(mozilla::CORSMode aCORSMode,
enum mozilla::dom::ReferrerPolicy aReferrerPolicy,
nsIPrincipal* aTriggeringPrincipal,
mozilla::dom::Element* aElement = nullptr);
nsIPrincipal* aTriggeringPrincipal);
/*
* The credentials mode used for the initial fetch (for module scripts)
@ -100,16 +93,9 @@ class ScriptFetchOptions {
const enum mozilla::dom::ReferrerPolicy mReferrerPolicy;
/*
* Used to determine CSP
* Related to cryptographic nonce, used to determine CSP
*/
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
/*
* Represents fields populated by DOM elements (nonce, parser metadata)
* Leave this field as a nullptr for any fetch that requires the
* default classic script options.
* (https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options)
*/
nsCOMPtr<mozilla::dom::Element> mElement;
};
/*

View File

@ -90,6 +90,11 @@ extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
JS::HandleObjectVector envChain,
JS::Handle<JSScript*> script);
// Callback for the embedding to map from a ScriptSourceObject private-value to
// an object that is exposed as the source "element" in debugger API. This hook
// must be infallible, but can return nullptr if no such element exists.
using JSSourceElementCallback = JSObject* (*)(JSContext*, JS::HandleValue);
namespace JS {
/**
@ -246,6 +251,14 @@ extern JS_PUBLIC_API bool UpdateDebugMetadata(
HandleValue privateValue, HandleString elementAttributeName,
HandleScript introScript, HandleScript scriptOrModule);
// The debugger API exposes an optional "element" property on DebuggerSource
// objects. The callback defined here provides that value. SpiderMonkey
// doesn't particularly care about this, but within Firefox the "element" is the
// HTML script tag for the script which DevTools can use for a better debugging
// experience.
extern JS_PUBLIC_API void SetSourceElementCallback(
JSContext* cx, JSSourceElementCallback callback);
} /* namespace JS */
#endif /* js_CompilationAndEvaluation_h */

View File

@ -171,6 +171,7 @@ struct MOZ_STACK_CLASS DebuggerSource::CallData {
bool getStartLine();
bool getId();
bool getDisplayURL();
bool getElement();
bool getElementProperty();
bool getIntroductionScript();
bool getIntroductionOffset();
@ -383,6 +384,29 @@ bool DebuggerSource::CallData::getDisplayURL() {
return true;
}
struct DebuggerSourceGetElementMatcher {
JSContext* mCx = nullptr;
explicit DebuggerSourceGetElementMatcher(JSContext* cx_) : mCx(cx_) {}
using ReturnType = JSObject*;
ReturnType match(HandleScriptSourceObject sourceObject) {
return sourceObject->unwrappedElement(mCx);
}
ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return nullptr; }
};
bool DebuggerSource::CallData::getElement() {
DebuggerSourceGetElementMatcher matcher(cx);
RootedValue elementValue(cx);
if (JSObject* element = referent.match(matcher)) {
elementValue.setObject(*element);
if (!obj->owner()->wrapDebuggeeValue(cx, &elementValue)) {
return false;
}
}
args.rval().set(elementValue);
return true;
}
struct DebuggerSourceGetElementPropertyMatcher {
using ReturnType = Value;
ReturnType match(HandleScriptSourceObject sourceObject) {
@ -650,6 +674,7 @@ const JSPropertySpec DebuggerSource::properties_[] = {
JS_DEBUG_PSG("url", getURL),
JS_DEBUG_PSG("startLine", getStartLine),
JS_DEBUG_PSG("id", getId),
JS_DEBUG_PSG("element", getElement),
JS_DEBUG_PSG("displayURL", getDisplayURL),
JS_DEBUG_PSG("introductionScript", getIntroductionScript),
JS_DEBUG_PSG("introductionOffset", getIntroductionOffset),

View File

@ -1,6 +1,13 @@
// Specifying an owning element in a cross-global evaluation shouldn't crash.
// That is, when 'evaluate' switches compartments, it should properly wrap
// the CompileOptions members that will become cross-compartment
// references.
// Source.prototype.element can be an object or undefined.
evaluate('42 + 1729', { global: newGlobal(), element: {} });
var g = newGlobal({newCompartment: true});
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
g.evaluate("function f(x) { return 2*x; }", {element: { foo: "bar" }});
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(typeof fw.script.source.element, "object");
assertEq(fw.script.source.element instanceof Debugger.Object, true);
assertEq(fw.script.source.element.getOwnPropertyDescriptor("foo").value, "bar");
g.evaluate("function f(x) { return 2*x; }");
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(typeof fw.script.source.element, "undefined");

View File

@ -0,0 +1,6 @@
// Specifying an owning element in a cross-global evaluation shouldn't crash.
// That is, when 'evaluate' switches compartments, it should properly wrap
// the CompileOptions members that will become cross-compartment
// references.
evaluate('42 + 1729', { global: newGlobal(), element: {} });

View File

@ -0,0 +1,29 @@
// |jit-test| skip-if: helperThreadCount() === 0
// Owning elements and attribute names are attached to scripts compiled
// off-thread.
var g = newGlobal({ newCompartment: true });
var dbg = new Debugger;
var gDO = dbg.addDebuggee(g);
var elt = new g.Object;
var eltDO = gDO.makeDebuggeeValue(elt);
var log = '';
dbg.onDebuggerStatement = function (frame) {
log += 'd';
var source = frame.script.source;
assertEq(source.element, eltDO);
assertEq(source.elementAttributeName, 'mass');
};
var job = g.offThreadCompileToStencil('debugger;');
var stencil = g.finishOffThreadCompileToStencil(job);
log += 'o';
g.evalStencil(stencil,
{
element: elt,
elementAttributeName: 'mass'
});
assertEq(log, 'od');

View File

@ -0,0 +1,13 @@
// source.element is undefined if the script was introduced using eval() or Function().
var g = newGlobal({newCompartment: true});
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
g.eval("function f(x) { return 2*x; }");
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(fw.script.source.element, undefined);
g.x = g.Function("return 13;");
var xw = gw.getOwnPropertyDescriptor('x').value;
assertEq(xw.script.source.element, undefined);

View File

@ -0,0 +1,11 @@
// source.element is undefined when bad values are passed to evaluate().
var g = newGlobal({newCompartment: true});
var dbg = new Debugger;
var gw = dbg.addDebuggee(g);
for (let nonObject of [32, "[object Object]", null, undefined]) {
g.evaluate("function f(x) { return 2*x; }", {element: nonObject});
var fw = gw.getOwnPropertyDescriptor('f').value;
assertEq(fw.script.source.element, undefined);
}

View File

@ -0,0 +1,32 @@
var g = newGlobal({ newCompartment: true });
var dbg = new Debugger(g);
var count = 0;
dbg.onNewScript = script => {
var source = script.source;
assertEq(source.element.getOwnPropertyDescriptor("name").value, "hello");
assertEq(source.elementAttributeName, "world");
count++;
};
var stencil = g.compileToStencil(`"a"`);
g.evalStencil(stencil, {
element: { name: "hello" },
elementAttributeName: "world",
});
assertEq(count, 1);
count = 0;
dbg.onNewScript = script => {
var source = script.source;
assertEq(source.element.getOwnPropertyDescriptor("name").value, "HELLO");
assertEq(source.elementAttributeName, "WORLD");
count++;
};
var stencilXDR = g.compileToStencilXDR(`"b"`);
g.evalStencilXDR(stencilXDR, {
element: { name: "HELLO" },
elementAttributeName: "WORLD",
});
assertEq(count, 1);

View File

@ -4444,6 +4444,38 @@ static void DestroyShellCompartmentPrivate(JS::GCContext* gcx,
static void SetWorkerContextOptions(JSContext* cx);
static bool ShellBuildId(JS::BuildIdCharVector* buildId);
static JSObject* ShellSourceElementCallback(JSContext* cx,
JS::HandleValue privateValue) {
if (!privateValue.isObject()) {
return nullptr;
}
// Due to nukeCCW shenanigans in the shell, we need to check for dead-proxy
// objects that may have replaced an CCW. Otherwise the GetProperty below
// would throw an exception which we do not want to support in this callback.
if (js::IsDeadProxyObject(&privateValue.toObject())) {
return nullptr;
}
RootedObject infoObject(cx,
CheckedUnwrapStatic(privateValue.toObjectOrNull()));
AutoRealm ar(cx, infoObject);
RootedValue elementValue(cx);
if (!JS_GetProperty(cx, infoObject, "element", &elementValue)) {
// This shouldn't happen in the shell, as ParseDebugMetadata always
// creates the infoObject with this property. In any case, this callback
// must not leave an exception pending, so:
MOZ_CRASH("error getting source element");
}
if (elementValue.isObject()) {
return &elementValue.toObject();
}
return nullptr;
}
static constexpr size_t gWorkerStackSize = 2 * 128 * sizeof(size_t) * 1024;
static void WorkerMain(UniquePtr<WorkerInput> input) {
@ -4479,6 +4511,7 @@ static void WorkerMain(UniquePtr<WorkerInput> input) {
DummyHasReleasedWrapperCallback);
JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate);
JS::SetSourceElementCallback(cx, ShellSourceElementCallback);
js::SetWindowProxyClass(cx, &ShellWindowProxyClass);
@ -12541,6 +12574,7 @@ int main(int argc, char** argv) {
JS_SetSecurityCallbacks(cx, &ShellPrincipals::securityCallbacks);
JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate);
JS::SetSourceElementCallback(cx, ShellSourceElementCallback);
js::SetWindowProxyClass(cx, &ShellWindowProxyClass);

View File

@ -494,6 +494,12 @@ JS_PUBLIC_API bool JS::UpdateDebugMetadata(
return true;
}
JS_PUBLIC_API void JS::SetSourceElementCallback(
JSContext* cx, JSSourceElementCallback callback) {
MOZ_ASSERT(cx->runtime());
cx->runtime()->setSourceElementCallback(cx->runtime(), callback);
}
MOZ_NEVER_INLINE static bool ExecuteScript(JSContext* cx, HandleObject envChain,
HandleScript script,
MutableHandleValue rval) {

View File

@ -830,6 +830,19 @@ void ScriptSourceObject::setPrivate(JSRuntime* rt, const Value& value) {
rt->addRefScriptPrivate(value);
}
JSObject* ScriptSourceObject::unwrappedElement(JSContext* cx) const {
JS::RootedValue privateValue(cx, getPrivate());
if (privateValue.isUndefined()) {
return nullptr;
}
if (cx->runtime()->sourceElementCallback) {
return (*cx->runtime()->sourceElementCallback)(cx, privateValue);
}
return nullptr;
}
class ScriptSource::LoadSourceMatcher {
JSContext* const cx_;
ScriptSource* const ss_;

View File

@ -322,6 +322,11 @@ void JSRuntime::setTelemetryCallback(
rt->telemetryCallback = callback;
}
void JSRuntime::setSourceElementCallback(JSRuntime* rt,
JSSourceElementCallback callback) {
rt->sourceElementCallback = callback;
}
void JSRuntime::setUseCounter(JSObject* obj, JSUseCounter counter) {
if (useCounterCallback) {
(*useCounterCallback)(obj, counter);

View File

@ -319,6 +319,8 @@ struct JSRuntime {
/* Call this to accumulate use counter data. */
js::MainThreadData<JSSetUseCounterCallback> useCounterCallback;
js::MainThreadData<JSSourceElementCallback> sourceElementCallback;
public:
// Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_*
// histogram. |key| provides an additional key to identify the histogram.
@ -330,6 +332,9 @@ struct JSRuntime {
void setTelemetryCallback(JSRuntime* rt,
JSAccumulateTelemetryDataCallback callback);
void setSourceElementCallback(JSRuntime* rt,
JSSourceElementCallback callback);
// Sets the use counter for a specific feature, measuring the presence or
// absence of usage of a feature on a specific web page and document which
// the passed JSObject belongs to.