зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1108096 - Langpack support for b2g/gaia r=ferjm,sicking
This commit is contained in:
Родитель
11548e91e7
Коммит
10b13ff557
|
@ -354,6 +354,15 @@ this.DOMApplicationRegistry = {
|
|||
aCallback(res);
|
||||
},
|
||||
|
||||
getAdditionalLanguages: function(aManifestURL) {
|
||||
for (let id in this.webapps) {
|
||||
if (this.webapps[id].manifestURL == aManifestURL) {
|
||||
return this.webapps[id].additionalLanguages || {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIAppsService API
|
||||
*/
|
||||
|
|
|
@ -22,6 +22,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
// Shared code for AppsServiceChild.jsm, TrustedHostedAppsUtils.jsm,
|
||||
// Webapps.jsm and Webapps.js
|
||||
|
||||
|
@ -485,6 +489,7 @@ this.AppsUtils = {
|
|||
* Checks if the app role is allowed:
|
||||
* Only certified apps can be themes.
|
||||
* Only privileged or certified apps can be addons.
|
||||
* Langpacks need to be privileged.
|
||||
* @param aRole : the role assigned to this app.
|
||||
* @param aStatus : the APP_STATUS_* for this app.
|
||||
*/
|
||||
|
@ -492,6 +497,13 @@ this.AppsUtils = {
|
|||
if (aRole == "theme" && aStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return false;
|
||||
}
|
||||
if (aRole == "langpack" && aStatus !== Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
|
||||
let allow = false;
|
||||
try {
|
||||
allow = Services.prefs.getBoolPref("dom.apps.allow_unsigned_langpacks");
|
||||
} catch(e) {}
|
||||
return allow;
|
||||
}
|
||||
if (!this.allowUnsignedAddons &&
|
||||
(aRole == "addon" &&
|
||||
aStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
|
||||
|
@ -732,7 +744,16 @@ this.AppsUtils = {
|
|||
// Returns the hash for a JS object.
|
||||
computeObjectHash: function(aObject) {
|
||||
return this.computeHash(JSON.stringify(aObject));
|
||||
}
|
||||
},
|
||||
|
||||
getAppManifestURLFromWindow: function(aWindow) {
|
||||
let appId = aWindow.document.nodePrincipal.appId;
|
||||
if (appId === Ci.nsIScriptSecurityManager.NO_APP_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return appsService.getManifestURLByLocalId(appId);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Langpacks"];
|
||||
|
||||
let debug = Services.prefs.getBoolPref("dom.mozApps.debug")
|
||||
? (aMsg) => {
|
||||
dump("-*-*- Langpacks: " + aMsg + "\n");
|
||||
}
|
||||
: (aMsg) => {};
|
||||
|
||||
/**
|
||||
* Langpack support
|
||||
*
|
||||
* Manifest format is:
|
||||
*
|
||||
* "languages-target" : { "app://*.gaiamobile.org/manifest.webapp": "2.2" },
|
||||
* "languages-provided": {
|
||||
* "de": {
|
||||
* "version": 201411051234,
|
||||
* "name": "Deutsch",
|
||||
* "apps": {
|
||||
* "app://calendar.gaiamobile.org/manifest.webapp": "/de/calendar",
|
||||
* "app://email.gaiamobile.org/manifest.webapp": "/de/email"
|
||||
* }
|
||||
* },
|
||||
* "role" : "langpack"
|
||||
*/
|
||||
|
||||
this.Langpacks = {
|
||||
|
||||
_data: {},
|
||||
_broadcaster: null,
|
||||
_appIdFromManifestURL: null,
|
||||
|
||||
init: function() {
|
||||
ppmm.addMessageListener("Webapps:GetLocalizationResource", this);
|
||||
},
|
||||
|
||||
registerRegistryFunctions: function(aBroadcaster, aIdGetter) {
|
||||
this._broadcaster = aBroadcaster;
|
||||
this._appIdFromManifestURL = aIdGetter;
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let data = aMessage.data;
|
||||
let mm = aMessage.target;
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:GetLocalizationResource":
|
||||
this.getLocalizationResource(data, mm);
|
||||
break;
|
||||
default:
|
||||
debug("Unexpected message: " + aMessage.name);
|
||||
}
|
||||
},
|
||||
|
||||
getAdditionalLanguages: function(aManifestURL) {
|
||||
debug("getAdditionalLanguages " + aManifestURL);
|
||||
let res = { langs: {} };
|
||||
let langs = res.langs;
|
||||
if (this._data[aManifestURL]) {
|
||||
res.appId = this._data[aManifestURL].appId;
|
||||
for (let lang in this._data[aManifestURL].langs) {
|
||||
if (!langs[lang]) {
|
||||
langs[lang] = [];
|
||||
}
|
||||
let current = this._data[aManifestURL].langs[lang];
|
||||
langs[lang].push({
|
||||
version: current.version,
|
||||
name: current.name,
|
||||
target: current.target
|
||||
});
|
||||
}
|
||||
}
|
||||
debug("Languages found: " + uneval(res));
|
||||
return res;
|
||||
},
|
||||
|
||||
sendAppUpdate: function(aManifestURL) {
|
||||
debug("sendAppUpdate " + aManifestURL);
|
||||
if (!this._broadcaster) {
|
||||
debug("No broadcaster!");
|
||||
return;
|
||||
}
|
||||
|
||||
let res = this.getAdditionalLanguages(aManifestURL);
|
||||
let message = {
|
||||
id: res.appId,
|
||||
app: {
|
||||
additionalLanguages: res.langs
|
||||
}
|
||||
}
|
||||
this._broadcaster("Webapps:UpdateState", message);
|
||||
},
|
||||
|
||||
getLocalizationResource: function(aData, aMm) {
|
||||
debug("getLocalizationResource " + uneval(aData));
|
||||
|
||||
function sendError(aMsg, aCode) {
|
||||
debug(aMsg);
|
||||
aMm.sendAsyncMessage("Webapps:GetLocalizationResource:Return",
|
||||
{ requestID: aData.requestID, oid: aData.oid, error: aCode });
|
||||
}
|
||||
|
||||
// No langpack available for this app.
|
||||
if (!this._data[aData.manifestURL]) {
|
||||
return sendError("No langpack for this app.", "NoLangpack");
|
||||
}
|
||||
|
||||
// We have langpack(s) for this app, but not for this language.
|
||||
if (!this._data[aData.manifestURL].langs[aData.lang]) {
|
||||
return sendError("No language " + aData.lang + " for this app.",
|
||||
"UnavailableLanguage");
|
||||
}
|
||||
|
||||
// Check that we have the right version.
|
||||
let item = this._data[aData.manifestURL].langs[aData.lang];
|
||||
if (item.target != aData.version) {
|
||||
return sendError("No version " + aData.version + " for this app.",
|
||||
"UnavailableVersion");
|
||||
}
|
||||
|
||||
// The path can't be an absolute uri.
|
||||
if (isAbsoluteURI(aData.path)) {
|
||||
return sendError("url can't be absolute.", "BadUrl");
|
||||
}
|
||||
|
||||
let href = item.url + aData.path;
|
||||
debug("Will load " + href);
|
||||
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.mozBackgroundRequest = true;
|
||||
xhr.open("GET", href);
|
||||
|
||||
// Default to text response type, but the webidl binding takes care of
|
||||
// validating the dataType value.
|
||||
xhr.responseType = "text";
|
||||
if (aData.dataType === "json") {
|
||||
xhr.responseType = "json";
|
||||
} else if (aData.dataType === "binary") {
|
||||
xhr.responseType = "blob";
|
||||
}
|
||||
|
||||
xhr.addEventListener("load", function() {
|
||||
debug("Success loading " + href);
|
||||
if (xhr.status >= 200 && xhr.status < 400) {
|
||||
aMm.sendAsyncMessage("Webapps:GetLocalizationResource:Return",
|
||||
{ requestID: aData.requestID, oid: aData.oid, data: xhr.response });
|
||||
} else {
|
||||
sendError("Error loading " + href, "UnavailableResource");
|
||||
}
|
||||
});
|
||||
xhr.addEventListener("error", function() {
|
||||
sendError("Error loading " + href, "UnavailableResource");
|
||||
});
|
||||
xhr.send(null);
|
||||
},
|
||||
|
||||
// Validates the langpack part of a manifest.
|
||||
checkManifest: function(aManifest) {
|
||||
if (!("languages-target" in aManifest)) {
|
||||
debug("Error: no 'languages-target' property.")
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!("languages-provided" in aManifest)) {
|
||||
debug("Error: no 'languages-provided' property.")
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let lang in aManifest["languages-provided"]) {
|
||||
let item = aManifest["languages-provided"][lang];
|
||||
|
||||
if (!item.version) {
|
||||
debug("Error: missing 'version' in languages-provided." + lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof item.version !== "number") {
|
||||
debug("Error: languages-provided." + lang +
|
||||
".version must be a number but is a " + (typeof item.version));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!item.apps) {
|
||||
debug("Error: missing 'apps' in languages-provided." + lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let app in item.apps) {
|
||||
// Keys should be manifest urls, ie. absolute urls.
|
||||
if (!isAbsoluteURI(app)) {
|
||||
debug("Error: languages-provided." + lang + "." + app +
|
||||
" must be an absolute manifest url.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof item.apps[app] !== "string") {
|
||||
debug("Error: languages-provided." + lang + ".apps." + app +
|
||||
" value must be a string but is " + (typeof item.apps[app]) +
|
||||
" : " + item.apps[app]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// Check if this app is a langpack and update registration if needed.
|
||||
register: function(aApp, aManifest) {
|
||||
debug("register app " + aApp.manifestURL + " role=" + aApp.role);
|
||||
|
||||
if (aApp.role !== "langpack") {
|
||||
debug("Not a langpack.");
|
||||
// Not a langpack, but that's fine.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.checkManifest(aManifest)) {
|
||||
debug("Invalid langpack manifest.");
|
||||
return;
|
||||
}
|
||||
|
||||
let platformVersion = aManifest["languages-target"]
|
||||
["app://*.gaiamobile.org/manifest.webapp"];
|
||||
let origin = Services.io.newURI(aApp.origin, null, null);
|
||||
|
||||
for (let lang in aManifest["languages-provided"]) {
|
||||
let item = aManifest["languages-provided"][lang];
|
||||
let version = item.version; // The langpack version, not the platform.
|
||||
let name = item.name || lang; // If no name specified, default to lang.
|
||||
for (let app in item.apps) {
|
||||
let sendEvent = false;
|
||||
if (!this._data[app] ||
|
||||
!this._data[app].langs[lang] ||
|
||||
this._data[app].langs[lang].version > version) {
|
||||
if (!this._data[app]) {
|
||||
this._data[app] = {
|
||||
appId: this._appIdFromManifestURL(app),
|
||||
langs: {}
|
||||
};
|
||||
}
|
||||
this._data[app].langs[lang] = {
|
||||
version: version,
|
||||
target: platformVersion,
|
||||
name: name,
|
||||
url: origin.resolve(item.apps[app]),
|
||||
from: aApp.manifestURL
|
||||
}
|
||||
sendEvent = true;
|
||||
debug("Registered " + app + " -> " + uneval(this._data[app].langs[lang]));
|
||||
}
|
||||
|
||||
// Fire additionallanguageschange event.
|
||||
// This will only be dispatched to documents using the langpack api.
|
||||
if (sendEvent) {
|
||||
this.sendAppUpdate(app);
|
||||
ppmm.broadcastAsyncMessage(
|
||||
"Webapps:AdditionalLanguageChange",
|
||||
{ manifestURL: app,
|
||||
languages: this.getAdditionalLanguages(app).langs });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Check if this app is a langpack and update registration by removing all
|
||||
// the entries from this app.
|
||||
unregister: function(aApp, aManifest) {
|
||||
debug("unregister app " + aApp.manifestURL + " role=" + aApp.role);
|
||||
|
||||
if (aApp.role !== "langpack") {
|
||||
debug("Not a langpack.");
|
||||
// Not a langpack, but that's fine.
|
||||
return;
|
||||
}
|
||||
|
||||
for (let app in this._data) {
|
||||
let sendEvent = false;
|
||||
for (let lang in this._data[app].langs) {
|
||||
if (this._data[app].langs[lang].from == aApp.manifestURL) {
|
||||
sendEvent = true;
|
||||
delete this._data[app].langs[lang];
|
||||
}
|
||||
}
|
||||
// Fire additionallanguageschange event.
|
||||
// This will only be dispatched to documents using the langpack api.
|
||||
if (sendEvent) {
|
||||
this.sendAppUpdate(app);
|
||||
ppmm.broadcastAsyncMessage(
|
||||
"Webapps:AdditionalLanguageChange",
|
||||
{ manifestURL: app,
|
||||
languages: this.getAdditionalLanguages(app).langs });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Langpacks.init();
|
|
@ -44,11 +44,18 @@ WebappsRegistry.prototype = {
|
|||
|
||||
receiveMessage: function(aMessage) {
|
||||
let msg = aMessage.json;
|
||||
if (msg.oid != this._id)
|
||||
return
|
||||
let req = this.getRequest(msg.requestID);
|
||||
if (!req)
|
||||
return;
|
||||
let req;
|
||||
if (msg.oid === this._id) {
|
||||
if (aMessage.name == "Webapps:GetLocalizationResource:Return") {
|
||||
req = this.takePromiseResolver(msg.requestID);
|
||||
} else {
|
||||
req = this.getRequest(msg.requestID);
|
||||
}
|
||||
if (!req) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let app = msg.app;
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Install:Return:OK":
|
||||
|
@ -78,6 +85,26 @@ WebappsRegistry.prototype = {
|
|||
this.removeMessageListeners(aMessage.name);
|
||||
Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
|
||||
break;
|
||||
case "Webapps:AdditionalLanguageChange":
|
||||
// Check if the current page is from the app receiving the event.
|
||||
let manifestURL = AppsUtils.getAppManifestURLFromWindow(this._window);
|
||||
if (manifestURL && manifestURL == msg.manifestURL) {
|
||||
// Let's dispatch an "additionallanguageschange" event on the document.
|
||||
let doc = this._window.document;
|
||||
let event = doc.createEvent("CustomEvent");
|
||||
event.initCustomEvent("additionallanguageschange", true, true,
|
||||
Cu.cloneInto(msg.languages, this._window));
|
||||
doc.dispatchEvent(event);
|
||||
}
|
||||
break;
|
||||
case "Webapps:GetLocalizationResource:Return":
|
||||
this.removeMessageListeners(["Webapps:GetLocalizationResource:Return"]);
|
||||
if (msg.error) {
|
||||
req.reject(new this._window.DOMError(msg.error));
|
||||
} else {
|
||||
req.resolve(Cu.cloneInto(msg.data, this._window));
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.removeRequest(msg.requestID);
|
||||
},
|
||||
|
@ -231,7 +258,8 @@ WebappsRegistry.prototype = {
|
|||
uninit: function() {
|
||||
this._mgmt = null;
|
||||
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
|
||||
["Webapps:Install:Return:OK"]);
|
||||
["Webapps:Install:Return:OK",
|
||||
"Webapps:AdditionalLanguageChange"]);
|
||||
},
|
||||
|
||||
installPackage: function(aURL, aParams) {
|
||||
|
@ -248,19 +276,69 @@ WebappsRegistry.prototype = {
|
|||
return request;
|
||||
},
|
||||
|
||||
_getCurrentAppManifestURL: function() {
|
||||
let appId = this._window.document.nodePrincipal.appId;
|
||||
if (appId === Ci.nsIScriptSecurityManager.NO_APP_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return appsService.getManifestURLByLocalId(appId);
|
||||
},
|
||||
|
||||
getAdditionalLanguages: function() {
|
||||
let manifestURL = AppsUtils.getAppManifestURLFromWindow(this._window);
|
||||
|
||||
return new this._window.Promise((aResolve, aReject) => {
|
||||
if (!manifestURL) {
|
||||
aReject("NotInApp");
|
||||
} else {
|
||||
let langs = DOMApplicationRegistry.getAdditionalLanguages(manifestURL);
|
||||
aResolve(Cu.cloneInto(langs, this._window));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getLocalizationResource: function(aLanguage, aVersion, aPath, aType) {
|
||||
let manifestURL = AppsUtils.getAppManifestURLFromWindow(this._window);
|
||||
|
||||
if (!manifestURL) {
|
||||
return new Promise((aResolve, aReject) => {
|
||||
aReject("NotInApp");
|
||||
});
|
||||
}
|
||||
|
||||
this.addMessageListeners(["Webapps:GetLocalizationResource:Return"]);
|
||||
return this.createPromise((aResolve, aReject) => {
|
||||
cpmm.sendAsyncMessage("Webapps:GetLocalizationResource", {
|
||||
manifestURL: manifestURL,
|
||||
lang: aLanguage,
|
||||
version: aVersion,
|
||||
path: aPath,
|
||||
dataType: aType,
|
||||
oid: this._id,
|
||||
requestID: this.getPromiseResolverId({
|
||||
resolve: aResolve,
|
||||
reject: aReject
|
||||
})
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// nsIDOMGlobalPropertyInitializer implementation
|
||||
init: function(aWindow) {
|
||||
const prefs = new Preferences();
|
||||
|
||||
this._window = aWindow;
|
||||
|
||||
this.initDOMRequestHelper(aWindow, "Webapps:Install:Return:OK");
|
||||
this.initDOMRequestHelper(aWindow, ["Webapps:Install:Return:OK",
|
||||
"Webapps:AdditionalLanguageChange"]);
|
||||
|
||||
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
this._id = util.outerWindowID;
|
||||
cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
|
||||
{ messages: ["Webapps:Install:Return:OK"]});
|
||||
{ messages: ["Webapps:Install:Return:OK",
|
||||
"Webapps:AdditionalLanguageChange"]});
|
||||
|
||||
let principal = aWindow.document.nodePrincipal;
|
||||
let appId = principal.appId;
|
||||
|
|
|
@ -81,6 +81,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "ScriptPreloader",
|
||||
"resource://gre/modules/ScriptPreloader.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Langpacks",
|
||||
"resource://gre/modules/Langpacks.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TrustedHostedAppsUtils",
|
||||
"resource://gre/modules/TrustedHostedAppsUtils.jsm");
|
||||
|
||||
|
@ -243,6 +246,9 @@ this.DOMApplicationRegistry = {
|
|||
["webapps", "webapps.json"], true).path;
|
||||
|
||||
this.loadAndUpdateApps();
|
||||
|
||||
Langpacks.registerRegistryFunctions(this.broadcastMessage.bind(this),
|
||||
this._appIdForManifestURL.bind(this));
|
||||
},
|
||||
|
||||
// loads the current registry, that could be empty on first run.
|
||||
|
@ -424,6 +430,7 @@ this.DOMApplicationRegistry = {
|
|||
}
|
||||
app.kind = this.appKind(app, aResult.manifest);
|
||||
UserCustomizations.register(aResult.manifest, app);
|
||||
Langpacks.register(app, aResult.manifest);
|
||||
});
|
||||
|
||||
// Nothing else to do but notifying we're ready.
|
||||
|
@ -1149,6 +1156,7 @@ this.DOMApplicationRegistry = {
|
|||
this._registerInterAppConnections(manifest, app);
|
||||
appsToRegister.push({ manifest: manifest, app: app });
|
||||
UserCustomizations.register(manifest, app);
|
||||
Langpacks.register(app, manifest);
|
||||
});
|
||||
this._safeToClone.resolve();
|
||||
this._registerActivitiesForApps(appsToRegister, aRunUpdate);
|
||||
|
@ -1520,6 +1528,8 @@ this.DOMApplicationRegistry = {
|
|||
this.safeToClone.then( () => {
|
||||
for (let id in this.webapps) {
|
||||
tmp.push({ id: id });
|
||||
this.webapps[id].additionalLanguages =
|
||||
Langpacks.getAdditionalLanguages(this.webapps[id].manifestURL).langs;
|
||||
}
|
||||
this._readManifests(tmp).then(
|
||||
function(manifests) {
|
||||
|
@ -1964,6 +1974,10 @@ this.DOMApplicationRegistry = {
|
|||
|
||||
// Update the asm.js scripts we need to compile.
|
||||
yield ScriptPreloader.preload(app, newManifest);
|
||||
|
||||
// Update langpack information.
|
||||
Langpacks.register(app, newManifest);
|
||||
|
||||
yield this._saveApps();
|
||||
// Update the handlers and permissions for this app.
|
||||
this.updateAppHandlers(oldManifest, newManifest, app);
|
||||
|
@ -2081,11 +2095,13 @@ this.DOMApplicationRegistry = {
|
|||
this.notifyAppsRegistryReady();
|
||||
}
|
||||
|
||||
// Update user customizations.
|
||||
// Update user customizations and langpacks.
|
||||
if (aOldManifest) {
|
||||
UserCustomizations.unregister(aOldManifest, aApp);
|
||||
Langpacks.unregister(aApp, aOldManifest);
|
||||
}
|
||||
UserCustomizations.register(aNewManifest, aApp);
|
||||
Langpacks.register(aApp, aNewManifest);
|
||||
},
|
||||
|
||||
checkForUpdate: function(aData, aMm) {
|
||||
|
@ -3185,6 +3201,9 @@ this.DOMApplicationRegistry = {
|
|||
// Check if we have asm.js code to preload for this application.
|
||||
yield ScriptPreloader.preload(aNewApp, aManifest);
|
||||
|
||||
// Update langpack information.
|
||||
yield Langpacks.register(aNewApp, aManifest);
|
||||
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: ["downloadsuccess", "downloadapplied"],
|
||||
manifestURL: aNewApp.manifestURL
|
||||
|
@ -4094,6 +4113,7 @@ this.DOMApplicationRegistry = {
|
|||
this._unregisterActivities(aApp.manifest, aApp);
|
||||
}
|
||||
UserCustomizations.unregister(aApp.manifest, aApp);
|
||||
Langpacks.unregister(aApp, aApp.manifest);
|
||||
|
||||
let dir = this._getAppDir(id);
|
||||
try {
|
||||
|
|
|
@ -34,6 +34,7 @@ EXTRA_JS_MODULES += [
|
|||
'AppsServiceChild.jsm',
|
||||
'FreeSpaceWatcher.jsm',
|
||||
'InterAppCommService.jsm',
|
||||
'Langpacks.jsm',
|
||||
'OfflineCacheInstaller.jsm',
|
||||
'PermissionsInstaller.jsm',
|
||||
'PermissionsTable.jsm',
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Langpack Test : event</title>
|
||||
<script>
|
||||
var baseURL = "http://mochi.test:8888/tests/dom/apps/tests/langpack/";
|
||||
var eventCount = 0;
|
||||
|
||||
function languageChanged(evt) {
|
||||
eventCount++;
|
||||
alert(JSON.stringify(evt.detail));
|
||||
if (eventCount == 1) {
|
||||
var req = navigator.mozApps.install(baseURL + "lang2.webapp");
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the event handler, and install an app.
|
||||
function run() {
|
||||
document.addEventListener("additionallanguageschange", languageChanged);
|
||||
|
||||
navigator.mozApps.install(baseURL + "lang1.webapp");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="run()">
|
||||
<h1>Langpack Test : event</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
{ "hello" : "Bonjour" }
|
|
@ -0,0 +1 @@
|
|||
hello=Bonjour
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Langpack Test : getAdditionalLanguages()</title>
|
||||
<script>
|
||||
function run() {
|
||||
navigator.mozApps.getAdditionalLanguages().then(languages => {
|
||||
alert(JSON.stringify(languages));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="run()">
|
||||
<h1>Langpack Test : getAdditionalLanguages()</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "French locale",
|
||||
"languages-target" : { "app://*.gaiamobile.org/manifest.webapp": "2.2" },
|
||||
"languages-provided": {
|
||||
"fr": {
|
||||
"version": 201411051234,
|
||||
"name": "Français",
|
||||
"apps": {
|
||||
"http://mochi.test:8888/tests/dom/apps/tests/langpack/manifest.webapp": "tests/dom/apps/tests/langpack/fr/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"role" : "langpack"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Content-Type: application/manifest+json
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "German an Polish locales",
|
||||
"languages-target" : { "app://*.gaiamobile.org/manifest.webapp": "2.2" },
|
||||
"languages-provided": {
|
||||
"de": {
|
||||
"version": 201411051234,
|
||||
"name": "Deutsch",
|
||||
"apps": {
|
||||
"http://mochi.test:8888/tests/dom/apps/tests/langpack/manifest.webapp": "tests/dom/apps/tests/langpack/de/"
|
||||
}
|
||||
},
|
||||
"pl": {
|
||||
"version": 201411051234,
|
||||
"name": "Polski",
|
||||
"apps": {
|
||||
"http://mochi.test:8888/tests/dom/apps/tests/langpack/manifest.webapp": "tests/dom/apps/tests/langpack/pl/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"role" : "langpack"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Content-Type: application/manifest+json
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "Localization test app"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Content-Type: application/manifest+json
|
|
@ -0,0 +1,92 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Langpack Test : resources</title>
|
||||
<script>
|
||||
function success(data) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof data === "object") {
|
||||
// Read what's inside the blob.
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
alert(e.target.result);
|
||||
resolve();
|
||||
};
|
||||
reader.readAsText(data);
|
||||
} else {
|
||||
alert(data);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function successJSON(data) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
alert(JSON.stringify(data));
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function error(domError) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
alert(domError.name);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// Error: Bad resource.
|
||||
function test1() {
|
||||
return navigator.mozApps.getLocalizationResource("fr", "2.2", "./foo.html", "binary")
|
||||
.then(success, error);
|
||||
}
|
||||
|
||||
// Error: Unknown locale.
|
||||
function test2() {
|
||||
return navigator.mozApps.getLocalizationResource("es", "2.2", "./foo.html", "binary")
|
||||
.then(success, error);
|
||||
}
|
||||
|
||||
// Error: Bad version.
|
||||
function test3() {
|
||||
return navigator.mozApps.getLocalizationResource("fr", "2.0", "./foo.html", "binary")
|
||||
.then(success, error);
|
||||
}
|
||||
|
||||
// Error: Absolute url.
|
||||
function test4() {
|
||||
return navigator.mozApps.getLocalizationResource("fr", "2.2", "http://example.com/foo.html", "binary")
|
||||
.then(success, error);
|
||||
}
|
||||
|
||||
// Ok, binary data.
|
||||
function test5() {
|
||||
return navigator.mozApps.getLocalizationResource("fr", "2.2", "./app.properties", "binary")
|
||||
.then(success, error);
|
||||
}
|
||||
|
||||
// Ok, text data.
|
||||
function test6() {
|
||||
return navigator.mozApps.getLocalizationResource("fr", "2.2", "./app.properties", "text")
|
||||
.then(success, error);
|
||||
}
|
||||
|
||||
// Ok, json data.
|
||||
function test7() {
|
||||
return navigator.mozApps.getLocalizationResource("fr", "2.2", "./app.json", "json")
|
||||
.then(successJSON, error);
|
||||
}
|
||||
|
||||
function run() {
|
||||
test1().then(test2)
|
||||
.then(test3)
|
||||
.then(test4)
|
||||
.then(test5)
|
||||
.then(test6)
|
||||
.then(test7);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="run()">
|
||||
<h1>Langpack Test : resources</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -22,6 +22,7 @@ support-files =
|
|||
file_widget_app.template.webapp
|
||||
file_widget_app.template.html
|
||||
file_test_widget.js
|
||||
langpack/*
|
||||
signed_app.sjs
|
||||
signed_app_template.webapp
|
||||
signed/*
|
||||
|
@ -38,6 +39,7 @@ skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in moch
|
|||
[test_import_export.html]
|
||||
[test_install_multiple_apps_origin.html]
|
||||
[test_install_receipts.html]
|
||||
[test_langpacks.html]
|
||||
[test_marketplace_pkg_install.html]
|
||||
skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
|
||||
[test_packaged_app_install.html]
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
<!DOCTYPE HTML><!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1108096
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1108096 - Langpack support</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="application/javascript;version=1.7">
|
||||
/**
|
||||
* Test for Bug 1108096
|
||||
* This file covers testing langpacks.
|
||||
*
|
||||
* The setup is as follows:
|
||||
* - app is the localizable application.
|
||||
* - langpack1 provides the French locale.
|
||||
* - langpack2 provides the German and Polish locales.
|
||||
*/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const uriPrefix = "http://mochi.test:8888/tests/dom/apps/tests/langpack/";
|
||||
|
||||
let appManifestURL = uriPrefix + "manifest.webapp";
|
||||
let lang1ManifestURL = uriPrefix + "lang1.webapp";
|
||||
let lang2ManifestURL = uriPrefix + "lang2.webapp";
|
||||
|
||||
let gGenerator = runTest();
|
||||
|
||||
function go() {
|
||||
gGenerator.next();
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
try {
|
||||
gGenerator.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function mozAppsError() {
|
||||
ok(false, "mozApps error: " + this.error.name);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Triggers one navigation test to the given page.
|
||||
// Waits for alert() messages before tearing down the iframe.
|
||||
function openPage(pageURL, messages) {
|
||||
info("Navigating to " + pageURL);
|
||||
let ifr = document.createElement("iframe");
|
||||
let listener = function(event) {
|
||||
let message = messages.shift();
|
||||
is(event.detail.message, message, "Checking alert message for " + pageURL);
|
||||
if (messages.length == 0) {
|
||||
ifr.removeEventListener("mozbrowsershowmodalprompt", listener);
|
||||
ifr.parentNode.removeChild(ifr);
|
||||
continueTest();
|
||||
}
|
||||
}
|
||||
|
||||
ifr.addEventListener("mozbrowsershowmodalprompt", listener, false);
|
||||
|
||||
// Open the app url in an iframe.
|
||||
ifr.setAttribute("mozapp", appManifestURL);
|
||||
ifr.setAttribute("mozbrowser", "true");
|
||||
ifr.setAttribute("src", uriPrefix + pageURL);
|
||||
document.getElementById("container").appendChild(ifr);
|
||||
}
|
||||
|
||||
let apps = [];
|
||||
|
||||
function installApp(manifestURL) {
|
||||
info("About to install app at " + manifestURL);
|
||||
let req = navigator.mozApps.install(manifestURL);
|
||||
req.onsuccess = function() {
|
||||
is(req.result.manifestURL, manifestURL, "app installed");
|
||||
if (req.result.installState == "installed") {
|
||||
is(req.result.installState, "installed", "app downloaded");
|
||||
continueTest();
|
||||
} else {
|
||||
req.result.ondownloadapplied = function() {
|
||||
is(req.result.installState, "installed", "app downloaded");
|
||||
continueTest();
|
||||
}
|
||||
}
|
||||
}
|
||||
req.onerror = mozAppsError;
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// Set up.
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
["dom.mozBrowserFramesEnabled", true],
|
||||
["dom.apps.allow_unsigned_langpacks", true] ]},continueTest);
|
||||
yield undefined;
|
||||
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "webapps-manage", "allow": 1, "context": document },
|
||||
{ "type": "embed-apps", "allow": 1, "context": document },
|
||||
{ "type": "browser", "allow": 1, "context": document } ],
|
||||
continueTest);
|
||||
yield undefined;
|
||||
|
||||
navigator.mozApps.mgmt.oninstall = function(evt) {
|
||||
apps.push(evt.application);
|
||||
};
|
||||
|
||||
let _ = JSON.stringify;
|
||||
|
||||
SpecialPowers.autoConfirmAppInstall(continueTest);
|
||||
yield undefined;
|
||||
|
||||
SpecialPowers.autoConfirmAppUninstall(continueTest);
|
||||
yield undefined;
|
||||
|
||||
// Install test app.
|
||||
installApp(appManifestURL);
|
||||
yield undefined;
|
||||
|
||||
// Opens the iframe to the test page, initial state.
|
||||
// No locale is available.
|
||||
openPage("index.html", [_({})]);
|
||||
yield undefined;
|
||||
|
||||
// Install the fr langpack.
|
||||
installApp(lang1ManifestURL);
|
||||
yield undefined;
|
||||
|
||||
// Opens the iframe to the test page.
|
||||
// Only the French locale is available.
|
||||
openPage("index.html",
|
||||
[_({"fr":[{"version":201411051234,"name":"Français","target":"2.2"}]})]);
|
||||
yield undefined;
|
||||
|
||||
// Install the de and pl langpack.
|
||||
installApp(lang2ManifestURL);
|
||||
yield undefined;
|
||||
|
||||
// Opens the iframe to the test page.
|
||||
// French, German and Polish locales are available.
|
||||
openPage("index.html",
|
||||
[_({"fr":[{"version":201411051234,"name":"Français","target":"2.2"}],"de":[{"version":201411051234,"name":"Deutsch","target":"2.2"}],"pl":[{"version":201411051234,"name":"Polski","target":"2.2"}]})]);
|
||||
yield undefined;
|
||||
|
||||
// Uninstall the second langpack.
|
||||
{
|
||||
let app = apps.pop();
|
||||
info("Uninstalling " + app.manifestURL);
|
||||
req = navigator.mozApps.mgmt.uninstall(app);
|
||||
req.onsuccess = continueTest;
|
||||
req.onerror = mozAppsError;
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
// Opens the iframe to the test page.
|
||||
// Only the French locale is available.
|
||||
openPage("index.html",
|
||||
[_({"fr":[{"version":201411051234,"name":"Français","target":"2.2"}]})]);
|
||||
yield undefined;
|
||||
|
||||
// Uninstall the first langpack.
|
||||
{
|
||||
let app = apps.pop();
|
||||
info("Uninstalling " + app.manifestURL);
|
||||
req = navigator.mozApps.mgmt.uninstall(app);
|
||||
req.onsuccess = continueTest;
|
||||
req.onerror = mozAppsError;
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
// Opens the iframe to the test page, initial state.
|
||||
// No locale is available.
|
||||
openPage("index.html",
|
||||
["{}"]);
|
||||
yield undefined;
|
||||
|
||||
// Opens the iframe to the event test page.
|
||||
// Will get additionallanguageschange events.
|
||||
openPage("event.html",
|
||||
[_({"fr":[{"version":201411051234,"name":"Français","target":"2.2"}]}),
|
||||
_({"fr":[{"version":201411051234,"name":"Français","target":"2.2"}],"de":[{"version":201411051234,"name":"Deutsch","target":"2.2"}]}),
|
||||
_({"fr":[{"version":201411051234,"name":"Français","target":"2.2"}],"de":[{"version":201411051234,"name":"Deutsch","target":"2.2"}],"pl":[{"version":201411051234,"name":"Polski","target":"2.2"}]})]);
|
||||
yield undefined;
|
||||
|
||||
// Opens the iframe to the resource test page.
|
||||
openPage("resources.html",
|
||||
["UnavailableResource",
|
||||
"UnavailableLanguage",
|
||||
"UnavailableVersion",
|
||||
"BadUrl",
|
||||
"hello=Bonjour",
|
||||
"hello=Bonjour",
|
||||
_({"hello":"Bonjour"})]);
|
||||
yield undefined;
|
||||
|
||||
// Clean up after ourselves by uninstalling apps.
|
||||
info(apps.length + " applications to uninstall.");
|
||||
while (apps.length) {
|
||||
let app = apps.pop();
|
||||
req = navigator.mozApps.mgmt.uninstall(app);
|
||||
req.onsuccess = continueTest;
|
||||
req.onerror = mozAppsError;
|
||||
yield undefined;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -20,7 +20,9 @@
|
|||
|
||||
var props = {
|
||||
checkInstalled: "function",
|
||||
getAdditionalLanguages: "function",
|
||||
getInstalled: "function",
|
||||
getLocalizationResource: "function",
|
||||
getSelf: "function",
|
||||
install: "function",
|
||||
installPackage: "function",
|
||||
|
|
|
@ -9,6 +9,18 @@ dictionary InstallParameters {
|
|||
sequence<DOMString> categories = [];
|
||||
};
|
||||
|
||||
dictionary LanguageDesc {
|
||||
DOMString target;
|
||||
DOMString version;
|
||||
DOMString name;
|
||||
};
|
||||
|
||||
enum LocaleResourceType {
|
||||
"binary",
|
||||
"json",
|
||||
"text"
|
||||
};
|
||||
|
||||
[NoInterfaceObject, NavigatorProperty="mozApps",
|
||||
JSImplementation="@mozilla.org/webapps;1"]
|
||||
interface DOMApplicationsRegistry {
|
||||
|
@ -19,6 +31,17 @@ interface DOMApplicationsRegistry {
|
|||
DOMRequest getSelf();
|
||||
DOMRequest getInstalled();
|
||||
DOMRequest checkInstalled(DOMString manifestUrl);
|
||||
|
||||
// Language pack API.
|
||||
// These promises will be rejected if the page is not in an app context,
|
||||
// i.e. they are implicitely acting on getSelf().
|
||||
Promise<MozMap<sequence<LanguageDesc>>> getAdditionalLanguages();
|
||||
// Resolves to a different object depending on the dataType value.
|
||||
Promise<any>
|
||||
getLocalizationResource(DOMString language,
|
||||
DOMString version,
|
||||
DOMString path,
|
||||
LocaleResourceType dataType);
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/webapps/application;1", ChromeOnly]
|
||||
|
@ -71,18 +94,18 @@ interface DOMApplication : EventTarget {
|
|||
* https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
|
||||
*
|
||||
*/
|
||||
Promise<MozInterAppConnection> connect(DOMString keyword, optional any rules);
|
||||
Promise<MozInterAppConnection> connect(DOMString keyword, optional any rules);
|
||||
|
||||
Promise<sequence<MozInterAppMessagePort>> getConnections();
|
||||
Promise<sequence<MozInterAppMessagePort>> getConnections();
|
||||
|
||||
// Receipts handling functions.
|
||||
DOMRequest addReceipt(optional DOMString receipt);
|
||||
DOMRequest removeReceipt(optional DOMString receipt);
|
||||
DOMRequest replaceReceipt(optional DOMString oldReceipt,
|
||||
optional DOMString newReceipt);
|
||||
// Receipts handling functions.
|
||||
DOMRequest addReceipt(optional DOMString receipt);
|
||||
DOMRequest removeReceipt(optional DOMString receipt);
|
||||
DOMRequest replaceReceipt(optional DOMString oldReceipt,
|
||||
optional DOMString newReceipt);
|
||||
|
||||
// Export this app as a shareable Blob.
|
||||
Promise<Blob> export();
|
||||
// Export this app as a shareable Blob.
|
||||
Promise<Blob> export();
|
||||
};
|
||||
|
||||
[JSImplementation="@mozilla.org/webapps/manager;1",
|
||||
|
|
Загрузка…
Ссылка в новой задаче