Backout af0a7f54f483 (bug 704691), 17b4093b5ec5, 23f26ba05f1a (bug 697383) for various failures
authorMarco Bonardo <mbonardo@mozilla.com>
Mon, 28 Nov 2011 23:53:22 +0100
changeset 82540 af3ceb7d6902100b215620fa813d447d197f3f76
parent 82539 af0a7f54f4830fc493bb34197bff4c1641799369
child 82541 d984d11bffea9ce760e2427dd60f508857636af2
push idunknown
push userunknown
push dateunknown
bugs704691, 697383
milestone11.0a1
backs outaf0a7f54f4830fc493bb34197bff4c1641799369
Backout af0a7f54f483 (bug 704691), 17b4093b5ec5, 23f26ba05f1a (bug 697383) for various failures
browser/installer/package-manifest.in
dom/Makefile.in
dom/base/Makefile.in
dom/base/Webapps.js
dom/base/Webapps.jsm
dom/base/Webapps.manifest
dom/interfaces/apps/Makefile.in
dom/interfaces/apps/nsIDOMApplicationRegistry.idl
dom/interfaces/base/Makefile.in
layout/printing/nsPrintData.cpp
layout/printing/nsPrintData.h
mobile/xul/installer/package-manifest.in
toolkit/Makefile.in
toolkit/mozapps/webapps/Makefile.in
toolkit/mozapps/webapps/OpenWebapps.idl
toolkit/mozapps/webapps/OpenWebapps.js
toolkit/mozapps/webapps/OpenWebapps.jsm
toolkit/mozapps/webapps/OpenWebapps.manifest
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -129,17 +129,16 @@
 @BINPATH@/components/content_htmldoc.xpt
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/content_xtf.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
-@BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_battery.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_notification.xpt
@@ -369,18 +368,16 @@
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/SyncComponents.manifest
 @BINPATH@/components/Weave.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
-@BINPATH@/components/Webapps.js
-@BINPATH@/components/Webapps.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsSafebrowsingApplication.manifest
 @BINPATH@/components/nsSafebrowsingApplication.js
 @BINPATH@/components/nsURLClassifier.manifest
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -61,17 +61,16 @@ DIRS = \
   interfaces/xul \
   interfaces/storage \
   interfaces/json \
   interfaces/offline \
   interfaces/geolocation \
   interfaces/notification \
   interfaces/svg \
   interfaces/smil \
-  interfaces/apps \
   $(NULL)
 
 DIRS += \
   base \
   battery \
   sms \
   src \
   locales \
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -47,23 +47,17 @@ LIBRARY_NAME	= jsdombase_s
 LIBXUL_LIBRARY	= 1
 FORCE_STATIC_LIB = 1
 
 EXTRA_PP_COMPONENTS = \
 		ConsoleAPI.js \
 		ConsoleAPI.manifest \
 		$(NULL)
 
-EXTRA_COMPONENTS = \
-	        Webapps.js \
-	        Webapps.manifest \
-		$(NULL)
-
 EXTRA_JS_MODULES = ConsoleAPIStorage.jsm \
-                Webapps.jsm \
 		$(NULL)
 
 XPIDLSRCS = \
   nsIEntropyCollector.idl \
   nsIScriptChannel.idl \
   $(NULL)
 
 EXPORTS = \
