mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 846019 - Part 1: amIAddonManager: Map URIs to AddonIDs. r=bmcbride
This commit is contained in:
parent
3d36dc133c
commit
d52f715bfa
@ -1455,6 +1455,35 @@ var AddonManagerInternal = {
|
||||
this.getInstallsByTypes(null, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronously map a URI to the corresponding Addon ID.
|
||||
*
|
||||
* Mappable URIs are limited to in-application resources belonging to the
|
||||
* add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
|
||||
* but do not include URIs from meta data, such as the add-on homepage.
|
||||
*
|
||||
* @param aURI
|
||||
* nsIURI to map to an addon id
|
||||
* @return string containing the Addon ID or null
|
||||
* @see amIAddonManager.mapURIToAddonID
|
||||
*/
|
||||
mapURIToAddonID: function AMI_mapURIToAddonID(aURI) {
|
||||
if (!(aURI instanceof Ci.nsIURI)) {
|
||||
throw Components.Exception("aURI is not a nsIURI",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
// Try all providers
|
||||
let providers = this.providers.slice(0);
|
||||
for (let provider of providers) {
|
||||
var id = callProvider(provider, "mapURIToAddonID", null, aURI);
|
||||
if (id !== null) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether installation is enabled for a particular mimetype.
|
||||
*
|
||||
@ -2340,6 +2369,10 @@ this.AddonManager = {
|
||||
AddonManagerInternal.getAllInstalls(aCallback);
|
||||
},
|
||||
|
||||
mapURIToAddonID: function AM_mapURIToAddonID(aURI) {
|
||||
return AddonManagerInternal.mapURIToAddonID(aURI);
|
||||
},
|
||||
|
||||
isInstallEnabled: function AM_isInstallEnabled(aType) {
|
||||
return AddonManagerInternal.isInstallEnabled(aType);
|
||||
},
|
||||
|
@ -25,6 +25,18 @@ XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"ChromeRegistry",
|
||||
"@mozilla.org/chrome/chrome-registry;1",
|
||||
"nsIChromeRegistry");
|
||||
XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"ResProtocolHandler",
|
||||
"@mozilla.org/network/protocol;1?name=resource",
|
||||
"nsIResProtocolHandler");
|
||||
|
||||
const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
|
||||
"initWithPath");
|
||||
|
||||
const PREF_DB_SCHEMA = "extensions.databaseSchema";
|
||||
const PREF_INSTALL_CACHE = "extensions.installCache";
|
||||
const PREF_BOOTSTRAP_ADDONS = "extensions.bootstrappedAddons";
|
||||
@ -1507,6 +1519,123 @@ var XPIProvider = {
|
||||
// Count of unpacked add-ons
|
||||
unpackedAddons: 0,
|
||||
|
||||
/**
|
||||
* Adds or updates a URI mapping for an Addon.id.
|
||||
*
|
||||
* Mappings should not be removed at any point. This is so that the mappings
|
||||
* will be still valid after an add-on gets disabled or uninstalled, as
|
||||
* consumers may still have URIs of (leaked) resources they want to map.
|
||||
*/
|
||||
_addURIMapping: function XPI__addURIMapping(aID, aFile) {
|
||||
try {
|
||||
// Always use our own mechanics instead of nsIIOService.newFileURI, so
|
||||
// that we can be sure to map things as we want them mapped.
|
||||
let uri = this._resolveURIToFile(getURIForResourceInFile(aFile, "."));
|
||||
if (!uri) {
|
||||
throw new Error("Cannot resolve");
|
||||
}
|
||||
this._ensureURIMappings();
|
||||
this._uriMappings[aID] = uri.spec;
|
||||
}
|
||||
catch (ex) {
|
||||
WARN("Failed to add URI mapping", ex);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the URI to Addon mappings are available.
|
||||
*
|
||||
* The function will add mappings for all non-bootstrapped but enabled
|
||||
* add-ons.
|
||||
* Bootstrapped add-on mappings will be added directly when the bootstrap
|
||||
* scope get loaded. (See XPIProvider._addURIMapping() and callers)
|
||||
*/
|
||||
_ensureURIMappings: function XPI__ensureURIMappings() {
|
||||
if (this._uriMappings) {
|
||||
return;
|
||||
}
|
||||
// XXX Convert to Map(), once it gets stable with stable iterators
|
||||
this._uriMappings = Object.create(null);
|
||||
|
||||
// XXX Convert to Set(), once it gets stable with stable iterators
|
||||
let enabled = Object.create(null);
|
||||
for (let a of this.enabledAddons.split(",")) {
|
||||
a = decodeURIComponent(a.split(":")[0]);
|
||||
enabled[a] = null;
|
||||
}
|
||||
|
||||
let cache = JSON.parse(Prefs.getCharPref(PREF_INSTALL_CACHE, "[]"));
|
||||
for (let loc of cache) {
|
||||
for (let [id, val] in Iterator(loc.addons)) {
|
||||
if (!(id in enabled)) {
|
||||
continue;
|
||||
}
|
||||
let file = new nsIFile(val.descriptor);
|
||||
let spec = Services.io.newFileURI(file).spec;
|
||||
this._uriMappings[id] = spec;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolve a URI back to physical file.
|
||||
*
|
||||
* Of course, this works only for URIs pointing to local resources.
|
||||
*
|
||||
* @param aURI
|
||||
* URI to resolve
|
||||
* @return
|
||||
* resolved nsIFileURL
|
||||
*/
|
||||
_resolveURIToFile: function XPI__resolveURIToFile(aURI) {
|
||||
switch (aURI.scheme) {
|
||||
case "jar":
|
||||
case "file":
|
||||
if (aURI instanceof Ci.nsIJARURI) {
|
||||
return this._resolveURIToFile(aURI.JARFile);
|
||||
}
|
||||
return aURI;
|
||||
|
||||
case "chrome":
|
||||
aURI = ChromeRegistry.convertChromeURL(aURI);
|
||||
return this._resolveURIToFile(aURI);
|
||||
|
||||
case "resource":
|
||||
aURI = Services.io.newURI(ResProtocolHandler.resolveURI(aURI), null,
|
||||
null);
|
||||
return this._resolveURIToFile(aURI);
|
||||
|
||||
case "view-source":
|
||||
aURI = Services.io.newURI(aURI.path, null, null);
|
||||
return this._resolveURIToFile(aURI);
|
||||
|
||||
case "about":
|
||||
if (aURI.spec == "about:blank") {
|
||||
// Do not attempt to map about:blank
|
||||
return null;
|
||||
}
|
||||
|
||||
let chan;
|
||||
try {
|
||||
chan = Services.io.newChannelFromURI(aURI);
|
||||
}
|
||||
catch (ex) {
|
||||
return null;
|
||||
}
|
||||
// Avoid looping
|
||||
if (chan.URI.equals(aURI)) {
|
||||
return null;
|
||||
}
|
||||
// We want to clone the channel URI to avoid accidentially keeping
|
||||
// unnecessary references to the channel or implementation details
|
||||
// around.
|
||||
return this._resolveURIToFile(chan.URI.clone());
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the XPI provider initializes the install locations and prefs.
|
||||
*
|
||||
@ -1732,6 +1861,9 @@ var XPIProvider = {
|
||||
// This is needed to allow xpcshell tests to simulate a restart
|
||||
this.extensionsActive = false;
|
||||
|
||||
// Remove URI mappings again
|
||||
delete this._uriMappings;
|
||||
|
||||
if (gLazyObjectsLoaded) {
|
||||
XPIDatabase.shutdown(function shutdownCallback() {
|
||||
Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
|
||||
@ -3358,6 +3490,34 @@ var XPIProvider = {
|
||||
aCallback(results);
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronously map a URI to the corresponding Addon ID.
|
||||
*
|
||||
* Mappable URIs are limited to in-application resources belonging to the
|
||||
* add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
|
||||
* but do not include URIs from meta data, such as the add-on homepage.
|
||||
*
|
||||
* @param aURI
|
||||
* nsIURI to map or null
|
||||
* @return string containing the Addon ID
|
||||
* @see AddonManager.mapURIToAddonID
|
||||
* @see amIAddonManager.mapURIToAddonID
|
||||
*/
|
||||
mapURIToAddonID: function XPI_mapURIToAddonID(aURI) {
|
||||
this._ensureURIMappings();
|
||||
let resolved = this._resolveURIToFile(aURI);
|
||||
if (!resolved) {
|
||||
return null;
|
||||
}
|
||||
resolved = resolved.spec;
|
||||
for (let [id, spec] in Iterator(this._uriMappings)) {
|
||||
if (resolved.startsWith(spec)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a new add-on has been enabled when only one add-on of that type
|
||||
* can be enabled.
|
||||
@ -3722,6 +3882,9 @@ var XPIProvider = {
|
||||
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
createInstance(Ci.mozIJSSubScriptLoader);
|
||||
|
||||
// Add a mapping for XPIProvider.mapURIToAddonID
|
||||
this._addURIMapping(aId, aFile);
|
||||
|
||||
try {
|
||||
// Copy the reason values from the global object into the bootstrap scope.
|
||||
for (let name in BOOTSTRAP_REASONS)
|
||||
|
@ -53,6 +53,14 @@ amManager.prototype = {
|
||||
AddonManagerPrivate.startup();
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIAddonManager.idl
|
||||
*/
|
||||
mapURIToAddonID: function AMC_mapURIToAddonID(uri, id) {
|
||||
id.value = AddonManager.mapURIToAddonID(uri);
|
||||
return !!id.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstaller.idl
|
||||
*/
|
||||
@ -201,7 +209,8 @@ amManager.prototype = {
|
||||
return gSingleton.QueryInterface(aIid);
|
||||
}
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstaller,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIAddonManager,
|
||||
Ci.amIWebInstaller,
|
||||
Ci.nsITimerCallback,
|
||||
Ci.nsIObserver])
|
||||
};
|
||||
|
29
toolkit/mozapps/extensions/amIAddonManager.idl
Normal file
29
toolkit/mozapps/extensions/amIAddonManager.idl
Normal file
@ -0,0 +1,29 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* A service to make some AddonManager functionality available to C++ callers.
|
||||
* Javascript callers should still use AddonManager.jsm directly.
|
||||
*/
|
||||
[scriptable, function, uuid(7b45d82d-7ad5-48d7-9b05-f32eb9818cd4)]
|
||||
interface amIAddonManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Synchronously map a URI to the corresponding Addon ID.
|
||||
*
|
||||
* Mappable URIs are limited to in-application resources belonging to the
|
||||
* add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
|
||||
* but do not include URIs from meta data, such as the add-on homepage.
|
||||
*
|
||||
* @param aURI
|
||||
* The nsIURI to map
|
||||
* @return
|
||||
* true if the URI has been mapped successfully to an Addon ID
|
||||
*/
|
||||
boolean mapURIToAddonID(in nsIURI aURI, out AUTF8String aID);
|
||||
};
|
@ -7,6 +7,7 @@
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'amIAddonManager.idl',
|
||||
'amIInstallTrigger.idl',
|
||||
'amIWebInstallListener.idl',
|
||||
'amIWebInstaller.idl',
|
||||
|
263
toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js
Normal file
263
toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js
Normal file
@ -0,0 +1,263 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This verifies that add-ons URIs can be mapped to add-on IDs
|
||||
//
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Enable loading extensions from the user scopes
|
||||
Services.prefs.setIntPref("extensions.enabledScopes",
|
||||
AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
profileDir.append("extensions");
|
||||
const userExtDir = gProfD.clone();
|
||||
userExtDir.append("extensions2");
|
||||
userExtDir.append(gAppInfo.ID);
|
||||
registerDirectory("XREUSysExt", userExtDir.parent);
|
||||
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
var testserver;
|
||||
|
||||
function TestProvider(result) {
|
||||
this.result = result;
|
||||
}
|
||||
TestProvider.prototype = {
|
||||
uri: Services.io.newURI("hellow://world", null, null),
|
||||
id: "valid@id",
|
||||
startup: function() {},
|
||||
shutdown: function() {},
|
||||
mapURIToAddonID: function(aURI) {
|
||||
if (aURI.spec === this.uri.spec) {
|
||||
return this.id;
|
||||
}
|
||||
throw Components.Exception("Not mapped", result);
|
||||
}
|
||||
};
|
||||
|
||||
function TestProviderNoMap() {}
|
||||
TestProviderNoMap.prototype = {
|
||||
startup: function() {},
|
||||
shutdown: function() {}
|
||||
};
|
||||
|
||||
function check_mapping(uri, id) {
|
||||
do_check_eq(AddonManager.mapURIToAddonID(uri), id);
|
||||
let svc = Components.classes["@mozilla.org/addons/integration;1"].
|
||||
getService(Components.interfaces.amIAddonManager);
|
||||
let val = {};
|
||||
do_check_true(svc.mapURIToAddonID(uri, val));
|
||||
do_check_eq(val.value, id);
|
||||
}
|
||||
|
||||
function resetPrefs() {
|
||||
Services.prefs.setIntPref("bootstraptest.active_version", -1);
|
||||
}
|
||||
|
||||
function waitForPref(aPref, aCallback) {
|
||||
function prefChanged() {
|
||||
Services.prefs.removeObserver(aPref, prefChanged);
|
||||
aCallback();
|
||||
}
|
||||
Services.prefs.addObserver(aPref, prefChanged, false);
|
||||
}
|
||||
|
||||
function getActiveVersion() {
|
||||
return Services.prefs.getIntPref("bootstraptest.active_version");
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
resetPrefs();
|
||||
|
||||
// Create and configure the HTTP server.
|
||||
testserver = new HttpServer();
|
||||
testserver.registerDirectory("/addons/", do_get_file("addons"));
|
||||
testserver.start(4444);
|
||||
|
||||
startupManager();
|
||||
|
||||
run_test_nomapping();
|
||||
}
|
||||
|
||||
function run_test_nomapping() {
|
||||
do_check_eq(AddonManager.mapURIToAddonID(TestProvider.prototype.uri), null);
|
||||
try {
|
||||
let svc = Components.classes["@mozilla.org/addons/integration;1"].
|
||||
getService(Components.interfaces.amIAddonManager);
|
||||
let val = {};
|
||||
do_check_false(svc.mapURIToAddonID(TestProvider.prototype.uri, val));
|
||||
}
|
||||
catch (ex) {
|
||||
do_throw(ex);
|
||||
}
|
||||
|
||||
run_test_1();
|
||||
}
|
||||
|
||||
|
||||
// Tests that add-on URIs are mappable after an install
|
||||
function run_test_1() {
|
||||
prepare_test({ }, [
|
||||
"onNewInstall"
|
||||
]);
|
||||
|
||||
AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
|
||||
ensure_test_completed();
|
||||
|
||||
let addon = install.addon;
|
||||
prepare_test({
|
||||
"bootstrap1@tests.mozilla.org": [
|
||||
["onInstalling", false],
|
||||
"onInstalled"
|
||||
]
|
||||
}, [
|
||||
"onInstallStarted",
|
||||
"onInstallEnded",
|
||||
], function() {
|
||||
let uri = addon.getResourceURI(".");
|
||||
check_mapping(uri, addon.id);
|
||||
|
||||
waitForPref("bootstraptest.active_version", function() {
|
||||
run_test_2(uri);
|
||||
});
|
||||
});
|
||||
install.install();
|
||||
});
|
||||
}
|
||||
|
||||
// Tests that add-on URIs are still mappable, even after the add-on gets
|
||||
// disabled in-session.
|
||||
function run_test_2(uri) {
|
||||
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
|
||||
prepare_test({
|
||||
"bootstrap1@tests.mozilla.org": [
|
||||
["onDisabling", false],
|
||||
"onDisabled"
|
||||
]
|
||||
});
|
||||
|
||||
b1.userDisabled = true;
|
||||
ensure_test_completed();
|
||||
|
||||
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
|
||||
do_check_true(newb1.userDisabled);
|
||||
check_mapping(uri, newb1.id);
|
||||
|
||||
run_test_3(uri);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Tests that add-on URIs aren't mappable if the add-on was never started in a
|
||||
// session
|
||||
function run_test_3(uri) {
|
||||
restartManager();
|
||||
|
||||
do_check_eq(AddonManager.mapURIToAddonID(uri), null);
|
||||
|
||||
run_test_4();
|
||||
}
|
||||
|
||||
// Tests that add-on URIs are mappable after a restart + reenable
|
||||
function run_test_4() {
|
||||
restartManager();
|
||||
|
||||
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
|
||||
prepare_test({
|
||||
"bootstrap1@tests.mozilla.org": [
|
||||
["onEnabling", false],
|
||||
"onEnabled"
|
||||
]
|
||||
});
|
||||
|
||||
b1.userDisabled = false;
|
||||
ensure_test_completed();
|
||||
|
||||
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
|
||||
let uri = newb1.getResourceURI(".");
|
||||
check_mapping(uri, newb1.id);
|
||||
|
||||
run_test_5();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Tests that add-on URIs are mappable after a restart
|
||||
function run_test_5() {
|
||||
restartManager();
|
||||
|
||||
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
|
||||
let uri = b1.getResourceURI(".");
|
||||
check_mapping(uri, b1.id);
|
||||
|
||||
run_test_invalidarg();
|
||||
});
|
||||
}
|
||||
|
||||
// Tests that the AddonManager will bail when mapURIToAddonID is called with an
|
||||
// invalid argument
|
||||
function run_test_invalidarg() {
|
||||
restartManager();
|
||||
|
||||
let tests = [undefined,
|
||||
null,
|
||||
1,
|
||||
"string",
|
||||
"chrome://global/content/",
|
||||
function() {}
|
||||
];
|
||||
for (var test of tests) {
|
||||
try {
|
||||
AddonManager.mapURIToAddonID(test);
|
||||
throw new Error("Shouldn't be able to map the URI in question");
|
||||
}
|
||||
catch (ex if ex.result) {
|
||||
do_check_eq(ex.result, Components.results.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
catch (ex) {
|
||||
do_throw(ex);
|
||||
}
|
||||
}
|
||||
|
||||
run_test_provider();
|
||||
}
|
||||
|
||||
// Tests that custom providers are correctly handled
|
||||
function run_test_provider() {
|
||||
restartManager();
|
||||
|
||||
const provider = new TestProvider(Components.results.NS_ERROR_NOT_AVAILABLE);
|
||||
AddonManagerPrivate.registerProvider(provider);
|
||||
|
||||
check_mapping(provider.uri, provider.id);
|
||||
|
||||
let u2 = provider.uri.clone();
|
||||
u2.path = "notmapped";
|
||||
do_check_eq(AddonManager.mapURIToAddonID(u2), null);
|
||||
|
||||
AddonManagerPrivate.unregisterProvider(provider);
|
||||
|
||||
run_test_provider_nomap();
|
||||
}
|
||||
|
||||
// Tests that custom providers are correctly handled, even not implementing
|
||||
// mapURIToAddonID
|
||||
function run_test_provider_nomap() {
|
||||
restartManager();
|
||||
|
||||
const provider = new TestProviderNoMap();
|
||||
AddonManagerPrivate.registerProvider(provider);
|
||||
|
||||
do_check_eq(AddonManager.mapURIToAddonID(TestProvider.prototype.uri), null);
|
||||
|
||||
AddonManagerPrivate.unregisterProvider(provider);
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,8 @@ const IGNORE = ["escapeAddonURI", "shouldAutoUpdate", "getStartupChanges",
|
||||
"addTypeListener", "removeTypeListener",
|
||||
"addAddonListener", "removeAddonListener",
|
||||
"addInstallListener", "removeInstallListener",
|
||||
"addManagerListener", "removeManagerListener"];
|
||||
"addManagerListener", "removeManagerListener",
|
||||
"mapURIToAddonID"];
|
||||
|
||||
const IGNORE_PRIVATE = ["AddonAuthor", "AddonCompatibilityOverride",
|
||||
"AddonScreenshot", "AddonType", "startup", "shutdown",
|
||||
|
@ -187,6 +187,9 @@ skip-if = os == "android"
|
||||
[test_locked2.js]
|
||||
[test_locked_strictcompat.js]
|
||||
[test_manifest.js]
|
||||
[test_mapURIToAddonID.js]
|
||||
# Same as test_bootstrap.js
|
||||
skip-if = os == "android"
|
||||
[test_migrate1.js]
|
||||
[test_migrate2.js]
|
||||
[test_migrate3.js]
|
||||
|
Loading…
Reference in New Issue
Block a user