Bug 1306037: Support options_ui in embedded WebExtensions. r=aswan

MozReview-Commit-ID: KZVPz52qrTS

--HG--
extra : rebase_source : 302bdead12c6bf36e30ed3782c6cb4526f1ef1c7
This commit is contained in:
Kris Maglione 2016-09-28 23:11:35 +01:00
parent 534d31c7d0
commit 7fe3c9e28a
4 changed files with 107 additions and 23 deletions

View File

@ -2,10 +2,20 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
requestLongerTimeout(2);
function add_tasks(task) {
add_task(task.bind(null, {embedded: false}));
add_task(task.bind(null, {embedded: true}));
}
function* loadExtension(options) {
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "temporary",
embedded: options.embedded,
manifest: Object.assign({
"permissions": ["tabs"],
}, options.manifest),
@ -37,10 +47,12 @@ function* loadExtension(options) {
return extension;
}
add_task(function* test_inline_options() {
add_tasks(function* test_inline_options(extraOptions) {
info(`Test options opened inline (${JSON.stringify(extraOptions)})`);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
let extension = yield loadExtension({
let extension = yield loadExtension(Object.assign({}, extraOptions, {
manifest: {
applications: {gecko: {id: "inline_options@tests.mozilla.org"}},
"options_ui": {
@ -123,7 +135,7 @@ add_task(function* test_inline_options() {
browser.test.notifyFail("options-ui");
});
},
});
}));
yield extension.awaitFinish("options-ui");
yield extension.unload();
@ -131,10 +143,12 @@ add_task(function* test_inline_options() {
yield BrowserTestUtils.removeTab(tab);
});
add_task(function* test_tab_options() {
add_tasks(function* test_tab_options(extraOptions) {
info(`Test options opened in a tab (${JSON.stringify(extraOptions)})`);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
let extension = yield loadExtension({
let extension = yield loadExtension(Object.assign({}, extraOptions, {
manifest: {
applications: {gecko: {id: "tab_options@tests.mozilla.org"}},
"options_ui": {
@ -221,7 +235,7 @@ add_task(function* test_tab_options() {
browser.test.notifyFail("options-ui-tab");
});
},
});
}));
yield extension.awaitFinish("options-ui-tab");
yield extension.unload();
@ -229,8 +243,10 @@ add_task(function* test_tab_options() {
yield BrowserTestUtils.removeTab(tab);
});
add_task(function* test_options_no_manifest() {
let extension = yield loadExtension({
add_tasks(function* test_options_no_manifest(extraOptions) {
info(`Test with no manifest key (${JSON.stringify(extraOptions)})`);
let extension = yield loadExtension(Object.assign({}, extraOptions, {
manifest: {
applications: {gecko: {id: "no_options@tests.mozilla.org"}},
},
@ -256,7 +272,7 @@ add_task(function* test_options_no_manifest() {
browser.test.notifyFail("options-no-manifest");
});
},
});
}));
yield extension.awaitFinish("options-no-manifest");
yield extension.unload();

View File

@ -611,12 +611,20 @@ SpecialPowersObserverAPI.prototype = {
// they're run on a bare archive rather than a running instance,
// as the add-on manager runs them.
let extensionData = new ExtensionData(extension.rootURI);
extensionData.readManifest().then(() => {
return extensionData.initAllLocales();
}).then(() => {
if (extensionData.errors.length) {
return Promise.reject("Extension contains packaging errors");
extensionData.readManifest().then(
() => {
return extensionData.initAllLocales().then(() => {
if (extensionData.errors.length) {
return Promise.reject("Extension contains packaging errors");
}
});
},
() => {
// readManifest() will throw if we're loading an embedded
// extension, so don't worry about locale errors in that
// case.
}
).then(() => {
return extension.startup();
}).then(() => {
this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionStarted", args: []});

View File

@ -1360,6 +1360,52 @@ this.Extension = class extends ExtensionData {
provide(files, ["manifest.json"], manifest);
if (data.embedded) {
// Package this as a webextension embedded inside a legacy
// extension.
let xpiFiles = {
"install.rdf": `<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest"
em:id="${manifest.applications.gecko.id}"
em:name="${manifest.name}"
em:type="2"
em:version="${manifest.version}"
em:description=""
em:hasEmbeddedWebExtension="true"
em:bootstrap="true">
<!-- Firefox -->
<em:targetApplication>
<Description
em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
em:minVersion="51.0a1"
em:maxVersion="*"/>
</em:targetApplication>
</Description>
</RDF>
`,
"bootstrap.js": `
function install() {}
function uninstall() {}
function shutdown() {}
function startup(data) {
data.webExtension.startup();
}
`,
};
for (let [path, data] of Object.entries(files)) {
xpiFiles[`webextension/${path}`] = data;
}
files = xpiFiles;
}
return this.generateZipFile(files);
}

View File

@ -1019,7 +1019,7 @@ var loadManifestFromWebManifest = Task.async(function*(aUri) {
* @throws if the install manifest in the RDF stream is corrupt or could not
* be read
*/
function loadManifestFromRDF(aUri, aStream) {
let loadManifestFromRDF = Task.async(function*(aUri, aStream) {
function getPropertyArray(aDs, aSource, aProperty) {
let values = [];
let targets = aDs.GetTargets(aSource, EM_R(aProperty), true);
@ -1156,6 +1156,7 @@ function loadManifestFromRDF(aUri, aStream) {
addon.bootstrap = getRDFProperty(ds, root, "bootstrap") == "true";
addon.multiprocessCompatible = getRDFProperty(ds, root, "multiprocessCompatible") == "true";
addon.hasEmbeddedWebExtension = getRDFProperty(ds, root, "hasEmbeddedWebExtension") == "true";
if (addon.optionsType &&
addon.optionsType != AddonManager.OPTIONS_TYPE_DIALOG &&
addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE &&
@ -1163,6 +1164,19 @@ function loadManifestFromRDF(aUri, aStream) {
addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_INFO) {
throw new Error("Install manifest specifies unknown type: " + addon.optionsType);
}
if (addon.hasEmbeddedWebExtension) {
let uri = NetUtil.newURI("webextension/manifest.json", null, aUri);
let embeddedAddon = yield loadManifestFromWebManifest(uri);
if (embeddedAddon.optionsURL) {
if (addon.optionsType || addon.optionsURL)
logger.warn(`Addon ${addon.id} specifies optionsType or optionsURL ` +
`in both install.rdf and manifest.json`);
addon.optionsURL = embeddedAddon.optionsURL;
addon.optionsType = embeddedAddon.optionsType;
}
}
}
else {
// Some add-on types are always restartless.
@ -1280,7 +1294,7 @@ function loadManifestFromRDF(aUri, aStream) {
addon.icons = {};
return addon;
}
});
function defineSyncGUID(aAddon) {
// Define .syncGUID as a lazy property which is also settable
@ -1344,7 +1358,7 @@ var loadManifestFromDir = Task.async(function*(aDir, aInstallLocation) {
return size;
}
function loadFromRDF(aUri) {
function* loadFromRDF(aUri) {
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fis.init(aUri.file, -1, -1, false);
@ -1352,7 +1366,7 @@ var loadManifestFromDir = Task.async(function*(aDir, aInstallLocation) {
createInstance(Ci.nsIBufferedInputStream);
bis.init(fis, 4096);
try {
var addon = loadManifestFromRDF(aUri, bis);
var addon = yield loadManifestFromRDF(aUri, bis);
} finally {
bis.close();
fis.close();
@ -1400,7 +1414,7 @@ var loadManifestFromDir = Task.async(function*(aDir, aInstallLocation) {
}
}
} else {
addon = loadFromRDF(uri);
addon = yield loadFromRDF(uri);
}
addon._sourceBundle = aDir.clone();
@ -1424,13 +1438,13 @@ var loadManifestFromDir = Task.async(function*(aDir, aInstallLocation) {
* @throws if the XPI file does not contain a valid install manifest
*/
var loadManifestFromZipReader = Task.async(function*(aZipReader, aInstallLocation) {
function loadFromRDF(aUri) {
function* loadFromRDF(aUri) {
let zis = aZipReader.getInputStream(entry);
let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
createInstance(Ci.nsIBufferedInputStream);
bis.init(zis, 4096);
try {
var addon = loadManifestFromRDF(aUri, bis);
var addon = yield loadManifestFromRDF(aUri, bis);
} finally {
bis.close();
zis.close();
@ -1470,7 +1484,7 @@ var loadManifestFromZipReader = Task.async(function*(aZipReader, aInstallLocatio
let addon = isWebExtension ?
yield loadManifestFromWebManifest(uri) :
loadFromRDF(uri);
yield loadFromRDF(uri);
addon._sourceBundle = aZipReader.file;
addon._installLocation = aInstallLocation;
@ -7324,7 +7338,7 @@ AddonWrapper.prototype = {
let addon = addonFor(this);
if (addon.optionsURL) {
if (this.isWebExtension) {
if (this.isWebExtension || this.hasEmbeddedWebExtension) {
// The internal object's optionsURL property comes from the addons
// DB and should be a relative URL. However, extensions with
// options pages installed before bug 1293721 was fixed got absolute