deleted file mode 100644
--- a/dom/base/Webapps.js
+++ /dev/null
@@ -1,347 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Open Web Apps.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Fabrice Desré <fabrice@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-function WebappsRegistry() {
-  this.messages = ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
-                   "Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO",
-                   "Webapps:Enumerate:Return:OK", "Webapps:Enumerate:Return:KO"];
-
-  this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
-
-  this.messages.forEach((function(msgName) {
-    this.mm.addMessageListener(msgName, this);
-  }).bind(this));
-
-  this._window = null;
-  this._id = this._getRandomId();
-  this._callbacks = [];
-}
-
-WebappsRegistry.prototype = {
-  _onerror: null,
-  _oninstall: null,
-  _onuninstall: null,
-
-  /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
-   * only the name property is mandatory
-   */
-  checkManifest: function(aManifest, aInstallOrigin) {
-    // TODO : check for install_allowed_from
-    if (aManifest.name == undefined)
-      return false;
-    
-    if (aManifest.installs_allowed_from) {
-      ok = false;
-      aManifest.installs_allowed_from.forEach(function(aOrigin) {
-        if (aOrigin == "*" || aOrigin == aInstallOrigin)
-          ok = true;
-      });
-      return ok;
-    }
-    return true;
-  },
-  
-  getCallbackId: function(aCallback) {
-    let id = "id" + this._getRandomId();
-    this._callbacks[id] = aCallback;
-    return id;
-  },
-  
-  getCallback: function(aId) {
-    return this._callbacks[aId];
-  },
-
-  removeCallback: function(aId) {
-    if (this._callbacks[aId])
-      delete this._callbacks[aId];
-  },
-  
-  _getRandomId: function() {
-    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
-  },
-
-  _convertAppsArray: function(aApps) {
-    let apps = new Array();
-    for (let i = 0; i < aApps.length; i++) {
-      let app = aApps[i];
-      apps.push(new WebappsApplication(app.origin, app.manifest, app.receipt, app.installOrigin, app.installTime));
-    }
-    return apps;
-  },
-
-  set oninstall(aCallback) {
-    if (this.hasPrivileges)
-      this._oninstall = aCallback;
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-  
-  set onuninstall(aCallback) {
-    if (this.hasPrivileges)
-      this._onuninstall = aCallback;
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-
-  set onerror(aCallback) {
-    this._onerror = aCallback;
-  },
-
-  receiveMessage: function(aMessage) {
-    let msg = aMessage.json;
-    if (!(msg.oid == this._id || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
-      return
-    let app = msg.app;
-    let cb;
-    switch (aMessage.name) {
-      case "Webapps:Install:Return:OK":
-        if (this._oninstall)
-          this._oninstall.handleEvent(new WebappsApplication(app.origin, app.manifest, app.receipt,
-                                                app.installOrigin, app.installTime));
-        break;
-      case "Webapps:Install:Return:KO":
-        if (this._onerror)
-          this._onerror.handleEvent(new RegistryError(Ci.nsIDOMApplicationRegistryError.DENIED));
-        break;
-      case "Webapps:Uninstall:Return:OK":
-        if (this._onuninstall)
-          this._onuninstall.handleEvent(new WebappsApplication(msg.origin, null, null, null, 0));
-        break;
-      case "Webapps:Uninstall:Return:KO":
-        if (this._onerror)
-          this._onerror.handleEvent(new RegistryError(Ci.nsIDOMApplicationRegistryError.PERMISSION_DENIED));
-        break;
-      case "Webapps:Enumerate:Return:OK":
-        cb = this.getCallback(msg.callbackID);
-        if (cb.success) {
-          let apps = this._convertAppsArray(msg.apps);
-          cb.success.handleEvent(apps, apps.length);
-        }
-        break;
-      case "Webapps:Enumerate:Return:KO":
-        cb = this.getCallback(msg.callbackID);
-        if (cb.error)
-          cb.error.handleEvent(new RegistryError(Ci.nsIDOMApplicationRegistryError.PERMISSION_DENIED));
-        break;
-    }
-    this.removeCallback(msg.callbackID);
-  },
-  
-  _fireError: function(aCode) {
-    if (!this._onerror)
-      return;
-    this._onerror.handleEvent(new RegistryError(aCode));
-  },
-
-  _getOrigin: function(aURL) {
-    let uri = Services.io.newURI(aURL, null, null);
-    return uri.prePath; 
-  },
-
-  // nsIDOMApplicationRegistry implementation
-  
-  install: function(aURL, aReceipt) {
-    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
-    xhr.open("GET", aURL, true);
-
-    xhr.addEventListener("load", (function() {
-      if (xhr.status == 200) {
-        try {
-          let installOrigin = this._getOrigin(this._window.location.href);
-          let manifest = JSON.parse(xhr.responseText, installOrigin);
-          if (!this.checkManifest(manifest, installOrigin)) {
-            this._fireError(Ci.nsIDOMApplicationRegistryError.INVALID_MANIFEST);
-          } else {
-            this.mm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
-                                                          origin: this._getOrigin(aURL),
-                                                          manifest: manifest,
-                                                          receipt: aReceipt },
-                                                          from: this._window.location.href,
-                                                          oid: this._id });
-          }
-        } catch(e) {
-          this._fireError(Ci.nsIDOMApplicationRegistryError.MANIFEST_PARSE_ERROR);
-        }
-      }
-      else {
-        this._fireError(Ci.nsIDOMApplicationRegistryError.MANIFEST_URL_ERROR);
-      }      
-    }).bind(this), false);
-
-    xhr.addEventListener("error", (function() {
-      this._fireError(Ci.nsIDOMApplicationRegistryError.NETWORK_ERROR);
-    }).bind(this), false);
-
-    xhr.send(null);
-  },
-
-  uninstall: function(aOrigin) {
-    if (this.hasPrivileges)
-      this.mm.sendAsyncMessage("Webapps:Uninstall", { from: this._window.location.href,
-                                                      origin: aOrigin,
-                                                      oid: this._id });
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-
-  launch: function(aOrigin) {
-    this.mm.sendAsyncMessage("Webapps:Launch", { origin: aOrigin,
-                                                 from: this._window.location.href});
-  },
-  
-  enumerate: function(aSuccess, aError) {
-    this.mm.sendAsyncMessage("Webapps:Enumerate", { from: this._window.location.href,
-                                                    origin: this._getOrigin(this._window.location.href),
-                                                    oid: this._id,
-                                                    callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
-  },
-
-  handleEvent: function(aEvent) {
-    if (aEvent.type == "unload") {
-      // remove all callbacks and event handlers so we don't call anything on a cleared scope
-      try {
-        this._oninstall = null;
-        this._onuninstall = null;
-        this._onerror = null;
-        this._callbacks = [];
-      } catch(e) {
-        dump("WebappsRegistry error:" + e + "\n");
-      }
-    }
-  },
-  
-  // nsIDOMGlobalPropertyInitializer implementation
-  init: function(aWindow) {
-    dump("DOMApplicationRegistry::init() " + aWindow + "\n");
-    this._window = aWindow;
-    this._window.addEventListener("unload", this, false);
-    this._window.appId = this._id;
-    let from = Services.io.newURI(this._window.location.href, null, null);
-    let perm = Services.perms.testExactPermission(from, "webapps-manage");
-
-    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
-    this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
-  },
-  
-  classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMApplicationRegistry, Ci.nsIDOMGlobalPropertyInitializer]),
-  
-  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
-                                    contractID: "@mozilla.org/webapps;1",
-                                    interfaces: [Ci.nsIDOMApplicationRegistry],
-                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
-                                    classDescription: "Webapps Registry"})
-}
-
-function WebappsApplication(aOrigin, aManifest, aReceipt, aInstallOrigin, aInstallTime) {
-  this._origin = aOrigin;
-  this._manifest = aManifest;
-  this._receipt = aReceipt;
-  this._installOrigin = aInstallOrigin;
-  this._installTime = aInstallTime;
-}
-
-WebappsApplication.prototype = {
-  _origin: null,
-  _manifest: null,
-  _receipt: null,
-  _installOrigin: null,
-  _installTime: 0,
-
-  get origin() {
-    return this._origin;
-  },
-
-  get manifest() {
-    return this._manifest;
-  },
-
-  get receipt() {
-    return this._receipt;
-  },
-
-  get installOrigin() {
-    return this._installOrigin;
-  },
-  
-  get installTime() {
-    return this._installTime;
-  },
-
-  classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMApplication]),
-
-  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
-                                    contractID: "@mozilla.org/webapps/application;1",
-                                    interfaces: [Ci.nsIDOMApplication],
-                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
-                                    classDescription: "Webapps Application"})
-}
-
-function RegistryError(aCode) {
-  this._code = aCode;
-}
-
-RegistryError.prototype = {
-  _code: null,
-  
-  get code() {
-    return this._code;
-  },
-  
-  classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMApplicationRegistryError]),
-
-  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
-                                    contractID: "@mozilla.org/webapps/error;1",
-                                    interfaces: [Ci.nsIDOMApplicationRegistryError],
-                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
-                                    classDescription: "Webapps Registry Error"})
-}
-
-const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication, RegistryError]);
-
deleted file mode 100644
--- a/dom/base/Webapps.jsm
+++ /dev/null
@@ -1,380 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Mobile Browser.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Fabrice Desré <fabrice@mozilla.com>
- *   Mark Finkle <mfinkle@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-const Cu = Components.utils; 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
-  Cu.import("resource://gre/modules/NetUtil.jsm");
-  return NetUtil;
-});
-
-let DOMApplicationRegistry = {
-  appsDir: null,
-  appsFile: null,
-  webapps: { },
-
-  init: function() {
-    this.mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
-    let messages = ["Webapps:Install", "Webapps:Uninstall",
-                    "Webapps:Enumerate", "Webapps:Launch"];
-
-    messages.forEach((function(msgName) {
-      this.mm.addMessageListener(msgName, this);
-    }).bind(this));
-
-    let file =  Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
-    file.append("webapps");
-    if (!file.exists() || !file.isDirectory()) {
-      file.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
-    }
-    this.appsDir = file;
-    this.appsFile = file.clone();
-    this.appsFile.append("webapps.json");
-    if (!this.appsFile.exists())
-      return;
-    
-    try {
-      let channel = NetUtil.newChannel(this.appsFile);
-      channel.contentType = "application/json";
-      let self = this;
-      NetUtil.asyncFetch(channel, function(aStream, aResult) {
-        if (!Components.isSuccessCode(aResult)) {
-          Cu.reportError("DOMApplicationRegistry: Could not read from json file " + this.appsFile.path);
-          return;
-        }
-
-        // Read json file into a string
-        let data = null;
-        try {
-          self.webapps = JSON.parse(NetUtil.readInputStreamToString(aStream, aStream.available()) || "");
-          aStream.close();
-        } catch (ex) {
-          Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " + ex);
-        }
-      });
-    } catch (ex) {
-      Cu.reportError("DOMApplicationRegistry: Could not read from " + aFile.path + " : " + ex);
-    }
-  },
-
-  receiveMessage: function(aMessage) {
-    let msg = aMessage.json;
-    let from = Services.io.newURI(msg.from, null, null);
-    let perm = Services.perms.testExactPermission(from, "webapps-manage");
-
-    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
-    let hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
-
-    switch (aMessage.name) {
-      case "Webapps:Install":
-        // always ask for UI to install
-        Services.obs.notifyObservers(this, "webapps-ask-install", JSON.stringify(msg));
-        break;
-      case "Webapps:Uninstall":
-        if (hasPrivileges)
-          this.uninstall(msg);
-        break;
-      case "Webapps:Launch":
-        Services.obs.notifyObservers(this, "webapps-launch", JSON.stringify(msg));
-        break;
-      case "Webapps:Enumerate":
-        if (hasPrivileges)
-          this.enumerateAll(msg)
-        else
-          this.enumerate(msg);
-        break;
-    }
-  },
-
-  _writeFile: function ss_writeFile(aFile, aData, aCallbak) {
-    // Initialize the file output stream.
-    let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
-    ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN);
-
-    // Obtain a converter to convert our data to a UTF-8 encoded input stream.
-    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
-    converter.charset = "UTF-8";
-
-    // Asynchronously copy the data to the file.
-    let istream = converter.convertToInputStream(aData);
-    NetUtil.asyncCopy(istream, ostream, function(rc) {
-      if (aCallbak)
-        aCallbak();
-    });
-  },
-  
-  // clones a app object, without the manifest
-  _cloneAppObject: function(aApp) {
-    let clone = {
-      installOrigin: aApp.installOrigin,
-      origin: aApp.origin,
-      receipt: aApp.receipt,
-      installTime: aApp.installTime
-    };
-    return clone;
-  },
-  
-  denyInstall: function(aData) {
-    this.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
-  },
-  
-  confirmInstall: function(aData) {
-    let app = aData.app;
-    let id = this._appId(app.origin);
-
-    // install an application again is considered as an update
-    if (id) {
-      let dir = this.appsDir.clone();
-      dir.append(id);
-      try {
-        dir.remove(true);
-      } catch(e) {
-      }
-    }
-    else {
-      let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-      id = uuidGenerator.generateUUID().toString();
-    }
-
-    let dir = this.appsDir.clone();
-    dir.append(id);
-    dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
-    
-    let manFile = dir.clone();
-    manFile.append("manifest.json");
-    this._writeFile(manFile, JSON.stringify(app.manifest));
-
-    this.webapps[id] = this._cloneAppObject(app);
-    delete this.webapps[id].manifest;
-    this.webapps[id].installTime = (new Date()).getTime()
-
-    this._writeFile(this.appsFile, JSON.stringify(this.webapps), (function() {
-      this.mm.sendAsyncMessage("Webapps:Install:Return:OK", aData);
-    }).bind(this));
-  },
- 
-  _appId: function(aURI) {
-    for (let id in this.webapps) {
-      if (this.webapps[id].origin == aURI)
-        return id;
-    }
-    return null;
-  },
-
-  _readManifest: function(aId) {
-    let file = this.appsDir.clone();
-    file.append(aId);
-    file.append("manifest.json");
-    let data = "";  
-    let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
-    var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
-    fstream.init(file, -1, 0, 0);
-    cstream.init(fstream, "UTF-8", 0, 0);
-    let (str = {}) {  
-      let read = 0;  
-      do {   
-        read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value  
-        data += str.value;  
-      } while (read != 0);  
-    }  
-    cstream.close(); // this closes fstream  
-    try {
-      return JSON.parse(data);
-    } catch(e) {
-      return null;
-    }
-  },
-  
-  uninstall: function(aData) {
-    for (let id in this.webapps) {
-      let app = this.webapps[id];
-      if (app.origin == aData.origin) {
-        delete this.webapps[id];
-        this._writeFile(this.appsFile, JSON.stringify(this.webapps));
-        let dir = this.appsDir.clone();
-        dir.append(id);
-        try {
-          dir.remove(true);
-        } catch (e) {
-        }
-        this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
-      }
-    }
-  },
-  
-  enumerate: function(aData) {
-    aData.apps = [];
-
-    let id = this._appId(aData.origin);
-    // if it's an app, add itself to the result
-    if (id) {
-      let app = this._cloneAppObject(this.webapps[id]);
-      app.manifest = this._readManifest(id);
-      aData.apps.push(app);
-    }
-
-    // check if it's a store.
-    let isStore = false;
-    for (id in this.webapps) {
-      let app = this._cloneAppObject(this.webapps[id]);
-      if (app.installOrigin == aData.origin) {
-        isStore = true;
-        break;
-      }
-    }
-
-    // add all the apps from this store
-    if (isStore) {
-      for (id in this.webapps) {
-        let app = this._cloneAppObject(this.webapps[id]);
-        if (app.installOrigin == aData.origin) {
-          app.manifest = this._readManifest(id);
-          aData.apps.push(app);
-        }
-      }
-    }
-
-    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
-  },
-
-  denyEnumerate: function(aData) {
-    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:KO", aData);
-  },
-
-  enumerateAll: function(aData) {
-    aData.apps = [];
-
-    for (id in this.webapps) {
-      let app = this._cloneAppObject(this.webapps[id]);
-      app.manifest = this._readManifest(id);
-      aData.apps.push(app);
-    }
-
-    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
-  },
-
-  getManifestFor: function(aOrigin) {
-    let id = this._appId(aOrigin);
-    if (!id)
-      return null;
-    return this._readManifest(id);
-  }
-};
-
-/**
- * Helper object to access manifest information with locale support
- */
-DOMApplicationManifest = function(aManifest, aOrigin) {
-  this._origin = Services.io.newURI(aOrigin, null, null);
-  this._manifest = aManifest;
-  let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry)
-                                                          .QueryInterface(Ci.nsIToolkitChromeRegistry);
-  let locale = chrome.getSelectedLocale("browser").toLowerCase();
-  this._localeRoot = this._manifest;
-  
-  if (this._manifest.locales && this._manifest.locales[locale]) {
-    this._localeRoot = this._manifest.locales[locale];
-  }
-  else if (this._manifest.locales) {
-    // try with the language part of the locale ("en" for en-GB) only
-    let lang = locale.split('-')[0];
-    if (land != locale && this._manifest.locales[lang])
-      this._localeRoot = this._manifest.locales[lang];
-  }
-}
-
-DOMApplicationManifest.prototype = {
-  _localeProp: function(aProp) {
-    if (this._localeRoot[aProp] != undefined)
-      return this._localeRoot[aProp];
-    return this._manifest[aProp];
-  },
-
-  get name() {
-    return this._localeProp("name");
-  },
-  
-  get description() {
-    return this._localeProp("description");
-  },
-  
-  get version() {
-    return this._localeProp("version");
-  },
-  
-  get launch_path() {
-    return this._localeProp("launch_path");
-  },
-  
-  get developer() {
-    return this._localeProp("developer");
-  },
-  
-  get icons() {
-    return this._localeProp("icons");
-  },
-  
-  iconURLForSize: function(aSize) {
-    let icons = this._localeProp("icons");
-    if (!icons)
-      return null;
-    let dist = 100000;
-    let icon = null;
-    for (let size in icons) {
-      let iSize = parseInt(size);
-      if (Math.abs(iSize - aSize) < dist) {
-        icon = this._origin.resolve(icons[size]);
-        dist = Math.abs(iSize - aSize);
-      }
-    }
-    return icon;
-  },
-  
-  fullLaunchPath: function() {
-    let launchPath = this._localeProp("launch_path");
-    return this._origin.resolve(launchPath ? launchPath : "");
-  }
-}
-
-DOMApplicationRegistry.init();
deleted file mode 100644
--- a/dom/base/Webapps.manifest
+++ /dev/null
@@ -1,10 +0,0 @@
-# Webapps.js
-component {fff440b3-fae2-45c1-bf03-3b5a2e432270} Webapps.js
-contract @mozilla.org/webapps;1 {fff440b3-fae2-45c1-bf03-3b5a2e432270}
-category JavaScript-navigator-property mozApps @mozilla.org/webapps;1
-
-component {723ed303-7757-4fb0-b261-4f78b1f6bd22} Webapps.js
-contract @mozilla.org/webapps/application;1 {723ed303-7757-4fb0-b261-4f78b1f6bd22}
-
-component {b4937718-11a3-400b-a69f-ab442a418569} Webapps.js
-contract @mozilla.org/webapps/error;1 {b4937718-11a3-400b-a69f-ab442a418569}
deleted file mode 100644
--- a/dom/interfaces/apps/Makefile.in
+++ /dev/null
@@ -1,53 +0,0 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-# http://www.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is web apps.
-#
-# The Initial Developer of the Original Code is Mozilla Foundation
-# Portions created by the Initial Developer are Copyright (C) 2011
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#  Andreas Gal <gal@mozilla.com>
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
-
-
-DEPTH          = ../../..
-topsrcdir      = @top_srcdir@
-srcdir         = @srcdir@
-VPATH          = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-MODULE         = dom
-XPIDL_MODULE   = dom_apps
-GRE_MODULE     = 1
-
-XPIDLSRCS =                               \
-            nsIDOMApplicationRegistry.idl \
-            $(NULL)
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
+++ /dev/null
@@ -1,91 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is web apps.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Andreas Gal <gal@mozilla.com>  (Original Author)
- *  Fabrice Desré <fabrice@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "domstubs.idl"
-
-//interface nsIArray;
-
-[scriptable, uuid(e0c271cb-266b-48c9-a7e4-96590b445c26)]
-interface nsIDOMApplicationRegistryError : nsISupports
-{
-  const unsigned short DENIED = 1;
-  const unsigned short PERMISSION_DENIED = 2;
-  const unsigned short MANIFEST_URL_ERROR = 3;
-  const unsigned short NETWORK_ERROR = 4;
-  const unsigned short MANIFEST_PARSE_ERROR = 5;
-  const unsigned short INVALID_MANIFEST = 6;
-
-  readonly attribute short code;
-};
-
-[scriptable, uuid(a6856a3d-dece-43ce-89b9-72dba07f4246)]
-interface nsIDOMApplication : nsISupports
-{
-  readonly attribute jsval manifest;
-  readonly attribute DOMString receipt;
-  readonly attribute DOMString origin;
-  readonly attribute DOMString installOrigin;
-  readonly attribute unsigned long installTime;
-};
-
-[scriptable, function, uuid(be170df5-9154-463b-9197-10a6195eba52)]
-interface nsIDOMApplicationRegistryEnumerateCallback : nsISupports
-{
-  void handleEvent([array, size_is(count)] in nsIDOMApplication apps,
-                    in unsigned long count);
-};
-
-[scriptable, function, uuid(ae0ed33d-35cf-443a-837b-a6cebf16bd49)]
-interface nsIDOMApplicationRegistryErrorCallback : nsISupports
-{
-  void handleEvent(in nsIDOMApplicationRegistryError error);
-};
-
-[scriptable, uuid(4070ea6f-dca1-4052-8bc6-7a9bcfc314ac)]
-interface nsIDOMApplicationRegistry : nsISupports
-{
-  void install(in DOMString manifestUrl,
-	       [optional] in DOMString receipt);
-  void uninstall(in DOMString origin);
-  void enumerate(in nsIDOMApplicationRegistryEnumerateCallback success,
-		 [optional] in nsIDOMApplicationRegistryErrorCallback error);
-  void launch(in DOMString origin);
-
-  attribute nsIDOMEventListener oninstall;
-  attribute nsIDOMEventListener onuninstall;
-  attribute nsIDOMEventListener onerror;
-};
--- a/dom/interfaces/base/Makefile.in
+++ b/dom/interfaces/base/Makefile.in
@@ -52,17 +52,17 @@ SDK_XPIDLSRCS =                         
 	nsIDOMWindow.idl			\
 	nsIDOMWindowCollection.idl		\
 	nsIDOMWindowUtils.idl			\
 	$(NULL)
 
 XPIDLSRCS =					\
 	nsIFrameRequestCallback.idl             \
 	nsIBrowserDOMWindow.idl			\
