Bug 779982 - Change behaviour of getSelf and add amInstalled function. r=sicking
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 25 Sep 2012 11:04:24 -0400
changeset 108129 878af0c5f508e19f6275123bffb1f7369fbbf266
parent 108128 a49366805f7d341fb1d139c31d6af434a6886123
child 108130 1ebd4fc2448cc461e6f77bf50e7a73f84ebd54ba
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewerssicking
bugs779982
milestone18.0a1
Bug 779982 - Change behaviour of getSelf and add amInstalled function. r=sicking
dom/apps/src/Webapps.js
dom/apps/src/Webapps.jsm
dom/interfaces/apps/nsIDOMApplicationRegistry.idl
dom/tests/mochitest/webapps/Makefile.in
dom/tests/mochitest/webapps/cross_origin.html
dom/tests/mochitest/webapps/file_bug_779982.html
dom/tests/mochitest/webapps/file_bug_779982.js
dom/tests/mochitest/webapps/test_bug_779982.html
dom/tests/mochitest/webapps/test_list_api.xul
testing/mochitest/android.json
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -60,22 +60,22 @@ WebappsRegistry.prototype = {
       case "Webapps:GetSelf:Return:OK":
         if (msg.apps.length) {
           app = msg.apps[0];
           Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app));
         } else {
           Services.DOMRequest.fireSuccess(req, null);
         }
         break;
+      case "Webapps:IsInstalled:Return:OK":
+        Services.DOMRequest.fireSuccess(req, msg.installed);
+        break;
       case "Webapps:GetInstalled:Return:OK":
         Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
         break;
-      case "Webapps:GetSelf:Return:KO":
-        Services.DOMRequest.fireError(req, "ERROR");
-        break;
     }
     this.removeRequest(msg.requestID);
   },
 
   _getOrigin: function(aURL) {
     let uri = Services.io.newURI(aURL, null, null);
     return uri.prePath;
   },
@@ -141,21 +141,34 @@ WebappsRegistry.prototype = {
 
     xhr.send(null);
     return request;
   },
 
   getSelf: function() {
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href),
+                                               appId: this._window.document.nodePrincipal.appId,
                                                oid: this._id,
                                                requestID: this.getRequestId(request) });
     return request;
   },
 
+  isInstalled: function(aManifestURL) {
+    let manifestURL = Services.io.newURI(aManifestURL, null, this._window.document.baseURIObject);
+    this._window.document.nodePrincipal.checkMayLoad(manifestURL, true, false);
+
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:IsInstalled", { origin: this._getOrigin(this._window.location.href),
+                                                   manifestURL: manifestURL.spec,
+                                                   oid: this._id,
+                                                   requestID: this.getRequestId(request) });
+    return request;
+  },
+
   getInstalled: function() {
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href),
                                                     oid: this._id,
                                                     requestID: this.getRequestId(request) });
     return request;
   },
 
