Bug 1570715 - Treat (deprecation) warnings as errors r=rpl
authorRob Wu <rob@robwu.nl>
Mon, 16 Sep 2019 16:35:59 +0000
changeset 493384 dc3646b4f5a2d8271cee391d72cc3d083f6c8298
parent 493383 1124b75d02a6305d4930011a75c0a1536cfe8b97
child 493385 1213d24d1214a06bf2be669adc60f57abc708645
push id36581
push usershindli@mozilla.com
push dateTue, 17 Sep 2019 04:35:35 +0000
treeherdermozilla-central@22ced1a079e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrpl
bugs1570715, 1495908
milestone71.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
Bug 1570715 - Treat (deprecation) warnings as errors r=rpl Add new preference `extensions.webextensions.warnings-as-errors` that defaults to `true` in tests. Tests that expect warnings are modified to briefly flip the pref for the specific part of the test that needs an exception. As part of the refactor, log entries for schema entries that contain `"onError": "warn"` will now be prefixed by "Warning" instead of "Error", to be consistent with the change from bug 1495908. Differential Revision: https://phabricator.services.mozilla.com/D40548
browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
browser/components/extensions/test/browser/browser_ext_pageAction_simple.js
devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_addons_warnings.js
devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_message_close.js
testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js
testing/profiles/common/user.js
testing/profiles/xpcshell/user.js
toolkit/components/extensions/ExtensionXPCShellUtils.jsm
toolkit/components/extensions/Schemas.jsm
toolkit/components/extensions/test/browser/browser_ext_themes_lwtsupport.js
toolkit/components/extensions/test/mochitest/test_chrome_ext_contentscript_unrecognizedprop_warning.html
toolkit/components/extensions/test/xpcshell/test_ext_eventpage_warning.js
toolkit/components/extensions/test/xpcshell/test_ext_manifest.js
toolkit/components/extensions/test/xpcshell/test_ext_manifest_content_security_policy.js
toolkit/components/extensions/test/xpcshell/test_ext_manifest_themes.js
toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
toolkit/components/extensions/test/xpcshell/test_ext_proxy_auth.js
toolkit/components/extensions/test/xpcshell/test_ext_proxy_socks.js
toolkit/components/extensions/test/xpcshell/test_ext_schema.js
toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
toolkit/components/extensions/test/xpcshell/test_ext_unknown_permissions.js
toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
toolkit/components/extensions/test/xpcshell/test_proxy_incognito.js
toolkit/components/extensions/test/xpcshell/test_proxy_scripts.js
toolkit/components/extensions/test/xpcshell/test_proxy_scripts_deprecated.js
toolkit/components/extensions/test/xpcshell/test_proxy_scripts_results.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
@@ -40,17 +40,19 @@ add_task(async function() {
   let waitForConsole = new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, [
       {
         message: /Reading manifest: Warning processing browser_action.unrecognized_property: An unexpected property was found/,
       },
     ]);
   });
 
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   // Do this a few times to make sure the pop-up is reloaded each time.
   for (let i = 0; i < 3; i++) {
     clickBrowserAction(extension);
 
     let widget = getBrowserActionWidget(extension).forWindow(window);
     let image = getComputedStyle(widget.node).listStyleImage;
 
--- a/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
+++ b/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
@@ -233,18 +233,19 @@ add_task(async function test_user_define
   SimpleTest.waitForExplicitFinish();
   let waitForConsole = new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, [
       {
         message: /Reading manifest: Warning processing commands.*.unrecognized_property: An unexpected property was found/,
       },
     ]);
   });