-	nsIContentPermissionPrompt.idl  \
+    nsIContentPermissionPrompt.idl  \
 	nsIContentPrefService.idl		\
 	nsIContentURIGrouper.idl		\
 	nsIDOMClientInformation.idl		\
 	nsIDOMConstructor.idl			\
 	nsIDOMCRMFObject.idl			\
 	nsIDOMCrypto.idl			\
 	nsIDOMHistory.idl			\
 	nsIDOMLocation.idl			\
--- a/layout/printing/nsPrintData.cpp
+++ b/layout/printing/nsPrintData.cpp
@@ -129,34 +129,35 @@ nsPrintData::~nsPrintData()
   if (mBrandName) {
     NS_Free(mBrandName);
   }
 }
 
 void nsPrintData::OnStartPrinting()
 {
   if (!mOnStartSent) {
-    DoOnProgressChange(0, 0, true, nsIWebProgressListener::STATE_START|nsIWebProgressListener::STATE_IS_DOCUMENT|nsIWebProgressListener::STATE_IS_NETWORK);
+    DoOnProgressChange(0, 0, true, nsIWebProgressListener::STATE_START|nsIWebProgressListener::STATE_IS_DOCUMENT);
     mOnStartSent = true;
   }
 }
 
 void nsPrintData::OnEndPrinting()
 {
   DoOnProgressChange(100, 100, true, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_DOCUMENT);
-  DoOnProgressChange(100, 100, true, nsIWebProgressListener::STATE_STOP|nsIWebProgressListener::STATE_IS_NETWORK);
 }
 
 void
