Bug 1190692: Load web extensions. r=billm

Also corrects a race condition where if an extension was disabled before it
had finished loading its manifest it would have called GlobalManager.init but
never call GlobalManager.uninit.
This commit is contained in:
Dave Townsend 2015-08-07 15:53:46 -07:00
parent 79b2a30863
commit a4d0d8568c
6 changed files with 64 additions and 87 deletions

View File

@ -1,83 +0,0 @@
#!/usr/bin/env python
# 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/.
import argparse
import json
import uuid
import sys
import os.path
parser = argparse.ArgumentParser(description='Create install.rdf from manifest.json')
parser.add_argument('--locale')
parser.add_argument('--profile')
parser.add_argument('--uuid')
parser.add_argument('dir')
args = parser.parse_args()
manifestFile = os.path.join(args.dir, 'manifest.json')
manifest = json.load(open(manifestFile))
locale = args.locale
if not locale:
locale = manifest.get('default_locale', 'en-US')
def process_locale(s):
if s.startswith('__MSG_') and s.endswith('__'):
tag = s[6:-2]
path = os.path.join(args.dir, '_locales', locale, 'messages.json')
data = json.load(open(path))
return data[tag]['message']
else:
return s
id = args.uuid
if not id:
id = '{' + str(uuid.uuid4()) + '}'
name = process_locale(manifest['name'])
desc = process_locale(manifest['description'])
version = manifest['version']
installFile = open(os.path.join(args.dir, 'install.rdf'), 'w')
print >>installFile, '<?xml version="1.0"?>'
print >>installFile, '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"'
print >>installFile, ' xmlns:em="http://www.mozilla.org/2004/em-rdf#">'
print >>installFile
print >>installFile, ' <Description about="urn:mozilla:install-manifest">'
print >>installFile, ' <em:id>{}</em:id>'.format(id)
print >>installFile, ' <em:type>2</em:type>'
print >>installFile, ' <em:name>{}</em:name>'.format(name)
print >>installFile, ' <em:description>{}</em:description>'.format(desc)
print >>installFile, ' <em:version>{}</em:version>'.format(version)
print >>installFile, ' <em:bootstrap>true</em:bootstrap>'
print >>installFile, ' <em:targetApplication>'
print >>installFile, ' <Description>'
print >>installFile, ' <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>'
print >>installFile, ' <em:minVersion>4.0</em:minVersion>'
print >>installFile, ' <em:maxVersion>50.0</em:maxVersion>'
print >>installFile, ' </Description>'
print >>installFile, ' </em:targetApplication>'
print >>installFile, ' </Description>'
print >>installFile, '</RDF>'
installFile.close()
bootstrapPath = os.path.join(os.path.dirname(sys.argv[0]), 'bootstrap.js')
data = open(bootstrapPath).read()
boot = open(os.path.join(args.dir, 'bootstrap.js'), 'w')
boot.write(data)
boot.close()
if args.profile:
os.system('mkdir -p {}/extensions'.format(args.profile))
output = open(args.profile + '/extensions/' + id, 'w')
print >>output, os.path.realpath(args.dir)
output.close()
else:
dir = os.path.realpath(args.dir)
if dir[-1] == os.sep:
dir = dir[:-1]
os.system('cd "{}"; zip ../"{}".xpi -r *'.format(args.dir, os.path.basename(dir)))

View File

@ -141,6 +141,10 @@ let Management = {
this.lazyInit();
this.emitter.emit(hook, ...args);
},
off(hook, callback) {
this.emitter.off(hook, callback);
}
};
// A MessageBroker that's used to send and receive messages for
@ -529,13 +533,13 @@ Extension.prototype = {
},
startup() {
GlobalManager.init(this);
return Promise.all([this.readManifest(), this.readLocaleMessages()]).then(([manifest, messages]) => {
if (this.hasShutdown) {
return;
}
GlobalManager.init(this);
this.manifest = manifest;
this.localeMessages = messages;

View File

@ -8,6 +8,10 @@ Components.utils.import("resource://gre/modules/Extension.jsm");
let extension;
function install(data, reason)
{
}
function startup(data, reason)
{
extension = new Extension(data);
@ -18,3 +22,7 @@ function shutdown(data, reason)
{
extension.shutdown();
}
function uninstall(data, reason)
{
}

View File

@ -4628,6 +4628,8 @@ this.XPIProvider = {
let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
if (aType == "dictionary")
uri = "resource://gre/modules/addons/SpellCheckDictionaryBootstrap.js"
else if (aType == "webextension")
uri = "resource://gre/modules/addons/WebExtensionBootstrap.js"
this.bootstrapScopes[aId] =
new Cu.Sandbox(principal, { sandboxName: uri,

View File

@ -13,6 +13,7 @@ EXTRA_JS_MODULES.addons += [
'GMPProvider.jsm',
'LightweightThemeImageOptimizer.jsm',
'SpellCheckDictionaryBootstrap.js',
'WebExtensionBootstrap.js',
]
# Don't ship unused providers on Android

View File

@ -10,8 +10,30 @@ profileDir.append("extensions");
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
startupManager();
const { GlobalManager, Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
function promiseAddonStartup() {
return new Promise(resolve => {
let listener = (extension) => {
Management.off("startup", listener);
resolve(extension);
}
Management.on("startup", listener);
});
}
add_task(function*() {
yield promiseInstallAllFiles([do_get_addon("webextension_1")], true);
do_check_eq(GlobalManager.count, 0);
do_check_false(GlobalManager.extensionMap.has(ID));
yield Promise.all([
promiseInstallAllFiles([do_get_addon("webextension_1")], true),
promiseAddonStartup()
]);
do_check_eq(GlobalManager.count, 1);
do_check_true(GlobalManager.extensionMap.has(ID));
let addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
@ -24,7 +46,16 @@ add_task(function*() {
do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
// Should persist through a restart
yield promiseRestartManager();
yield promiseShutdownManager();
do_check_eq(GlobalManager.count, 0);
do_check_false(GlobalManager.extensionMap.has(ID));
startupManager();
yield promiseAddonStartup();
do_check_eq(GlobalManager.count, 1);
do_check_true(GlobalManager.extensionMap.has(ID));
addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
@ -39,8 +70,22 @@ add_task(function*() {
let file = getFileForAddon(profileDir, ID);
do_check_true(file.exists());
addon.userDisabled = true;
do_check_eq(GlobalManager.count, 0);
do_check_false(GlobalManager.extensionMap.has(ID));
addon.userDisabled = false;
yield promiseAddonStartup();
do_check_eq(GlobalManager.count, 1);
do_check_true(GlobalManager.extensionMap.has(ID));
addon.uninstall();
do_check_eq(GlobalManager.count, 0);
do_check_false(GlobalManager.extensionMap.has(ID));
yield promiseShutdownManager();
});