Commit 81a19d22 authored by Ryan Hunt's avatar Ryan Hunt
Browse files

Bug 1721686 - Add test for exposed interfaces on WebAssembly namespace. r=smaug

This commit modifies test_interfaces.js to also test the exposed interfaces
in the WebAssembly namespace. We have conditional features that we'd like
to have confidence that we're not accidentally exposing to web content.

Currently there are:
  * WebAssembly exceptions, enabled only in nightly with a default-off pref
  * mozIntGemm accelerator function, available only in system or addon principals

Depends on D120731

Differential Revision: https://phabricator.services.mozilla.com/D120732
parent 23fa4923
Loading
Loading
Loading
Loading
+71 −36
Original line number Diff line number Diff line
@@ -19,6 +19,28 @@
//
// See createInterfaceMap() below for a complete list of properties.

// IMPORTANT: Do not change this list without review from
//            a JavaScript Engine peer!
var wasmGlobalEntry = {
  name: "WebAssembly",
  insecureContext: true,
  disabled: !getJSTestingFunctions().wasmIsSupportedByHardware(),
};
var wasmGlobalInterfaces = [
  { name: "Module", insecureContext: true },
  { name: "Instance", insecureContext: true },
  { name: "Memory", insecureContext: true },
  { name: "Table", insecureContext: true },
  { name: "Global", insecureContext: true },
  { name: "CompileError", insecureContext: true },
  { name: "LinkError", insecureContext: true },
  { name: "RuntimeError", insecureContext: true },
  {
    name: "Function",
    insecureContext: true,
    nightly: true,
  },
];
// IMPORTANT: Do not change this list without review from
//            a JavaScript Engine peer!
var ecmaGlobals = [
@@ -76,7 +98,7 @@ var ecmaGlobals = [
  "WeakMap",
  "WeakRef",
  "WeakSet",
  { name: "WebAssembly", optional: true },
  wasmGlobalEntry,
];
// IMPORTANT: Do not change the list above without review from
//            a JavaScript Engine peer!
@@ -265,7 +287,9 @@ var interfaceNamesInGlobalScope = [
];
// IMPORTANT: Do not change the list above without review from a DOM peer!

function createInterfaceMap({
function entryDisabled(
  entry,
  {
    isNightly,
    isEarlyBetaOrEarlier,
    isRelease,
@@ -274,20 +298,12 @@ function createInterfaceMap({
    isInsecureContext,
    isFennec,
    isCrossOriginIsolated,
}) {
  var interfaceMap = {};

  function addInterfaces(interfaces) {
    for (var entry of interfaces) {
      if (typeof entry === "string") {
        interfaceMap[entry] = true;
      } else {
        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
        if (
  }
) {
  return (
    entry.nightly === !isNightly ||
    (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
          (entry.nonReleaseAndroid === !(isAndroid && !isRelease) &&
            isAndroid) ||
    (entry.nonReleaseAndroid === !(isAndroid && !isRelease) && isAndroid) ||
    entry.desktop === !isDesktop ||
    (entry.android === !isAndroid &&
      !entry.nonReleaseAndroid &&
@@ -298,7 +314,19 @@ function createInterfaceMap({
    entry.earlyBetaOrEarlier === !isEarlyBetaOrEarlier ||
    entry.crossOriginIsolated === !isCrossOriginIsolated ||
    entry.disabled
        ) {
  );
}

function createInterfaceMap(data, ...interfaceGroups) {
  var interfaceMap = {};

  function addInterfaces(interfaces) {
    for (var entry of interfaces) {
      if (typeof entry === "string") {
        interfaceMap[entry] = true;
      } else {
        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
        if (entryDisabled(entry, data)) {
          interfaceMap[entry.name] = false;
        } else if (entry.optional) {
          interfaceMap[entry.name] = "optional";
@@ -309,15 +337,16 @@ function createInterfaceMap({
    }
  }

  addInterfaces(ecmaGlobals);
  addInterfaces(interfaceNamesInGlobalScope);
  for (let interfaceGroup of interfaceGroups) {
    addInterfaces(interfaceGroup);
  }

  return interfaceMap;
}

function runTest(data) {
  var interfaceMap = createInterfaceMap(data);
  for (var name of Object.getOwnPropertyNames(self)) {
function runTest(parentName, parent, data, ...interfaceGroups) {
  var interfaceMap = createInterfaceMap(data, ...interfaceGroups);
  for (var name of Object.getOwnPropertyNames(parent)) {
    // An interface name should start with an upper case character.
    if (!/^[A-Z]/.test(name)) {
      continue;
@@ -326,7 +355,9 @@ function runTest(data) {
      interfaceMap[name] === "optional" || interfaceMap[name],
      "If this is failing: DANGER, are you sure you want to expose the new interface " +
        name +
        " to all webpages as a property on the service worker? Do not make a change to this file without a " +
        " to all webpages as a property on " +
        parentName +
        "? Do not make a change to this file without a " +
        " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"
    );
    delete interfaceMap[name];
@@ -336,11 +367,12 @@ function runTest(data) {
      delete interfaceMap[name];
    } else {
      ok(
        name in self === interfaceMap[name],
        name in parent === interfaceMap[name],
        name +
          " should " +
          (interfaceMap[name] ? "" : " NOT") +
          " be defined on the global scope"
          " be defined on " +
          parentName
      );
      if (!interfaceMap[name]) {
        delete interfaceMap[name];
@@ -356,6 +388,9 @@ function runTest(data) {
}

workerTestGetHelperData(function(data) {
  runTest(data);
  runTest("self", self, data, ecmaGlobals, interfaceNamesInGlobalScope);
  if (WebAssembly && !entryDisabled(wasmGlobalEntry, data)) {
    runTest("WebAssembly", WebAssembly, data, wasmGlobalInterfaces);
  }
  workerTestDone();
});
+72 −47
Original line number Diff line number Diff line
@@ -47,6 +47,28 @@ const isFennec =
  ).isFennec;
const isCrossOriginIsolated = window.crossOriginIsolated;

// IMPORTANT: Do not change this list without review from
//            a JavaScript Engine peer!
var wasmGlobalEntry = {
  name: "WebAssembly",
  insecureContext: true,
  disabled: !SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupportedByHardware(),
};
var wasmGlobalInterfaces = [
  { name: "Module", insecureContext: true },
  { name: "Instance", insecureContext: true },
  { name: "Memory", insecureContext: true },
  { name: "Table", insecureContext: true },
  { name: "Global", insecureContext: true },
  { name: "CompileError", insecureContext: true },
  { name: "LinkError", insecureContext: true },
  { name: "RuntimeError", insecureContext: true },
  {
    name: "Function",
    insecureContext: true,
    nightly: true,
  },
];
// IMPORTANT: Do not change this list without review from
//            a JavaScript Engine peer!
var ecmaGlobals = [
@@ -105,11 +127,7 @@ var ecmaGlobals = [
  { name: "WeakMap", insecureContext: true },
  { name: "WeakRef", insecureContext: true },
  { name: "WeakSet", insecureContext: true },
  {
    name: "WebAssembly",
    insecureContext: true,
    disabled: !SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupportedByHardware(),
  },
  wasmGlobalEntry,
];
// IMPORTANT: Do not change the list above without review from
//            a JavaScript Engine peer!
@@ -1381,16 +1399,8 @@ var interfaceNamesInGlobalScope = [
];
// IMPORTANT: Do not change the list above without review from a DOM peer!

function createInterfaceMap() {
  var interfaceMap = {};

  function addInterfaces(interfaces) {
    for (var entry of interfaces) {
      if (typeof entry === "string") {
        interfaceMap[entry] = !isInsecureContext;
      } else {
        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
        if (
function entryDisabled(entry) {
  return (
    entry.nightly === !isNightly ||
    (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
    entry.desktop === !isDesktop ||
@@ -1401,8 +1411,7 @@ function createInterfaceMap() {
    entry.fennecOrDesktop === (isAndroid && !isFennec) ||
    entry.fennec === !isFennec ||
    entry.release === !isRelease ||
          entry.releaseNonWindowsAndMac ===
            !(isRelease && !isWindows && !isMac) ||
    entry.releaseNonWindowsAndMac === !(isRelease && !isWindows && !isMac) ||
    entry.releaseNonWindows === !(isRelease && !isWindows) ||
    // The insecureContext test is very purposefully converting
    // entry.insecureContext to boolean, so undefined will convert to
@@ -1413,24 +1422,33 @@ function createInterfaceMap() {
    entry.earlyBetaOrEarlier === !isEarlyBetaOrEarlier ||
    entry.crossOriginIsolated === !isCrossOriginIsolated ||
    entry.disabled
        ) {
          interfaceMap[entry.name] = false;
        } else {
          interfaceMap[entry.name] = true;
  );
}

function createInterfaceMap(...interfaceGroups) {
  var interfaceMap = {};

  function addInterfaces(interfaces) {
    for (var entry of interfaces) {
      if (typeof entry === "string") {
        interfaceMap[entry] = !isInsecureContext;
      } else {
        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
        interfaceMap[entry.name] = !entryDisabled(entry);
      }
    }
  }

  addInterfaces(ecmaGlobals);
  addInterfaces(interfaceNamesInGlobalScope);
  for (let interfaceGroup of interfaceGroups) {
    addInterfaces(interfaceGroup);
  }

  return interfaceMap;
}

function runTest() {
  var interfaceMap = createInterfaceMap();
  for (var name of Object.getOwnPropertyNames(window)) {
function runTest(parentName, parent, ...interfaceGroups) {
  var interfaceMap = createInterfaceMap(...interfaceGroups);
  for (var name of Object.getOwnPropertyNames(parent)) {
    // An interface name should start with an upper case character.
    // However, we have a couple of legacy interfaces that start with 'moz', so
    // we want to allow those until we can remove them.
@@ -1441,28 +1459,32 @@ function runTest() {
      interfaceMap[name],
      "If this is failing: DANGER, are you sure you want to expose the new interface " +
        name +
        " to all webpages as a property on the window? Do not make a change to this file without a " +
        " to all webpages as a property on '" +
        parentName +
        "'? Do not make a change to this file without a " +
        " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"
    );

    ok(
      name in window,
      `${name} is exposed as an own property on the window but tests false for "in" in the global scope`
      name in parent,
      `${name} is exposed as an own property on '" + parentName + "' but tests false for "in" in the global scope`
    );
    ok(
      Object.getOwnPropertyDescriptor(window, name),
      `${name} is exposed as an own property on the window but has no property descriptor in the global scope`
      Object.getOwnPropertyDescriptor(parent, name),
      `${name} is exposed as an own property on '" + parentName + "' but has no property descriptor in the global scope`
    );

    delete interfaceMap[name];
  }
  for (var name of Object.keys(interfaceMap)) {
    ok(
      name in window === interfaceMap[name],
      name in parent === interfaceMap[name],
      name +
        " should " +
        (interfaceMap[name] ? "" : " NOT") +
        " be defined on the global scope"
        " be defined on '" +
        parentName +
        "' scope"
    );
    if (!interfaceMap[name]) {
      delete interfaceMap[name];
@@ -1476,4 +1498,7 @@ function runTest() {
  );
}

runTest();
runTest("window", window, ecmaGlobals, interfaceNamesInGlobalScope);
if (window.WebAssembly && !entryDisabled(wasmGlobalEntry)) {
  runTest("WebAssembly", window.WebAssembly, wasmGlobalInterfaces);
}
+74 −46
Original line number Diff line number Diff line
@@ -25,6 +25,28 @@
// value needs to depend on channel or OS, we will need to make sure
// we have that information before setting up the property lists.

// IMPORTANT: Do not change this list without review from
//            a JavaScript Engine peer!
var wasmGlobalEntry = {
  name: "WebAssembly",
  insecureContext: true,
  disabled: !getJSTestingFunctions().wasmIsSupportedByHardware(),
};
var wasmGlobalInterfaces = [
  { name: "Module", insecureContext: true },
  { name: "Instance", insecureContext: true },
  { name: "Memory", insecureContext: true },
  { name: "Table", insecureContext: true },
  { name: "Global", insecureContext: true },
  { name: "CompileError", insecureContext: true },
  { name: "LinkError", insecureContext: true },
  { name: "RuntimeError", insecureContext: true },
  {
    name: "Function",
    insecureContext: true,
    nightly: true,
  },
];
// IMPORTANT: Do not change this list without review from
//            a JavaScript Engine peer!
var ecmaGlobals = [
@@ -85,11 +107,7 @@ var ecmaGlobals = [
  { name: "WeakMap", insecureContext: true },
  { name: "WeakRef", insecureContext: true },
  { name: "WeakSet", insecureContext: true },
  {
    name: "WebAssembly",
    insecureContext: true,
    disabled: !getJSTestingFunctions().wasmIsSupportedByHardware(),
  },
  wasmGlobalEntry,
];
// IMPORTANT: Do not change the list above without review from
//            a JavaScript Engine peer!
@@ -293,7 +311,9 @@ var interfaceNamesInGlobalScope = [
];
// IMPORTANT: Do not change the list above without review from a DOM peer!

function createInterfaceMap({
function entryDisabled(
  entry,
  {
    isNightly,
    isEarlyBetaOrEarlier,
    isRelease,
@@ -302,16 +322,9 @@ function createInterfaceMap({
    isInsecureContext,
    isFennec,
    isCrossOringinIsolated,
}) {
  var interfaceMap = {};

  function addInterfaces(interfaces) {
    for (var entry of interfaces) {
      if (typeof entry === "string") {
        interfaceMap[entry] = !isInsecureContext;
      } else {
        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
        if (
  }
) {
  return (
    entry.nightly === !isNightly ||
    (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
    entry.desktop === !isDesktop ||
@@ -328,24 +341,33 @@ function createInterfaceMap({
    entry.earlyBetaOrEarlier === !isEarlyBetaOrEarlier ||
    entry.crossOringinIsolated === !isCrossOringinIsolated ||
    entry.disabled
        ) {
          interfaceMap[entry.name] = false;
        } else {
          interfaceMap[entry.name] = true;
  );
}

function createInterfaceMap(data, ...interfaceGroups) {
  var interfaceMap = {};

  function addInterfaces(interfaces) {
    for (var entry of interfaces) {
      if (typeof entry === "string") {
        interfaceMap[entry] = !isInsecureContext;
      } else {
        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
        interfaceMap[entry.name] = !entryDisabled(entry, data);
      }
    }
  }

  addInterfaces(ecmaGlobals);
  addInterfaces(interfaceNamesInGlobalScope);
  for (let interfaceGroup of interfaceGroups) {
    addInterfaces(interfaceGroup);
  }

  return interfaceMap;
}

function runTest(data) {
  var interfaceMap = createInterfaceMap(data);
  for (var name of Object.getOwnPropertyNames(self)) {
function runTest(parentName, parent, data, ...interfaceGroups) {
  var interfaceMap = createInterfaceMap(data, ...interfaceGroups);
  for (var name of Object.getOwnPropertyNames(parent)) {
    // An interface name should start with an upper case character.
    if (!/^[A-Z]/.test(name)) {
      continue;
@@ -354,18 +376,21 @@ function runTest(data) {
      interfaceMap[name],
      "If this is failing: DANGER, are you sure you want to expose the new interface " +
        name +
        " to all webpages as a property on the worker? Do not make a change to this file without a " +
        " to all webpages as a property of " +
        parentName +
        "? Do not make a change to this file without a " +
        " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"
    );
    delete interfaceMap[name];
  }
  for (var name of Object.keys(interfaceMap)) {
    ok(
      name in self === interfaceMap[name],
      name in parent === interfaceMap[name],
      name +
        " should " +
        (interfaceMap[name] ? "" : " NOT") +
        " be defined on the global scope"
        " be defined on " +
        parentName
    );
    if (!interfaceMap[name]) {
      delete interfaceMap[name];
@@ -380,6 +405,9 @@ function runTest(data) {
}

workerTestGetHelperData(function(data) {
  runTest(data);
  runTest("self", self, data, ecmaGlobals, interfaceNamesInGlobalScope);
  if (WebAssembly && !entryDisabled(wasmGlobalEntry, data)) {
    runTest("WebAssembly", WebAssembly, data, wasmGlobalInterfaces);
  }
  workerTestDone();
});