author Myk Melez <myk@mozilla.org>
Thu, 12 Jul 2012 13:59:45 -0700
changeset 104394 260f3b5e03f494c59e538f3a343a900df91bd29d
parent 103280 df17f2f4ab63777c252b88b9e68211422a9c4501
child 108424 07b53bdc212ac3876cea6c2c7906e3106985043e
permissions -rw-r--r--
bug 773411 - when running tests, freeze WebappRT config object after calling confirmInstall(); r=adw

/* 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/. */

const EXPORTED_SYMBOLS = ["WebappRT"];

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;


XPCOMUtils.defineLazyGetter(this, "FileUtils", function() {
  return FileUtils;

XPCOMUtils.defineLazyGetter(this, "DOMApplicationRegistry", function() {
  return DOMApplicationRegistry;

// In test mode, observe webapps-ask-install so tests can install apps.
Services.obs.addObserver(function observeCmdLine(subj, topic, data) {
  Services.obs.removeObserver(observeCmdLine, "webapprt-command-line");
  let args = subj.QueryInterface(Ci.nsIPropertyBag2);
  if (!args.hasKey("test-mode"))
  Services.obs.addObserver(function observeInstall(subj, topic, data) {
    // observeInstall is present for the lifetime of the runtime.
    let config = JSON.parse(data);
    config.registryDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
    delete WebappRT.config;
    WebappRT.config = deepFreeze(config);
    Services.obs.notifyObservers(null, "webapprt-test-did-install",
  }, "webapps-ask-install", false);
}, "webapprt-command-line", false);

let WebappRT = {
  get config() {
    let webappFile = FileUtils.getFile("AppRegD", ["webapp.json"]);
    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
    inputStream.init(webappFile, -1, 0, 0);
    let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
    let config = json.decodeFromStream(inputStream, webappFile.fileSize);

    // Memoize the getter, freezing the `config` object in the meantime so
    // consumers don't inadvertently (or intentionally) change it, as the object
    // is meant to be a read-only representation of the webapp's configuration.
    config = deepFreeze(config);
    delete this.config;
    Object.defineProperty(this, "config", { get: function getConfig() config });
    return this.config;

function deepFreeze(o) {
  // First, freeze the object.

  // Then recursively call deepFreeze() to freeze its properties.
  for (let p in o) {
    // If the object is on the prototype, not an object, or is already frozen,
    // skip it.  Note that this might leave an unfrozen reference somewhere in
    // the object if there is an already frozen object containing an unfrozen
    // object.
    if (!o.hasOwnProperty(p) || !(typeof o[p] == "object") ||


  return o;