-
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   await extension.awaitMessage("ready");
 
   async function runTest(window) {
     for (let testCommand of testCommands) {
       if (testCommand.shortcutMac && !testCommand.shortcut && !isMac) {
         continue;
       }
       EventUtils.synthesizeKey(testCommand.key, testCommand.modifiers, window);
@@ -318,17 +319,21 @@ add_task(async function test_user_define
   // Test that given permission the keyset is added to the private window.
   extension = ExtensionTestUtils.loadExtension({
     manifest: {
       commands: commands,
     },
     incognitoOverride: "spanning",
     background,
   });
+
+  // unrecognized_property in manifest triggers warning.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   await extension.awaitMessage("ready");
   keysetID = `ext-keyset-id-${makeWidgetId(extension.id)}`;
 
   keyset = win1.document.getElementById(keysetID);
   ok(keyset != null, "Expected keyset to exist on win1");
   is(
     keyset.children.length,
     expectedCommandsRegistered,
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_simple.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_simple.js
@@ -50,17 +50,19 @@ add_task(async function test_pageAction_
   let waitForConsole = new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, [
       {
         message: /Reading manifest: Warning processing page_action.unrecognized_property: An unexpected property was found/,
       },
     ]);
   });
 
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   await extension.awaitMessage("page-action-shown");
 
   let elem = await getPageActionButton(extension);
   let parent = window.document.getElementById("page-action-buttons");
   is(
     elem && elem.parentNode,
     parent,
     `pageAction pinned to urlbar ${elem.parentNode.getAttribute("id")}`
--- a/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_addons_warnings.js
+++ b/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_addons_warnings.js
@@ -9,27 +9,29 @@ Services.scriptloader.loadSubScript(CHRO
 // Test that extension warnings are displayed in about:debugging.
 add_task(async function() {
   const EXTENSION_NAME = "Temporary web extension";
   const EXTENSION_ID = "test-devtools@mozilla.org";
 
   const { document, tab, window } = await openAboutDebugging();
   await selectThisFirefoxPage(document, window.AboutDebugging.store);
 
+  await pushPref("extensions.webextensions.warnings-as-errors", false);
   await installTemporaryExtensionFromXPI(
     {
       id: EXTENSION_ID,
       name: EXTENSION_NAME,
       extraProperties: {
         // This property is not expected in the manifest and should trigger a warning!
         wrongProperty: {},
       },
     },
     document
   );
+  await SpecialPowers.popPrefEnv();
 
   info("Wait until a debug target item appears");
   await waitUntil(() => findDebugTargetByText(EXTENSION_NAME, document));
   const target = findDebugTargetByText(EXTENSION_NAME, document);
 
   const warningMessage = target.querySelector(".qa-message");
   ok(
     !!warningMessage,
--- a/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_message_close.js
+++ b/devtools/client/aboutdebugging/test/browser/browser_aboutdebugging_message_close.js
@@ -49,27 +49,29 @@ async function testCloseMessageWithButto
   info("Click on the button and wait for the message to disappear");
   EventUtils.synthesizeMouse(closeButton, 1, 1, {}, doc.defaultView);
 
   const target = findDebugTargetByText(EXTENSION_NAME, doc);
   await waitUntil(() => target.querySelector(".qa-message") === null);
 }
 
 async function installExtensionWithWarning(doc) {
+  await pushPref("extensions.webextensions.warnings-as-errors", false);
   await installTemporaryExtensionFromXPI(
     {
       id: EXTENSION_ID,
       name: EXTENSION_NAME,
       extraProperties: {
         // This property is not expected in the manifest and should trigger a warning!
         wrongProperty: {},
       },
     },
     doc
   );
+  await SpecialPowers.popPrefEnv();
 
   info("Wait until a debug target item appears");
   await waitUntil(() => findDebugTargetByText(EXTENSION_NAME, doc));
 
   const target = findDebugTargetByText(EXTENSION_NAME, doc);
   const warningMessage = target.querySelector(".qa-message");
   ok(
     !!warningMessage,
--- a/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js
+++ b/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js
@@ -133,9 +133,23 @@ ExtensionTestUtils.loadExtension = funct
         SimpleTest.is(actual, msg, "test result correct");
       }
       return actual;
     });
   };
 
   SimpleTest.info(`Extension loaded`);
   return extension;
-}
+};
+
+ExtensionTestUtils.failOnSchemaWarnings = (warningsAsErrors = true) => {
+  let prefName = "extensions.webextensions.warnings-as-errors";
+  SpecialPowers.setBoolPref(prefName, warningsAsErrors);
+  if (!warningsAsErrors) {
+    let registerCleanup;
+    if (typeof registerCleanupFunction != "undefined") {
+      registerCleanup = registerCleanupFunction;
+    } else {
+      registerCleanup = SimpleTest.registerCleanupFunction.bind(SimpleTest);
+    }
+    registerCleanup(() => SpecialPowers.setBoolPref(prefName, true));
+  }
+};
--- a/testing/profiles/common/user.js
+++ b/testing/profiles/common/user.js
@@ -36,16 +36,18 @@ user_pref("dom.send_after_paint_to_conte
 // Only load extensions from the application and user profile
 // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
 user_pref("extensions.enabledScopes", 5);
 user_pref("extensions.legacy.enabled", true);
 // Turn off extension updates so they don't bother tests
 user_pref("extensions.update.enabled", false);
 // Prevent network access for recommendations by default. The payload is {"results":[]}.
 user_pref("extensions.getAddons.discovery.api_url", "data:;base64,eyJyZXN1bHRzIjpbXX0%3D");
+// Treat WebExtension API/schema warnings as errors.
+user_pref("extensions.webextensions.warnings-as-errors", true);
 // Disable useragent updates.
 user_pref("general.useragent.updates.enabled", false);
 // Ensure WR doesn't get enabled in tests unless we do it explicitly with the MOZ_WEBRENDER envvar.
 user_pref("gfx.webrender.all.qualified", false);
 user_pref("hangmonitor.timeout", 0); // no hang monitor
 user_pref("media.gmp-manager.updateEnabled", false);
 // Don't do network connections for mitm priming
 user_pref("security.certerrors.mitm.priming.enabled", false);
--- a/testing/profiles/xpcshell/user.js
+++ b/testing/profiles/xpcshell/user.js
@@ -1,15 +1,17 @@
 // Base preferences file used by the xpcshell harness
 /* globals user_pref */
 /* eslint quotes: 0 */
 user_pref("app.normandy.api_url", "https://%(server)s/selfsupport-dummy/");
 user_pref("browser.safebrowsing.downloads.remote.url", "https://%(server)s/safebrowsing-dummy");
 user_pref("browser.search.geoip.url", "https://%(server)s/geoip-dummy");
 user_pref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
+// Treat WebExtension API/schema warnings as errors.
+user_pref("extensions.webextensions.warnings-as-errors", true);
 // Always use network provider for geolocation tests
 // so we bypass the OSX dialog raised by the corelocation provider
 user_pref("geo.provider.testing", true);
 user_pref("media.gmp-manager.updateEnabled", false);
 user_pref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
 user_pref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy");
 // Prevent Remote Settings to issue non local connections.
 user_pref("services.settings.server", "http://localhost/remote-settings-dummy/v1");
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
@@ -950,16 +950,26 @@ var ExtensionTestUtils = {
   },
 
   // Create a wrapper for a webextension that will be installed
   // by some external process (e.g., Normandy)
   expectExtension(id) {
     return new ExternallyInstalledWrapper(this.currentScope, id);
   },
 
+  failOnSchemaWarnings(warningsAsErrors = true) {
+    let prefName = "extensions.webextensions.warnings-as-errors";
+    Services.prefs.setBoolPref(prefName, warningsAsErrors);
+    if (!warningsAsErrors) {
+      this.currentScope.registerCleanupFunction(() => {
+        Services.prefs.setBoolPref(prefName, true);
+      });
+    }
+  },
+
   get remoteContentScripts() {
     return REMOTE_CONTENT_SCRIPTS;
   },
 
   set remoteContentScripts(val) {
     REMOTE_CONTENT_SCRIPTS = !!val;
   },
 
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -45,16 +45,23 @@ XPCOMUtils.defineLazyServiceGetter(
 );
 
 XPCOMUtils.defineLazyGetter(
   this,
   "StartupCache",
   () => ExtensionParent.StartupCache
 );
 
+XPCOMUtils.defineLazyPreferenceGetter(
+  this,
+  "treatWarningsAsErrors",
+  "extensions.webextensions.warnings-as-errors",
+  false
+);
+
 var EXPORTED_SYMBOLS = ["SchemaRoot", "Schemas"];
 
 const KEY_CONTENT_SCHEMAS = "extensions-framework/schemas/content";
 const KEY_PRIVILEGED_SCHEMAS = "extensions-framework/schemas/privileged";
 
 const { DEBUG } = AppConstants;
 
 const isParentProcess =
@@ -1188,17 +1195,41 @@ class Entry {
           value = JSON.stringify(value);
         } catch (e) {
           value = String(value);
         }
         message = message.replace(/\$\{value\}/g, () => value);
       }
     }
 
-    context.logError(context.makeError(message, { warning: true }));
+    this.logWarning(context, message);
+  }
+
+  /**
+   * @param {Context} context
+   * @param {string} warningMessage
+   */
+  logWarning(context, warningMessage) {
+    let error = context.makeError(warningMessage, { warning: true });
+    context.logError(error);
+
+    if (treatWarningsAsErrors) {
+      // This pref is false by default, and true by default in tests to
+      // discourage the use of deprecated APIs in our unit tests.
+      // If a warning is an expected part of a test, temporarily set the pref
+      // to false, e.g. with the ExtensionTestUtils.failOnSchemaWarnings helper.
+      Services.console.logStringMessage(
+        "Treating warning as error because the preference " +
+          "extensions.webextensions.warnings-as-errors is set to true"
+      );
+      if (typeof error === "string") {
+        error = new Error(error);
+      }
+      throw error;
+    }
   }
 
   /**
    * Checks whether the entry is deprecated and, if so, logs a
    * deprecation message.
    *
    * @param {Context} context
    * @param {value} [value]
@@ -1869,17 +1900,17 @@ class ObjectType extends Type {
         `contain the required "${prop}" property`
       );
     } else if (optional !== "omit-key-if-missing") {
       result[prop] = propType.default;
     }
 
     if (error) {
       if (onError == "warn") {
-        context.logError(forceString(error.error));
+        this.logWarning(context, forceString(error.error));
       } else if (onError != "ignore") {
         throw error;
       }
 
       result[prop] = propType.default;
     }
   }
 
@@ -2167,17 +2198,17 @@ class ArrayType extends Type {
 
     let result = [];
     for (let [i, element] of value.entries()) {
       element = context.withPath(String(i), () =>
         this.itemType.normalize(element, context)
       );
       if (element.error) {
         if (this.onError == "warn") {
-          context.logError(forceString(element.error));
+          this.logWarning(context, forceString(element.error));
         } else if (this.onError != "ignore") {
           return element;
         }
         continue;
       }
       result.push(element.value);
     }
 
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_lwtsupport.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_lwtsupport.js
@@ -1,14 +1,17 @@
 "use strict";
 
 const DEFAULT_THEME_BG_COLOR = "rgb(255, 255, 255)";
 const DEFAULT_THEME_TEXT_COLOR = "rgb(0, 0, 0)";
 
 add_task(async function test_deprecated_LWT_properties_ignored() {
+  // This test uses deprecated theme properties, so warnings are expected.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
+
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       theme: {
         images: {
           headerURL: "image1.png",
         },
         colors: {
           accentcolor: ACCENT_COLOR,
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_contentscript_unrecognizedprop_warning.html
+++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_contentscript_unrecognizedprop_warning.html
@@ -55,17 +55,19 @@ add_task(async function test_contentscri
 
   SimpleTest.waitForExplicitFinish();
   let waitForConsole = new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, [{
       message: /Reading manifest: Warning processing content_scripts.*.unrecognized_property: An unexpected property was found/,
     }]);
   });
 
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   window.open(`${BASE}/file_sample.html`);
 
   await Promise.all([extension.awaitFinish("content-script-loaded")]);
   info("test page loaded");
 
   await extension.unload();
 
--- a/toolkit/components/extensions/test/xpcshell/test_ext_eventpage_warning.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_eventpage_warning.js
@@ -1,11 +1,13 @@
 "use strict";
 
 AddonTestUtils.init(this);
+// This test expects and checks deprecation warnings.
+ExtensionTestUtils.failOnSchemaWarnings(false);
 
 function createEventPageExtension(eventPage) {
   return ExtensionTestUtils.loadExtension({
     manifest: {
       background: eventPage,
     },
     files: {
       "event_page_script.js"() {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_manifest.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_manifest.js
@@ -70,17 +70,19 @@ add_task(async function test_manifest_wa
         wrong_prop: true,
       },
     },
     files: {
       "bg.js": "",
     },
   });
 
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   // Retrieve the warning message collected by the Extension class
   // packagingWarning method.
   const { warnings } = extension.extension;
   equal(warnings.length, 1, "Got the expected number of manifest warnings");
 
   const expectedMessage =
     "Reading manifest: Warning processing background.wrong_prop";
--- a/toolkit/components/extensions/test/xpcshell/test_ext_manifest_content_security_policy.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_manifest_content_security_policy.js
@@ -10,19 +10,21 @@ add_task(async function test_manifest_cs
   equal(normalized.error, undefined, "Should not have an error");
   equal(normalized.errors.length, 0, "Should not have warnings");
   equal(
     normalized.value.content_security_policy,
     "script-src 'self'; object-src 'none'",
     "Should have the expected poilcy string"
   );
 
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   normalized = await ExtensionTestUtils.normalizeManifest({
     content_security_policy: "object-src 'none'",
   });
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   equal(normalized.error, undefined, "Should not have an error");
 
   Assert.deepEqual(
     normalized.errors,
     [
       "Error processing content_security_policy: SyntaxError: Policy is missing a required \u2018script-src\u2019 directive",
     ],
--- a/toolkit/components/extensions/test/xpcshell/test_ext_manifest_themes.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_manifest_themes.js
@@ -24,10 +24,12 @@ async function test_theme_property(prope
     equal(normalized.errors.length, 0, "Should have a warning");
   }
   equal(normalized.error, undefined, "Should not have an error");
 }
 
 add_task(async function test_manifest_themes() {
   await test_theme_property("images");
   await test_theme_property("colors");
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await test_theme_property("unrecognized_key");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 });
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
@@ -13,17 +13,20 @@ if (AppConstants.MOZ_APP_NAME == "thunde
   bundle = Services.strings.createBundle(
     "chrome://browser/locale/browser.properties"
   );
 }
 const DUMMY_APP_NAME = "Dummy brandName";
 
 async function getManifestPermissions(extensionData) {
   let extension = ExtensionTestCommon.generate(extensionData);
+  // Some tests contain invalid permissions; ignore the warnings about their invalidity.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.loadManifest();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   return extension.manifestPermissions;
 }
 
 function getPermissionWarnings(manifestPermissions) {
   let info = {
     permissions: manifestPermissions,
     appName: DUMMY_APP_NAME,
   };
--- a/toolkit/components/extensions/test/xpcshell/test_ext_proxy_auth.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_proxy_auth.js
@@ -110,18 +110,21 @@ add_task(async function test_webRequest_
     );
 
     await browser.proxy.register("proxy.js");
     browser.test.sendMessage("pac-ready");
   }
 
   let handlingExt = getExtension(background);
 
+  // proxy.register is deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await handlingExt.startup();
   await handlingExt.awaitMessage("pac-ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   authManager.clearAll();
 
   let contentPage = await ExtensionTestUtils.loadContentPage(
     `http://mozilla.org/`
   );
 
   await handlingExt.awaitMessage("done");
@@ -152,18 +155,21 @@ add_task(async function test_webRequest_
     );
 
     await browser.proxy.register("proxy.js");
     browser.test.sendMessage("pac-ready");
   }
 
   let handlingExt = getExtension(background);
 
+  // proxy.register is deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await handlingExt.startup();
   await handlingExt.awaitMessage("pac-ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   function fetch(url) {
     return new Promise((resolve, reject) => {
       let xhr = new XMLHttpRequest();
       xhr.mozBackgroundRequest = true;
       xhr.open("GET", url);
       xhr.onload = () => {
         resolve(xhr.responseText);
--- a/toolkit/components/extensions/test/xpcshell/test_ext_proxy_socks.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_proxy_socks.js
@@ -538,18 +538,21 @@ add_task(async function test_webRequest_
             port: ${socksServer.listener.localPort},
             username: "foo",
             password: "bar",
           }];
         }`,
     },
   });
 
+  // proxy.register is deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await handlingExt.startup();
   await handlingExt.awaitMessage("pac-ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   let contentPage = await ExtensionTestUtils.loadContentPage(
     `http://localhost/`
   );
 
   await handlingExt.awaitMessage("done");
   await contentPage.close();
   await handlingExt.unload();
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schema.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schema.js
@@ -28,29 +28,47 @@ add_task(async function testEmptySchema(
     },
   });
 
   await extension.startup();
   await extension.awaitFinish("schema");
   await extension.unload();
 });
 
+add_task(async function test_warnings_as_errors() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: { unrecognized_property_that_should_be_treated_as_a_warning: 1 },
+  });
+
+  // Tests should be run with extensions.webextensions.warnings-as-errors=true
+  // by default, and prevent extensions with manifest warnings from loading.
+  await Assert.rejects(
+    extension.startup(),
+    /unrecognized_property_that_should_be_treated_as_a_warning/,
+    "extension with invalid manifest should not load if warnings-as-errors=true"
+  );
+  // When ExtensionTestUtils.failOnSchemaWarnings(false) is called, startup is
+  // expected to succeed, as shown by the next "testUnknownProperties" test.
+});
+
 add_task(async function testUnknownProperties() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["unknownPermission"],
 
       unknown_property: {},
     },
 
     background() {},
   });
 
   let { messages } = await promiseConsoleOutput(async () => {
+    ExtensionTestUtils.failOnSchemaWarnings(false);
     await extension.startup();
+    ExtensionTestUtils.failOnSchemaWarnings(true);
   });
 
   AddonTestUtils.checkMessages(messages, {
     expected: [
       { message: /processing permissions\.0: Value "unknownPermission"/ },
       {
         message: /processing unknown_property: An unexpected property was found in the WebExtension manifest/,
       },
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
@@ -946,17 +946,19 @@ add_task(async function() {
   checkErrors([]);
 
   root.testing.errors({ default: "0123", ignore: "x123", warn: "0123" });
   verify("call", "testing", "errors", [
     { default: "0123", ignore: null, warn: "0123" },
   ]);
   checkErrors([]);
 
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   root.testing.errors({ default: "0123", ignore: "0123", warn: "x123" });
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   verify("call", "testing", "errors", [
     { default: "0123", ignore: "0123", warn: null },
   ]);
   checkErrors(['String "x123" must match /^\\d+$/']);
 
   root.testing.onFoo.addListener(f);
   Assert.equal(
     JSON.stringify(tallied.slice(0, -1)),
@@ -1247,16 +1249,19 @@ let deprecatedJson = [
         type: "function",
         deprecated: "This event does not work",
       },
     ],
   },
 ];
 
 add_task(async function testDeprecation() {
+  // This whole test expects deprecation warnings.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
+
   let url = "data:," + JSON.stringify(deprecatedJson);
   Schemas._rootSchema = null;
   await Schemas.load(url);
 
   let root = {};
   Schemas.inject(root, wrapper);
 
   talliedErrors.length = 0;
@@ -1301,16 +1306,24 @@ add_task(async function testDeprecation(
   root.deprecated.onDeprecated.addListener(() => {});
   checkErrors(["This event does not work"]);
 
   root.deprecated.onDeprecated.removeListener(() => {});
   checkErrors(["This event does not work"]);
 
   root.deprecated.onDeprecated.hasListener(() => {});
   checkErrors(["This event does not work"]);
+
+  ExtensionTestUtils.failOnSchemaWarnings(true);
+
+  Assert.throws(
+    () => root.deprecated.onDeprecated.hasListener(() => {}),
+    /This event does not work/,
+    "Deprecation warning with extensions.webextensions.warnings-as-errors=true"
+  );
 });
 
 let choicesJson = [
   {
     namespace: "choices",
 
     types: [],
 
--- a/toolkit/components/extensions/test/xpcshell/test_ext_unknown_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_unknown_permissions.js
@@ -1,10 +1,13 @@
 "use strict";
 
+// This test expects and checks warnings for unknown permissions.
+ExtensionTestUtils.failOnSchemaWarnings(false);
+
 add_task(async function test_unknown_permissions() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: [
         "activeTab",
         "fooUnknownPermission",
         "http://*/",
         "chrome://favicon/",
