Commit e51750a9 authored by Kris Maglione's avatar Kris Maglione
Browse files

Bug 1322235: Part 6 - Replace AddonPolicyService with a stub implementation in...

Bug 1322235: Part 6 - Replace AddonPolicyService with a stub implementation in ExtensionPolicyService. r=mixedpuppy,zombie

This replaces the JS policy service stubs with a pure C++ version which
directly makes policy decisions based on active WebExtensionPolicy objects.

This is the first step in a larger refactoring, which will remove the
ExtensionManagement module entirely, and replace the current add-on policy
service with direct, non-virtual access to native WebExtensionPolicy objects.

It will also be followed by related changes to migrate the content script and
extension page matching to native code, based on the existing MatchPattern and
WebExtensionPolicy bindings.

MozReview-Commit-ID: 2MpbmXZGiPZ

--HG--
extra : rebase_source : 8b268618164b45605143e858665e592de829a6fa
parent 414584f4
Loading
Loading
Loading
Loading
+22 −9
Original line number Diff line number Diff line
@@ -15,16 +15,31 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1180921
  const Cc = Components.classes;
  const Ci = Components.interfaces;
  const Cu = Components.utils;
  Cu.import("resource://gre/modules/Services.jsm");
  let module = Cu.import("resource://gre/modules/Services.jsm", window);
  let ssm = Services.scriptSecurityManager;
  let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService).wrappedJSObject;

  function StubPolicy(id, subdomain) {
    /* globals MatchPatternSet */
    return new module.WebExtensionPolicy({
      id,
      mozExtensionHostname: id,
      baseURL: `file:///{id}`,

      allowedOrigins: new MatchPatternSet([`*://${subdomain}.example.org/*`]),
      localizeCallback(string) {},
    });
  }

  /* globals WebExtensionPolicy */
  let policyA = StubPolicy("addona", "test1");
  let policyB = StubPolicy("addonb", "test2");
  policyA.active = true;
  policyB.active = true;

  SimpleTest.waitForExplicitFinish();
  let oldAddonIdCallback = aps.setExtensionURIToAddonIdCallback(uri => uri.host);
  SimpleTest.registerCleanupFunction(function() {
    aps.setAddonLoadURICallback("addona", null);
    aps.setAddonLoadURICallback("addonb", null);
    aps.setExtensionURIToAddonIdCallback(oldAddonIdCallback);
    policyA.active = false;
    policyB.active = false;
  });

  function tryLoad(sb, uri) {
@@ -48,11 +63,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1180921

  function uriForDomain(d) { return d + "/tests/caps/tests/mochitest/file_data.txt" }

  tryLoad(addonA, uriForDomain("http://test1.example.org"))
  tryLoad(addonA, uriForDomain("http://test4.example.org"))
  .then(function(success) {
    ok(!success, "cross-origin load should fail for addon A");
    aps.setAddonLoadURICallback("addona", function(uri) { return /test1/.test(uri.host); });
    aps.setAddonLoadURICallback("addonb", function(uri) { return /test2/.test(uri.host); });
    return tryLoad(addonA, uriForDomain("http://test1.example.org"));
  }).then(function(success) {
    ok(success, "whitelisted cross-origin load of test1 should succeed for addon A");
+32 −19
Original line number Diff line number Diff line
@@ -13,20 +13,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
  /** Test for Bug 1161831 **/
  SimpleTest.waitForExplicitFinish();

  var aps = SpecialPowers.Cc["@mozilla.org/addons/policy-service;1"]
                         .getService(SpecialPowers.Ci.nsIAddonPolicyService).wrappedJSObject;
  var oldLoadCallback = aps.setExtensionURILoadCallback(null);
  var oldMapCallback = aps.setExtensionURIToAddonIdCallback(null);
  let module = SpecialPowers.Cu.import("resource://gre/modules/Services.jsm", {});
  var {MatchGlob, MatchPatternSet, WebExtensionPolicy} = module;

  var policy1, policy2;

  var resourceHandler = SpecialPowers.Services.io.getProtocolHandler("resource")
                                     .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
  var extensionHandler = SpecialPowers.Services.io.getProtocolHandler("moz-extension")
                                     .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);

  SimpleTest.registerCleanupFunction(function() {
      extensionHandler.setSubstitution("cherise", null);
      extensionHandler.setSubstitution("liebchen", null);
      aps.setExtensionURILoadCallback(oldLoadCallback);
      aps.setExtensionURIToAddonIdCallback(oldMapCallback);
      policy1.active = false;
      policy2.active = false;
  });

  addLoadEvent(function() {
@@ -38,11 +37,24 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
    ok(filePath.startsWith("file://"), "resource:// URI resolves where we expect: " + filePath);
    var fileURI = SpecialPowers.Services.io.newURI(filePath);

    // Register a moz-extension:// URI.
    extensionHandler.setSubstitution("cherise", fileURI);
    function StubPolicy(id, accessible) {
      let policy = new WebExtensionPolicy(SpecialPowers.Cu.cloneInto({
        id: `imaginaryaddon-${id[0]}`,
        mozExtensionHostname: id,
        baseURL: fileURI.spec,

    // Alias the above.
    extensionHandler.setSubstitution("liebchen", SpecialPowers.Services.io.newURI("moz-extension://cherise"));
        allowedOrigins: SpecialPowers.unwrap(new MatchPatternSet([])),
        webAccessibleResources: accessible ? [SpecialPowers.unwrap(new MatchGlob("*"))] : [],
        localizeCallback(string) {},
      }, module, {cloneFunctions: true, wrapReflectors: true}));

      policy.active = true;
      return policy;
    }

    // Register a moz-extension:// URI.
    policy1 = StubPolicy("cherise", false);
    policy2 = StubPolicy("liebchen", false);

    //
    // Make sure that non-file:// URIs don't work.
@@ -76,12 +88,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
    }


    function setWhitelistCallback(rgxp) {
      var cb = SpecialPowers.wrapCallback(function(uri) { return rgxp.test(uri.spec); });
      aps.setExtensionURILoadCallback(cb);
    }
    function setWhitelistCallback(paths) {
      policy1.active = false;
      policy2.active = false;

    aps.setExtensionURIToAddonIdCallback(SpecialPowers.wrapCallback(function(uri) { return "imaginaryaddon-" + uri.host[0]; }));
      policy1 = StubPolicy("cherise", paths.includes("cherise"));
      policy2 = StubPolicy("liebchen", paths.includes("liebchen"));
    }

    function testLoad(url, navigate, shouldThrow) {
      var ifr = document.createElement("iframe");
@@ -138,12 +151,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
    .then(testLoad.bind(null, "moz-extension://cherise", navigateFromChromeWithWebNav))
    .then(testLoad.bind(null, "moz-extension://cherise", navigateWithLocation, /* shouldThrow = */ true))
    .then(testXHR.bind(null, "moz-extension://cherise", /* shouldError = */ true))
    .then(setWhitelistCallback.bind(null, /cherise/))
    .then(setWhitelistCallback.bind(null, ["cherise"]))
    .then(testLoad.bind(null, "moz-extension://cherise", navigateWithLocation))
    .then(testXHR.bind(null, "moz-extension://cherise"))
    .then(testLoad.bind(null, "moz-extension://liebchen", navigateWithLocation, /* shouldThrow = */ true))
    .then(testXHR.bind(null, "moz-extension://liebchen", /* shouldError = */ true))
    .then(setWhitelistCallback.bind(null, /cherise|liebchen/))
    .then(setWhitelistCallback.bind(null, ["cherise", "liebchen"]))
    .then(testLoad.bind(null, "moz-extension://liebchen", navigateWithLocation))
    .then(testLoad.bind(null, "moz-extension://liebchen", navigateWithSrc))
    .then(testLoad.bind(null, "moz-extension://cherise", navigateWithSrc))
+18 −8
Original line number Diff line number Diff line
@@ -7,9 +7,6 @@ const { console, ConsoleAPI } = require("resource://gre/modules/Console.jsm");
const { ConsoleAPIListener } = require("devtools/server/actors/utils/webconsole-listeners");
const Services = require("Services");

// FIXME: This test shouldn't need to rely on low-level internals.
const {Service} = Cu.import("resource://gre/modules/ExtensionManagement.jsm", {});

var seenMessages = 0;
var seenTypes = 0;

@@ -32,14 +29,29 @@ var callback = {
  }
};

let policy;
do_register_cleanup(() => {
  policy.active = false;
});

function createFakeAddonWindow({addonId} = {}) {
  const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
  const uuid = uuidGen.generateUUID().number.slice(1, -1);

  const url = `moz-extension://${uuid}/`;
  Service.uuidMap.set(uuid, {id: addonId});
  if (policy) {
    policy.active = false;
  }
  /* globals MatchPatternSet, WebExtensionPolicy */
  policy = new WebExtensionPolicy({
    id: addonId,
    mozExtensionHostname: uuid,
    baseURL: "file:///",
    allowedOrigins: new MatchPatternSet([]),
    localizeCallback() {},
  });
  policy.active = true;

  let baseURI = Services.io.newURI(url);
  let baseURI = Services.io.newURI(`moz-extension://${uuid}/`);
  let principal = Services.scriptSecurityManager
        .createCodebasePrincipal(baseURI, {});
  let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
@@ -56,8 +68,6 @@ function createFakeAddonWindow({addonId} = {}) {
 * through to console messages.
 */
function run_test() {
  Service.init();

  // console1 Test Console.jsm messages tagged by the Addon SDK
  // are still filtered correctly.
  let console1 = new ConsoleAPI({
+11 −4
Original line number Diff line number Diff line
@@ -4,8 +4,7 @@
const ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
      .getService(Ci.nsIConsoleAPIStorage);

// FIXME: This test shouldn't need to rely on low-level internals.
const {Service} = Cu.import("resource://gre/modules/ExtensionManagement.jsm", {});
const {WebExtensionPolicy} = Cu.import("resource://gre/modules/Services.jsm", {});

const FAKE_ADDON_ID = "test-webext-addon@mozilla.org";
const EXPECTED_CONSOLE_ID = `addon/${FAKE_ADDON_ID}`;
@@ -46,7 +45,6 @@ const ConsoleObserver = {

function test()
{
  Service.init();
  ConsoleObserver.init();

  waitForExplicitFinish();
@@ -56,7 +54,15 @@ function test()
  uuid = uuid.slice(1, -1); // Strip { and } off the UUID.

  const url = `moz-extension://${uuid}/`;
  Service.uuidMap.set(uuid, {id: FAKE_ADDON_ID});
  /* globals MatchPatternSet, WebExtensionPolicy */
  let policy = new WebExtensionPolicy({
    id: FAKE_ADDON_ID,
    mozExtensionHostname: uuid,
    baseURL: "file:///",
    allowedOrigins: new MatchPatternSet([]),
    localizeCallback() {},
  });
  policy.active = true;

  let baseURI = Services.io.newURI(url);
  let principal = Services.scriptSecurityManager
@@ -70,6 +76,7 @@ function test()
  info("fake webextension docShell created");

  registerCleanupFunction(function() {
    policy.active = false;
    if (chromeWebNav) {
      chromeWebNav.close();
      chromeWebNav = null;
+16 −11
Original line number Diff line number Diff line
@@ -7,21 +7,12 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://testing-common/TestUtils.jsm");

let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService).wrappedJSObject;

let oldAddonIdCallback = aps.setExtensionURIToAddonIdCallback(uri => uri.host);
do_register_cleanup(() => {
  aps.setExtensionURIToAddonIdCallback(oldAddonIdCallback);
});

function getWindowlessBrowser(url) {
  let ssm = Services.scriptSecurityManager;

  let uri = NetUtil.newURI(url);
  // TODO: Remove when addonId origin attribute is removed.
  let attrs = {addonId: uri.host};

  let principal = ssm.createCodebasePrincipal(uri, attrs);
  let principal = ssm.createCodebasePrincipal(uri, {});

  let webnav = Services.appShell.createWindowlessBrowser(false);

@@ -33,8 +24,20 @@ function getWindowlessBrowser(url) {
  return webnav;
}

function StubPolicy(id) {
  return new WebExtensionPolicy({
    id,
    mozExtensionHostname: id,
    baseURL: `file:///{id}`,

    allowedOrigins: new MatchPatternSet([]),
    localizeCallback(string) {},
  });
}

add_task(function*() {
  let ssm = Services.scriptSecurityManager;
  let policy = StubPolicy("foo");
  policy.active = true;

  let webnavA = getWindowlessBrowser("moz-extension://foo/a.html");
  let webnavB = getWindowlessBrowser("moz-extension://foo/b.html");
@@ -67,4 +70,6 @@ add_task(function*() {
     `Result should show a dead wrapper error: ${result}`);

  webnavA.close();

  policy.active = false;
});
Loading