author | Luca Greco <lgreco@mozilla.com> |
Fri, 30 Nov 2018 16:11:26 +0000 | |
changeset 449004 | 911cad5eea69459f2fa6f7354476d196f614348e |
parent 449003 | 541cb39b63231ebdd85159013b9f384d39a13b29 |
child 449005 | a9a926fcd4b2d0a40a18e315b7de9fd97b903891 |
push id | 35139 |
push user | ccoroiu@mozilla.com |
push date | Sat, 01 Dec 2018 02:30:08 +0000 |
treeherder | mozilla-central@22425b629a9d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | zombie, robwu |
bugs | 1509339 |
milestone | 65.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/toolkit/components/extensions/child/ext-userScripts-content.js +++ b/toolkit/components/extensions/child/ext-userScripts-content.js @@ -128,22 +128,51 @@ class UserScript { if (className === "Function") { return this.wrapFunction(valueToExport); } if (className === "Object") { return this.exportLazyGetters(valueToExport); } + if (className === "Array") { + return this.exportArray(valueToExport); + } + let valueType = className || typeof valueToExport; throw new ExportError(privateOptions.errorMessage || `${valueType} cannot be exported to the userScript`); } /** + * Export all the elements of the `srcArray` into a newly created userScript array. + * + * @param {Array} srcArray + * The apiScript array to export to the userScript code. + * + * @returns {Array} + * The resulting userScript array. + * + * @throws {UserScriptError} + * Throws an error when the array can't be exported successfully. + */ + exportArray(srcArray) { + const destArray = Cu.cloneInto([], this.scriptSandbox); + + for (let [idx, value] of this.shallowCloneEntries(srcArray)) { + destArray[idx] = this.export(value, { + errorMessage: `Error accessing disallowed element at index "${idx}"`, + Error: this.UserScriptError, + }); + } + + return destArray; + } + + /** * Export all the properties of the `src` plain object as lazy getters on the `dest` object, * or in a newly created userScript object if `dest` is `undefined`. * * @param {Object} src * A set of properties to define on a `dest` object as lazy getters. * @param {Object} [dest] * An optional `dest` object (a new userScript object is created by default when not specified). *
--- a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js @@ -870,40 +870,39 @@ add_task(async function test_apiScript_m await test_userScript_APIMethod({ userScript, apiScript, }); }); add_task(async function test_apiScript_method_return_proxy_object() { function apiScript(sharedTestAPIMethods) { - const {cloneInto} = this; let proxyTrapsCount = 0; let scriptTrapsCount = 0; browser.userScripts.onBeforeScript.addListener(script => { script.defineGlobals({ ...sharedTestAPIMethods, testAPIMethodError() { return new Proxy(["expectedArrayValue"], { getPrototypeOf(target) { proxyTrapsCount++; return Object.getPrototypeOf(target); }, }); }, testAPIMethodOk() { return new script.global.Proxy( - cloneInto(["expectedArrayValue"], script.global), - cloneInto({ + script.export(["expectedArrayValue"]), + script.export({ getPrototypeOf(target) { scriptTrapsCount++; return script.global.Object.getPrototypeOf(target); }, - }, script.global, {cloneFunctions: true})); + })); }, assertNoProxyTrapTriggered() { browser.test.assertEq(0, proxyTrapsCount, "Proxy traps should not be triggered"); }, assertScriptProxyTrapsCount(expected) { browser.test.assertEq(expected, scriptTrapsCount, "Script Proxy traps should have been triggered"); }, }); @@ -1165,16 +1164,56 @@ add_task(async function test_apiScript_m } await test_userScript_APIMethod({ userScript, apiScript, }); }); +add_task(async function test_apiScript_method_export_sparse_arrays() { + function apiScript(sharedTestAPIMethods) { + browser.userScripts.onBeforeScript.addListener(script => { + script.defineGlobals({ + ...sharedTestAPIMethods, + testAPIMethod() { + const sparseArray = []; + sparseArray[3] = "third-element"; + sparseArray[5] = "fifth-element"; + return script.export(sparseArray); + }, + }); + }); + } + + async function userScript() { + const {assertTrue, notifyFinish, testAPIMethod} = this; + + const result = testAPIMethod(window, document); + + // We expect the returned value to be the uncloneable window object. + assertTrue(result && result.length === 6, + `the returned value should be an array of the expected length: ${result}`); + assertTrue(result[3] === "third-element", + `the third array element should have the expected value: ${result[3]}`); + assertTrue(result[5] === "fifth-element", + `the fifth array element should have the expected value: ${result[5]}`); + assertTrue(result[0] === undefined, + `the first array element should have the expected value: ${result[0]}`); + assertTrue(!("0" in result), "Holey array should still be holey"); + + notifyFinish(); + } + + await test_userScript_APIMethod({ + userScript, + apiScript, + }); +}); + // This test verify that a cached script is still able to catch the document // while it is still loading (when we do not block the document parsing as // we do for a non cached script). add_task(async function test_cached_userScript_on_document_start() { function apiScript() { browser.userScripts.onBeforeScript.addListener(script => { script.defineGlobals({ sendTestMessage(name, params) {