--- a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
@@ -396,17 +396,19 @@ add_task(async function test_cached_user
       },
     },
     background,
     files: {
       "api-script.js": apiScript,
     },
   });
 
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   await extension.awaitMessage("user-script-registered");
 
   let url = `${BASE_URL}/file_sample.html`;
   let contentPage = await ExtensionTestUtils.loadContentPage(url);
 
   let msg = await extension.awaitMessage("user-script-loaded");
   Assert.deepEqual(
     msg,
--- a/toolkit/components/extensions/test/xpcshell/test_proxy_incognito.js
+++ b/toolkit/components/extensions/test/xpcshell/test_proxy_incognito.js
@@ -128,18 +128,21 @@ add_task(async function test_incognito_p
         function FindProxyForURL() {
           // Shoot a message off to the background.
           browser.runtime.sendMessage("incognito fail");
           return null;
         }
       }),
     },
   });
+  // proxy.register is deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
   await extension.awaitMessage("ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   // This extension will succeed if it gets a request
   let pb_extension = ExtensionTestUtils.loadExtension({
     incognitoOverride: "spanning",
     manifest: {
       permissions: ["proxy"],
     },
     async background() {
@@ -156,18 +159,21 @@ add_task(async function test_incognito_p
         function FindProxyForURL() {
           // Shoot a message off to the background.
           browser.runtime.sendMessage("success");
           return null;
         }
       }),
     },
   });
