Bug 1036717 - Test webapp runtime update service. r=myk
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Sat, 12 Jul 2014 02:09:50 +0200
changeset 215633 89076de4e3c3e10ec5f22cdc85a9f737b65888f8
parent 215632 9f869623bd08d47db2f246c52ab05964642fadc6
child 215634 44776990ebd01fada385c16d9944337828032f54
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmyk
bugs1036717
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1036717 - Test webapp runtime update service. r=myk
toolkit/webapps/tests/app.sjs
toolkit/webapps/tests/chrome.ini
toolkit/webapps/tests/data/app/hosted_manifest.webapp
toolkit/webapps/tests/data/app/manifest.webapp
toolkit/webapps/tests/head.js
toolkit/webapps/tests/test_hosted_checkforupdates_from_webapp_runtime.xul
toolkit/webapps/tests/test_packaged_checkforupdates_from_webapp_runtime.xul
webapprt/WebappRT.jsm
webapprt/prefs.js
--- a/toolkit/webapps/tests/app.sjs
+++ b/toolkit/webapps/tests/app.sjs
@@ -1,19 +1,88 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
 function getQuery(request) {
   let query = {};
 
   request.queryString.split('&').forEach(function(val) {
     let [name, value] = val.split('=');
     query[name] = unescape(value);
   });
 
   return query;
 }
 
