Backed out changeset df960e299bcd (bug 1108096) for m1 test failures
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 12 Jan 2015 09:02:13 +0100
changeset 223282 4583ade26b7e1af024bccacfaa3bccb9a9051e1d
parent 223281 adfe0cd4c6318630b6d4381ad248054bd836fefa
child 223283 f3f06c6800fc73f0cd6bf26195529b61b69378f7
push id10769
push usercbook@mozilla.com
push dateMon, 12 Jan 2015 14:15:52 +0000
treeherderfx-team@0e9765732906 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1108096
milestone37.0a1
backs outdf960e299bcd2bf93e95368623b5973056143f7e
Backed out changeset df960e299bcd (bug 1108096) for m1 test failures
dom/apps/AppsServiceChild.jsm
dom/apps/AppsUtils.jsm
dom/apps/Langpacks.jsm
dom/apps/Webapps.js
dom/apps/Webapps.jsm
dom/apps/moz.build
dom/apps/tests/langpack/event.html
dom/apps/tests/langpack/fr/app.json
dom/apps/tests/langpack/fr/app.properties
dom/apps/tests/langpack/index.html
dom/apps/tests/langpack/lang1.webapp
dom/apps/tests/langpack/lang1.webapp^headers^
dom/apps/tests/langpack/lang2.webapp
dom/apps/tests/langpack/lang2.webapp^headers^
dom/apps/tests/langpack/manifest.webapp
dom/apps/tests/langpack/manifest.webapp^headers^
dom/apps/tests/langpack/resources.html
dom/apps/tests/mochitest.ini
dom/apps/tests/test_langpacks.html
dom/tests/mochitest/webapps/test_list_api.xul
dom/webidl/Apps.webidl
--- a/dom/apps/AppsServiceChild.jsm
+++ b/dom/apps/AppsServiceChild.jsm
@@ -349,25 +349,16 @@ this.DOMApplicationRegistry = {
 
     let res = [];
     for (let id in this.webapps) {
       res.push(this.webapps[id]);
     }
     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
    */
   getAppByManifestURL: function getAppByManifestURL(aManifestURL) {
     debug("getAppByManifestURL " + aManifestURL);
     return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
   },
 
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -17,20 +17,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
   "resource://gre/modules/WebappOSUtils.jsm");
 
 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
 
 this.EXPORTED_SYMBOLS =
   ["AppsUtils", "ManifestHelper", "isAbsoluteURI", "mozIApplication"];
 
 function debug(s) {
   //dump("-*- AppsUtils.jsm: " + s + "\n");
@@ -484,31 +480,23 @@ this.AppsUtils = {
   },
 
   allowUnsignedAddons: false, // for testing purposes.
 
   /**
    * 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.
    */
   checkAppRole: function(aRole, aStatus) {
     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 &&
          aStatus !== Ci.nsIPrincipal.APP_STATUS_PRIVILEGED)) {
       return false;
     }
     return true;
   },
@@ -739,26 +727,17 @@ this.AppsUtils = {
 
     // Convert the binary hash data to a hex string.
     return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
   },
 
   // 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);
