Bug 1900410 - Add filename option to ChromeUtils.compileScript a=dmeehan

This option enables internal callers to specify a different file name,
which we will rely upon for redacting the extension URL and filenames.

Original Revision: https://phabricator.services.mozilla.com/D214295

Differential Revision: https://phabricator.services.mozilla.com/D214815
This commit is contained in:
Rob Wu 2024-06-27 12:35:54 +00:00
parent aad7f1bca5
commit 6c54b9b35e
5 changed files with 64 additions and 6 deletions

View file

@ -966,6 +966,11 @@ dictionary CompileScriptOptionsDictionary {
*/
DOMString charset = "utf-8";
/**
* The filename to associate with the script. Defaults to the source's URL.
*/
DOMString filename;
/**
* If true, certain parts of the script may be parsed lazily, the first time
* they are used, rather than eagerly parsed at load time.

View file

@ -29,7 +29,8 @@ interface PrecompiledScript {
any executeInGlobal(object global, optional ExecuteInGlobalOptions options = {});
/**
* The URL that the script was loaded from.
* The URL that the script was loaded from. Could also be an arbitrary other
* value as specified by the "filename" option to ChromeUtils.compileScript.
*/
[Pure]
readonly attribute DOMString url;

View file

@ -292,7 +292,14 @@ nsresult AsyncScriptCompiler::Start(
mCharset = aOptions.mCharset;
CompileOptions options(aCx);
options.setFile(mURL.get()).setNoScriptRval(!aOptions.mHasReturnValue);
nsAutoCString filename;
if (aOptions.mFilename.WasPassed()) {
filename = NS_ConvertUTF16toUTF8(aOptions.mFilename.Value());
options.setFile(filename.get());
} else {
options.setFile(mURL.get());
}
options.setNoScriptRval(!aOptions.mHasReturnValue);
if (!aOptions.mLazilyParse) {
options.setForceFullParse();
@ -418,7 +425,8 @@ void AsyncScriptCompiler::Reject(JSContext* aCx, const char* aMsg) {
nsAutoString msg;
msg.AppendASCII(aMsg);
msg.AppendLiteral(": ");
AppendUTF8toUTF16(mURL, msg);
nsDependentCString filename(mOptions.filename().c_str());
AppendUTF8toUTF16(filename, msg);
RootedValue exn(aCx);
if (xpc::NonVoidStringToJsval(aCx, msg, &exn)) {
@ -501,7 +509,7 @@ PrecompiledScript::PrecompiledScript(nsISupports* aParent,
JS::ReadOnlyCompileOptions& aOptions)
: mParent(aParent),
mStencil(aStencil),
mURL(aOptions.filename().c_str()),
mPublicURL(aOptions.filename().c_str()),
mHasReturnValue(!aOptions.noScriptRval) {
MOZ_ASSERT(aParent);
MOZ_ASSERT(aStencil);
@ -551,7 +559,9 @@ void PrecompiledScript::ExecuteInGlobal(JSContext* aCx, HandleObject aGlobal,
JS_WrapValue(aCx, aRval);
}
void PrecompiledScript::GetUrl(nsAString& aUrl) { CopyUTF8toUTF16(mURL, aUrl); }
void PrecompiledScript::GetUrl(nsAString& aUrl) {
CopyUTF8toUTF16(mPublicURL, aUrl);
}
bool PrecompiledScript::HasReturnValue() { return mHasReturnValue; }

View file

@ -53,7 +53,7 @@ class PrecompiledScript : public nsISupports, public nsWrapperCache {
nsCOMPtr<nsISupports> mParent;
RefPtr<JS::Stencil> mStencil;
nsCString mURL;
nsCString mPublicURL;
const bool mHasReturnValue;
};

View file

@ -73,6 +73,48 @@ add_task(async function test_syntaxError() {
SyntaxError);
});
add_task(async function test_Error_filename() {
// This function will be serialized as a data:-URL and called.
function getMyError() {
let err = new Error();
return {
fileName: err.fileName,
stackFirstLine: err.stack.split("\n")[0],
};
}
const scriptUrl = `data:,(${encodeURIComponent(getMyError)})()`;
const dummyFilename = "dummy filename";
let script1 = await ChromeUtils.compileScript(scriptUrl, { hasReturnValue: true });
let script2 = await ChromeUtils.compileScript(scriptUrl, { hasReturnValue: true, filename: dummyFilename });
equal(script1.url, scriptUrl, "Script URL is correct");
equal(script2.url, "dummy filename", "Script URL overridden");
let sandbox = Cu.Sandbox("http://example.com");
let err1 = script1.executeInGlobal(sandbox);
equal(err1.fileName, scriptUrl, "fileName is original script URL");
equal(err1.stackFirstLine, `getMyError@${scriptUrl}:2:15`, "Stack has original URL");
let err2 = script2.executeInGlobal(sandbox);
equal(err2.fileName, dummyFilename, "fileName is overridden filename");
equal(err2.stackFirstLine, `getMyError@${dummyFilename}:2:15`, "Stack has overridden URL");
});
add_task(async function test_invalid_url() {
// In this test we want a URL that doesn't resolve to a valid file.
// Moreover, the name is chosen such that it does not trigger the
// CheckForBrokenChromeURL check.
await Assert.rejects(
ChromeUtils.compileScript("resource:///invalid.ftl"),
/^Unable to load script: resource:\/\/\/invalid\.ftl$/
);
await Assert.rejects(
ChromeUtils.compileScript("resource:///invalid.ftl", { filename: "bye bye" }),
/^Unable to load script: bye bye$/
);
});
/**
* Assert that executeInGlobal throws a special exception when the content script throws.
* And the content script exception is notified to the console.