+  // proxy.register is deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await pb_extension.startup();
   await pb_extension.awaitMessage("ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   let finished = pb_extension.awaitFinish("success");
   let contentPage = await ExtensionTestUtils.loadContentPage(
     "http://example.com/dummy",
     { privateBrowsing: true }
   );
   await finished;
 
--- a/toolkit/components/extensions/test/xpcshell/test_proxy_scripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_proxy_scripts.js
@@ -52,18 +52,21 @@ async function testProxyScript(script, e
       });
     },
     files: {
       "proxy.js": scriptData,
     },
   };
 
   let extension = ExtensionTestUtils.loadExtension(extensionData);
+  // proxy.register and proxy.onProxyError are deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
   await extension.awaitMessage("ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   let errorWait = extension.awaitMessage("proxy-error-received");
 
   let proxyInfo = await getProxyInfo();
 
   let error = await errorWait;
   equal(error.message, expected.message, "Correct error message received");
   if (!expected.proxyInfo) {
@@ -77,18 +80,21 @@ async function testProxyScript(script, e
   if (expected.errorInfo) {
     ok(error.fileName.includes("proxy.js"), "Error should include file name");
     equal(error.lineNumber, 3, "Error should include line number");
     ok(
       error.stack.includes("proxy.js:3:9"),
       "Error should include stack trace"
     );
   }
+  // proxy.unregister is deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   extension.sendMessage("unregister-proxy-script");
   await extension.awaitFinish("proxy");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   await extension.unload();
 }
 
 add_task(async function test_invalid_FindProxyForURL_function() {
   await testProxyScript(() => {}, {
     message: "The proxy script must define FindProxyForURL as a function",
   });
 
@@ -157,18 +163,21 @@ async function getExtension(proxyResult)
     files: {
       "proxy.js": `
         function FindProxyForURL(url, host) {
           return ${proxyResult};
         }`,
     },
   };
   let extension = ExtensionTestUtils.loadExtension(extensionData);
+  // proxy.register is deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
   await extension.awaitMessage("ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
   return extension;
 }
 
 add_task(async function test_passthrough() {
   let ext1 = await getExtension(null);
   let ext2 = await getExtension('"PROXY 1.2.3.4:8888"');
 
   let proxyInfo = await getProxyInfo();
--- a/toolkit/components/extensions/test/xpcshell/test_proxy_scripts_deprecated.js
+++ b/toolkit/components/extensions/test/xpcshell/test_proxy_scripts_deprecated.js
@@ -1,11 +1,13 @@
 "use strict";
 
 AddonTestUtils.init(this);
+// This test expects and checks deprecation messages.
+ExtensionTestUtils.failOnSchemaWarnings(false);
 
 add_task(async function test_proxy_deprecation_messages() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["proxy"],
     },
     background() {
       async function callProxyAPIs() {
--- a/toolkit/components/extensions/test/xpcshell/test_proxy_scripts_results.js
+++ b/toolkit/components/extensions/test/xpcshell/test_proxy_scripts_results.js
@@ -46,18 +46,21 @@ add_task(async function setup() {
             settings.proxy = msg.proxy;
             return Promise.resolve(settings.proxy);
           }
         });
       `,
     },
   };
   extension = ExtensionTestUtils.loadExtension(extensionData);
+  // proxy.register and proxy.onProxyError are deprecated - bug 1443259.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
   await extension.awaitMessage("ready");
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 });
 
 async function setupProxyScript(proxy) {
   extension.sendMessage("set-proxy", { proxy });
   let proxyInfoSent = await extension.awaitMessage("proxy-set");
   deepEqual(proxyInfoSent, proxy, "got back proxy data from proxy script");
 }
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
@@ -383,32 +383,34 @@ add_task(async function developerEmpty()
       homepageURL: "https://example.net",
     });
 
     await addon.uninstall();
   }
 });
 
 add_task(async function authorNotString() {
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   for (let author of [{}, [], 42]) {
     let addon = await promiseInstallWebExtension({
       manifest: {
         author,
         manifest_version: 2,
         name: "Web Extension Name",
         version: "1.0",
       },
     });
 
     checkAddon(ID, addon, {
       creator: null,
     });
 
     await addon.uninstall();
   }
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 });
 
 add_task(async function testThemeExtension() {
   let addon = await promiseInstallWebExtension({
     manifest: {
       author: "Some author",
       manifest_version: 2,
       name: "Web Extension Name",
@@ -429,25 +431,27 @@ add_task(async function testThemeExtensi
     type: "theme",
     isWebExtension: true,
     signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
   });
 
   await addon.uninstall();
 
   // Also test one without a proper 'theme' section.
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   addon = await promiseInstallWebExtension({
     manifest: {
       author: "Some author",
       manifest_version: 2,
       name: "Web Extension Name",
       version: "1.0",
       theme: null,
     },
   });
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   checkAddon(ID, addon, {
     type: "extension",
     isWebExtension: true,
   });
 
   await addon.uninstall();
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
@@ -649,15 +649,17 @@ add_task(async function test_non_gecko_b
       },
     },
   };
 
   const extension = ExtensionTestUtils.loadExtension({
     manifest,
     useAddonManager: "temporary",
   });
+  ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.startup();
+  ExtensionTestUtils.failOnSchemaWarnings(true);
 
   const addon = await promiseAddonByID(ID);
   notEqual(addon, null, "Add-on is installed");
 
   await extension.unload();
 });