-  },
+  }
 }
 
 /**
  * Helper object to access manifest information with locale support
  */
 this.ManifestHelper = function(aManifest, aOrigin, aManifestURL) {
   // If the app is packaged, we resolve uris against the origin.
   // If it's not, against the manifest url.
deleted file mode 100644
--- a/dom/apps/Langpacks.jsm
+++ /dev/null
@@ -1,315 +0,0 @@
-/* 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();
\ No newline at end of file
--- a/dom/apps/Webapps.js
+++ b/dom/apps/Webapps.js
@@ -39,28 +39,21 @@ function convertAppsArray(aApps, aWindow
 function WebappsRegistry() {
 }
 
 WebappsRegistry.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
-    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;
-      }
-    }
-
+    if (msg.oid != this._id)
+      return
+    let req = this.getRequest(msg.requestID);
+    if (!req)
+      return;
     let app = msg.app;
     switch (aMessage.name) {
       case "Webapps:Install:Return:OK":
         this.removeMessageListeners("Webapps:Install:Return:KO");
         Services.DOMRequest.fireSuccess(req, createContentApplicationObject(this._window, app));
         cpmm.sendAsyncMessage("Webapps:Install:Return:Ack",
                               { manifestURL : app.manifestURL });
         break;
@@ -80,36 +73,16 @@ WebappsRegistry.prototype = {
       case "Webapps:CheckInstalled:Return:OK":
         this.removeMessageListeners(aMessage.name);
         Services.DOMRequest.fireSuccess(req, msg.app);
         break;
       case "Webapps:GetInstalled:Return:OK":
         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);
   },
 
   _getOrigin: function(aURL) {
     let uri = Services.io.newURI(aURL, null, null);
     return uri.prePath;
   },
@@ -253,97 +226,46 @@ WebappsRegistry.prototype = {
         : this._window.DOMApplicationsManager._create(this._window, mgmt.wrappedJSObject);
     }
     return this._mgmt;
   },
 
   uninit: function() {
     this._mgmt = null;
     cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
-                          ["Webapps:Install:Return:OK",
-                           "Webapps:AdditionalLanguageChange"]);
+                          ["Webapps:Install:Return:OK"]);
   },
 
   installPackage: function(aURL, aParams) {
     let request = this.createRequest();
 
     let uri = this._validateURL(aURL, request);
 
     if (uri && this._ensureForeground(request)) {
       this.addMessageListeners("Webapps:Install:Return:KO");
       cpmm.sendAsyncMessage("Webapps:InstallPackage",
                             this._prepareInstall(uri, request, aParams, true));
     }
 
     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",
-                                        "Webapps:AdditionalLanguageChange"]);
+    this.initDOMRequestHelper(aWindow, "Webapps:Install:Return:OK");
 
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
     this._id = util.outerWindowID;
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
-                          { messages: ["Webapps:Install:Return:OK",
-                                       "Webapps:AdditionalLanguageChange"]});
+                          { messages: ["Webapps:Install:Return:OK"]});
 
     let principal = aWindow.document.nodePrincipal;
     let appId = principal.appId;
     let app = appId && appsService.getAppByLocalId(appId);
 
     let isCurrentHomescreen = app &&
       app.manifestURL == prefs.get("dom.mozApps.homescreenURL") &&
       app.appStatus != Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED;
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -76,19 +76,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/WebappOSUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
 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");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ImportExport",
                                   "resource://gre/modules/ImportExport.jsm");
 
 #ifdef MOZ_WIDGET_GONK
 XPCOMUtils.defineLazyGetter(this, "libcutils", function() {
@@ -241,19 +238,16 @@ this.DOMApplicationRegistry = {
     Services.obs.addObserver(this, "memory-pressure", false);
 
     AppDownloadManager.registerCancelFunction(this.cancelDownload.bind(this));
 
     this.appsFile = FileUtils.getFile(DIRECTORY_NAME,
                                       ["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.
   loadCurrentRegistry: function() {
     return AppsUtils.loadJSONAsync(this.appsFile).then((aData) => {
       if (!aData) {
         return;
       }
@@ -425,17 +419,16 @@ this.DOMApplicationRegistry = {
         let localeManifest = new ManifestHelper(aResult.manifest, app.origin, app.manifestURL);
         this._saveWidgetsFullPath(localeManifest, app);
 
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(aResult.redirects);
         }
         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.
       this.notifyAppsRegistryReady();
     }
   }),
 
   updateDataStoreForApp: Task.async(function*(aId) {
@@ -1151,17 +1144,16 @@ this.DOMApplicationRegistry = {
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(manifest.redirects);
         }
         app.kind = this.appKind(app, aResult.manifest);
         this._registerSystemMessages(manifest, app);
         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);
     });
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic == "xpcom-shutdown") {
@@ -1523,18 +1515,16 @@ this.DOMApplicationRegistry = {
 
     let res = {};
     let done = false;
 
     // We allow cloning the registry when the local processing has been done.
     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) {
           manifests.forEach((item) => {
             res[item.id] = item.manifest;
           });
           done = true;
         }
@@ -1969,20 +1959,16 @@ this.DOMApplicationRegistry = {
       }
       delete app.staged;
     }
 
     delete app.retryingDownload;
 
     // 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);
 
     let updateManifest = yield AppsUtils.loadJSONAsync(staged.path);
     let appObject = AppsUtils.cloneAppObject(app);
     appObject.updateManifest = updateManifest;
     this.notifyUpdateHandlers(appObject, newManifest, appFile.path);
@@ -2090,23 +2076,21 @@ this.DOMApplicationRegistry = {
       this._registerSystemMessages(aNewManifest, aApp);
       this._registerActivities(aNewManifest, aApp, true);
       this._registerInterAppConnections(aNewManifest, aApp);
     } else {
       // Nothing else to do but notifying we're ready.
       this.notifyAppsRegistryReady();
     }
 
-    // Update user customizations and langpacks.
+    // Update user customizations.
     if (aOldManifest) {
       UserCustomizations.unregister(aOldManifest, aApp);
-      Langpacks.unregister(aApp, aOldManifest);
     }
     UserCustomizations.register(aNewManifest, aApp);
-    Langpacks.register(aApp, aNewManifest);
   },
 
   checkForUpdate: function(aData, aMm) {
     debug("checkForUpdate for " + aData.manifestURL);
 
     function sendError(aError) {
       debug("checkForUpdate error " + aError);
       aData.error = aError;
@@ -3196,19 +3180,16 @@ this.DOMApplicationRegistry = {
       app: app,
       manifest: aManifest,
       manifestURL: aNewApp.manifestURL
     });
 
     // 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
     });
   }),
 
   _nextLocalId: function() {
     let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1;
@@ -4108,17 +4089,16 @@ this.DOMApplicationRegistry = {
 
     // Then notify observers.
     Services.obs.notifyObservers(null, "webapps-uninstall", JSON.stringify(aApp));
 
     if (supportSystemMessages()) {
       this._unregisterActivities(aApp.manifest, aApp);
     }
     UserCustomizations.unregister(aApp.manifest, aApp);
-    Langpacks.unregister(aApp, aApp.manifest);
 
     let dir = this._getAppDir(id);
     try {
       dir.remove(true);
     } catch (e) {}
 
     delete this.webapps[id];
 
--- a/dom/apps/moz.build
+++ b/dom/apps/moz.build
@@ -29,17 +29,16 @@ EXTRA_COMPONENTS += [
     'Webapps.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'AppDownloadManager.jsm',
     'AppsServiceChild.jsm',
     'FreeSpaceWatcher.jsm',
     'InterAppCommService.jsm',
-    'Langpacks.jsm',
     'OfflineCacheInstaller.jsm',
     'PermissionsInstaller.jsm',
     'PermissionsTable.jsm',
     'StoreTrustAnchor.jsm',
     'UserCustomizations.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
deleted file mode 100644
--- a/dom/apps/tests/langpack/event.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!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>
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/langpack/fr/app.json
+++ /dev/null
@@ -1,1 +0,0 @@
-{ "hello" : "Bonjour" }
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/langpack/fr/app.properties
+++ /dev/null
@@ -1,1 +0,0 @@
-hello=Bonjour
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/langpack/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!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>
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/langpack/lang1.webapp
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "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"
-}
deleted file mode 100644
--- a/dom/apps/tests/langpack/lang1.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/manifest+json
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/langpack/lang2.webapp
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "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"
-}
deleted file mode 100644
--- a/dom/apps/tests/langpack/lang2.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/manifest+json
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/langpack/manifest.webapp
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "name": "Localization test app"
-}
deleted file mode 100644
--- a/dom/apps/tests/langpack/manifest.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/manifest+json
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/langpack/resources.html
+++ /dev/null
@@ -1,92 +0,0 @@
-<!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>
\ No newline at end of file
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -17,34 +17,32 @@ support-files =
   file_trusted_app.template.webapp
   file_invalidWidget_app.template.webapp
   file_packaged_app.sjs
   file_packaged_app.template.html
   file_packaged_app.template.webapp
   file_widget_app.template.webapp
   file_widget_app.template.html
   file_test_widget.js
-  langpack/*
   signed_app.sjs
   signed_app_template.webapp
   signed/*
   test_packaged_app_common.js
   marketplace/*
   pkg_install_iframe.html
 
 [test_app_addons.html]
 skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_app_enabled.html]
 [test_app_update.html]
 skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_bug_795164.html]
 [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]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_packaged_app_update.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_receipt_operations.html]
 [test_signed_pkg_install.html]
deleted file mode 100644
--- a/dom/apps/tests/test_langpacks.html
+++ /dev/null
@@ -1,221 +0,0 @@
-<!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>
--- a/dom/tests/mochitest/webapps/test_list_api.xul
+++ b/dom/tests/mochitest/webapps/test_list_api.xul
@@ -15,19 +15,17 @@
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
      target="_blank">Mozilla Bug 741549</a>
   </body>
 
 <script>
 
 var props = {
   checkInstalled: "function",
-  getAdditionalLanguages: "function",
   getInstalled: "function",
-  getLocalizationResource: "function",
   getSelf: "function",
   install: "function",
   installPackage: "function",
   mgmt: "object",
 };
 
 isDeeply([p for (p in navigator.mozApps)].sort(), Object.keys(props).sort(),
          "navigator.mozApps has only the expected properties");
--- a/dom/webidl/Apps.webidl
+++ b/dom/webidl/Apps.webidl
@@ -4,49 +4,26 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 dictionary InstallParameters {
   sequence<DOMString> receipts = [];
   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 {
   [CheckPermissions="webapps-manage"]
   readonly attribute DOMApplicationsManager mgmt;
   DOMRequest install(DOMString url, optional InstallParameters params);
   DOMRequest installPackage(DOMString url, optional InstallParameters params);
   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]
 interface DOMApplication : EventTarget {
   // manifest and updateManifest will be turned into dictionaries once
   // in bug 1053033 once bug 963382 is fixed.
   readonly attribute any manifest;
   readonly attribute any updateManifest;
@@ -89,28 +66,28 @@ interface DOMApplication : EventTarget {
   DOMRequest checkForUpdate();
 
   /**
    * Inter-App Communication APIs.
    *
    * 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",
  ChromeOnly,
  CheckPermissions="webapps-manage"]
 interface DOMApplicationsManager : EventTarget {
   DOMRequest getAll();
   DOMRequest getNotInstalled();