@@ -192,17 +205,18 @@ WebappsRegistry.prototype = {
                                                       installOrigin: this._getOrigin(this._window.location.href) });
     return request;
   },
 
   // nsIDOMGlobalPropertyInitializer implementation
   init: function(aWindow) {
     this.initHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
                               "Webapps:GetInstalled:Return:OK",
-                              "Webapps:GetSelf:Return:OK", "Webapps:GetSelf:Return:KO"]);
+                              "Webapps:GetSelf:Return:OK",
+                              "Webapps:IsInstalled:Return:OK" ]);
 
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
     this._id = util.outerWindowID;
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
                           ["Webapps:Install:Return:OK"]);
   },
 
   classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -51,17 +51,17 @@ XPCOMUtils.defineLazyGetter(this, "msgmg
 let DOMApplicationRegistry = {
   appsFile: null,
   webapps: { },
   children: [ ],
   allAppsLaunchable: false,
 
   init: function() {
     this.messages = ["Webapps:Install", "Webapps:Uninstall",
-                     "Webapps:GetSelf",
+                     "Webapps:GetSelf", "Webapps:IsInstalled",
                      "Webapps:GetInstalled", "Webapps:GetNotInstalled",
                      "Webapps:Launch", "Webapps:GetAll",
                      "Webapps:InstallPackage", "Webapps:GetBasePath",
                      "Webapps:GetList", "Webapps:RegisterForMessages",
                      "Webapps:UnregisterForMessages"];
 
     this.messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
@@ -442,16 +442,19 @@ let DOMApplicationRegistry = {
         break;
       case "Webapps:Uninstall":
         Services.obs.notifyObservers(mm, "webapps-uninstall", JSON.stringify(msg));
         this.uninstall(msg);
         break;
       case "Webapps:Launch":
         Services.obs.notifyObservers(mm, "webapps-launch", JSON.stringify(msg));
         break;
+      case "Webapps:IsInstalled":
+        this.isInstalled(msg, mm);
+        break;
       case "Webapps:GetInstalled":
         this.getInstalled(msg, mm);
         break;
       case "Webapps:GetNotInstalled":
         this.getNotInstalled(msg, mm);
         break;
       case "Webapps:GetAll":
         if (msg.hasPrivileges)
@@ -851,32 +854,61 @@ let DOMApplicationRegistry = {
 
     if (!found) {
       aData.mm.broadcastMessage("Webapps:Uninstall:Return:KO", aData);
     }
   },
 
   getSelf: function(aData, aMm) {
     aData.apps = [];
+
+    if (aData.appId == Ci.nsIScriptSecurityManager.NO_APP_ID ||
+        aData.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) {
+      aMm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
+      return;
+    }
+
     let tmp = [];
-    let id = this._appId(aData.origin);
 
-    if (id && this._isLaunchable(this.webapps[id].origin)) {
-      let app = AppsUtils.cloneAppObject(this.webapps[id]);
-      aData.apps.push(app);
-      tmp.push({ id: id });
+    for (let id in this.webapps) {
+      if (this.webapps[id].origin == aData.origin &&
+          this.webapps[id].localId == aData.appId &&
+          this._isLaunchable(this.webapps[id].origin)) {
+        let app = AppsUtils.cloneAppObject(this.webapps[id]);
+        aData.apps.push(app);
+        tmp.push({ id: id });
+        break;
+      }
+    }
+
+    if (!aData.apps.length) {
+      aMm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
+      return;
     }
 
     this._readManifests(tmp, (function(aResult) {
       for (let i = 0; i < aResult.length; i++)
         aData.apps[i].manifest = aResult[i].manifest;
       aMm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
     }).bind(this));
   },
 
+  isInstalled: function(aData, aMm) {
+    aData.installed = false;
+
+    for (let appId in this.webapps) {
+      if (this.webapps[appId].manifestURL == aData.manifestURL) {
+        aData.installed = true;
+        break;
+      }
+    }
+
+    aMm.sendAsyncMessage("Webapps:IsInstalled:Return:OK", aData);
+  },
+
   getInstalled: function(aData, aMm) {
     aData.apps = [];
     let tmp = [];
 
     for (let id in this.webapps) {
       if (this.webapps[id].installOrigin == aData.origin &&
           this._isLaunchable(this.webapps[id].origin)) {
         aData.apps.push(AppsUtils.cloneAppObject(this.webapps[id]));
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
+++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
@@ -68,17 +68,17 @@ interface mozIDOMApplicationMgmt : nsISu
   /**
    * event listener to get notified of application uninstalls. Only settable by
    * privileged callers.
    * the event will be a mozIDOMApplicationEvent
    */
   attribute nsIDOMEventListener onuninstall;
 };
 
-[scriptable, uuid(dd9a044c-1073-4d2b-a17d-c9b5834b3420)]
+[scriptable, uuid(7ca34d3e-d855-4d0a-a3b3-58c0acad9ec3)]
 interface mozIDOMApplicationRegistry : nsISupports
 {
   /**
    * Install a web app.
    *
    * @param manifestUrl : the URL of the webapps manifest.
    * @param parameters  : A structure with optional information.
    *                      {
@@ -90,14 +90,19 @@ interface mozIDOMApplicationRegistry : n
   nsIDOMDOMRequest install(in DOMString manifestUrl, [optional] in jsval parameters);
 
   /**
    * the request will return the application currently installed, or null.
    */
   nsIDOMDOMRequest getSelf();
 
   /**
+   * the request will return true if the app from that origin is installed
+   */
+  nsIDOMDOMRequest isInstalled(in DOMString manifestUrl);
+
+  /**
    * the request will return the applications installed from this origin, or null.
    */
   nsIDOMDOMRequest getInstalled();
 
   readonly attribute mozIDOMApplicationMgmt mgmt;
 };
--- a/dom/tests/mochitest/webapps/Makefile.in
+++ b/dom/tests/mochitest/webapps/Makefile.in
@@ -24,9 +24,15 @@ MOCHITEST_CHROME_FILES = \
     test_list_api.xul \
     test_install_errors.xul \
     test_install_utf8.xul \
     test_install_receipts.xul \
     test_getNotInstalled.xul \
     test_launch_paths.xul \
     $(NULL)
 
+MOCHITEST_FILES = \
+    test_bug_779982.html \
+    file_bug_779982.js \
+    file_bug_779982.html \
+    $(NULL)
+
 include $(topsrcdir)/config/rules.mk
--- a/dom/tests/mochitest/webapps/cross_origin.html
+++ b/dom/tests/mochitest/webapps/cross_origin.html
@@ -21,20 +21,17 @@ var parent = SpecialPowers.wrap(window).
 
 confirmNextInstall();
 navigator.mozApps.install(parent.url2, null).onsuccess = function onInstall() {
   // Give the test page a reference to the installed app, so it can uninstall it
   // after it finishes the tests.
   parent.app2 = this.result;
 
   navigator.mozApps.getSelf().onsuccess = function onGetSelf() {
-    parent.ok(this.result instanceof SpecialPowers.Ci.mozIDOMApplication,
-              "getSelf() from app's origin returns mozIDOMApplication");
-    parent.is(this.result.manifestURL, parent.url2,
-              "getSelf() from app's origin returns origin's app");
+    parent.ok(this.result == null, "getSelf() from app's origin returns null if called from a browser");
 
     // Tell the test page to continue the tests.
     parent.postMessage("next", "*");
   }
 }
 
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webapps/file_bug_779982.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    foobar!
+  </body>
+  <script>
+    var finished = false;
+    var data = window.location.search.substring(1).split('&');
+
+    function finish(value) {
+      value ? alert('success') : alert('failure');
+      finished = true;
+    }
+
+    switch (data[0]) {
+      case "getSelf":
+        navigator.mozApps.getSelf().onsuccess = function onGetSelf() {
+          if (data[1] == 'true') {
+            finish(this.result == null);
+          } else {
+            finish(this.result != null);
+          }
+        }
+        break;
+
+      case "isInstalled":
+        navigator.mozApps.isInstalled('http://example.org/manifest.webapp').onsuccess = function onIsInstalled() {
+          if (data[1] == 'true') {
+            finish(this.result == false);
+          } else {
+            finish(this.result == true);
+          }
+        }
+        break;
+
+      case "isInstalledWrong":
+        try {
+          navigator.mozApps.isInstalled('http://something.org/manifest.webapp');
+          finish(false);
+        } catch (e) {
+          finish(true);
+        }
+        break;
+
+      default:
+        finish(false);
+        break;
+    }
+  </script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webapps/file_bug_779982.js
@@ -0,0 +1,125 @@
+function makeAllAppsLaunchable() {
+  var Webapps = {};
+  SpecialPowers.wrap(Components).utils.import("resource://gre/modules/Webapps.jsm", Webapps);
+  var appRegistry = SpecialPowers.wrap(Webapps.DOMApplicationRegistry);
+
+  var originalValue = appRegistry.allAppsLaunchable;
+  appRegistry.allAppsLaunchable = true;
+
+  // Clean up after ourselves once tests are done so the test page is unloaded.
+  window.addEventListener("unload", function restoreAllAppsLaunchable(event) {
+    if (event.target == window.document) {
+      window.removeEventListener("unload", restoreAllAppsLaunchable, false);
+      appRegistry.allAppsLaunchable = originalValue;
+    }
+  }, false);
+}
+
+SimpleTest.waitForExplicitFinish();
+makeAllAppsLaunchable();
+
+var fileTestOnCurrentOrigin = 'http://example.org/tests/dom/tests/mochitest/webapps/file_bug_779982.html';
+
+var previousPrefs = {
+  mozBrowserFramesEnabled: undefined,
+  oop_by_default: undefined,
+};
+
+try {
+  previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
+} catch(e)
+{
+}
+
+SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
+
+SpecialPowers.addPermission("browser", true, window.document);
+
+var gData = [
+  // APP 1
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'getSelf',
+    isnull: false,
+    src: fileTestOnCurrentOrigin,
+    message: 'getSelf() for app should return something'
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'isInstalled',
+    isnull: false,
+    src: fileTestOnCurrentOrigin,
+    message: 'isInstalled() for app should return true'
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'isInstalledWrong',
+    isnull: true,
+    src: fileTestOnCurrentOrigin,
+    message: 'isInstalled() for browser should return true'
+  },
+  // Browser
+  {
+    browser: true,
+    action: 'getSelf',
+    isnull: true,
+    src: fileTestOnCurrentOrigin,
+    message: 'getSelf() for browser should return null'
+  },
+  {
+    browser: true,
+    action: 'isInstalled',
+    isnull: false,
+    src: fileTestOnCurrentOrigin,
+    message: 'isInstalled() for browser should return true'
+  },
+  {
+    browser: true,
+    action: 'isInstalledWrong',
+    isnull: true,
+    src: fileTestOnCurrentOrigin,
+    message: 'isInstalled() for browser should return true'
+  },
+];
+
+function runTest() {
+  for (var i in gData) {
+    var iframe = document.createElement('iframe');
+    var data = gData[i];
+
+    if (data.app) {
+      iframe.setAttribute('mozbrowser', '');
+      iframe.setAttribute('mozapp', data.app);
+    } else if (data.browser) {
+      iframe.setAttribute('mozbrowser', '');
+    }
+
+    if (data.app || data.browser) {
+      iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+        is(e.detail.message, 'success', data.message);
+
+        i++;
+        if (i >= gData.length) {
+          if (previousPrefs.mozBrowserFramesEnabled !== undefined) {
+            SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
+          }
+
+          SpecialPowers.removePermission("browser", window.document);
+
+          SimpleTest.finish();
+        } else {
+          gTestRunner.next();
+        }
+      });
+    }
+
+    iframe.src = data.src + '?' + data.action + '&' + data.isnull;
+    document.getElementById('content').appendChild(iframe);
+
+    yield;
+  }
+}
+
+var gTestRunner = runTest();
+
+gTestRunner.next();
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webapps/test_bug_779982.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=779982
+-->
+<head>
+  <title>Test for getSelf()/isInstalled() in browser and in apps</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=779982">Mozilla Bug 779982</a>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7" src="file_bug_779982.js">
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/webapps/test_list_api.xul
+++ b/dom/tests/mochitest/webapps/test_list_api.xul
@@ -15,16 +15,17 @@
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
      target="_blank">Mozilla Bug 741549</a>
   </body>
 
 <script>
 
 var props = {
   QueryInterface: "function",
+  isInstalled: "function",
   getInstalled: "function",
   getSelf: "function",
   install: "function",
   mgmt: "object",
 };
 
 isDeeply([p for (p in navigator.mozApps)].sort(), Object.keys(props).sort(),
          "navigator.mozApps has only the expected properties");
--- a/testing/mochitest/android.json
+++ b/testing/mochitest/android.json
@@ -201,16 +201,17 @@
  "dom/tests/mochitest/localstorage/test_localStorageQuotaSessionOnly.html": "TIMED_OUT",
  "dom/tests/mochitest/localstorage/test_localStorageQuotaSessionOnly2.html": "TIMED_OUT",
  "dom/tests/mochitest/localstorage/test_localStorageReplace.html": "",
  "dom/tests/mochitest/localstorage/test_removeOwnersAPI.html": "TIMED_OUT",
  "dom/tests/mochitest/localstorage/test_removeOwnersAPISessionOnly.html": "TIMED_OUT",
  "dom/tests/mochitest/pointerlock/test_pointerlock-api.html": "TIMED_OUT",
  "dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html": "",
  "dom/tests/mochitest/sessionstorage/test_sessionStorageHttpHttps.html": "TIMED_OUT",
+ "dom/tests/mochitest/webapps/test_bug_779982.html": "Bug 793211",
  "dom/tests/mochitest/whatwg/test_bug500328.html": "TIMED_OUT",
  "editor/composer/test/test_bug389350.html": "",
  "editor/libeditor/base/tests/test_bug408231.html": "",
  "editor/libeditor/base/tests/test_bug586662.html": "",
  "editor/libeditor/html/tests/test_bug372345.html": "",
  "editor/libeditor/html/tests/test_bug410986.html": "",
  "editor/libeditor/html/tests/test_bug432225.html": "",
  "editor/libeditor/html/tests/test_bug478725.html": "",