Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 23 Sep 2012 15:26:30 -0400
changeset 107964 74c9983198dddeedf526c960ee4e6ea239fe9581
parent 107963 9e32aa6fe544f436b3c8a6a915d5360ef2412ae4 (current diff)
parent 107955 95f1163d4fe35a603634647046da9ff2500135ef (diff)
child 107965 63643fbbde88afe83dc416d8be9c7baf1d47befd
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
Merge m-c to inbound.
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -197,18 +197,26 @@ DebuggerUI.prototype = {
         targetWindow.gBrowser.selectedTab = scriptDebugger.ownerTab;
         targetWindow.focus();
       }.bind(this)
     }, {
       id: "debugger.confirmTabSwitch.buttonOpen",
       label: L10N.getStr("confirmTabSwitch.buttonOpen"),
       accessKey: L10N.getStr("confirmTabSwitch.buttonOpen.accessKey"),
       callback: function DUI_notificationButtonOpen() {
-        this.findDebugger().close();
-        this.toggleDebugger();
+        let scriptDebugger = this.findDebugger();
+        let targetWindow = scriptDebugger.globalUI.chromeWindow;
+        scriptDebugger.close();
+        let self = this;
+        targetWindow.addEventListener("Debugger:Shutdown", function toggle() {
+          targetWindow.removeEventListener("Debugger:Shutdown", toggle, false);
+          Services.tm.currentThread.dispatch({ run: function() {
+            self.toggleDebugger();
+          }}, 0);
+        }, false);
       }.bind(this)
     }];
 
     let message = L10N.getStr("confirmTabSwitch.message");
     let imageURL = "chrome://browser/skin/Info.png";
 
     notification = nbox.appendNotification(
       message, TAB_SWITCH_NOTIFICATION,
--- a/layout/style/test/ccd-quirks.html
+++ b/layout/style/test/ccd-quirks.html
@@ -56,41 +56,55 @@ p + p { left: 22px }
 @import url("redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC2iq");
 @import url("redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC3iq");
 @import url("http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD1iq");
 @import url("http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD2iq");
 @import url("http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD3iq");
 </style>
 
 <!-- link directives -->
-<link rel="stylesheet" href="ccd.sjs?IA1lq">
-<link rel="stylesheet" href="ccd.sjs?IA2lq">
-<link rel="stylesheet" href="ccd.sjs?IA3lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?IB1lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?IB2lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?IB3lq">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC1lq">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC2lq">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC3lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID1lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID2lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID3lq">
-<link rel="stylesheet" href="ccd.sjs?JA1lq">
-<link rel="stylesheet" href="ccd.sjs?JA2lq">
-<link rel="stylesheet" href="ccd.sjs?JA3lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?JB1lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?JB2lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?JB3lq">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC1lq">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC2lq">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC3lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD1lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD2lq">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD3lq">
+<link rel="stylesheet" data-href="ccd.sjs?IA1lq">
+<link rel="stylesheet" data-href="ccd.sjs?IA2lq">
+<link rel="stylesheet" data-href="ccd.sjs?IA3lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?IB1lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?IB2lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?IB3lq">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC1lq">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC2lq">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC3lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID1lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID2lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID3lq">
+<link rel="stylesheet" data-href="ccd.sjs?JA1lq">
+<link rel="stylesheet" data-href="ccd.sjs?JA2lq">
+<link rel="stylesheet" data-href="ccd.sjs?JA3lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?JB1lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?JB2lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?JB3lq">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC1lq">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC2lq">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC3lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD1lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD2lq">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD3lq">
+<script>
+// attach onload and onerror handlers to every link tag, then activate
+// them.  see audit logic in test_css_cross_domain.html
+function setCompleted() {
+  this.dataset.completed = true;
+}
 
+var links = document.getElementsByTagName("link");
+for (var i = 0; i < links.length; i++) {
+  links[i].dataset.completed = false;
+  links[i].addEventListener("load", setCompleted);
+  links[i].addEventListener("error", setCompleted);
+  links[i].href = links[i].dataset.href;
+}
+</script>
 </head><body>
 <div></div>
 <div></div>
 <div><p id="IA1i"></p><p id="IA1l"></p></div>
 <div><p id="IA2i"></p><p id="IA2l"></p></div>
 <div><p id="IA3i"></p><p id="IA3l"></p></div>
 <div></div>
 <div><p id="IB1i"></p><p id="IB1l"></p></div>
@@ -116,9 +130,10 @@ p + p { left: 22px }
 <div></div>
 <div><p id="JC1i"></p><p id="JC1l"></p></div>
 <div><p id="JC2i"></p><p id="JC2l"></p></div>
 <div><p id="JC3i"></p><p id="JC3l"></p></div>
 <div></div>
 <div><p id="JD1i"></p><p id="JD1l"></p></div>
 <div><p id="JD2i"></p><p id="JD2l"></p></div>
 <div><p id="JD3i"></p><p id="JD3l"></p></div>
+
 </body></html>
--- a/layout/style/test/ccd-standards.html
+++ b/layout/style/test/ccd-standards.html
@@ -55,41 +55,55 @@ p + p { left: 22px }
 @import url("redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC2is");
 @import url("redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC3is");
 @import url("http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD1is");
 @import url("http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD2is");
 @import url("http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD3is");
 </style>
 
 <!-- link directives -->
-<link rel="stylesheet" href="ccd.sjs?IA1ls">
-<link rel="stylesheet" href="ccd.sjs?IA2ls">
-<link rel="stylesheet" href="ccd.sjs?IA3ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?IB1ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?IB2ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?IB3ls">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC1ls">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC2ls">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC3ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID1ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID2ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID3ls">
-<link rel="stylesheet" href="ccd.sjs?JA1ls">
-<link rel="stylesheet" href="ccd.sjs?JA2ls">
-<link rel="stylesheet" href="ccd.sjs?JA3ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?JB1ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?JB2ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/ccd.sjs?JB3ls">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC1ls">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC2ls">
-<link rel="stylesheet" href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC3ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD1ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD2ls">
-<link rel="stylesheet" href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD3ls">
+<link rel="stylesheet" data-href="ccd.sjs?IA1ls">
+<link rel="stylesheet" data-href="ccd.sjs?IA2ls">
+<link rel="stylesheet" data-href="ccd.sjs?IA3ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?IB1ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?IB2ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?IB3ls">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC1ls">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC2ls">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?IC3ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID1ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID2ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?ccd.sjs?ID3ls">
+<link rel="stylesheet" data-href="ccd.sjs?JA1ls">
+<link rel="stylesheet" data-href="ccd.sjs?JA2ls">
+<link rel="stylesheet" data-href="ccd.sjs?JA3ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?JB1ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?JB2ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/ccd.sjs?JB3ls">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC1ls">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC2ls">
+<link rel="stylesheet" data-href="redirect.sjs?http://example.org/tests/layout/style/test/ccd.sjs?JC3ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD1ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD2ls">
+<link rel="stylesheet" data-href="http://example.org/tests/layout/style/test/redirect.sjs?http://mochi.test:8888/tests/layout/style/test/ccd.sjs?JD3ls">
+<script>
+// attach onload and onerror handlers to every link tag, then activate
+// them.  see audit logic in test_css_cross_domain.html
+function setCompleted() {
+  this.dataset.completed = true;
+}
 
+var links = document.getElementsByTagName("link");
+for (var i = 0; i < links.length; i++) {
+  links[i].dataset.completed = false;
+  links[i].addEventListener("load", setCompleted);
+  links[i].addEventListener("error", setCompleted);
+  links[i].href = links[i].dataset.href;
+}
+</script>
 </head><body>
 <div></div>
 <div></div>
 <div><p id="IA1i"></p><p id="IA1l"></p></div>
 <div><p id="IA2i"></p><p id="IA2l"></p></div>
 <div><p id="IA3i"></p><p id="IA3l"></p></div>
 <div></div>
 <div><p id="IB1i"></p><p id="IB1l"></p></div>
--- a/layout/style/test/test_css_cross_domain.html
+++ b/layout/style/test/test_css_cross_domain.html
@@ -74,26 +74,48 @@
 <iframe id="standards" src="ccd-standards.html"></iframe>
 </div>
 
 <script type="application/javascript">
 
 /** Test for Bug 524223 **/
 function check_iframe(ifr) {
     var doc = ifr.contentDocument;
+    var i;
+
+    // Check that all links have in fact either loaded or errored.
+    // Doesn't seem to be possible to do this for @import, but this is
+    // just a diagnostic, anyway.  See corresponding code in ccd-*.html.
+    var links = doc.getElementsByTagName("link");
+    for (i = 0; i < links.length; i++) {
+       ok(links[i].dataset.completed, ifr.id + " " + links[i].href + " loaded");
+    }
+
     var cases = doc.getElementsByTagName("p");
-    for (var i = 0; i < cases.length; i++) {
+    for (i = 0; i < cases.length; i++) {
 	var color = doc.defaultView.getComputedStyle(cases[i], "")
 	    .getPropertyValue("background-color");
 
 	is(color, "rgb(0, 255, 0)", ifr.id + " " + cases[i].id);
     }
 }
 
+var quirks = document.getElementById("quirks");
+var standards = document.getElementById("standards");
+var quirks_loaded = false;
+var standards_loaded = false;
+
 SimpleTest.waitForExplicitFinish();
+
+quirks.onload = function() { quirks_loaded = true; }
+standards.onload = function() { standards_loaded = true; }
+
 window.onload = function() {
-    check_iframe(document.getElementById("quirks"));
-    check_iframe(document.getElementById("standards"));
+    ok(quirks_loaded, "quirks frame loaded");
+    ok(standards_loaded, "standards frame loaded");
+
+    check_iframe(quirks);
+    check_iframe(standards);
     SimpleTest.finish();
 };
 </script>
 </body>
 </html>
--- a/toolkit/content/LightweightThemeConsumer.jsm
+++ b/toolkit/content/LightweightThemeConsumer.jsm
@@ -1,47 +1,76 @@
 /* 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/. */
 
 let EXPORTED_SYMBOLS = ["LightweightThemeConsumer"];
 
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeImageOptimizer",
+  "resource://gre/modules/LightweightThemeImageOptimizer.jsm");
+
 function LightweightThemeConsumer(aDocument) {
   this._doc = aDocument;
+  this._win = aDocument.defaultView;
   this._footerId = aDocument.documentElement.getAttribute("lightweightthemesfooter");
 
+  let screen = this._win.screen;
+  this._lastScreenWidth = screen.width;
+  this._lastScreenHeight = screen.height;
+
   Components.classes["@mozilla.org/observer-service;1"]
             .getService(Components.interfaces.nsIObserverService)
             .addObserver(this, "lightweight-theme-styling-update", false);
 
   var temp = {};
   Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
   this._update(temp.LightweightThemeManager.currentThemeForDisplay);
+  this._win.addEventListener("resize", this);
 }
 
 LightweightThemeConsumer.prototype = {
+  _lastData: null,
+  _lastScreenWidth: null,
+  _lastScreenHeight: null,
+
   observe: function (aSubject, aTopic, aData) {
     if (aTopic != "lightweight-theme-styling-update")
       return;
 
     this._update(JSON.parse(aData));
   },
 
+  handleEvent: function (aEvent) {
+    let {width, height} = this._win.screen;
+
+    if (this._lastScreenWidth != width || this._lastScreenHeight != height) {
+      this._lastScreenWidth = width;
+      this._lastScreenHeight = height;
+      this._update(this._lastData);
+    }
+  },
+
   destroy: function () {
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .removeObserver(this, "lightweight-theme-styling-update");
 
-    this._doc = null;
+    this._win.removeEventListener("resize", this);
+    this._win = this._doc = null;
   },
 
   _update: function (aData) {
     if (!aData)
       aData = { headerURL: "", footerURL: "", textcolor: "", accentcolor: "" };
 
+    this._lastData = aData;
+    aData = LightweightThemeImageOptimizer.optimize(aData, this._win.screen);
+
     var root = this._doc.documentElement;
     var active = !!aData.headerURL;
 
     if (active) {
       root.style.color = aData.textcolor || "black";
       root.style.backgroundColor = aData.accentcolor || "white";
       let [r, g, b] = _parseRGB(this._doc.defaultView.getComputedStyle(root, "").color);
       let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -370,18 +370,20 @@ DebuggerClient.prototype = {
       this.notify("connected",
                   aPacket.applicationType,
                   aPacket.traits);
       return;
     }
 
     try {
       if (!aPacket.from) {
-        Cu.reportError("Server did not specify an actor, dropping packet: " +
-                       JSON.stringify(aPacket));
+        let msg = "Server did not specify an actor, dropping packet: " +
+                  JSON.stringify(aPacket);
+        Cu.reportError(msg);
+        dumpn(msg);
         return;
       }
 
       let onResponse;
       // Don't count unsolicited notifications or pauses as responses.
       if (aPacket.from in this._activeRequests &&
           !(aPacket.type in UnsolicitedNotifications) &&
           !(aPacket.type == ThreadStateTypes.paused &&
--- a/toolkit/devtools/debugger/dbg-transport.js
+++ b/toolkit/devtools/debugger/dbg-transport.js
@@ -159,21 +159,99 @@ DebuggerTransport.prototype = {
       var parsed = JSON.parse(packet);
     } catch(e) {
       dumpn("Error parsing incoming packet: " + packet + " (" + e + " - " + e.stack + ")");
       return true;
     }
 
     try {
       dumpn("Got: " + packet);
-      let thr = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
       let self = this;
-      thr.dispatch({run: function() {
+      Services.tm.currentThread.dispatch({run: function() {
         self.hooks.onPacket(parsed);
       }}, 0);
     } catch(e) {
       dumpn("Error handling incoming packet: " + e + " - " + e.stack);
       dumpn("Packet was: " + packet);
     }
 
     return true;
   }
 }
+
+
+/**
+ * An adapter that handles data transfers between the debugger client and
+ * server when they both run in the same process. It presents the same API as
+ * DebuggerTransport, but instead of transmitting serialized messages across a
+ * connection it merely calls the packet dispatcher of the other side.
+ *
+ * @param aOther LocalDebuggerTransport
+ *        The other endpoint for this debugger connection.
+ *
+ * @see DebuggerTransport
+ */
+function LocalDebuggerTransport(aOther)
+{
+  this.other = aOther;
+  this.hooks = null;
+}
+
+LocalDebuggerTransport.prototype = {
+  /**
+   * Transmit a message by directly calling the onPacket handler of the other
+   * endpoint.
+   */
+  send: function LDT_send(aPacket) {
+    try {
+      // Avoid the cost of uneval() when logging is disabled.
+      if (wantLogging) {
+        dumpn("Got: " + uneval(aPacket));
+      }
+      this._deepFreeze(aPacket);
+      let self = this;
+      Services.tm.currentThread.dispatch({run: function() {
+        self.other.hooks.onPacket(aPacket);
+      }}, 0);
+    } catch(e) {
+      dumpn("Error handling incoming packet: " + e + " - " + e.stack);
+      dumpn("Packet was: " + aPacket);
+    }
+  },
+
+  /**
+   * Close the transport.
+   */
+  close: function LDT_close() {
+    if (this.other) {
+      // Remove the reference to the other endpoint before calling close(), to
+      // avoid infinite recursion.
+      let other = this.other;
+      delete this.other;
+      other.close();
+    }
+    this.hooks.onClosed();
+  },
+
+  /**
+   * An empty method for emulating the DebuggerTransport API.
+   */
+  ready: function LDT_ready() {},
+
+  /**
+   * Helper function that makes an object fully immutable.
+   */
+  _deepFreeze: function LDT_deepFreeze(aObject) {
+    Object.freeze(aObject);
+    for (let prop in aObject) {
+      // Freeze the properties that are objects, not on the prototype, and not
+      // already frozen. Note that this might leave an unfrozen reference
+      // somewhere in the object if there is an already frozen object containing
+      // an unfrozen object.
+      if (aObject.hasOwnProperty(prop) && typeof aObject === "object" &&
+          !Object.isFrozen(aObject)) {
+        this._deepFreeze(o[prop]);
+      }
+    }
+  }
+
+}
+
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -67,21 +67,29 @@ var DebuggerServer = {
   globalActorFactories: null,
   // Map of tab actor names to actor constructors provided by extensions.
   tabActorFactories: null,
 
   LONG_STRING_LENGTH: 10000,
   LONG_STRING_INITIAL_LENGTH: 1000,
 
   /**
-   * Prompt the user to accept or decline the incoming connection.
+   * A handler function that prompts the user to accept or decline the incoming
+   * connection.
+   */
+  _allowConnection: null,
+
+  /**
+   * Prompt the user to accept or decline the incoming connection. This is the
+   * default implementation that products embedding the debugger server may
+   * choose to override.
    *
    * @return true if the connection should be permitted, false otherwise
    */
-  _allowConnection: function DH__allowConnection() {
+  _defaultAllowConnection: function DH__defaultAllowConnection() {
     let title = L10N.getStr("remoteIncomingPromptTitle");
     let msg = L10N.getStr("remoteIncomingPromptMessage");
     let disableButton = L10N.getStr("remoteIncomingPromptDisable");
     let prompt = Services.prompt;
     let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK +
                 prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL +
                 prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
                 prompt.BUTTON_POS_1_DEFAULT;
@@ -128,35 +136,38 @@ var DebuggerServer = {
   initTransport: function DH_initTransport(aAllowConnectionCallback) {
     if (this._transportInitialized) {
       return;
     }
 
     this._connections = {};
     this._nextConnID = 0;
     this._transportInitialized = true;
-    if (aAllowConnectionCallback) {
-      this._allowConnection = aAllowConnectionCallback;
-    }
+    this._allowConnection = aAllowConnectionCallback ?
+                            aAllowConnectionCallback :
+                            this._defaultAllowConnection;
   },
 
   get initialized() { return !!this.globalActorFactories; },
 
   /**
    * Performs cleanup tasks before shutting down the debugger server, if no
    * connections are currently open. Such tasks include clearing any actor
    * constructors added at runtime. This method should be called whenever a
    * debugger server is no longer useful, to avoid memory leaks. After this
    * method returns, the debugger server must be initialized again before use.
    */
   destroy: function DH_destroy() {
     if (Object.keys(this._connections).length == 0) {
-      dumpn("Shutting down debugger server.");
+      this.closeListener();
       delete this.globalActorFactories;
       delete this.tabActorFactories;
+      delete this._allowConnection;
+      this._transportInitialized = false;
+      dumpn("Debugger server is shut down.");
     }
   },
 
   /**
    * Load a subscript into the debugging global.
    *
    * @param aURL string A url that will be loaded as a subscript into the
    *        debugging global.  The user must load at least one script
@@ -215,53 +226,48 @@ var DebuggerServer = {
   /**
    * Close a previously-opened TCP listener.
    *
    * @param aForce boolean [optional]
    *        If set to true, then the socket will be closed, regardless of the
    *        number of open connections.
    */
   closeListener: function DH_closeListener(aForce) {
-    this._checkInit();
-
     if (!this._listener || this._socketConnections == 0) {
       return false;
     }
 
     // Only close the listener when the last connection is closed, or if the
     // aForce flag is passed.
     if (--this._socketConnections == 0 || aForce) {
       this._listener.close();
       this._listener = null;
       this._socketConnections = 0;
     }
 
     return true;
   },
 
   /**
-   * Creates a new connection to the local debugger speaking over an
-   * nsIPipe.
+   * Creates a new connection to the local debugger speaking over a fake
+   * transport. This connection results in straightforward calls to the onPacket
+   * handlers of each side.
    *
    * @returns a client-side DebuggerTransport for communicating with
    *          the newly-created connection.
    */
   connectPipe: function DH_connectPipe() {
     this._checkInit();
 
-    let toServer = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
-    toServer.init(true, true, 0, 0, null);
-    let toClient = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
-    toClient.init(true, true, 0, 0, null);
-
-    let serverTransport = new DebuggerTransport(toServer.inputStream,
-                                                toClient.outputStream);
+    let serverTransport = new LocalDebuggerTransport;
+    let clientTransport = new LocalDebuggerTransport(serverTransport);
+    serverTransport.other = clientTransport;
     this._onConnection(serverTransport);
 
-    return new DebuggerTransport(toClient.inputStream, toServer.outputStream);
+    return clientTransport;
   },
 
 
   // nsIServerSocketListener implementation
 
   onSocketAccepted: function DH_onSocketAccepted(aSocket, aTransport) {
     if (!this._allowConnection()) {
       return;
--- a/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
+++ b/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource:///modules/devtools/dbg-server.jsm");
 Cu.import("resource:///modules/devtools/dbg-client.jsm");
 
 function run_test()
 {
   // Allow incoming connections.
-  DebuggerServer.init(function () { return true; });
+  DebuggerServer.init(function () true);
   DebuggerServer.addActors("resource://test/testactors.js");
 
   add_test(test_socket_conn);
   add_test(test_socket_shutdown);
   add_test(test_pipe_conn);
 
   run_next_test();
 }
@@ -54,20 +54,45 @@ function test_socket_conn()
       run_next_test();
     },
   };
   transport.ready();
 }
 
 function test_socket_shutdown()
 {
-  do_check_eq(DebuggerServer._socketConnections, 1);
-  do_check_true(DebuggerServer.closeListener());
+  let count = 0;
+  wait_for_server_shutdown(count);
+}
+
+function wait_for_server_shutdown(aCount)
+{
+  do_timeout(100, function() {
+    dump("count: "+aCount+" ");
+    if (++aCount > 20) {
+      do_throw("Timed out waiting for the server to shut down.");
+      return;
+    }
+    if (DebuggerServer.initialized) {
+      wait_for_server_shutdown(aCount);
+      return;
+    }
+    real_test_socket_shutdown(aCount);
+  });
+}
+
+function real_test_socket_shutdown()
+{
+  // After the last conection was closed, the server must be initialized again.
+  // Allow incoming connections.
+  DebuggerServer.init(function () true);
+  DebuggerServer.addActors("resource://test/testactors.js");
+
   do_check_eq(DebuggerServer._socketConnections, 0);
-  // Make sure closing the listener twice does nothing.
+  // Make sure closing a non-started listener does nothing.
   do_check_false(DebuggerServer.closeListener());
   do_check_eq(DebuggerServer._socketConnections, 0);
 
   let transport = debuggerSocketConnect("127.0.0.1", 2929);
   transport.hooks = {
     onPacket: function(aPacket) {
       // Shouldn't reach this, should never connect.
       do_check_true(false);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/LightweightThemeImageOptimizer.jsm
@@ -0,0 +1,186 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let EXPORTED_SYMBOLS = ["LightweightThemeImageOptimizer"];
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+  "resource://gre/modules/FileUtils.jsm");
+
+const ORIGIN_TOP_RIGHT = 1;
+const ORIGIN_BOTTOM_LEFT = 2;
+
+let LightweightThemeImageOptimizer = {
+  optimize: function LWTIO_optimize(aThemeData, aScreen) {
+    let data = Utils.createCopy(aThemeData);
+    if (!data.headerURL) {
+      return data;
+    }
+
+    data.headerURL = ImageCropper.getCroppedImageURL(
+      data.headerURL, aScreen, ORIGIN_TOP_RIGHT);
+
+    if (data.footerURL) {
+      data.footerURL = ImageCropper.getCroppedImageURL(
+        data.footerURL, aScreen, ORIGIN_BOTTOM_LEFT);
+    }
+
+    return data;
+  },
+
+  purge: function LWTIO_purge() {
+    let dir = FileUtils.getDir("ProfD", ["lwtheme"]);
+    dir.followLinks = false;
+    try {
+      dir.remove(true);
+    } catch (e) {}
+  }
+};
+
+Object.freeze(LightweightThemeImageOptimizer);
+
+let ImageCropper = {
+  _inProgress: {},
+
+  getCroppedImageURL:
+  function ImageCropper_getCroppedImageURL(aImageURL, aScreen, aOrigin) {
+    // We can crop local files, only.
+    if (!aImageURL.startsWith("file://")) {
+      return aImageURL;
+    }
+
+    // Generate the cropped image's file name using its
+    // base name and the current screen size.
+    let uri = Services.io.newURI(aImageURL, null, null);
+    let file = uri.QueryInterface(Ci.nsIFileURL).file;
+
+    // Make sure the source file exists.
+    if (!file.exists()) {
+      return aImageURL;
+    }
+
+    let fileName = file.leafName + "-" + aScreen.width + "x" + aScreen.height;
+    let croppedFile = FileUtils.getFile("ProfD", ["lwtheme", fileName]);
+
+    // If we have a local file that is not in progress, return it.
+    if (croppedFile.exists() && !(croppedFile.path in this._inProgress)) {
+      let fileURI = Services.io.newFileURI(croppedFile);
+
+      // Copy the query part to avoid wrong caching.
+      fileURI.QueryInterface(Ci.nsIURL).query = uri.query;
+      return fileURI.spec;
+    }
+
+    // Crop the given image in the background.
+    this._crop(uri, croppedFile, aScreen, aOrigin);
+
+    // Return the original image while we're waiting for the cropped version
+    // to be written to disk.
+    return aImageURL;
+  },
+
+  _crop: function ImageCropper_crop(aURI, aTargetFile, aScreen, aOrigin) {
+    let inProgress = this._inProgress;
+    inProgress[aTargetFile.path] = true;
+
+    function resetInProgress() {
+      delete inProgress[aTargetFile.path];
+    }
+
+    ImageFile.read(aURI, function (aInputStream, aContentType) {
+      if (aInputStream && aContentType) {
+        let image = ImageTools.decode(aInputStream, aContentType);
+        if (image && image.width && image.height) {
+          let stream = ImageTools.encode(image, aScreen, aOrigin, aContentType);
+          if (stream) {
+            ImageFile.write(aTargetFile, stream, resetInProgress);
+            return;
+          }
+        }
+      }
+
+      resetInProgress();
+    });
+  }
+};
+
+let ImageFile = {
+  read: function ImageFile_read(aURI, aCallback) {
+    this._netUtil.asyncFetch(aURI, function (aInputStream, aStatus, aRequest) {
+      if (Components.isSuccessCode(aStatus) && aRequest instanceof Ci.nsIChannel) {
+        let channel = aRequest.QueryInterface(Ci.nsIChannel);
+        aCallback(aInputStream, channel.contentType);
+      } else {
+        aCallback();
+      }
+    });
+  },
+
+  write: function ImageFile_write(aFile, aInputStream, aCallback) {
+    let fos = FileUtils.openSafeFileOutputStream(aFile);
+    this._netUtil.asyncCopy(aInputStream, fos, function (aResult) {
+      FileUtils.closeSafeFileOutputStream(fos);
+
+      // Remove the file if writing was not successful.
+      if (!Components.isSuccessCode(aResult)) {
+        try {
+          aFile.remove(false);
+        } catch (e) {}
+      }
+
+      aCallback();
+    });
+  }
+};
+
+XPCOMUtils.defineLazyModuleGetter(ImageFile, "_netUtil",
+  "resource://gre/modules/NetUtil.jsm", "NetUtil");
+
+let ImageTools = {
+  decode: function ImageTools_decode(aInputStream, aContentType) {
+    let outParam = {value: null};
+
+    try {
+      this._imgTools.decodeImageData(aInputStream, aContentType, outParam);
+    } catch (e) {}
+
+    return outParam.value;
+  },
+
+  encode: function ImageTools_encode(aImage, aScreen, aOrigin, aContentType) {
+    let stream;
+    let width = Math.min(aImage.width, aScreen.width);
+    let height = Math.min(aImage.height, aScreen.height);
+    let x = aOrigin == ORIGIN_TOP_RIGHT ? aImage.width - width : 0;
+
+    try {
+      stream = this._imgTools.encodeCroppedImage(aImage, aContentType, x, 0,
+                                                 width, height);
+    } catch (e) {}
+
+    return stream;
+  }
+};
+
+XPCOMUtils.defineLazyServiceGetter(ImageTools, "_imgTools",
+  "@mozilla.org/image/tools;1", "imgITools");
+
+let Utils = {
+  createCopy: function Utils_createCopy(aData) {
+    let copy = {};
+    for (let [k, v] in Iterator(aData)) {
+      copy[k] = v;
+    }
+    return copy;
+  }
+};
--- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm
+++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
@@ -4,16 +4,17 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["LightweightThemeManager"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const ID_SUFFIX              = "@personas.mozilla.org";
 const PREF_LWTHEME_TO_SELECT = "extensions.lwThemeToSelect";
 const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
 const PREF_EM_DSS_ENABLED    = "extensions.dss.enabled";
 const ADDON_TYPE             = "theme";
@@ -33,16 +34,19 @@ const OPTIONAL = ["footerURL", "textcolo
 
 const PERSIST_ENABLED = true;
 const PERSIST_BYPASS_CACHE = false;
 const PERSIST_FILES = {
   headerURL: "lightweighttheme-header",
   footerURL: "lightweighttheme-footer"
 };
 
+XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeImageOptimizer",
+  "resource://gre/modules/LightweightThemeImageOptimizer.jsm");
+
 __defineGetter__("_prefs", function () {
   delete this._prefs;
   return this._prefs = Services.prefs.getBranch("lightweightThemes.");
 });
 
 __defineGetter__("_maxUsedThemes", function() {
   delete this._maxUsedThemes;
   try {
@@ -222,18 +226,22 @@ var LightweightThemeManager = {
       _previewTimer.cancel();
       _previewTimer = null;
     }
 
     if (aData) {
       let usedThemes = _usedThemesExceptId(aData.id);
       usedThemes.unshift(aData);
       _updateUsedThemes(usedThemes);
-      if (PERSIST_ENABLED)
-        _persistImages(aData);
+      if (PERSIST_ENABLED) {
+        LightweightThemeImageOptimizer.purge();
+        _persistImages(aData, function () {
+          _notifyWindows(this.currentThemeForDisplay);
+        }.bind(this));
+      }
     }
 
     _prefs.setBoolPref("isThemeSelected", aData != null);
     _notifyWindows(aData);
     Services.obs.notifyObservers(null, "lightweight-theme-changed", null);
   },
 
   /**
@@ -711,27 +719,34 @@ function _prefObserver(aSubject, aTopic,
         _maxUsedThemes = DEFAULT_MAX_USED_THEMES_COUNT;
       }
       // Update the theme list to remove any themes over the number we keep
       _updateUsedThemes(LightweightThemeManager.usedThemes);
       break;
   }
 }
 
-function _persistImages(aData) {
+function _persistImages(aData, aCallback) {
   function onSuccess(key) function () {
     let current = LightweightThemeManager.currentTheme;
-    if (current && current.id == aData.id)
+    if (current && current.id == aData.id) {
       _prefs.setBoolPref("persisted." + key, true);
+    }
+    if (--numFilesToPersist == 0 && aCallback) {
+      aCallback();
+    }
   };
 
+  let numFilesToPersist = 0;
   for (let key in PERSIST_FILES) {
     _prefs.setBoolPref("persisted." + key, false);
-    if (aData[key])
+    if (aData[key]) {
+      numFilesToPersist++;
       _persistImage(aData[key], PERSIST_FILES[key], onSuccess(key));
+    }
   }
 }
 
 function _getLocalImageURI(localFileName) {
   var localFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
   localFile.append(localFileName);
   return Services.io.newFileURI(localFile);
 }
--- a/toolkit/mozapps/extensions/Makefile.in
+++ b/toolkit/mozapps/extensions/Makefile.in
@@ -50,16 +50,17 @@ EXTRA_PP_JS_MODULES = \
   XPIProviderUtils.js \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   AddonLogging.jsm \
   AddonRepository.jsm \
   AddonUpdateChecker.jsm \
   ChromeManifestParser.jsm \
+  LightweightThemeImageOptimizer.jsm \
   LightweightThemeManager.jsm \
   PluginProvider.jsm \
   SpellCheckDictionaryBootstrap.js \
   $(NULL)
 
 TEST_DIRS += test
 
 EXTRA_DSO_LDOPTS = \
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -275,33 +275,41 @@ else {
 }
 
 var gEventManager = {
   _listeners: {},
   _installListeners: [],
 
   initialize: function gEM_initialize() {
     var self = this;
-    ["onEnabling", "onEnabled", "onDisabling", "onDisabled", "onUninstalling",
-     "onUninstalled", "onInstalled", "onOperationCancelled",
-     "onUpdateAvailable", "onUpdateFinished", "onCompatibilityUpdateAvailable",
-     "onPropertyChanged"].forEach(function(aEvent) {
-      self[aEvent] = function initialize_delegateAddonEvent(...aArgs) {
-        self.delegateAddonEvent(aEvent, aArgs);
+    const ADDON_EVENTS = ["onEnabling", "onEnabled", "onDisabling",
+                          "onDisabled", "onUninstalling", "onUninstalled",
+                          "onInstalled", "onOperationCancelled",
+                          "onUpdateAvailable", "onUpdateFinished",
+                          "onCompatibilityUpdateAvailable",
+                          "onPropertyChanged"];
+    for (let evt of ADDON_EVENTS) {
+      let event = evt;
+      self[event] = function initialize_delegateAddonEvent(...aArgs) {
+        self.delegateAddonEvent(event, aArgs);
       };
-    });
-
-    ["onNewInstall", "onDownloadStarted", "onDownloadEnded", "onDownloadFailed",
-     "onDownloadProgress", "onDownloadCancelled", "onInstallStarted",
-     "onInstallEnded", "onInstallFailed", "onInstallCancelled",
-     "onExternalInstall"].forEach(function(aEvent) {
-      self[aEvent] = function initialize_delegateInstallEvent(...aArgs) {
-        self.delegateInstallEvent(aEvent, aArgs);
+    }
+
+    const INSTALL_EVENTS = ["onNewInstall", "onDownloadStarted",
+                            "onDownloadEnded", "onDownloadFailed",
+                            "onDownloadProgress", "onDownloadCancelled",
+                            "onInstallStarted", "onInstallEnded",
+                            "onInstallFailed", "onInstallCancelled",
+                            "onExternalInstall"];
+    for (let evt of INSTALL_EVENTS) {
+      let event = evt;
+      self[event] = function initialize_delegateInstallEvent(...aArgs) {
+        self.delegateInstallEvent(event, aArgs);
       };
-    });
+    }
 
     AddonManager.addManagerListener(this);
     AddonManager.addInstallListener(this);
     AddonManager.addAddonListener(this);
 
     this.refreshGlobalWarning();
     this.refreshAutoUpdateDefault();
 
@@ -705,20 +713,20 @@ var gViewController = {
         }
       }
     },
 
     cmd_resetAddonAutoUpdate: {
       isEnabled: function cmd_resetAddonAutoUpdate_isEnabled() true,
       doCommand: function cmd_resetAddonAutoUpdate_doCommand() {
         AddonManager.getAllAddons(function cmd_resetAddonAutoUpdate_getAllAddons(aAddonList) {
-          aAddonList.forEach(function(aAddon) {
-            if ("applyBackgroundUpdates" in aAddon)
-              aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
-          });
+          for (let addon of aAddonList) {
+            if ("applyBackgroundUpdates" in addon)
+              addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+          }
         });
       }
     },
 
     cmd_goToDiscoverPane: {
       isEnabled: function cmd_goToDiscoverPane_isEnabled() {
         return gDiscoverView.enabled;
       },
@@ -832,23 +840,23 @@ var gViewController = {
           },
           onUpdateFinished: function cmd_findAllUpdates_updateFinished(aAddon, aError) {
             gEventManager.delegateAddonEvent("onUpdateFinished",
                                              [aAddon, aError]);
           }
         };
 
         AddonManager.getAddonsByTypes(null, function cmd_findAllUpdates_getAddonsByTypes(aAddonList) {
-          aAddonList.forEach(function(aAddon) {
-            if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) {
+          for (let addon of aAddonList) {
+            if (addon.permissions & AddonManager.PERM_CAN_UPGRADE) {
               pendingChecks++;
-              aAddon.findUpdates(updateCheckListener,
-                                 AddonManager.UPDATE_WHEN_USER_REQUESTED);
+              addon.findUpdates(updateCheckListener,
+                                AddonManager.UPDATE_WHEN_USER_REQUESTED);
             }
-          });
+          }
 
           if (pendingChecks == 0)
             updateStatus();
         });
       }
     },
 
     cmd_findItemUpdates: {
@@ -1368,19 +1376,18 @@ function sortElements(aElements, aSortBy
 
 function sortList(aList, aSortBy, aAscending) {
   var elements = Array.slice(aList.childNodes, 0);
   sortElements(elements, [aSortBy], aAscending);
 
   while (aList.listChild)
     aList.removeChild(aList.lastChild);
 
-  elements.forEach(function(aElement) {
-    aList.appendChild(aElement);
-  });
+  for (let element of elements)
+    aList.appendChild(element);
 }
 
 function getAddonsAndInstalls(aType, aCallback) {
   let addons = null, installs = null;
   let types = (aType != null) ? [aType] : null;
 
   AddonManager.getAddonsByTypes(types, function getAddonsAndInstalls_getAddonsByTypes(aAddonsList) {
     addons = aAddonsList;
@@ -1407,17 +1414,18 @@ function doPendingUninstalls(aListBox) {
   var listitem = aListBox.firstChild;
   while (listitem) {
     if (listitem.getAttribute("pending") == "uninstall" &&
         !listitem.isPending("uninstall"))
       items.push(listitem.mAddon);
     listitem = listitem.nextSibling;
   }
 
-  items.forEach(function(aAddon) { aAddon.uninstall(); });
+  for (let addon of items)
+    addon.uninstall();
 }
 
 var gCategories = {
   node: null,
   _search: null,
 
   initialize: function gCategories_initialize() {
     this.node = document.getElementById("categories");
@@ -1778,32 +1786,32 @@ var gDiscoverView = {
     if (Services.prefs.getBoolPref(PREF_GETADDONS_CACHE_ENABLED) == false) {
       setURL(url);
       return;
     }
 
     gPendingInitializations++;
     AddonManager.getAllAddons(function initialize_getAllAddons(aAddons) {
       var list = {};
-      aAddons.forEach(function(aAddon) {
+      for (let addon of aAddons) {
         var prefName = PREF_GETADDONS_CACHE_ID_ENABLED.replace("%ID%",
-                                                               aAddon.id);
+                                                               addon.id);
         try {
           if (!Services.prefs.getBoolPref(prefName))
-            return;
+            continue;
         } catch (e) { }
-        list[aAddon.id] = {
-          name: aAddon.name,
-          version: aAddon.version,
-          type: aAddon.type,
-          userDisabled: aAddon.userDisabled,
-          isCompatible: aAddon.isCompatible,
-          isBlocklisted: aAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED
+        list[addon.id] = {
+          name: addon.name,
+          version: addon.version,
+          type: addon.type,
+          userDisabled: addon.userDisabled,
+          isCompatible: addon.isCompatible,
+          isBlocklisted: addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED
         }
-      });
+      }
 
       setURL(url + "#" + JSON.stringify(list));
     });
   },
 
   destroy: function gDiscoverView_destroy() {
     try {
       this._browser.removeProgressListener(this);
@@ -1965,19 +1973,18 @@ var gDiscoverView = {
       // Got a successful load, make sure the browser is visible
       this.node.selectedPanel = this._browser;
       gViewController.updateCommands();
     }
 
     var listeners = this._loadListeners;
     this._loadListeners = [];
 
-    listeners.forEach(function(aListener) {
-      aListener();
-    });
+    for (let listener of listeners)
+      listener();
   },
 
   onProgressChange: function gDiscoverView_onProgressChange() { },
   onStatusChange: function gDiscoverView_onStatusChange() { },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                          Ci.nsISupportsWeakReference]),
 
@@ -2057,42 +2064,42 @@ var gSearchView = {
     var self = this;
     gCachedAddons = {};
     this._pendingSearches = 2;
     this._sorters.setSort("relevancescore", false);
 
     var elements = [];
 
     function createSearchResults(aObjsList, aIsInstall, aIsRemote) {
-      aObjsList.forEach(function(aObj, aIndex) {
-        let score = aObjsList.length - aIndex;
+      for (let index in aObjsList) {
+        let obj = aObjsList[index];
+        let score = aObjsList.length - index;
         if (!aIsRemote && aQuery.length > 0) {
-          score = self.getMatchScore(aObj, aQuery);
+          score = self.getMatchScore(obj, aQuery);
           if (score == 0)
-            return;
+            continue;
         }
 
-        let item = createItem(aObj, aIsInstall, aIsRemote);
+        let item = createItem(obj, aIsInstall, aIsRemote);
         item.setAttribute("relevancescore", score);
         if (aIsRemote) {
-          gCachedAddons[aObj.id] = aObj;
-          if (aObj.purchaseURL)
+          gCachedAddons[obj.id] = obj;
+          if (obj.purchaseURL)
             self._sorters.showprice = true;
         }
 
         elements.push(item);
-      });
+      }
     }
 
     function finishSearch(createdCount) {
       if (elements.length > 0) {
         sortElements(elements, [self._sorters.sortBy], self._sorters.ascending);
-        elements.forEach(function(aElement) {
-          self._listBox.insertBefore(aElement, self._listBox.lastChild);
-        });
+        for (let element of elements)
+          self._listBox.insertBefore(element, self._listBox.lastChild);
         self.updateListAttributes();
       }
 
       self._pendingSearches--;
       self.updateView();
 
       if (!self.isSearching)
         gViewController.notifyViewChanged();
@@ -2356,19 +2363,18 @@ var gListView = {
         elements.push(createItem(addonItem));
 
       for (let installItem of aInstallsList)
         elements.push(createItem(installItem, true));
 
       self.showEmptyNotice(elements.length == 0);
       if (elements.length > 0) {
         sortElements(elements, ["uiState", "name"], true);
-        elements.forEach(function(aElement) {
-          self._listBox.appendChild(aElement);
-        });
+        for (let element of elements)
+          self._listBox.appendChild(element);
       }
 
       gEventManager.registerInstallListener(self);
       gViewController.updateCommands();
       gViewController.notifyViewChanged();
     });
   },
 
@@ -2740,20 +2746,22 @@ var gDetailView = {
   updateState: function gDetailView_updateState() {
     gViewController.updateCommands();
 
     var pending = this._addon.pendingOperations;
     if (pending != AddonManager.PENDING_NONE) {
       this.node.removeAttribute("notification");
 
       var pending = null;
-      ["enable", "disable", "install", "uninstall", "upgrade"].forEach(function(aOp) {
-        if (isPending(this._addon, aOp))
-          pending = aOp;
-      }, this);
+      const PENDING_OPERATIONS = ["enable", "disable", "install", "uninstall",
+                                  "upgrade"];
+      for (let op of PENDING_OPERATIONS) {
+        if (isPending(this._addon, op))
+          pending = op;
+      }
 
       this.node.setAttribute("pending", pending);
       document.getElementById("detail-pending").textContent = gStrings.ext.formatStringFromName(
         "details.notification." + pending,
         [this._addon.name, gStrings.brandShortName], 2
       );
     } else {
       this.node.removeAttribute("pending");
@@ -3049,29 +3057,28 @@ var gUpdatesView = {
   _showRecentUpdates: function gUpdatesView_showRecentUpdates(aRequest) {
     var self = this;
     AddonManager.getAllAddons(function showRecentUpdates_getAllAddons(aAddonsList) {
       if (gViewController && aRequest != gViewController.currentViewRequest)
         return;
 
       var elements = [];
       let threshold = Date.now() - UPDATES_RECENT_TIMESPAN;
-      aAddonsList.forEach(function(aAddon) {
-        if (!aAddon.updateDate || aAddon.updateDate.getTime() < threshold)
-          return;
-
-        elements.push(createItem(aAddon));
-      });
+      for (let addon of aAddonsList) {
+        if (!addon.updateDate || addon.updateDate.getTime() < threshold)
+          continue;
+
+        elements.push(createItem(addon));
+      }
 
       self.showEmptyNotice(elements.length == 0);
       if (elements.length > 0) {
         sortElements(elements, [self._sorters.sortBy], self._sorters.ascending);
-        elements.forEach(function(aElement) {
-          self._listBox.appendChild(aElement);
-        });
+        for (let element of elements)
+          self._listBox.appendChild(element);
       }
 
       gViewController.notifyViewChanged();
     });
   },
 
   _showAvailableUpdates: function gUpdatesView_showAvailableUpdates(aIsRefresh, aRequest) {
     /* Disable the Update Selected button so it can't get clicked
@@ -3090,35 +3097,34 @@ var gUpdatesView = {
         self._updateSelected.hidden = true;
 
         while (self._listBox.itemCount > 0)
           self._listBox.removeItemAt(0);
       }
 
       var elements = [];
 
-      aInstallsList.forEach(function(aInstall) {
-        if (!self.isManualUpdate(aInstall))
-          return;
-
-        let item = createItem(aInstall.existingAddon);
+      for (let install of aInstallsList) {
+        if (!self.isManualUpdate(install))
+          continue;
+
+        let item = createItem(install.existingAddon);
         item.setAttribute("upgrade", true);
         item.addEventListener("IncludeUpdateChanged", function item_onIncludeUpdateChanged() {
           self.maybeDisableUpdateSelected();
         }, false);
         elements.push(item);
-      });
+      }
 
       self.showEmptyNotice(elements.length == 0);
       if (elements.length > 0) {
         self._updateSelected.hidden = false;
         sortElements(elements, [self._sorters.sortBy], self._sorters.ascending);
-        elements.forEach(function(aElement) {
-          self._listBox.appendChild(aElement);
-        });
+        for (let element of elements)
+          self._listBox.appendChild(element);
       }
 
       // ensure badge count is in sync
       self._categoryItem.badgeCount = self._listBox.itemCount;
 
       gViewController.notifyViewChanged();
     });
   },
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1132,20 +1132,22 @@
           if (this.parentNode.selectedItem == this)
             gViewController.updateCommands();
 
           var pending = this.mAddon.pendingOperations;
           if (pending != AddonManager.PENDING_NONE) {
             this.removeAttribute("notification");
 
             var pending = null;
-            ["enable", "disable", "install", "uninstall", "upgrade"].forEach(function(aOp) {
-              if (this.isPending(aOp))
-                pending = aOp;
-            }, this);
+            const PENDING_OPERATIONS = ["enable", "disable", "install",
+                                        "uninstall", "upgrade"];
+            for (let op of PENDING_OPERATIONS) {
+              if (this.isPending(op))
+                pending = op;
+            }
 
             this.setAttribute("pending", pending);
             this._pending.textContent = gStrings.ext.formatStringFromName(
               "notification." + pending,
               [this.mAddon.name, gStrings.brandShortName], 2
             );
           } else {
             this.removeAttribute("pending");
--- a/toolkit/mozapps/extensions/content/selectAddons.js
+++ b/toolkit/mozapps/extensions/content/selectAddons.js
@@ -95,28 +95,28 @@ var gChecking = {
       self._progress.mode = "determined";
 
       // Ensure compatibility overrides are up to date before checking for
       // individual addon updates.
       let ids = [addon.id for each (addon in aAddons)];
       AddonRepository.repopulateCache(ids, function() {
         AddonManagerPrivate.updateAddonRepositoryData(function() {
 
-          aAddons.forEach(function(aAddon) {
+          for (let addonItem of aAddons) {
             // Ignore disabled themes
-            if (aAddon.type != "theme" || !aAddon.userDisabled) {
-              gAddons[aAddon.id] = {
-                addon: aAddon,
+            if (addonItem.type != "theme" || !addonItem.userDisabled) {
+              gAddons[addonItem.id] = {
+                addon: addonItem,
                 install: null,
-                wasActive: aAddon.isActive
+                wasActive: addonItem.isActive
               }
             }
 
-            aAddon.findUpdates(self, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
-          });
+            addonItem.findUpdates(self, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+          }
         });
       });
     });
   },
 
   onUpdateAvailable: function(aAddon, aInstall) {
     // If the add-on can be upgraded then remember the new version
     if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE)
@@ -139,35 +139,35 @@ var gChecking = {
       if (orderA != orderB)
         return orderA - orderB;
 
       return String.localeCompare(a.addon.name, b.addon.name);
     });
 
     let rows = document.getElementById("select-rows");
     let lastAddon = null;
-    addons.forEach(function(aEntry) {
+    for (let entry of addons) {
       if (lastAddon &&
-          orderForScope(aEntry.addon.scope) != orderForScope(lastAddon.scope)) {
+          orderForScope(entry.addon.scope) != orderForScope(lastAddon.scope)) {
         let separator = document.createElement("separator");
         rows.appendChild(separator);
       }
 
       let row = document.createElement("row");
-      row.setAttribute("id", aEntry.addon.id);
+      row.setAttribute("id", entry.addon.id);
       row.setAttribute("class", "addon");
       rows.appendChild(row);
-      row.setAddon(aEntry.addon, aEntry.install, aEntry.wasActive,
-                   isAddonDistroInstalled(aEntry.addon.id));
+      row.setAddon(entry.addon, entry.install, entry.wasActive,
+                   isAddonDistroInstalled(entry.addon.id));
 
-      if (aEntry.install)
-        aEntry.install.addListener(gUpdate);
+      if (entry.install)
+        entry.install.addListener(gUpdate);
 
-      lastAddon = aEntry.addon;
-    });
+      lastAddon = entry.addon;
+    }
 
     showView(gSelect);
   }
 };
 
 var gSelect = {
   nodeID: "select",
 
--- a/toolkit/mozapps/extensions/content/update.js
+++ b/toolkit/mozapps/extensions/content/update.js
@@ -167,19 +167,18 @@ var gVersionInfoPage = {
       AddonRepository.repopulateCache(ids, function() {
         AddonManagerPrivate.updateAddonRepositoryData(function() {
           gInteruptable = true;
           if (gPendingClose) {
             window.close();
             return;
           }
 
-          gUpdateWizard.addons.forEach(function(aAddon) {
-            aAddon.findUpdates(gVersionInfoPage, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
-          }, this);
+          for (let addon of gUpdateWizard.addons)
+            addon.findUpdates(gVersionInfoPage, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
         });
       });
     });
   },
 
   onAllUpdatesFinished: function() {
     // Filter out any add-ons that were disabled before the application was
     // upgraded or are already compatible
@@ -232,21 +231,21 @@ var gMismatchPage = {
   onPageShow: function ()
   {
     gUpdateWizard.setButtonLabels(null, true,
                                   "mismatchCheckNow", false,
                                   "mismatchDontCheck", false);
     document.documentElement.getButton("next").focus();
 
     var incompatible = document.getElementById("mismatch.incompatible");
-    gUpdateWizard.addons.forEach(function(aAddon) {
+    for (let addon of gUpdateWizard.addons) {
       var listitem = document.createElement("listitem");
-      listitem.setAttribute("label", aAddon.name + " " + aAddon.version);
+      listitem.setAttribute("label", addon.name + " " + addon.version);
       incompatible.appendChild(listitem);
-    });
+    }
   }
 };
 
 var gUpdatePage = {
   _totalCount: 0,
   _completeCount: 0,
   onPageShow: function ()
   {
@@ -258,19 +257,18 @@ var gUpdatePage = {
     gUpdateWizard.setButtonLabels(null, true,
                                   "nextButtonText", true,
                                   "cancelButtonText", false);
     document.documentElement.getButton("next").focus();
 
     gUpdateWizard.errorItems = [];
 
     this._totalCount = gUpdateWizard.addons.length;
-    gUpdateWizard.addons.forEach(function(aAddon) {
-      aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
-    }, this);
+    for (let addon of gUpdateWizard.addons)
+      addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
   },
 
   onAllUpdatesFinished: function() {
     var nextPage = document.getElementById("noupdates");
     if (gUpdateWizard.addonsToUpdate.length > 0)
       nextPage = document.getElementById("found");
     document.documentElement.currentPage = nextPage;
   },
@@ -305,22 +303,22 @@ var gFoundPage = {
   onPageShow: function ()
   {
     gUpdateWizard.setButtonLabels(null, true,
                                   "installButtonText", false,
                                   null, false);
 
     var foundUpdates = document.getElementById("found.updates");
     var itemCount = gUpdateWizard.addonsToUpdate.length;
-    gUpdateWizard.addonsToUpdate.forEach(function(aInstall) {
-      var listItem = foundUpdates.appendItem(aInstall.name + " " + aInstall.version);
+    for (let install of gUpdateWizard.addonsToUpdate) {
+      let listItem = foundUpdates.appendItem(install.name + " " + install.version);
       listItem.setAttribute("type", "checkbox");
       listItem.setAttribute("checked", "true");
-      listItem.install = aInstall;
-    });
+      listItem.install = install;
+    }
 
     if (!gUpdateWizard.xpinstallEnabled) {
       document.getElementById("xpinstallDisabledAlert").hidden = false;
       document.getElementById("enableXPInstall").focus();
       document.documentElement.getButton("next").disabled = true;
     }
     else {
       document.documentElement.getButton("next").focus();
--- a/toolkit/mozapps/extensions/content/xpinstallConfirm.js
+++ b/toolkit/mozapps/extensions/content/xpinstallConfirm.js
@@ -137,21 +137,19 @@ XPInstallConfirm.init = function ()
 }
 
 XPInstallConfirm.onOK = function ()
 {
   Components.classes["@mozilla.org/base/telemetry;1"].
     getService(Components.interfaces.nsITelemetry).
     getHistogramById("SECURITY_UI").
     add(Components.interfaces.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
-  args.installs.forEach(function(install) {
+  for (let install of args.installs)
     install.install();
-  });
   return true;
 }
 
 XPInstallConfirm.onCancel = function ()
 {
-  args.installs.forEach(function(install) {
+  for (let install of args.installs)
     install.cancel();
-  });
   return true;
 }
--- a/toolkit/mozapps/extensions/test/browser/browser_addonrepository_performance.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_addonrepository_performance.js
@@ -9,20 +9,20 @@ Components.utils.import("resource://gre/
 let AddonRepository = tmp.AddonRepository;
 
 var gManagerWindow;
 var gProvider;
 
 function parseParams(aQuery) {
   let params = {};
 
-  aQuery.split("&").forEach(function(aParam) {
-    let [key, value] = aParam.split("=");
+  for (let param of aQuery.split("&")) {
+    let [key, value] = param.split("=");
     params[key] = value;
-  });
+  }
 
   return params;
 }
 
 function test() {
   waitForExplicitFinish();
 
   var gSeenRequest = false;
--- a/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
@@ -32,81 +32,81 @@ function test() {
   waitForExplicitFinish();
 
   run_next_test();
 }
 
 function end_test() {
   // Test generates a lot of available installs so just cancel them all
   AddonManager.getAllInstalls(function(aInstalls) {
-    aInstalls.forEach(function(aInstall) {
-      aInstall.cancel();
-    });
+    for (let install of aInstalls)
+      install.cancel();
 
     finish();
   });
 }
 
 function install_test_addons(aCallback) {
   var installs = [];
 
   // Use a blank update URL
   Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
 
-  ["browser_bug557956_1",
-   "browser_bug557956_2",
-   "browser_bug557956_3",
-   "browser_bug557956_4",
-   "browser_bug557956_5",
-   "browser_bug557956_6",
-   "browser_bug557956_7",
-   "browser_bug557956_8_1",
-   "browser_bug557956_9_1",
-   "browser_bug557956_10"].forEach(function(aName) {
-    AddonManager.getInstallForURL(TESTROOT + "addons/" + aName + ".xpi", function(aInstall) {
+  let names = ["browser_bug557956_1",
+               "browser_bug557956_2",
+               "browser_bug557956_3",
+               "browser_bug557956_4",
+               "browser_bug557956_5",
+               "browser_bug557956_6",
+               "browser_bug557956_7",
+               "browser_bug557956_8_1",
+               "browser_bug557956_9_1",
+               "browser_bug557956_10"];
+  for (let name of names) {
+    AddonManager.getInstallForURL(TESTROOT + "addons/" + name + ".xpi", function(aInstall) {
       installs.push(aInstall);
     }, "application/x-xpinstall");
-  });
+  }
 
   var listener = {
     installCount: 0,
 
     onInstallEnded: function() {
       this.installCount++;
       if (this.installCount == installs.length) {
         // Switch to the test update URL
         Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "browser_bug557956.rdf");
 
         aCallback();
       }
     }
   };
 
-  installs.forEach(function(aInstall) {
-    aInstall.addListener(listener);
-    aInstall.install();
-  });
+  for (let install of installs) {
+    install.addListener(listener);
+    install.install();
+  }
 }
 
 function uninstall_test_addons(aCallback) {
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org",
                                "addon6@tests.mozilla.org",
                                "addon7@tests.mozilla.org",
                                "addon8@tests.mozilla.org",
                                "addon9@tests.mozilla.org",
                                "addon10@tests.mozilla.org"],
                                function(aAddons) {
-    aAddons.forEach(function(aAddon) {
-      if (aAddon)
-        aAddon.uninstall();
-    });
+    for (let addon of aAddons) {
+      if (addon)
+        addon.uninstall();
+    }
     aCallback();
   });
 }
 
 function open_compatibility_window(aInactiveAddonIds, aCallback) {
   // This will reset the longer timeout multiplier to 2 which will give each
   // test that calls open_compatibility_window a minimum of 60 seconds to
   // complete.
@@ -348,19 +348,18 @@ add_test(function() {
                                  "addon4@tests.mozilla.org",
                                  "addon5@tests.mozilla.org",
                                  "addon6@tests.mozilla.org",
                                  "addon7@tests.mozilla.org",
                                  "addon8@tests.mozilla.org",
                                  "addon9@tests.mozilla.org",
                                  "addon10@tests.mozilla.org"],
                                  function(aAddons) {
-      aAddons.forEach(function(aAddon) {
-        aAddon.userDisabled = true;
-      });
+      for (let addon of aAddons)
+        addon.userDisabled = true;
 
       // These add-ons were inactive in the old application
       var inactiveAddonIds = [
         "addon1@tests.mozilla.org",
         "addon2@tests.mozilla.org",
         "addon3@tests.mozilla.org",
         "addon4@tests.mozilla.org",
         "addon5@tests.mozilla.org",
@@ -384,19 +383,18 @@ add_test(function() {
 // Tests that the right UI shows for when no updates are available
 add_test(function() {
   install_test_addons(function() {
     AddonManager.getAddonsByIDs(["addon7@tests.mozilla.org",
                                  "addon8@tests.mozilla.org",
                                  "addon9@tests.mozilla.org",
                                  "addon10@tests.mozilla.org"],
                                  function(aAddons) {
-      aAddons.forEach(function(aAddon) {
-        aAddon.uninstall();
-      });
+      for (let addon of aAddons)
+        addon.uninstall();
 
       // These add-ons were inactive in the old application
       var inactiveAddonIds = [
         "addon2@tests.mozilla.org",
         "addon4@tests.mozilla.org",
         "addon5@tests.mozilla.org"
       ];
 
@@ -445,22 +443,23 @@ add_test(function() {
                                  "addon4@tests.mozilla.org",
                                  "addon5@tests.mozilla.org",
                                  "addon6@tests.mozilla.org",
                                  "addon7@tests.mozilla.org",
                                  "addon8@tests.mozilla.org",
                                  "addon9@tests.mozilla.org",
                                  "addon10@tests.mozilla.org"],
                                  function(aAddons) {
-      aAddons.forEach(function(aAddon) {
-        if (aAddon.id == "addon10@tests.mozilla.org")
-          is(aAddon.isCompatible, true, "Addon10 should be compatible before compat overrides are refreshed");
+
+      for (let addon of aAddons) {
+        if (addon.id == "addon10@tests.mozilla.org")
+          is(addon.isCompatible, true, "Addon10 should be compatible before compat overrides are refreshed");
         else
-          aAddon.uninstall();
-      });
+          addon.uninstall();
+      }
 
       Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, TESTROOT + "browser_bug557956.xml");
       Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
 
       open_compatibility_window([], function(aWindow) {
         var doc = aWindow.document;
         wait_for_page(aWindow, "mismatch", function(aWindow) {
           var items = get_list_names(doc.getElementById("mismatch.incompatible"));
--- a/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
@@ -79,27 +79,26 @@ var gInstallNotificationObserver = {
   }
 };
 
 
 function test_confirmation(aWindow, aExpectedURLs) {
   var list = aWindow.document.getElementById("itemList");
   is(list.childNodes.length, aExpectedURLs.length, "Should be the right number of installs");
 
-  aExpectedURLs.forEach(function(aURL) {
-    var node = list.firstChild;
-    while (node) {
-      if (node.url == aURL) {
-        ok(true, "Should have seen " + aURL + " in the list");
-        return;
+  for (let url of aExpectedURLs) {
+    let found = false;
+    for (let node of list.children) {
+      if (node.url == url) {
+        found = true;
+        break;
       }
-      node = node.nextSibling;
     }
-    ok(false, "Should have seen " + aURL + " in the list");
-  });
+    ok(found, "Should have seen " + url + " in the list");
+  }
 
   aWindow.document.documentElement.cancelDialog();
 }
 
 
 function test() {
   waitForExplicitFinish();
   
--- a/toolkit/mozapps/extensions/test/browser/browser_bug581076.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug581076.js
@@ -24,19 +24,18 @@ function test() {
     gManagerWindow = aWindow;
     run_next_test();
   });
 }
 
 function end_test() {
   // Test generates a lot of available installs so just cancel them all
   AddonManager.getAllInstalls(function(aInstalls) {
-    aInstalls.forEach(function(aInstall) {
-      aInstall.cancel();
-    });
+    for (let install of aInstalls)
+      install.cancel();
 
     close_manager(gManagerWindow, finish);
   });
 }
 
 function search(aRemoteSearch, aCallback) {
   waitForFocus(function() {
     var searchBox = gManagerWindow.document.getElementById("header-search");
--- a/toolkit/mozapps/extensions/test/browser/browser_checkAddonCompatibility.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_checkAddonCompatibility.js
@@ -12,23 +12,23 @@ function test() {
 
   AddonManager.getAllAddons(function gAACallback(aAddons) {
     // Sort add-ons (by type and name) to improve output.
     aAddons.sort(function compareTypeName(a, b) {
       return a.type.localeCompare(b.type) || a.name.localeCompare(b.name);
     });
 
     let allCompatible = true;
-    aAddons.forEach(function checkCompatibility(a) {
+    for (let a of aAddons) {
       // Ignore plugins.
       if (a.type == "plugin")
-        return;
+        continue;
 
       ok(a.isCompatible, a.type + " " + a.name + " " + a.version + " should be compatible");
       allCompatible = allCompatible && a.isCompatible;
-    });
+    }
     // Add a reminder.
     if (!allCompatible)
       ok(false, "As this test failed, test browser_bug557956.js should have failed, too.");
 
     finish();
   });
 }
--- a/toolkit/mozapps/extensions/test/browser/browser_discovery.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_discovery.js
@@ -125,34 +125,33 @@ function testHash(aBrowser, aTestAddonVi
   if (aTestAddonVisible[2])
     ok("addon3@tests.mozilla.org" in data, "Test add-on 3 should be listed");
   else
     ok(!("addon3@tests.mozilla.org" in data), "Test add-on 3 should not be listed");
 
   // Test against all the add-ons the manager knows about since plugins and
   // app extensions may exist
   AddonManager.getAllAddons(function(aAddons) {
-    aAddons.forEach(function(aAddon) {
-      if (!(aAddon.id in data)) {
+    for (let addon of aAddons) {
+      if (!(addon.id in data)) {
         // Test add-ons will have shown an error if necessary above
-        if (aAddon.id.substring(6) != "@tests.mozilla.org")
-          ok(false, "Add-on " + aAddon.id + " was not included in the data");
-        return;
+        if (addon.id.substring(6) != "@tests.mozilla.org")
+          ok(false, "Add-on " + addon.id + " was not included in the data");
+        continue;
       }
 
-      info("Testing data for add-on " + aAddon.id);
-      var addonData = data[aAddon.id];
-      is(addonData.name, aAddon.name, "Name should be correct");
-      is(addonData.version, aAddon.version, "Version should be correct");
-      is(addonData.type, aAddon.type, "Type should be correct");
-      is(addonData.userDisabled, aAddon.userDisabled, "userDisabled should be correct");
-      is(addonData.isBlocklisted, aAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED, "blocklisted should be correct");
-      is(addonData.isCompatible, aAddon.isCompatible, "isCompatible should be correct");
-    });
-
+      info("Testing data for add-on " + addon.id);
+      var addonData = data[addon.id];
+      is(addonData.name, addon.name, "Name should be correct");
+      is(addonData.version, addon.version, "Version should be correct");
+      is(addonData.type, addon.type, "Type should be correct");
+      is(addonData.userDisabled, addon.userDisabled, "userDisabled should be correct");
+      is(addonData.isBlocklisted, addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED, "blocklisted should be correct");
+      is(addonData.isCompatible, addon.isCompatible, "isCompatible should be correct");
+    }
     aCallback();
   });
 }
 
 function isLoading() {
   var loading = gManagerWindow.document.getElementById("discover-view").selectedPanel ==
                 gManagerWindow.document.getElementById("discover-loading");
   if (loading) {
--- a/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
@@ -100,27 +100,26 @@ function end_test() {
     finish();
   });
 }
 
 function test_confirmation(aWindow, aExpectedURLs) {
   var list = aWindow.document.getElementById("itemList");
   is(list.childNodes.length, aExpectedURLs.length, "Should be the right number of installs");
 
-  aExpectedURLs.forEach(function(aURL) {
-    var node = list.firstChild;
-    while (node) {
-      if (node.url == aURL) {
-        ok(true, "Should have seen " + aURL + " in the list");
-        return;
+  for (let url of aExpectedURLs) {
+    let found = false;
+    for (let node of list.children) {
+      if (node.url == url) {
+        found = true;
+        break;
       }
-      node = node.nextSibling;
     }
-    ok(false, "Should have seen " + aURL + " in the list");
-  });
+    ok(found, "Should have seen " + url + " in the list");
+  }
 
   aWindow.document.documentElement.cancelDialog();
 }
 
 // Simulates dropping a URL onto the manager
 add_test(function() {
   var url = TESTROOT + "addons/browser_dragdrop1.xpi";
 
--- a/toolkit/mozapps/extensions/test/browser/browser_searching.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_searching.js
@@ -56,17 +56,18 @@ function test() {
     name: "PASS - g - reSEARCHing SEARCH",
     sourceURI: "http://example.com/install2.xpi"
   }, {
     // Does not match query
     name: "FAIL",
     sourceURI: "http://example.com/fail-install1.xpi"
   }]);
 
-  installs.forEach(function(aInstall) { aInstall.install(); });
+  for (let install of installs )
+    install.install();
 
   open_manager("addons://list/extension", function(aWindow) {
     gManagerWindow = aWindow;
     gCategoryUtilities = new CategoryUtilities(gManagerWindow);
     run_next_test();
   });
 }
 
--- a/toolkit/mozapps/extensions/test/browser/browser_select_selection.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_selection.js
@@ -98,48 +98,49 @@ function test() {
 
   gProvider = new MockProvider();
 
   // Set prefs for Distributed Extension Source tests.
   Services.prefs.setBoolPref("extensions.installedDistroAddon.test3@tests.mozilla.org", true);
   Services.prefs.setBoolPref("extensions.installedDistroAddon.test12@tests.mozilla.org", true);
   Services.prefs.setBoolPref("extensions.installedDistroAddon.test15@tests.mozilla.org", true);
 
-  ADDONS.forEach(function(aAddon, aPos) {
-    var addon = new MockAddon("test" + aPos + "@tests.mozilla.org",
-                              "Test Add-on " + aPos, "extension");
+  for (let pos in ADDONS) {
+    let addonItem = ADDONS[pos];
+    let addon = new MockAddon("test" + pos + "@tests.mozilla.org",
+                              "Test Add-on " + pos, "extension");
     addon.version = "1.0";
-    addon.userDisabled = aAddon[0];
-    addon.appDisabled = aAddon[1];
-    addon.isActive = aAddon[3];
-    addon.applyBackgroundUpdates = aAddon[5] ? AddonManager.AUTOUPDATE_ENABLE
+    addon.userDisabled = addonItem[0];
+    addon.appDisabled = addonItem[1];
+    addon.isActive = addonItem[3];
+    addon.applyBackgroundUpdates = addonItem[5] ? AddonManager.AUTOUPDATE_ENABLE
                                              : AddonManager.AUTOUPDATE_DISABLE;
-    addon.scope = aAddon[6];
+    addon.scope = addonItem[6];
 
     // Remove the upgrade permission from non-profile add-ons
     if (addon.scope != AddonManager.SCOPE_PROFILE)
       addon._permissions -= AddonManager.PERM_CAN_UPGRADE;
 
     addon.findUpdates = function(aListener, aReason, aAppVersion, aPlatformVersion) {
-      addon.appDisabled = aAddon[2];
+      addon.appDisabled = addonItem[2];
       addon.isActive = addon.shouldBeActive;
 
-      if (aAddon[4]) {
+      if (addonItem[4]) {
         var newAddon = new MockAddon(this.id, this.name, "extension");
         newAddon.version = "2.0";
         var install = new MockInstall(this.name, this.type, newAddon);
         install.existingAddon = this;
         aListener.onUpdateAvailable(this, install);
       }
 
       aListener.onUpdateFinished(this, AddonManager.UPDATE_STATUS_NO_ERROR);
     };
 
     gProvider.addAddon(addon);
-  });
+  }
 
   gWin = Services.ww.openWindow(null,
                                 "chrome://mozapps/content/extensions/selectAddons.xul",
                                 "",
                                 "chrome,centerscreen,dialog,titlebar",
                                 null);
   waitForFocus(function() {
     waitForView("select", run_next_test);
--- a/toolkit/mozapps/extensions/test/browser/browser_sorting.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_sorting.js
@@ -207,19 +207,18 @@ function set_order(aSortBy, aAscending) 
   var list = gManagerWindow.document.getElementById("addon-list");
   var elements = [];
   var node = list.firstChild;
   while (node) {
     elements.push(node);
     node = node.nextSibling;
   }
   gManagerWindow.sortElements(elements, ["uiState", aSortBy], aAscending);
-  elements.forEach(function(aElement) {
-    list.appendChild(aElement);
-  });
+  for (let element of elements)
+    list.appendChild(element);
 }
 
 function check_order(aExpectedOrder) {
   var order = [];
   var list = gManagerWindow.document.getElementById("addon-list");
   var node = list.firstChild;
   while (node) {
     var id = node.getAttribute("value");
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -64,45 +64,45 @@ var gRestorePrefs = [{name: PREF_LOGGING
                      {name: "extensions.getAddons.getWithPerformance.url"},
                      {name: "extensions.getAddons.search.browseURL"},
                      {name: "extensions.getAddons.search.url"},
                      {name: "extensions.getAddons.cache.enabled"},
                      {name: PREF_SEARCH_MAXRESULTS},
                      {name: PREF_STRICT_COMPAT},
                      {name: PREF_CHECK_COMPATIBILITY}];
 
-gRestorePrefs.forEach(function(aPref) {
-  if (!Services.prefs.prefHasUserValue(aPref.name)) {
-    aPref.type = "clear";
-    return;
+for (let pref of gRestorePrefs) {
+  if (!Services.prefs.prefHasUserValue(pref.name)) {
+    pref.type = "clear";
+    continue;
   }
-  aPref.type = Services.prefs.getPrefType(aPref.name);
-  if (aPref.type == Services.prefs.PREF_BOOL)
-    aPref.value = Services.prefs.getBoolPref(aPref.name);
-  else if (aPref.type == Services.prefs.PREF_INT)
-    aPref.value = Services.prefs.getIntPref(aPref.name);
-  else if (aPref.type == Services.prefs.PREF_STRING)
-    aPref.value = Services.prefs.getCharPref(aPref.name);
-});
+  pref.type = Services.prefs.getPrefType(pref.name);
+  if (pref.type == Services.prefs.PREF_BOOL)
+    pref.value = Services.prefs.getBoolPref(pref.name);
+  else if (pref.type == Services.prefs.PREF_INT)
+    pref.value = Services.prefs.getIntPref(pref.name);
+  else if (pref.type == Services.prefs.PREF_STRING)
+    pref.value = Services.prefs.getCharPref(pref.name);
+}
 
 // Turn logging on for all tests
 Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
 
 registerCleanupFunction(function() {
   // Restore prefs
-  gRestorePrefs.forEach(function(aPref) {
-    if (aPref.type == "clear")
-      Services.prefs.clearUserPref(aPref.name);
-    else if (aPref.type == Services.prefs.PREF_BOOL)
-      Services.prefs.setBoolPref(aPref.name, aPref.value);
-    else if (aPref.type == Services.prefs.PREF_INT)
-      Services.prefs.setIntPref(aPref.name, aPref.value);
-    else if (aPref.type == Services.prefs.PREF_STRING)
-      Services.prefs.setCharPref(aPref.name, aPref.value);
-  });
+  for (let pref of gRestorePrefs) {
+    if (pref.type == "clear")
+      Services.prefs.clearUserPref(pref.name);
+    else if (pref.type == Services.prefs.PREF_BOOL)
+      Services.prefs.setBoolPref(pref.name, pref.value);
+    else if (pref.type == Services.prefs.PREF_INT)
+      Services.prefs.setIntPref(pref.name, pref.value);
+    else if (pref.type == Services.prefs.PREF_STRING)
+      Services.prefs.setCharPref(pref.name, pref.value);
+  }
 
   // Throw an error if the add-ons manager window is open anywhere
   var windows = Services.wm.getEnumerator("Addons:Manager");
   if (windows.hasMoreElements())
     ok(false, "Found unexpected add-ons manager window still open");
   while (windows.hasMoreElements())
     windows.getNext().QueryInterface(Ci.nsIDOMWindow).close();
 
@@ -117,23 +117,23 @@ registerCleanupFunction(function() {
     ok(false, "Found unexpected add-ons installation window still open");
   while (windows.hasMoreElements())
     windows.getNext().QueryInterface(Ci.nsIDOMWindow).close();
 
 
   // We can for now know that getAllInstalls actually calls its callback before
   // it returns so this will complete before the next test start.
   AddonManager.getAllInstalls(function(aInstalls) {
-    aInstalls.forEach(function(aInstall) {
-      if (aInstall instanceof MockInstall)
-        return;
+    for (let install of aInstalls) {
+      if (install instanceof MockInstall)
+        continue;
 
-      ok(false, "Should not have seen an install of " + aInstall.sourceURI.spec + " in state " + aInstall.state);
-      aInstall.cancel();
-    });
+      ok(false, "Should not have seen an install of " + install.sourceURI.spec + " in state " + install.state);
+      install.cancel();
+    }
   });
 });
 
 function log_exceptions(aCallback, ...aArgs) {
   try {
     return aCallback.apply(null, aArgs);
   }
   catch (e) {
@@ -658,73 +658,73 @@ MockProvider.prototype = {
    * managed by this provider.
    *
    * @param  aAddonProperties
    *         An array of objects containing properties describing the add-ons
    * @return Array of the new MockAddons
    */
   createAddons: function MP_createAddons(aAddonProperties) {
     var newAddons = [];
-    aAddonProperties.forEach(function(aAddonProp) {
-      var addon = new MockAddon(aAddonProp.id);
-      for (var prop in aAddonProp) {
+    for (let addonProp of aAddonProperties) {
+      let addon = new MockAddon(addonProp.id);
+      for (let prop in addonProp) {
         if (prop == "id")
           continue;
         if (prop == "applyBackgroundUpdates") {
-          addon._applyBackgroundUpdates = aAddonProp[prop];
+          addon._applyBackgroundUpdates = addonProp[prop];
           continue;
         }
         if (prop == "appDisabled") {
-          addon._appDisabled = aAddonProp[prop];
+          addon._appDisabled = addonProp[prop];
           continue;
         }
-        addon[prop] = aAddonProp[prop];
+        addon[prop] = addonProp[prop];
       }
       if (!addon.optionsType && !!addon.optionsURL)
         addon.optionsType = AddonManager.OPTIONS_TYPE_DIALOG;
 
       // Make sure the active state matches the passed in properties
       addon.isActive = addon.shouldBeActive;
 
       this.addAddon(addon);
       newAddons.push(addon);
-    }, this);
+    }
 
     return newAddons;
   },
 
   /**
    * Creates a set of mock add-on install objects and adds them to the list
    * of installs managed by this provider.
    *
    * @param  aInstallProperties
    *         An array of objects containing properties describing the installs
    * @return Array of the new MockInstalls
    */
   createInstalls: function MP_createInstalls(aInstallProperties) {
     var newInstalls = [];
-    aInstallProperties.forEach(function(aInstallProp) {
-      var install = new MockInstall(aInstallProp.name || null,
-                                    aInstallProp.type || null,
+    for (let installProp of aInstallProperties) {
+      let install = new MockInstall(installProp.name || null,
+                                    installProp.type || null,
                                     null);
-      for (var prop in aInstallProp) {
+      for (let prop in installProp) {
         switch (prop) {
           case "name":
           case "type":
             break;
           case "sourceURI":
-            install[prop] = NetUtil.newURI(aInstallProp[prop]);
+            install[prop] = NetUtil.newURI(installProp[prop]);
             break;
           default:
-            install[prop] = aInstallProp[prop];
+            install[prop] = installProp[prop];
         }
       }
       this.addInstall(install);
       newInstalls.push(install);
-    }, this);
+    }
 
     return newInstalls;
   },
 
   /***** AddonProvider implementation *****/
 
   /**
    * Called to initialize the provider.
@@ -732,19 +732,18 @@ MockProvider.prototype = {
   startup: function MP_startup() {
     this.started = true;
   },
 
   /**
    * Called when the provider should shutdown.
    */
   shutdown: function MP_shutdown() {
-    this.callbackTimers.forEach(function(aTimer) {
-      aTimer.cancel();
-    });
+    for (let timer of this.callbackTimers)
+      timer.cancel();
     this.callbackTimers = [];
 
     this.started = false;
   },
 
   /**
    * Called to get an Addon with a particular ID.
    *
@@ -1213,23 +1212,23 @@ MockInstall.prototype = {
   },
 
   callListeners: function(aMethod) {
     var result = AddonManagerPrivate.callInstallListeners(aMethod, this.listeners,
                                                           this, this.addon);
 
     // Call test listeners after standard listeners to remove race condition
     // between standard and test listeners
-    this.testListeners.forEach(function(aListener) {
+    for (let listener of this.testListeners) {
       try {
-        if (aMethod in aListener)
-          if (aListener[aMethod].call(aListener, this, this.addon) === false)
+        if (aMethod in listener)
+          if (listener[aMethod].call(listener, this, this.addon) === false)
             result = false;
       }
       catch (e) {
         ok(false, "Test listener threw exception: " + e);
       }
-    }, this);
+    }
 
     return result;
   }
 };