+function getTestFile(aName) {
+  var file = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+
+  var path = "chrome/toolkit/webapps/tests/data/app/" + aName;
+
+  path.split("/").forEach(function(component) {
+    file.append(component);
+  });
+
+  return file;
+}
+
+function readFile(aFile) {
+  var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
+                createInstance(Ci.nsIFileInputStream);
+  fstream.init(aFile, -1, 0, 0);
+  var data = NetUtil.readInputStreamToString(fstream, fstream.available());
+  fstream.close();
+  return data;
+}
+
+function getHostedManifest(aVersion) {
+  return readFile(getTestFile("hosted_manifest.webapp")).
+           replace(/VERSION_TOKEN/g, aVersion);
+}
+
+function getManifest(aVersion) {
+  return readFile(getTestFile("manifest.webapp")).
+           replace(/VERSION_TOKEN/g, aVersion);
+}
+
+function buildAppPackage(aVersion) {
+  const PR_RDWR        = 0x04;
+  const PR_CREATE_FILE = 0x08;
+  const PR_TRUNCATE    = 0x20;
+
+  let zipFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+  zipFile.append("application.zip");
+
+  let zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
+  zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+
+  // Add index.html file to the zip file
+  zipWriter.addEntryFile("index.html",
+                         Ci.nsIZipWriter.COMPRESSION_NONE,
+                         getTestFile("index.html"),
+                         false);
+
+  // Add manifest to the zip file
+  var manStream = Cc["@mozilla.org/io/string-input-stream;1"].
+                  createInstance(Ci.nsIStringInputStream);
+  var manifest = getManifest(aVersion);
+  manStream.setData(manifest, manifest.length);
+  zipWriter.addEntryStream("manifest.webapp", Date.now(),
+                           Ci.nsIZipWriter.COMPRESSION_NONE,
+                           manStream, false);
+
+  zipWriter.close();
+
+  return readFile(zipFile);
+}
+
 function handleRequest(request, response) {
   response.setHeader("Cache-Control", "no-cache", false);
 
   let query = getQuery(request);
 
   if ("appreq" in query) {
     response.setHeader("Content-Type", "text/plain", false);
     response.write("Hello world!");
@@ -25,9 +94,58 @@ function handleRequest(request, response
 
   if ("testreq" in query) {
     response.setHeader("Content-Type", "text/plain", false);
 
     response.write(getState("appreq"));
 
     return;
   }
+
+  if ("setVersion" in query) {
+    setState("version", query.setVersion);
+    response.write("OK");
+    return;
+  }
+  var version = Number(getState("version"));
+
+  if ("getPackage" in query) {
+    response.setHeader("Content-Type", "application/zip", false);
+    response.write(buildAppPackage(version));
+
+    var getPackageQueries = Number(getState("getPackageQueries"));
+    setState("getPackageQueries", String(++getPackageQueries));
+
+    return;
+  }
+
+  if ("getPackageQueries" in query) {
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write(String(Number(getState("getPackageQueries"))));
+    return;
+  }
+
+  if ("getManifest" in query) {
+    response.setHeader("Content-Type", "application/x-web-app-manifest+json", false);
+    response.write(getManifest(version));
+
+    var getManifestQueries = Number(getState("getManifestQueries"));
+    setState("getManifestQueries", String(++getManifestQueries));
+
+    return;
+  }
+
+  if ("getHostedManifest" in query) {
+    response.setHeader("Content-Type", "application/x-web-app-manifest+json", false);
+    response.write(getHostedManifest(version));
+
+    var getManifestQueries = Number(getState("getManifestQueries"));
+    setState("getManifestQueries", String(++getManifestQueries));
+
+    return;
+  }
+
+  if ("getManifestQueries" in query) {
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write(String(Number(getState("getManifestQueries"))));
+    return;
+  }
 }
--- a/toolkit/webapps/tests/chrome.ini
+++ b/toolkit/webapps/tests/chrome.ini
@@ -23,10 +23,14 @@ skip-if = asan
 skip-if = os == "win" && os_version == "5.1" # see bug 981251
 [test_packaged_uninstall.xul]
 skip-if = os == "win" && os_version == "5.1" # see bug 981251
 [test_hosted_update_from_webapp_runtime.xul]
 skip-if = asan
 [test_packaged_update_from_webapp_runtime.xul]
 skip-if = asan
 [test_hosted_icons.xul]
+[test_hosted_checkforupdates_from_webapp_runtime.xul]
+skip-if = asan
 [test_packaged_icons.xul]
+[test_packaged_checkforupdates_from_webapp_runtime.xul]
+skip-if = asan
 [test_webapp_runtime_executable_update.xul]
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/app/hosted_manifest.webapp
@@ -0,0 +1,11 @@
+{
+  "name" : "Test app",
+  "version" : "VERSION_TOKEN",
+  "description": "Test app",
+  "launch_path": "/chrome/toolkit/webapps/tests/app.sjs?appreq",
+  "developer": {
+    "name": "marco",
+    "url": "http://www.example.com/"
+  },
+  "default_locale": "en-US"
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/app/manifest.webapp
@@ -0,0 +1,12 @@
+{
+  "name" : "Test app",
+  "version" : "VERSION_TOKEN",
+  "package_path": "app.sjs?getPackage=true",
+  "description": "Test app",
+  "launch_path": "/index.html",
+  "developer": {
+    "name": "marco",
+    "url": "http://www.example.com/"
+  },
+  "default_locale": "en-US"
+}
--- a/toolkit/webapps/tests/head.js
+++ b/toolkit/webapps/tests/head.js
@@ -445,35 +445,50 @@ function buildAppPackage(aManifest, aIco
                            false);
   }
 
   zipWriter.close();
 
   return zipFile.path;
 }
 
-function wasAppSJSAccessed() {
+function xhrRequest(aQueryString) {
   let deferred = Promise.defer();
 
   var xhr = new XMLHttpRequest();
 
   xhr.addEventListener("load", function() {
-    let ret = (xhr.responseText == "done") ? true : false;
-    deferred.resolve(ret);
+    deferred.resolve(xhr.responseText);
   });
 
   xhr.addEventListener("error", aError => deferred.reject(aError));
   xhr.addEventListener("abort", aError => deferred.reject(aError));
 
-  xhr.open('GET', 'http://test/chrome/toolkit/webapps/tests/app.sjs?testreq', true);
+  xhr.open('GET', 'http://test/chrome/toolkit/webapps/tests/app.sjs' + aQueryString, true);
   xhr.send();
 
   return deferred.promise;
 }
 
+function wasAppSJSAccessed() {
+  return xhrRequest('?testreq').then((aResponseText) => {
+    return (aResponseText == 'done') ? true : false;
+  });
+}
+
+function setState(aVar, aState) {
+  return xhrRequest('?set' + aVar + '=' + aState).then((aResponseText) => {
+    is(aResponseText, "OK", "set" + aVar + " OK");
+  });
+}
+
+function getState(aVar) {
+  return xhrRequest('?get' + aVar);
+}
+
 function generateDataURI(aFile) {
   var contentType = Cc["@mozilla.org/mime;1"].
                     getService(Ci.nsIMIMEService).
                     getTypeFromFile(aFile);
 
   var inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
                     createInstance(Ci.nsIFileInputStream);
   inputStream.init(aFile, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF);
@@ -512,8 +527,20 @@ let setMacRootInstallDir = Task.async(fu
                                                      "Applications");
   yield OS.File.makeDir(NativeApp.prototype._rootInstallDir,
                         { ignoreExisting: true });
 
   SimpleTest.registerCleanupFunction(function() {
     NativeApp.prototype._rootInstallDir = oldRootInstallDir;
   });
 });
+
+let writeToFile = Task.async(function*(aPath, aData) {
+  let data = new TextEncoder().encode(aData);
+
+  let file;
+  try {
+    file = yield OS.File.open(aPath, { truncate: true, write: true }, { unixMode: 0o777 });
+    yield file.write(data);
+  } finally {
+    yield file.close();
+  }
+});
copy from toolkit/webapps/tests/test_custom_origin.xul
copy to toolkit/webapps/tests/test_hosted_checkforupdates_from_webapp_runtime.xul
--- a/toolkit/webapps/tests/test_custom_origin.xul
+++ b/toolkit/webapps/tests/test_hosted_checkforupdates_from_webapp_runtime.xul
@@ -1,129 +1,111 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1029674
+https://bugzilla.mozilla.org/show_bug.cgi?id=1036717
 -->
-<window title="Mozilla Bug 1029674"
+<window title="Mozilla Bug 1036717"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/chrome-harness.js"></script>
   <script type="application/javascript" src="head.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1029674"
-     target="_blank">Mozilla Bug 1029674</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1036717"
+     target="_blank">Mozilla Bug 1036717</a>
   </body>
 
 <script type="application/javascript">
 <![CDATA[
 
-/** Test for Bug 1029674 **/
+/** Test for Bug 1036717 **/
 
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NativeApp.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 let runTest = Task.async(function*() {
-  let manifest = yield readJSON(getTestFilePath("data/custom_origin.webapp"));
+  let manifest = yield readJSON(getTestFilePath("data/app/hosted_manifest.webapp"));
 
   let app = {
     name: manifest.name,
-    manifestURL: "http://test/chrome/toolkit/webapps/tests/data/custom_origin.webapp",
-    origin: "app://test.origin.privileged.app",
+    manifestURL: "http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?getHostedManifest=true",
+    origin: "http://127.0.0.1:8888",
   };
 
   let testAppInfo = new TestAppInfo(app, true);
 
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
 
-  // Use the test root certificate for the test
-  Cu.import("resource://gre/modules/StoreTrustAnchor.jsm");
-  let oldIndex = TrustedRootCertificate.index;
-  TrustedRootCertificate.index = Ci.nsIX509CertDB.AppXPCShellRoot;
-
-  SimpleTest.registerCleanupFunction(function() {
-    TrustedRootCertificate.index = oldIndex;
-  });
-
-  // Allow signed apps to be installed from the test origin
-  let oldSignedAppOrigins;
-  try {
-    oldSignedAppOrigins = Services.prefs.getCharPref("dom.mozApps.signed_apps_installable_from");
-  } catch (ex) {}
-
-  let newSignedAppOrigins = oldSignedAppOrigins.concat(",chrome://mochitests");
-  Services.prefs.setCharPref("dom.mozApps.signed_apps_installable_from", newSignedAppOrigins);
-
-  SimpleTest.registerCleanupFunction(function() {
-    Services.prefs.setCharPref("dom.mozApps.signed_apps_installable_from", oldSignedAppOrigins);
-  });
-
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
     yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   confirmNextInstall();
 
-  let request = navigator.mozApps.installPackage("http://test/chrome/toolkit/webapps/tests/data/custom_origin.webapp");
+  yield setState("Version", 1);
+
+  let request = navigator.mozApps.install(app.manifestURL);
 
   let (deferred = Promise.defer()) {
     request.onerror = function() {
       deferred.reject(this.error.name);
     };
     request.onsuccess = deferred.resolve;
     yield deferred.promise;
   }
 
   let appObject = request.result;
   ok(appObject, "app is non-null");
 
-  let (deferred = Promise.defer()) {
-    appObject.ondownloaderror = function() {
-      deferred.reject(this.error.name);
-    };
-    appObject.ondownloadapplied = deferred.resolve;
-    yield deferred.promise;
-  };
-
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
-  let exeFile = getFile(testAppInfo.exePath);
+  // Force the update timer to fire in 1 second
+  let prefsFile = OS.Path.join(testAppInfo.profileDir.path, "prefs.js");
+  yield writeToFile(prefsFile, "user_pref(\"webapprt.app_update_interval\", 1);");
 
-  ok(exeFile.isExecutable(), "webapprt executable is executable");
+  // Update app version
+  yield setState("Version", 2);
+
+  // Launch the webapprt executable that will verify the presence of an update
+  let exeFile = getFile(testAppInfo.exePath);
 
   let appClosed = false;
 
   testAppInfo.appProcess.init(exeFile)
   testAppInfo.appProcess.runAsync([], 0, () => appClosed = true);
 
-  while (!(yield wasAppSJSAccessed()) && !appClosed) {
+  // If there was a second request to get the manifest, then the webapprt
+  // has searched for an update.
+  while (Number(yield getState("ManifestQueries")) < 2 &&
+         !appClosed) {
     yield wait(1000);
   }
   ok(!appClosed, "App was launched and is still running");
+  ok(Number(yield getState("ManifestQueries")) >= 2, "Two manifest requests");
 
   SimpleTest.finish();
 });
 
 runTest().catch((e) => {
   ok(false, "Error during test: " + e);
   SimpleTest.finish();
 });
copy from toolkit/webapps/tests/test_custom_origin.xul
copy to toolkit/webapps/tests/test_packaged_checkforupdates_from_webapp_runtime.xul
--- a/toolkit/webapps/tests/test_custom_origin.xul
+++ b/toolkit/webapps/tests/test_packaged_checkforupdates_from_webapp_runtime.xul
@@ -1,129 +1,122 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1029674
+https://bugzilla.mozilla.org/show_bug.cgi?id=1036717
 -->
-<window title="Mozilla Bug 1029674"
+<window title="Mozilla Bug 1036717"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/chrome-harness.js"></script>
   <script type="application/javascript" src="head.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1029674"
-     target="_blank">Mozilla Bug 1029674</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1036717"
+     target="_blank">Mozilla Bug 1036717</a>
   </body>
 
 <script type="application/javascript">
 <![CDATA[
 
-/** Test for Bug 1029674 **/
+/** Test for Bug 1036717 **/
 
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NativeApp.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 let runTest = Task.async(function*() {
-  let manifest = yield readJSON(getTestFilePath("data/custom_origin.webapp"));
+  let manifest = yield readJSON(getTestFilePath("data/app/manifest.webapp"));
 
   let app = {
     name: manifest.name,
-    manifestURL: "http://test/chrome/toolkit/webapps/tests/data/custom_origin.webapp",
-    origin: "app://test.origin.privileged.app",
+    manifestURL: "http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?getManifest=true",
+    origin: "app://test.app",
   };
 
   let testAppInfo = new TestAppInfo(app, true);
 
   // Get to a clean state before the test
   yield testAppInfo.cleanup();
 
   SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
 
   setDryRunPref();
 
-  // Use the test root certificate for the test
-  Cu.import("resource://gre/modules/StoreTrustAnchor.jsm");
-  let oldIndex = TrustedRootCertificate.index;
-  TrustedRootCertificate.index = Ci.nsIX509CertDB.AppXPCShellRoot;
-
-  SimpleTest.registerCleanupFunction(function() {
-    TrustedRootCertificate.index = oldIndex;
-  });
-
-  // Allow signed apps to be installed from the test origin
-  let oldSignedAppOrigins;
-  try {
-    oldSignedAppOrigins = Services.prefs.getCharPref("dom.mozApps.signed_apps_installable_from");
-  } catch (ex) {}
-
-  let newSignedAppOrigins = oldSignedAppOrigins.concat(",chrome://mochitests");
-  Services.prefs.setCharPref("dom.mozApps.signed_apps_installable_from", newSignedAppOrigins);
-
-  SimpleTest.registerCleanupFunction(function() {
-    Services.prefs.setCharPref("dom.mozApps.signed_apps_installable_from", oldSignedAppOrigins);
-  });
-
   // On Mac build servers, we don't have enough privileges to write to /Applications,
   // so we install apps in a user-owned directory.
   if (MAC) {
     yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
   }
 
   confirmNextInstall();
 
-  let request = navigator.mozApps.installPackage("http://test/chrome/toolkit/webapps/tests/data/custom_origin.webapp");
+  yield setState("Version", 1);
+
+  let request = navigator.mozApps.installPackage(app.manifestURL);
 
   let (deferred = Promise.defer()) {
     request.onerror = function() {
       deferred.reject(this.error.name);
     };
     request.onsuccess = deferred.resolve;
     yield deferred.promise;
   }
 
   let appObject = request.result;
   ok(appObject, "app is non-null");
 
   let (deferred = Promise.defer()) {
     appObject.ondownloaderror = function() {
-      deferred.reject(this.error.name);
+      deferred.reject(appObject.downloadError.name);
     };
     appObject.ondownloadapplied = deferred.resolve;
     yield deferred.promise;
   };
 
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
 
-  let exeFile = getFile(testAppInfo.exePath);
+  // Force the update timer to fire in 1 second
+  let prefsFile = OS.Path.join(testAppInfo.profileDir.path, "prefs.js");
+  yield writeToFile(prefsFile, "user_pref(\"webapprt.app_update_interval\", 1);");
 
-  ok(exeFile.isExecutable(), "webapprt executable is executable");
+  // Update app version
+  yield setState("Version", 2);
+
+  // Launch the webapprt executable that will verify the presence of an update
+  let exeFile = getFile(testAppInfo.exePath);
 
   let appClosed = false;
 
   testAppInfo.appProcess.init(exeFile)
   testAppInfo.appProcess.runAsync([], 0, () => appClosed = true);
 
-  while (!(yield wasAppSJSAccessed()) && !appClosed) {
+  // If there was a second request to get the manifest and the package,
+  // then the webapprt has searched for an update and has started to
+  // download it.
+  while ((Number(yield getState("PackageQueries")) < 2 ||
+          Number(yield getState("ManifestQueries")) < 2) &&
+         !appClosed) {
     yield wait(1000);
   }
   ok(!appClosed, "App was launched and is still running");
+  ok(Number(yield getState("ManifestQueries")) >= 2, "Two manifest requests");
+  ok(Number(yield getState("PackageQueries")) >= 2, "Two package requests");
 
   SimpleTest.finish();
 });
 
 runTest().catch((e) => {
   ok(false, "Error during test: " + e);
   SimpleTest.finish();
 });
--- a/webapprt/WebappRT.jsm
+++ b/webapprt/WebappRT.jsm
@@ -128,11 +128,11 @@ this.WebappRT = {
         };
 
         thisApp.ondownloadapplied = () => {
           // Application updated, nothing to do.
         };
 
         thisApp.checkForUpdate();
       }
-    }, 24 * 60 * 60);
+    }, Services.prefs.getIntPref("webapprt.app_update_interval"));
   },
 };
--- a/webapprt/prefs.js
+++ b/webapprt/prefs.js
@@ -1,12 +1,15 @@
 /* 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/. */
 
+// Check for updates once a day.
+pref("webapprt.app_update_interval", 86400);
+
 pref("browser.chromeURL", "chrome://webapprt/content/webapp.xul");
 pref("browser.download.folderList", 1);
 
 // Disable all add-on locations other than the profile (which can't be disabled this way)
 pref("extensions.enabledScopes", 1);
 // Auto-disable any add-ons that are "dropped in" to the profile
 pref("extensions.autoDisableScopes", 1);
 // Disable add-on installation via the web-exposed APIs