-nsPrintData::DoOnProgressChange(PRInt32      aProgress,
+nsPrintData::DoOnProgressChange(PRInt32      aProgess,
                                 PRInt32      aMaxProgress,
                                 bool         aDoStartStop,
                                 PRInt32      aFlag)
 {
+  if (aProgess == 0) return;
+
   for (PRInt32 i=0;i<mPrintProgressListeners.Count();i++) {
     nsIWebProgressListener* wpl = mPrintProgressListeners.ObjectAt(i);
-    wpl->OnProgressChange(nsnull, nsnull, aProgress, aMaxProgress, aProgress, aMaxProgress);
+    wpl->OnProgressChange(nsnull, nsnull, aProgess, aMaxProgress, aProgess, aMaxProgress);
     if (aDoStartStop) {
       wpl->OnStateChange(nsnull, nsnull, aFlag, 0);
     }
   }
 }
 
--- a/layout/printing/nsPrintData.h
+++ b/layout/printing/nsPrintData.h
@@ -80,17 +80,17 @@ public:
 
 
   nsPrintData(ePrintDataType aType);
   ~nsPrintData(); // non-virtual
 
   // Listener Helper Methods
   void OnEndPrinting();
   void OnStartPrinting();
-  void DoOnProgressChange(PRInt32      aProgress,
+  void DoOnProgressChange(PRInt32      aProgess,
                           PRInt32      aMaxProgress,
                           bool         aDoStartStop,
                           PRInt32      aFlag);
 
 
   ePrintDataType               mType;            // the type of data this is (Printing or Print Preview)
   nsRefPtr<nsDeviceContext>   mPrintDC;
   FILE                        *mDebugFilePtr;    // a file where information can go to when printing
--- a/mobile/xul/installer/package-manifest.in
+++ b/mobile/xul/installer/package-manifest.in
@@ -272,16 +272,17 @@
 @BINPATH@/components/xpcom_threads.xpt
 @BINPATH@/components/xpcom_xpti.xpt
 @BINPATH@/components/xpconnect.xpt
 @BINPATH@/components/xulapp.xpt
 @BINPATH@/components/xul.xpt
 @BINPATH@/components/xuldoc.xpt
 @BINPATH@/components/xultmpl.xpt
 @BINPATH@/components/zipwriter.xpt
+@BINPATH@/components/openwebapps.xpt
 
 ; JavaScript components
 @BINPATH@/components/ConsoleAPI.manifest
 @BINPATH@/components/ConsoleAPI.js
 @BINPATH@/components/FeedProcessor.manifest
 @BINPATH@/components/FeedProcessor.js
 @BINPATH@/components/BrowserFeeds.manifest
 @BINPATH@/components/FeedConverter.js
@@ -325,16 +326,17 @@
 @BINPATH@/components/GPSDGeolocationProvider.js
 @BINPATH@/components/nsSidebar.manifest
 @BINPATH@/components/nsSidebar.js
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
+@BINPATH@/components/OpenWebapps.manifest
 
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.manifest
 @BINPATH@/components/nsUpdateTimerManager.js
@@ -600,16 +602,17 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/HelperAppDialog.js
 @BINPATH@/components/LoginManager.js
 @BINPATH@/components/LoginManagerPrompter.js
 @BINPATH@/components/MobileComponents.manifest
 @BINPATH@/components/MobileComponents.xpt
 @BINPATH@/components/PromptService.js
 @BINPATH@/components/SessionStore.js
 @BINPATH@/components/Sidebar.js
+@BINPATH@/components/OpenWebapps.js
 #ifdef MOZ_SAFE_BROWSING
 @BINPATH@/components/SafeBrowsing.js
 #endif
 #ifdef MOZ_UPDATER
 @BINPATH@/components/UpdatePrompt.js
 #endif
 @BINPATH@/components/XPIDialogService.js
 @BINPATH@/components/CapturePicker.js
--- a/toolkit/Makefile.in
+++ b/toolkit/Makefile.in
@@ -51,16 +51,17 @@ PARALLEL_DIRS = \
   locales \
   mozapps/downloads \
   mozapps/extensions \
   mozapps/handling \
   mozapps/preferences \
   mozapps/plugins \
   mozapps/shared \
   mozapps/update \
+  mozapps/webapps \
   obsolete \
   profile \
   themes \
   $(NULL)
 
 ifneq (,$(filter gtk2 qt,$(MOZ_WIDGET_TOOLKIT)))
 PARALLEL_DIRS += system/unixproxy
 endif
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/webapps/Makefile.in
@@ -0,0 +1,57 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Open Web Apps.
+#
+# The Initial Developer of the Original Code is Ben Goodger.
+# Portions created by the Initial Developer are Copyright (C) 2004
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Fabrice Desré <fabrice@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = openwebapps
+
+XPIDLSRCS = OpenWebapps.idl
+
+EXTRA_COMPONENTS += \
+  OpenWebapps.js \
+  OpenWebapps.manifest \
+  $(NULL)
+
+EXTRA_JS_MODULES = \
+  OpenWebapps.jsm \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/webapps/OpenWebapps.idl
@@ -0,0 +1,118 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Open Web Apps.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Fabrice Desré <fabrice@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// IDL for https://developer.mozilla.org/en/OpenWebApps/The_JavaScript_API
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(3b937eb5-679b-41e9-aefa-543849fa61dd)]
+interface nsIOpenWebappsApplication : nsISupports {
+    attribute jsval manifest;
+    attribute DOMString origin;
+    attribute jsval install_data;
+    attribute DOMString install_origin;
+    attribute unsigned long install_time;
+};
+
+[scriptable, function, uuid(fa3ac1bb-ad7d-44d7-8585-9ecdf3782d65)]
+interface nsIOpenWebappsSuccessInstalled : nsISupports {
+    void handle(in nsIOpenWebappsApplication application);
+};
+
+[scriptable, function, uuid(a8a83f45-4cbe-4806-b867-017554e30bd4)]
+interface nsIOpenWebappsSuccessList : nsISupports {
+    void handle([array, size_is(count)] in nsIOpenWebappsApplication apps,
+                in unsigned long count);
+};
+
+[scriptable, function, uuid(75e44e3f-ccda-4497-af68-8abd3f5e1d7b)]
+interface nsIOpenWebappsError : nsISupports {
+    attribute DOMString code;
+    attribute DOMString message;
+};
+
+[scriptable, function, uuid(8b29495e-a5e4-4e76-9af8-0f6fe97b8959)]
+interface nsIOpenWebappsErrorCB : nsISupports {
+    void handle(in nsIOpenWebappsError error);
+};
+
+[scriptable, function, uuid(b86669ab-6a36-4ceb-a4bf-a980dd496144)]
+interface nsIOpenWebappsSuccessEmpty : nsISupports {
+    void handle();
+};
+
+[scriptable, function, uuid(a458afcf-eee9-42fb-bd90-75d5e41c0d9e)]
+interface nsIOpenWebappsChangeCallback : nsISupports {
+    // what is either "add" when new apps are added to the repository, or
+    // "remove" when they are deleted.
+    void update(in DOMString what, [array, size_is(count)] in nsIOpenWebappsApplication apps,
+		in unsigned long count);
+};
+
+[scriptable, uuid(f3ec76a6-abca-4d90-b8c9-e221033068ef)]
+interface nsIOpenWebappsMgmt : nsISupports {
+    void launch(in DOMString origin,
+                [optional] in nsIOpenWebappsSuccessEmpty onsuccess,
+                [optional] in nsIOpenWebappsErrorCB onerror);
+    
+    void list(in nsIOpenWebappsSuccessList onsuccess,
+              [optional] in nsIOpenWebappsErrorCB onerror);
+    
+    void uninstall(in DOMString origin,
+                   in nsIOpenWebappsSuccessEmpty onsuccess,
+                   [optional] in nsIOpenWebappsErrorCB onerror);
+
+    long watchUpdates(in nsIOpenWebappsChangeCallback callback);
+
+    void clearWatch(in long watchId);
+};
+
+[scriptable, uuid(cecd9de7-ea4e-45fd-8a01-a5861d9109ab)]
+interface nsIOpenWebapps : nsISupports {
+    void install(in DOMString manifestURI,
+                 [optional] in jsval install_data,
+                 [optional] in nsIOpenWebappsSuccessEmpty onsuccess,
+                 [optional] in nsIOpenWebappsErrorCB onerror);
+    
+    void amInstalled(in nsIOpenWebappsSuccessInstalled onsuccess,
+                     [optional] in nsIOpenWebappsErrorCB onerror);
+    
+    void getInstalledBy(in nsIOpenWebappsSuccessList onsuccess,
+                        [optional] in nsIOpenWebappsErrorCB onerror);
+    
+    readonly attribute nsIOpenWebappsMgmt mgmt;
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/webapps/OpenWebapps.js
@@ -0,0 +1,296 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Open Web Apps.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Fabrice Desré <fabrice@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function OpenWebapps() {
+  this.messages = ["OpenWebapps:InstallDone", "OpenWebapps:InstallAborted", "OpenWebapps:GetInstalledBy:Return",
+                   "OpenWebapps:AmInstalled:Return", "OpenWebapps:MgmtLaunch:Return", "OpenWebapps:MgmtList:Return", 
+                   "OpenWebapps:MgmtUninstall:Return"];
+
+  this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
+
+  this.messages.forEach((function(msgName) {
+    this.mm.addMessageListener(msgName, this);
+  }).bind(this));
+
+  this._callbacks = [];
+  this._window = null;
+  this._watchId = 0;
+}
+
+OpenWebapps.prototype = {
+  
+  /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
+   * only the name property is mandatory
+   */
+  checkManifest: function(aManifest) {
+    return ("name" in aManifest);
+  },
+  
+  getCallbackId: function(aCallback) {
+    let id = "id" + this._getRandomId();
+    this._callbacks[id] = aCallback;
+    return id;
+  },
+  
+  getCallback: function(aId) {
+    return this._callbacks[aId];
+  },
+
+  removeCallback: function(aId) {
+    if (this._callbacks[aId])
+      delete this._callbacks[aId];
+  },
+  
+  _getRandomId: function() {
+    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
+  },
+
+  _convertAppsArray: function(aApps) {
+    let apps = new Array();
+    for (let i = 0; i < aApps.length; i++) {
+      let app = aApps[i];
+      let xapp = Cc["@mozilla.org/openwebapps/application;1"].createInstance(Ci.nsIOpenWebappsApplication);
+      xapp.origin = app.origin;
+      xapp.manifest = app.manifest;
+      xapp.install_data = app.install_data;
+      xapp.install_origin = app.install_origin;
+      xapp.install_time = app.install_time;
+      apps.push(xapp);
+    }
+    return apps;
+  },
+
+  receiveMessage: function(aMessage) {
+    let msg = aMessage.json;
+    let callbacks = this.getCallback(msg.callbackID);
+
+    // if we have no such callback and this is not a broadcast message, bail out
+    if (!callbacks && aMessage.name != "OpenWebapps:InstallDone"
+                   && aMessage.name != "OpenWebapps:MgmtUninstall:Return")
+      return;
+
+    switch(aMessage.name) {
+      case "OpenWebapps:InstallAborted" :
+        if (callbacks.error)
+          callbacks.error.handle({ code: "denied", message: "User denied installation" });
+        break;
+      case "OpenWebapps:InstallDone" :
+        if (callbacks && callbacks.success)
+          callbacks.success.handle();
+        this._onInstalled([msg.app]);
+        break;
+      case "OpenWebapps:GetInstalledBy:Return":
+        if (callbacks && callbacks.success) {
+          let apps = this._convertAppsArray(msg.apps);
+          callbacks.success.handle(apps, apps.length);
+        }
+        break;
+      case "OpenWebapps:AmInstalled:Return":
+        if (callbacks.success)
+          callbacks.success.handle(msg.installed ? msg.app : null);
+        break;
+      case "OpenWebapps:MgmtLaunch:Return":
+        if (msg.ok && callbacks && callbacks.success)
+          callbacks.success.handle();
+        else if (!msg.ok && callbacks.error)
+          callbacks.error.handle({ code: "noSuchApp", message: "Unable to launch application"});
+        break;
+      case "OpenWebapps:MgmtList:Return":
+        if (msg.ok && callbacks && callbacks.success) {
+          let apps = this._convertAppsArray(msg.apps);
+          callbacks.success.handle(apps, apps.length);
+        }
+        else if (!msg.ok && callbacks && callbacks.error) {
+          callbacks.error.handle({ code: "noAppList", message: "Unable to get application list"});
+        }
+        break;
+      case "OpenWebapps:MgmtUninstall:Return":
+        if (msg.ok) {
+          if (callbacks && callbacks.success)
+            callbacks.success.handle();
+          this._onUninstalled([msg.app]);
+        }
+        else if (!msg.ok && callbacks.error)
+          callbacks.error.handle({ code: "noSuchApp", message: "Unable to uninstall application"});
+        break;
+    }
+    this.removeCallback(msg.callbackID);
+  },
+  
+  // nsIOpenWebapps implementation
+  
+  install: function(aURL, aInstallData, aSuccess, aError) {
+    let self = this;
+
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
+    xhr.open("GET", aURL, true);
+
+    xhr.addEventListener("load", function() {
+      if (xhr.status == 200) {
+        try {
+          let manifest = JSON.parse(xhr.responseText);
+          if (!self.checkManifest(manifest)) {
+            if (aError)
+              aError.handle({ code: "invalidManifest", message: "Invalid manifest" });
+          } else {
+            self.mm.sendAsyncMessage("OpenWebapps:Install", { storeURI: self._window.location.href, manifestURI: aURL, manifest: xhr.responseText,
+                             installData: aInstallData, callbackID: self.getCallbackId({ success: aSuccess, error: aError }) });
+          }
+        } catch(e) {
+          if (aError)
+            aError.handle({ code: "manifestParseError", message: "Unable to parse the manifest" });
+        }
+      }
+      else if (aError) {
+        aError.handle({ code: "networkError", message: "Unable to retrieve manifest" });
+      }      
+    }, false);
+
+    xhr.addEventListener("error", function() {
+      if (aError)
+        aError.handle({ code: "networkError", message: "Unable to retrieve manifest" });
+    }, false);
+
+    xhr.send(null);
+  },
+  
+  amInstalled: function(aSuccess, aError) {
+    this.mm.sendAsyncMessage("OpenWebapps:AmInstalled", { appURI: this._window.location.href, callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  },
+  
+  getInstalledBy: function(aSuccess, aError) {
+    this.mm.sendAsyncMessage("OpenWebapps:GetInstalledBy", { storeURI: this._window.location.href, callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  },
+  
+  // nsIOpenWebappsMgmt implementation
+  launch: function(aOrigin, aSuccess, aError) {
+    this.mm.sendAsyncMessage("OpenWebapps:MgmtLaunch", { origin: aOrigin, callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  },
+  
+  list: function(aSuccess, aError) {
+    this.mm.sendAsyncMessage("OpenWebapps:MgmtList", { from: this._window.location.href, callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  },
+  
+  uninstall: function(aOrigin, aSuccess, aError) {
+    this.mm.sendAsyncMessage("OpenWebapps:MgmtUninstall", { from: this._window.location.href, origin: aOrigin, callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  },
+
+  _onRepoChange: function(aWhat, aApps) {
+    for (let prop in this._callbacks) {
+      if (this._callbacks[prop].isWatch) {
+        let apps = this._convertAppsArray(aApps);
+        this._callbacks[prop].callback.update(aWhat, apps, apps.length);
+      }
+    }
+  },
+
+  _onInstalled: function(aApps) {
+    this._onRepoChange("add", aApps);
+  },
+
+  _onUninstalled: function(aApps) {
+    this._onRepoChange("remove", aApps);
+  },
+
+  watchUpdates: function(aCallback) {
+    this._watchId++;
+    this._callbacks["_watch" + this._getRandomId()] = { isWatch: true, callback: aCallback };
+    return this._watchId;
+  },
+
+  clearWatch: function(aWatchId) {
+    this.removeCallback("_watch" + aWatchId);
+  },
+
+  handleEvent: function(aEvent) {
+    if (aEvent.type == "unload") {
+      // remove all callbacks so we don't call anything on a cleared scope
+      this._callbacks = [];
+    }
+  },
+  
+  // nsIDOMGlobalPropertyInitializer implementation
+  init: function(aWindow) {
+    this._window = aWindow;
+    this._window.addEventListener("unload", this, false);
+  },
+  
+  get mgmt() {
+    return this.QueryInterface(Ci.nsIOpenWebappsMgmt);
+  },
+  
+  classID: Components.ID("{d8fd4d63-27ea-47b9-a931-481214bb8b5b}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIOpenWebapps, Ci.nsIOpenWebappsMgmt, Ci.nsIDOMGlobalPropertyInitializer]),
+  
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{d8fd4d63-27ea-47b9-a931-481214bb8b5b}"),
+                                    contractID: "@mozilla.org/openwebapps;1",
+                                    interfaces: [Ci.nsIOpenWebapps],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "OpenWebapps"})
+}
+
+function OpenWebappsApplication() {
+}
+
+OpenWebappsApplication.prototype = {
+  origin: null,
+  manifest: null,
+  install_data: null,
+  install_origin: null,
+  install_time: 0,
+
+  classID: Components.ID("{34456347-0792-45a4-8eb1-7b5f94f2d700}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIOpenWebappsApplication]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{34456347-0792-45a4-8eb1-7b5f94f2d700}"),
+                                    contractID: "@mozilla.org/openwebapps/application;1",
+                                    interfaces: [Ci.nsIOpenWebappsApplication],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "OpenWebapps Application"})
+}
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([OpenWebapps, OpenWebappsApplication]);
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/webapps/OpenWebapps.jsm
@@ -0,0 +1,260 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Mobile Browser.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Fabrice Desré <fabrice@mozilla.com>
+ *   Mark Finkle <mfinkle@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cu = Components.utils; 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+let EXPORTED_SYMBOLS = ["OpenWebapps"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
+  Cu.import("resource://gre/modules/NetUtil.jsm");
+  return NetUtil;
+});
+
+let OpenWebapps = {
+  appsDir: null,
+  appsFile: null,
+  webapps: { },
+
+  init: function() {
+    let file =  Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
+    file.append("webapps");
+    if (!file.exists() || !file.isDirectory()) {
+      file.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
+    }
+    this.appsDir = file;
+    this.appsFile = file.clone();
+    this.appsFile.append("webapps.json");
+    if (!this.appsFile.exists())
+      return;
+    
+    try {
+      let channel = NetUtil.newChannel(this.appsFile);
+      channel.contentType = "application/json";
+      let self = this;
+      NetUtil.asyncFetch(channel, function(aStream, aResult) {
+        if (!Components.isSuccessCode(aResult)) {
+          Cu.reportError("OpenWebappsSupport: Could not read from json file " + this.appsFile.path);
+          return;
+        }
+
+        // Read json file into a string
+        let data = null;
+        try {
+          self.webapps = JSON.parse(NetUtil.readInputStreamToString(aStream, aStream.available()) || "");
+          aStream.close();
+        } catch (ex) {
+          Cu.reportError("OpenWebsappsStore: Could not parse JSON: " + ex);
+        }
+      });
+    } catch (ex) {
+      Cu.reportError("OpenWebappsSupport: Could not read from " + aFile.path + " : " + ex);
+    }
+  },
+
+  _writeFile: function ss_writeFile(aFile, aData) {
+    // Initialize the file output stream.
+    let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+    ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN);
+
+    // Obtain a converter to convert our data to a UTF-8 encoded input stream.
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
+    converter.charset = "UTF-8";
+
+    // Asynchronously copy the data to the file.
+    let istream = converter.convertToInputStream(aData);
+    NetUtil.asyncCopy(istream, ostream, function(rc) {
+      // nothing to do
+    });
+  },
+  
+  install: function(aApplication) {
+    let id = this._appId(aApplication.appURI);
+
+    // install an application again is considered as an update
+    if (id) {
+      let dir = this.appsDir.clone();
+      dir.append(id);
+      try {
+        dir.remove(true);
+      } catch(e) {
+      }
+    }
+    else {
+      let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
+      id = uuidGenerator.generateUUID().toString();
+    }
+
+    let dir = this.appsDir.clone();
+    dir.append(id);
+    dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
+    
+    let manFile = dir.clone();
+    manFile.append("manifest.json");
+    this._writeFile(manFile, JSON.stringify(aApplication.manifest));
+    
+    this.webapps[id] = {
+      title: aApplication.title,
+      storeURI: aApplication.storeURI,
+      appURI: aApplication.appURI,
+      installData: aApplication.installData,
+      installTime: (new Date()).getTime()
+    };
+    this._writeFile(this.appsFile, JSON.stringify(this.webapps));
+
+    // now save the icon as icon.png in the app directory
+    let iconURI = aApplication.iconURI ? aApplication.iconURI : "chrome://browser/skin/images/homescreen-default-hdpi.png";
+    let iconFile = dir.clone();
+    iconFile.append("icon.png");
+    let uri = Services.io.newURI(iconURI, null, null);
+    let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
+    persist.persistFlags = persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | persist.PERSIST_FLAGS_BYPASS_CACHE;
+    persist.saveURI(uri, null, null, null, "", iconFile);
+  },
+ 
+  _appId: function(aURI) {
+    for (let id in this.webapps) {
+      if (this.webapps[id].appURI == aURI)
+        return id;
+    }
+    return null;
+  },
+
+  _readManifest: function(aId) {
+    let file = this.appsDir.clone();
+    file.append(aId);
+    file.append("manifest.json");
+    let data = "";  
+    let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+    var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
+    fstream.init(file, -1, 0, 0);
+    cstream.init(fstream, "UTF-8", 0, 0);
+    let (str = {}) {  
+      let read = 0;  
+      do {   
+        read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value  
+        data += str.value;  
+      } while (read != 0);  
+    }  
+    cstream.close(); // this closes fstream  
+    try {
+      return JSON.parse(data);
+    } catch(e) {
+      return null;
+    }
+  },
+
+  amInstalled: function(aURI) {
+    for (let id in this.webapps) {
+      let app = this.webapps[id];
+      if (app.appURI == aURI) {
+        return { origin: app.appURI,
+                 install_origin: app.storeURI,
+                 install_data: app.installData,
+                 install_time: app.installTime,
+                 manifest: this._readManifest(id) };
+      }
+    }
+    return null;
+  },
+
+  getInstalledBy: function(aStoreURI) {
+    let res = [];
+    for (let id in this.webapps) {
+      let app = this.webapps[id];
+      if (app.storeURI == aStoreURI)
+        res.push({ origin: app.appURI,
+                   install_origin: app.storeURI,
+                   install_data: app.installData,
+                   install_time: app.installTime,
+                   manifest: this._readManifest(id) });
+    }
+    return res;
+  },
+  
+  mgmtList: function() {
+    let res = new Array();
+    for (let id in this.webapps) {
+      let app = this.webapps[id];
+      res.push({ origin: app.appURI,
+                 install_origin: app.storeURI,
+                 install_data: app.installData,
+                 install_time: app.installTime,
+                 manifest: this._readManifest(id) });
+    }
+    return res;
+  },
+  
+  mgmtLaunch: function(aOrigin) {
+    for (let id in this.webapps) {
+      let app = this.webapps[id];
+      app.manifest = this._readManifest(id);
+      if (app.appURI == aOrigin) {
+        let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
+        let uri = Services.io.newURI(aOrigin + (app.manifest.launch_path ? app.manifest.launch_path : ""), null, null);
+        browserWin.browserDOMWindow.openURI(uri, null, browserWin.OPEN_APPTAB, Ci.nsIBrowserDOMWindow.OPEN_NEW);
+        return true;
+      }
+    }
+    return false;
+  },
+  
+  mgmtUninstall: function(aOrigin) {
+    for (let id in this.webapps) {
+      let app = this.webapps[id];
+      if (app.appURI == aOrigin) {
+        delete this.webapps[id];
+        this._writeFile(this.appsFile, JSON.stringify(this.webapps));
+        let dir = this.appsDir.clone();
+        dir.append(id);
+        try {
+          dir.remove(true);
+        } catch (e) {
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+};
+
+OpenWebapps.init();
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/webapps/OpenWebapps.manifest
@@ -0,0 +1,8 @@
+# OpenWebapps.js
+component {d8fd4d63-27ea-47b9-a931-481214bb8b5b} OpenWebapps.js
+contract @mozilla.org/openwebapps;1 {d8fd4d63-27ea-47b9-a931-481214bb8b5b}
+category JavaScript-navigator-property mozApps @mozilla.org/openwebapps;1
+
+component {34456347-0792-45a4-8eb1-7b5f94f2d700} OpenWebapps.js
+contract @mozilla.org/openwebapps/application;1 {34456347-0792-45a4-8eb1-7b5f94f2d700}
+