Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 20 Nov 2014 12:40:14 +0100
changeset 241033 87e96cf43c685eb1d476ab3a42d29a315a68e75d
parent 241032 bb53837e9532bb8bf9d90b5dcb22d83d5f2d234c (current diff)
parent 240901 6ce1b906c690fd60abfbf4c7dea60d9e405f0ad8 (diff)
child 241034 539c68f30b964e6b6b47b55839ce16662865a7b9
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone36.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
Merge mozilla-central to b2g-inbound
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -116,17 +116,17 @@ skip-if = os == "linux" || e10s # Bug 10
 skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [browser_action_searchengine_alias.js]
 skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
 skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [browser_alltabslistener.js]
 [browser_autocomplete_a11y_label.js]
-skip-if = e10s # Bug ????? - no e10s switch-to-tab support yet
+skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
 [browser_backButtonFitts.js]
 skip-if = os != "win" || e10s # The Fitts Law back button is only supported on Windows (bug 571454) / e10s - Bug 1099154: test touches content (attempts to add an event listener directly to the contentWindow)
 [browser_blob-channelname.js]
 [browser_bookmark_titles.js]
 skip-if = buildapp == 'mulet' || toolkit == "windows" || e10s # Disabled on Windows due to frequent failures (bugs 825739, 841341) / e10s - Bug 1094205 - places doesn't return the right thing in e10s mode, for some reason
 [browser_bug304198.js]
 skip-if = e10s
 [browser_bug321000.js]
@@ -153,55 +153,55 @@ skip-if = e10s # Bug 1093155 - tries to 
 skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug422590.js]
 [browser_bug846489.js]
 [browser_bug423833.js]
 skip-if = true # bug 428712
 [browser_bug424101.js]
 skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 [browser_bug427559.js]
-skip-if = e10s # Bug ?????? - "content window is focused - Got [object ChromeWindow], expected [object XrayWrapper [object Window]]"
+skip-if = e10s # Bug 1102015 - "content window is focused - Got [object ChromeWindow], expected [object CPOW [object Window]]"
 [browser_bug431826.js]
 [browser_bug432599.js]
 [browser_bug435035.js]
 [browser_bug435325.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1099156 - test directly manipulates content
 [browser_bug441778.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug455852.js]
 skip-if = e10s
 [browser_bug460146.js]
 skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
 [browser_bug462289.js]
-skip-if = toolkit == "cocoa" || e10s # Bug ?????? - not sure why this is timing out and crashing!!
+skip-if = toolkit == "cocoa" || e10s # Bug 1102017 - middle-button mousedown on selected tab2 does not activate tab - Didn't expect [object XULElement], but got it
 [browser_bug462673.js]
 skip-if = e10s # Bug 1093404 - test expects sync window opening from content and is disappointed in that expectation
 [browser_bug477014.js]
 skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s
 [browser_bug479408.js]
 skip-if = buildapp == 'mulet'
 [browser_bug481560.js]
-skip-if = e10s # Bug ????? - This bug attached an event listener directly to the content
+skip-if = e10s # Bug 1102018 - This bug attaches an event listener directly to the content, which then never gets called.
 [browser_bug484315.js]
 skip-if = e10s
 [browser_bug491431.js]
 skip-if = buildapp == 'mulet'
 [browser_bug495058.js]
 skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al (and thus replaceTabWithWindow) for e10s
 [browser_bug517902.js]
 skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
 [browser_bug519216.js]
 skip-if = e10s # Bug ?????? - some weird timing issue with progress listeners that fails intermittently
 [browser_bug520538.js]
 [browser_bug521216.js]
 [browser_bug533232.js]
 [browser_bug537013.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls replaceTabWithWindow)
 [browser_bug537474.js]
-skip-if = e10s # Bug ?????? - test doesn't wait for document to be created before it checks it
+skip-if = e10s # Bug 1102020 - test tries to use browserDOMWindow.openURI to open a link, and gets a null rv where it expects a window
 [browser_bug550565.js]
 [browser_bug553455.js]
 skip-if = true # Bug 1094312
 #skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works on mulet? ; for e10s, indefinite waiting halfway through the test, tracked in bug 1093586
 [browser_bug555224.js]
 skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug555767.js]
 skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory
@@ -326,17 +326,17 @@ skip-if = buildapp == 'mulet' || e10s ||
 skip-if = e10s # Bug 863514 - no gesture support.
 [browser_getshortcutoruri.js]
 [browser_hide_removing.js]
 [browser_homeDrop.js]
 skip-if = buildapp == 'mulet'
 [browser_identity_UI.js]
 skip-if = e10s # Bug ?????? - this test fails for obscure reasons on non-windows builds only.
 [browser_keywordBookmarklets.js]
-skip-if = e10s # Bug ?????? - this test fails for obscure reasons on non-windows builds only.
+skip-if = e10s # Bug 1102025 - different principals for the bookmarklet only in e10s mode (unclear if test or 'real' issue)
 [browser_keywordSearch.js]
 skip-if = e10s # Bug 921957 - remote webprogress doesn't supply cancel method on the request object
 [browser_keywordSearch_postData.js]
 [browser_lastAccessedTab.js]
 skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405)
 [browser_locationBarCommand.js]
 skip-if = os == "linux" || e10s # Linux: Intermittent failures, bug 917535; e10s: Bug 1094252 - Focus issues (There should be no focused element - Got [object XULElement], expected null)
 [browser_locationBarExternalLoad.js]
@@ -364,17 +364,17 @@ skip-if = asan # Disabled because it tak
 
 [browser_pinnedTabs.js]
 [browser_plainTextLinks.js]
 skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 [browser_popupUI.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1100707 - test fails in e10s because it can't get accel-w to close the popup (?)
 [browser_popup_blocker.js]
 [browser_printpreview.js]
-skip-if = buildapp == 'mulet' || e10s # Bug ?????? - timeout after logging "Error: Channel closing: too late to send/recv, messages will be lost"
+skip-if = buildapp == 'mulet' || e10s # Bug 1101973 - breaks the next test in e10s, and may be responsible for later timeout after logging "Error: Channel closing: too late to send/recv, messages will be lost"
 [browser_private_browsing_window.js]
 skip-if = buildapp == 'mulet'
 [browser_private_no_prompt.js]
 skip-if = buildapp == 'mulet'
 [browser_relatedTabs.js]
 [browser_remoteTroubleshoot.js]
 support-files =
   test_remoteTroubleshoot.html
--- a/browser/components/loop/LoopRooms.jsm
+++ b/browser/components/loop/LoopRooms.jsm
@@ -155,20 +155,16 @@ let LoopRoomsInternal = {
    */
   getAll: function(version = null, callback) {
     if (!callback) {
       callback = version;
       version = null;
     }
 
     Task.spawn(function* () {
-      let deferredInitialization = Promise.defer();
-      MozLoopService.delayedInitialize(deferredInitialization);
-      yield deferredInitialization.promise;
-
       if (!gDirty) {
         callback(null, [...this.rooms.values()]);
         return;
       }
 
       // Fetch the rooms from the server.
       let url = "/rooms" + (version ? "?version=" + encodeURIComponent(version) : "");
       let response = yield MozLoopService.hawkRequest(this.sessionType, url, "GET");
@@ -195,16 +191,22 @@ let LoopRoomsInternal = {
           eventEmitter.emit("update" + ":" + room.roomToken, room);
         } else {
           // Next, request the detailed information for each room. If the request
           // fails the room data will not be added to the map.
           yield LoopRooms.promise("get", room.roomToken);
         }
       }
 
+      // If there's no rooms in the list, remove the guest created room flag, so that
+      // we don't keep registering for guest when we don't need to.
+      if (this.sessionType == LOOP_SESSION_TYPE.GUEST && !this.rooms.size) {
+        this.setGuestCreatedRoom(false);
+      }
+
       // Set the 'dirty' flag back to FALSE, since the list is as fresh as can be now.
       gDirty = false;
       callback(null, [...this.rooms.values()]);
     }.bind(this)).catch(error => {
       callback(error);
     });
   },
 
@@ -264,21 +266,49 @@ let LoopRoomsInternal = {
     MozLoopService.hawkRequest(this.sessionType, "/rooms", "POST", room)
       .then(response => {
         let data = JSON.parse(response.body);
         extend(room, data);
         // Do not keep this value - it is a request to the server.
         delete room.expiresIn;
         this.rooms.set(room.roomToken, room);
 
+        if (this.sessionType == LOOP_SESSION_TYPE.GUEST) {
+          this.setGuestCreatedRoom(true);
+        }
+
         eventEmitter.emit("add", room);
         callback(null, room);
       }, error => callback(error)).catch(error => callback(error));
   },
 
+  /**
+   * Sets whether or not the user has created a room in guest mode.
+   *
+   * @param {Boolean} created If the user has created the room.
+   */
+  setGuestCreatedRoom: function(created) {
+    if (created) {
+      Services.prefs.setBoolPref("loop.createdRoom", created);
+    } else {
+      Services.prefs.clearUserPref("loop.createdRoom");
+    }
+  },
+
+  /**
+   * Returns true if the user has a created room in guest mode.
+   */
+  getGuestCreatedRoom: function() {
+    try {
+      return Services.prefs.getBoolPref("loop.createdRoom");
+    } catch (x) {
+      return false;
+    }
+  },
+
   open: function(roomToken) {
     let windowData = {
       roomToken: roomToken,
       type: "room"
     };
 
     MozLoopService.openChatWindow(windowData);
   },
@@ -481,16 +511,20 @@ this.LoopRooms = {
   leave: function(roomToken, sessionToken, callback) {
     return LoopRoomsInternal.leave(roomToken, sessionToken, callback);
   },
 
   rename: function(roomToken, newRoomName, callback) {
     return LoopRoomsInternal.rename(roomToken, newRoomName, callback);
   },
 
+  getGuestCreatedRoom: function() {
+    return LoopRoomsInternal.getGuestCreatedRoom();
+  },
+
   promise: function(method, ...params) {
     return new Promise((resolve, reject) => {
       this[method](...params, (error, result) => {
         if (error) {
           reject(error);
         } else {
           resolve(result);
         }
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -363,42 +363,16 @@ function injectLoopAPI(targetWindow) {
           callback(null, chosenButton == 0);
         } catch (ex) {
           callback(cloneValueInto(ex, targetWindow));
         }
       }
     },
 
     /**
-     * Call to ensure that any necessary registrations for the Loop Service
-     * have taken place.
-     *
-     * Callback parameters:
-     * - err null on successful registration, non-null otherwise.
-     *
-     * @param {LOOP_SESSION_TYPE} sessionType
-     * @param {Function} callback Will be called once registration is complete,
-     *                            or straight away if registration has already
-     *                            happened.
-     */
-    ensureRegistered: {
-      enumerable: true,
-      writable: true,
-      value: function(sessionType, callback) {
-        // We translate from a promise to a callback, as we can't pass promises from
-        // Promise.jsm across the priv versus unpriv boundary.
-        MozLoopService.promiseRegisteredWithServers(sessionType).then(() => {
-          callback(null);
-        }, err => {
-          callback(cloneValueInto(err, targetWindow));
-        }).catch(Cu.reportError);
-      }
-    },
-
-    /**
      * Used to note a call url expiry time. If the time is later than the current
      * latest expiry time, then the stored expiry time is increased. For times
      * sooner, this function is a no-op; this ensures we always have the latest
      * expiry time for a url.
      *
      * This is used to determine whether or not we should be registering with the
      * push server on start.
      *
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -414,32 +414,32 @@ let MozLoopServiceInternal = {
       this.deferredRegistrations.delete(sessionType);
       log.debug("Cleared deferredRegistration for sessionType:", sessionType);
     });
 
     return result;
   },
 
   /**
-   * Performs a hawk based request to the loop server.
+   * Performs a hawk based request to the loop server - there is no pre-registration
+   * for this request, if this is required, use hawkRequest.
    *
    * @param {LOOP_SESSION_TYPE} sessionType The type of session to use for the request.
    *                                        This is one of the LOOP_SESSION_TYPE members.
    * @param {String} path The path to make the request to.
    * @param {String} method The request method, e.g. 'POST', 'GET'.
    * @param {Object} payloadObj An object which is converted to JSON and
    *                            transmitted with the request.
    * @returns {Promise}
    *        Returns a promise that resolves to the response of the API call,
    *        or is rejected with an error.  If the server response can be parsed
    *        as JSON and contains an 'error' property, the promise will be
    *        rejected with this JSON-parsed response.
    */
-  hawkRequest: function(sessionType, path, method, payloadObj) {
-    log.debug("hawkRequest: " + path, sessionType);
+  hawkRequestInternal: function(sessionType, path, method, payloadObj) {
     if (!gHawkClient) {
       gHawkClient = new HawkClient(this.loopServerUri);
     }
 
     let sessionToken;
     try {
       sessionToken = Services.prefs.getCharPref(this.getSessionTokenPrefName(sessionType));
     } catch (x) {
@@ -475,16 +475,42 @@ let MozLoopServiceInternal = {
           this.setError("registration", error);
         }
       }
       throw error;
     });
   },
 
   /**
+   * Performs a hawk based request to the loop server, registering if necessary.
+   *
+   * @param {LOOP_SESSION_TYPE} sessionType The type of session to use for the request.
+   *                                        This is one of the LOOP_SESSION_TYPE members.
+   * @param {String} path The path to make the request to.
+   * @param {String} method The request method, e.g. 'POST', 'GET'.
+   * @param {Object} payloadObj An object which is converted to JSON and
+   *                            transmitted with the request.
+   * @returns {Promise}
+   *        Returns a promise that resolves to the response of the API call,
+   *        or is rejected with an error.  If the server response can be parsed
+   *        as JSON and contains an 'error' property, the promise will be
+   *        rejected with this JSON-parsed response.
+   */
+  hawkRequest: function(sessionType, path, method, payloadObj) {
+    log.debug("hawkRequest: " + path, sessionType);
+    return new Promise((resolve, reject) => {
+      MozLoopService.promiseRegisteredWithServers(sessionType).then(() => {
+        this.hawkRequestInternal(sessionType, path, method, payloadObj).then(resolve, reject);
+      }, err => {
+        reject(err);
+      }).catch(reject);
+    });
+  },
+
+  /**
    * Generic hawkRequest onError handler for the hawkRequest promise.
    *
    * @param {Object} error - error reporting object
    *
    */
 
   _hawkRequestError: function(error) {
     log.error("Loop hawkRequest error:", error);
@@ -576,17 +602,17 @@ let MozLoopServiceInternal = {
     // that will register only the calls notification.
     let msg = {
         simplePushURL: callsPushURL,
         simplePushURLs: {
           calls: callsPushURL,
           rooms: roomsPushURL,
         },
     };
-    return this.hawkRequest(sessionType, "/registration", "POST", msg)
+    return this.hawkRequestInternal(sessionType, "/registration", "POST", msg)
       .then((response) => {
         // If this failed we got an invalid token.
         if (!this.storeSessionToken(sessionType, response.headers)) {
           return Promise.reject("session-token-wrong-size");
         }
 
         log.debug("Successfully registered with server for sessionType", sessionType);
         this.clearError("registration");
@@ -632,17 +658,17 @@ let MozLoopServiceInternal = {
    */
   unregisterFromLoopServer: function(sessionType, pushURL) {
     let prefType = Services.prefs.getPrefType(this.getSessionTokenPrefName(sessionType));
     if (prefType == Services.prefs.PREF_INVALID) {
       return Promise.resolve("already unregistered");
     }
 
     let unregisterURL = "/registration?simplePushURL=" + encodeURIComponent(pushURL);
-    return this.hawkRequest(sessionType, unregisterURL, "DELETE")
+    return this.hawkRequestInternal(sessionType, unregisterURL, "DELETE")
       .then(() => {
         log.debug("Successfully unregistered from server for sessionType", sessionType);
       },
       error => {
         if (error.code === 401) {
           // Authorization failed, invalid token. This is fine since it may mean we already logged out.
           return;
         }
@@ -821,17 +847,17 @@ let MozLoopServiceInternal = {
 
   /**
    * Fetch Firefox Accounts (FxA) OAuth parameters from the Loop Server.
    *
    * @return {Promise} resolved with the body of the hawk request for OAuth parameters.
    */
   promiseFxAOAuthParameters: function() {
     const SESSION_TYPE = LOOP_SESSION_TYPE.FXA;
-    return this.hawkRequest(SESSION_TYPE, "/fxa-oauth/params", "POST").then(response => {
+    return this.hawkRequestInternal(SESSION_TYPE, "/fxa-oauth/params", "POST").then(response => {
       if (!this.storeSessionToken(SESSION_TYPE, response.headers)) {
         throw new Error("Invalid FxA hawk token returned");
       }
       let prefType = Services.prefs.getPrefType(this.getSessionTokenPrefName(SESSION_TYPE));
       if (prefType == Services.prefs.PREF_INVALID) {
         throw new Error("No FxA hawk token returned and we don't have one saved");
       }
 
@@ -1021,16 +1047,17 @@ this.MozLoopService = {
       if (window) {
         window.LoopUI.playSound("room-joined");
       }
     });
 
     // If expiresTime is not in the future and the user hasn't
     // previously authenticated then skip registration.
     if (!MozLoopServiceInternal.urlExpiryTimeIsInFuture() &&
+        !LoopRooms.getGuestCreatedRoom() &&
         !MozLoopServiceInternal.fxAOAuthTokenData) {
       return Promise.resolve("registration not needed");
     }
 
     let deferredInitialization = Promise.defer();
     gInitializeTimerFunc(deferredInitialization);
 
     return deferredInitialization.promise;
@@ -1054,17 +1081,18 @@ this.MozLoopService = {
     error => {
       // If we get a non-object then setError was already called for a different error type.
       if (typeof(error) == "object") {
         MozLoopServiceInternal.setError("initialization", error, () => MozLoopService.delayedInitialize(Promise.defer()));
       }
     });
 
     try {
-      if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
+      if (MozLoopServiceInternal.urlExpiryTimeIsInFuture() ||
+          LoopRooms.getGuestCreatedRoom()) {
         yield this.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
       } else {
         log.debug("delayedInitialize: URL expiry time isn't in the future so not registering as a guest");
       }
     } catch (ex) {
       log.debug("MozLoopService: Failure of guest registration", ex);
       deferredInitialization.reject(ex);
       yield completedPromise;
--- a/browser/components/loop/content/js/client.js
+++ b/browser/components/loop/content/js/client.js
@@ -77,50 +77,39 @@ loop.Client = (function($) {
      */
     _failureHandler: function(cb, error) {
       var message = "HTTP " + error.code + " " + error.error + "; " + error.message;
       console.error(message);
       cb(error);
     },
 
     /**
-     * Ensures the client is registered with the push server.
+     * Requests a call URL from the Loop server. It will note the
+     * expiry time for the url with the mozLoop api.  It will select the
+     * appropriate hawk session to use based on whether or not the user
+     * is currently logged into a Firefox account profile.
      *
      * Callback parameters:
-     * - err null on successful registration, non-null otherwise.
-     *
-     * @param {LOOP_SESSION_TYPE} sessionType Guest or FxA
-     * @param {Function} cb Callback(err)
-     */
-    _ensureRegistered: function(sessionType, cb) {
-      this.mozLoop.ensureRegistered(sessionType, function(error) {
-        if (error) {
-          console.log("Error registering with Loop server, code: " + error);
-          cb(error);
-          return;
-        } else {
-          cb(null);
-        }
-      });
-    },
-
-    /**
-     * Internal handler for requesting a call url from the server.
-     *
-     * Callback parameters:
-     * - err null on successful registration, non-null otherwise.
+     * - err null on successful request, non-null otherwise.
      * - callUrlData an object of the obtained call url data if successful:
      * -- callUrl: The url of the call
      * -- expiresAt: The amount of hours until expiry of the url
      *
-     * @param {LOOP_SESSION_TYPE} sessionType
+     * @param  {String} simplepushUrl a registered Simple Push URL
      * @param  {string} nickname the nickname of the future caller
      * @param  {Function} cb Callback(err, callUrlData)
      */
-    _requestCallUrlInternal: function(sessionType, nickname, cb) {
+    requestCallUrl: function(nickname, cb) {
+      var sessionType;
+      if (this.mozLoop.userProfile) {
+        sessionType = this.mozLoop.LOOP_SESSION_TYPE.FXA;
+      } else {
+        sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
+      }
+
       this.mozLoop.hawkRequest(sessionType, "/call-url/", "POST",
                                {callerId: nickname},
         function (error, responseText) {
           if (error) {
             this._telemetryAdd("LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", false);
             this._failureHandler(cb, error);
             return;
           }
@@ -149,27 +138,16 @@ loop.Client = (function($) {
      * @param {mozLoop.LOOP_SESSION_TYPE} sessionType The type of session which
      *                                                the url belongs to.
      * @param {function} cb Callback function used for handling an error
      *                      response. XXX The incoming call panel does not
      *                      exist after the block button is clicked therefore
      *                      it does not make sense to display an error.
      **/
     deleteCallUrl: function(token, sessionType, cb) {
-      this._ensureRegistered(sessionType, function(err) {
-        if (err) {
-          cb(err);
-          return;
-        }
-
-        this._deleteCallUrlInternal(token, sessionType, cb);
-      }.bind(this));
-    },
-
-    _deleteCallUrlInternal: function(token, sessionType, cb) {
       function deleteRequestCallback(error, responseText) {
         if (error) {
           this._failureHandler(cb, error);
           return;
         }
 
         try {
           cb(null);
@@ -180,50 +158,16 @@ loop.Client = (function($) {
       }
 
       this.mozLoop.hawkRequest(sessionType,
                                "/call-url/" + token, "DELETE", null,
                                deleteRequestCallback.bind(this));
     },
 
     /**
-     * Requests a call URL from the Loop server. It will note the
-     * expiry time for the url with the mozLoop api.  It will select the
-     * appropriate hawk session to use based on whether or not the user
-     * is currently logged into a Firefox account profile.
-     *
-     * Callback parameters:
-     * - err null on successful registration, non-null otherwise.
-     * - callUrlData an object of the obtained call url data if successful:
-     * -- callUrl: The url of the call
-     * -- expiresAt: The amount of hours until expiry of the url
-     *
-     * @param  {String} simplepushUrl a registered Simple Push URL
-     * @param  {string} nickname the nickname of the future caller
-     * @param  {Function} cb Callback(err, callUrlData)
-     */
-    requestCallUrl: function(nickname, cb) {
-      var sessionType;
-      if (this.mozLoop.userProfile) {
-        sessionType = this.mozLoop.LOOP_SESSION_TYPE.FXA;
-      } else {
-        sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
-      }
-
-      this._ensureRegistered(sessionType, function(err) {
-        if (err) {
-          cb(err);
-          return;
-        }
-
-        this._requestCallUrlInternal(sessionType, nickname, cb);
-      }.bind(this));
-    },
-
-    /**
      * Sets up an outgoing call, getting the relevant data from the server.
      *
      * Callback parameters:
      * - err null on successful registration, non-null otherwise.
      * - result an object of the obtained data for starting the call, if successful
      *
      * @param {Array} calleeIds an array of emails and phone numbers.
      * @param {String} callType the type of call.
--- a/browser/components/loop/test/desktop-local/client_test.js
+++ b/browser/components/loop/test/desktop-local/client_test.js
@@ -27,17 +27,16 @@ describe("loop.Client", function() {
     sandbox = sinon.sandbox.create();
     callback = sinon.spy();
     fakeToken = "fakeTokenText";
     mozLoop = {
       getLoopPref: sandbox.stub()
         .returns(null)
         .withArgs("hawk-session-token")
         .returns(fakeToken),
-      ensureRegistered: sinon.stub().callsArgWith(1, null),
       noteCallUrlExpiry: sinon.spy(),
       hawkRequest: sinon.stub(),
       LOOP_SESSION_TYPE: {
         GUEST: 1,
         FXA: 2
       },
       userProfile: null,
       telemetryAdd: sinon.spy()
@@ -50,31 +49,16 @@ describe("loop.Client", function() {
   });
 
   afterEach(function() {
     sandbox.restore();
   });
 
   describe("loop.Client", function() {
     describe("#deleteCallUrl", function() {
-      it("should ensure loop is registered", function() {
-        client.deleteCallUrl("fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, callback);
-
-        sinon.assert.calledOnce(mozLoop.ensureRegistered);
-      });
-
-      it("should send an error when registration fails", function() {
-        mozLoop.ensureRegistered.callsArgWith(1, "offline");
-
-        client.deleteCallUrl("fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, callback);
-
-        sinon.assert.calledOnce(callback);
-        sinon.assert.calledWithExactly(callback, "offline");
-      });
-
       it("should make a delete call to /call-url/{fakeToken}", function() {
         client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.GUEST, callback);
 
         sinon.assert.calledOnce(hawkRequestStub);
         sinon.assert.calledWith(hawkRequestStub,
                                 mozLoop.LOOP_SESSION_TYPE.GUEST,
                                 "/call-url/" + fakeToken, "DELETE");
       });
@@ -101,31 +85,16 @@ describe("loop.Client", function() {
         sinon.assert.calledOnce(callback);
         sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
           return err.code == 400 && "invalid token" == err.message;
         }));
       });
     });
 
     describe("#requestCallUrl", function() {
-      it("should ensure loop is registered", function() {
-        client.requestCallUrl("foo", callback);
-
-        sinon.assert.calledOnce(mozLoop.ensureRegistered);
-      });
-
-      it("should send an error when registration fails", function() {
-        mozLoop.ensureRegistered.callsArgWith(1, "offline");
-
-        client.requestCallUrl("foo", callback);
-
-        sinon.assert.calledOnce(callback);
-        sinon.assert.calledWithExactly(callback, "offline");
-      });
-
       it("should post to /call-url/", function() {
         client.requestCallUrl("foo", callback);
 
         sinon.assert.calledOnce(hawkRequestStub);
         sinon.assert.calledWithExactly(hawkRequestStub, sinon.match.number,
           "/call-url/", "POST", {callerId: "foo"}, sinon.match.func);
       });
 
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -546,53 +546,54 @@ describe("loop.conversation", function()
 
           sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
           sinon.assert.calledWithExactly(
             navigator.mozLoop.calls.clearCallInProgress, "8699");
         });
       });
 
       describe("#blocked", function() {
-        var mozLoop;
+        var mozLoop, deleteCallUrlStub;
 
         beforeEach(function() {
           icView = mountTestComponent();
 
           icView._websocket = {
             decline: sinon.spy(),
             close: sinon.stub()
           };
           sandbox.stub(window, "close");
 
           mozLoop = {
             LOOP_SESSION_TYPE: {
               GUEST: 1,
               FXA: 2
             }
           };
+
+          deleteCallUrlStub = sandbox.stub(loop.Client.prototype,
+                                           "deleteCallUrl");
         });
 
         it("should call mozLoop.stopAlerting", function() {
           icView.declineAndBlock();
 
           sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
         });
 
         it("should call delete call", function() {
           sandbox.stub(conversation, "get").withArgs("callToken")
                                            .returns("fakeToken")
                                            .withArgs("sessionType")
                                            .returns(mozLoop.LOOP_SESSION_TYPE.FXA);
 
-          var deleteCallUrl = sandbox.stub(loop.Client.prototype,
-                                           "deleteCallUrl");
           icView.declineAndBlock();
 
-          sinon.assert.calledOnce(deleteCallUrl);
-          sinon.assert.calledWithExactly(deleteCallUrl,
+          sinon.assert.calledOnce(deleteCallUrlStub);
+          sinon.assert.calledWithExactly(deleteCallUrlStub,
             "fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, sinon.match.func);
         });
 
         it("should get callToken from conversation model", function() {
           sandbox.stub(conversation, "get");
           icView.declineAndBlock();
 
           sinon.assert.called(conversation.get);
@@ -601,19 +602,17 @@ describe("loop.conversation", function()
         });
 
         it("should trigger error handling in case of error", function() {
           // XXX just logging to console for now
           var log = sandbox.stub(console, "log");
           var fakeError = {
             error: true
           };
-          sandbox.stub(loop.Client.prototype, "deleteCallUrl", function(_, __, cb) {
-            cb(fakeError);
-          });
+          deleteCallUrlStub.callsArgWith(2, fakeError);
           icView.declineAndBlock();
 
           sinon.assert.calledOnce(log);
           sinon.assert.calledWithExactly(log, fakeError);
         });
 
         it("should close the window", function() {
           icView.declineAndBlock();
--- a/browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js
@@ -32,17 +32,17 @@ add_task(function* setup_server() {
   loopServer.registerPathHandler("/401", errorRequestHandler);
   loopServer.registerPathHandler("/404", errorRequestHandler);
   loopServer.registerPathHandler("/500", errorRequestHandler);
   loopServer.registerPathHandler("/503", errorRequestHandler);
 });
 
 add_task(function* error_offline() {
   Services.io.offline = true;
-  yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/offline", "GET").then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/offline", "GET").then(
     () => Assert.ok(false, "Should have rejected"),
     (error) => {
       MozLoopServiceInternal.setError("testing", error);
       Assert.strictEqual(MozLoopService.errors.size, 1, "Should be one error");
 
       // Network errors are converted to the "network" errorType.
       let err = MozLoopService.errors.get("network");
       Assert.strictEqual(err.code, null);
@@ -53,17 +53,17 @@ add_task(function* error_offline() {
   Services.io.offline = false;
 });
 
 add_task(cleanup_between_tests);
 
 add_task(function* guest_401() {
   Services.prefs.setCharPref("loop.hawk-session-token", "guest");
   Services.prefs.setCharPref("loop.hawk-session-token.fxa", "fxa");
-  yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/401", "POST").then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/401", "POST").then(
     () => Assert.ok(false, "Should have rejected"),
     (error) => {
       Assert.strictEqual(Services.prefs.getPrefType("loop.hawk-session-token"),
                          Services.prefs.PREF_INVALID,
                          "Guest session token should have been cleared");
       Assert.strictEqual(Services.prefs.getCharPref("loop.hawk-session-token.fxa"),
                          "fxa",
                          "FxA session token should NOT have been cleared");
@@ -78,17 +78,17 @@ add_task(function* guest_401() {
   });
 });
 
 add_task(cleanup_between_tests);
 
 add_task(function* fxa_401() {
   Services.prefs.setCharPref("loop.hawk-session-token", "guest");
   Services.prefs.setCharPref("loop.hawk-session-token.fxa", "fxa");
-  yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.FXA, "/401", "POST").then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.FXA, "/401", "POST").then(
     () => Assert.ok(false, "Should have rejected"),
     (error) => {
       Assert.strictEqual(Services.prefs.getCharPref("loop.hawk-session-token"),
                          "guest",
                          "Guest session token should NOT have been cleared");
       Assert.strictEqual(Services.prefs.getPrefType("loop.hawk-session-token.fxa"),
                          Services.prefs.PREF_INVALID,
                          "Fxa session token should have been cleared");
@@ -100,68 +100,68 @@ add_task(function* fxa_401() {
       Assert.strictEqual(err.friendlyDetails, getLoopString("password_changed_question"));
       Assert.strictEqual(err.friendlyDetailsButtonLabel, getLoopString("retry_button"));
   });
 });
 
 add_task(cleanup_between_tests);
 
 add_task(function* error_404() {
-  yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/404", "GET").then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/404", "GET").then(
     () => Assert.ok(false, "Should have rejected"),
     (error) => {
       MozLoopServiceInternal.setError("testing", error);
       Assert.strictEqual(MozLoopService.errors.size, 1, "Should be one error");
 
       let err = MozLoopService.errors.get("testing");
       Assert.strictEqual(err.code, 404);
       Assert.strictEqual(err.friendlyMessage, getLoopString("generic_failure_title"));
       Assert.equal(err.friendlyDetails, null);
       Assert.equal(err.friendlyDetailsButtonLabel, null);
   });
 });
 
 add_task(cleanup_between_tests);
 
 add_task(function* error_500() {
-  yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
     () => Assert.ok(false, "Should have rejected"),
     (error) => {
       MozLoopServiceInternal.setError("testing", error);
       Assert.strictEqual(MozLoopService.errors.size, 1, "Should be one error");
 
       let err = MozLoopService.errors.get("testing");
       Assert.strictEqual(err.code, 500);
       Assert.strictEqual(err.friendlyMessage, getLoopString("service_not_available"));
       Assert.strictEqual(err.friendlyDetails, getLoopString("try_again_later"));
       Assert.strictEqual(err.friendlyDetailsButtonLabel, getLoopString("retry_button"));
   });
 });
 
 add_task(cleanup_between_tests);
 
 add_task(function* profile_500() {
-  yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
     () => Assert.ok(false, "Should have rejected"),
     (error) => {
       MozLoopServiceInternal.setError("profile", error);
       Assert.strictEqual(MozLoopService.errors.size, 1, "Should be one error");
 
       let err = MozLoopService.errors.get("profile");
       Assert.strictEqual(err.code, 500);
       Assert.strictEqual(err.friendlyMessage, getLoopString("problem_accessing_account"));
       Assert.equal(err.friendlyDetails, null);
       Assert.equal(err.friendlyDetailsButtonLabel, null);
   });
 });
 
 add_task(cleanup_between_tests);
 
 add_task(function* error_503() {
-  yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/503", "GET").then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/503", "GET").then(
     () => Assert.ok(false, "Should have rejected"),
     (error) => {
       MozLoopServiceInternal.setError("testing", error);
       Assert.strictEqual(MozLoopService.errors.size, 1, "Should be one error");
 
       let err = MozLoopService.errors.get("testing");
       Assert.strictEqual(err.code, 503);
       Assert.strictEqual(err.friendlyMessage, getLoopString("service_not_available"));
--- a/browser/devtools/framework/test/browser_devtools_api.js
+++ b/browser/devtools/framework/test/browser_devtools_api.js
@@ -71,17 +71,20 @@ function runTests1(aTab) {
     is(toolbox.target, target, "toolbox target is correct");
     is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct");
 
     ok(events["init"], "init event fired");
     ok(events["ready"], "ready event fired");
 
     gDevTools.unregisterTool(toolId1);
 
-    runTests2();
+    // Wait for unregisterTool to select the next tool before calling runTests2,
+    // otherwise we will receive the wrong select event when waiting for
+    // unregisterTool to select the next tool in continueTests below.
+    toolbox.once("select", runTests2);
   });
 }
 
 // Test scenario 2: the tool definition build method returns panel instance.
 function runTests2() {
   let toolDefinition = {
     id: toolId2,
     isTargetSupported: function() true,
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -87,18 +87,20 @@ VIAddVersionKey "LegalTrademarks" "${Bra
 #endif
 VIAddVersionKey "LegalCopyright"  "${CompanyName}"
 VIAddVersionKey "FileVersion"     "${AppVersion}"
 VIAddVersionKey "ProductVersion"  "${AppVersion}"
 # Comments is not used but left below commented out for future reference
 # VIAddVersionKey "Comments"        "Comments"
 
 # It isn't possible to get the size of the installation prior to downloading
-# so the stub installer uses an estimate.
-!define APPROXIMATE_REQUIRED_SPACE_MB "42.2"
+# so the stub installer uses an estimate. The size is derived from the size of
+# the complete installer, the size of the extracted complete installer, and at
+# least 15 MB additional for working room.
+!define APPROXIMATE_REQUIRED_SPACE_MB "145"
 
 # Control positions in Dialog Units so they are placed correctly with
 # non-default DPI settings
 !define OPTIONS_ITEM_EDGE_DU 90u
 !define OPTIONS_ITEM_WIDTH_DU 356u
 !define OPTIONS_SUBITEM_EDGE_DU 119u
 !define OPTIONS_SUBITEM_WIDTH_DU 327u
 !define INSTALL_BLURB_TOP_DU 78u
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -1742,54 +1742,34 @@ Function UpdateFreeSpaceLabel
   Call CheckSpace
 
   StrCpy $0 "$SpaceAvailableBytes"
 
   StrCpy $1 "$(BYTE)"
 
   ${If} $0 > 1024
   ${OrIf} $0 < 0
-    ; Multiply by 10 so it is possible to display a decimal in the size
-    System::Int64Op $0 * 10
-    Pop $0
     System::Int64Op $0 / 1024
     Pop $0
     StrCpy $1 "$(KILO)$(BYTE)"
-    ${If} $0 > 10240
+    ${If} $0 > 1024
     ${OrIf} $0 < 0
       System::Int64Op $0 / 1024
       Pop $0
       StrCpy $1 "$(MEGA)$(BYTE)"
-      ${If} $0 > 10240
+      ${If} $0 > 1024
       ${OrIf} $0 < 0
         System::Int64Op $0 / 1024
         Pop $0
         StrCpy $1 "$(GIGA)$(BYTE)"
       ${EndIf}
     ${EndIf}
-    StrLen $3 "$0"
-    ${If} $3 > 1
-      StrCpy $2 "$0" -1 ; All characters except the last one
-      StrCpy $0 "$0" "" -1 ; The last character
-      ${If} "$0" == "0"
-        StrCpy $0 "$2" ; Don't display the decimal if it is 0
-      ${Else}
-        StrCpy $0 "$2.$0"
-      ${EndIf}
-    ${ElseIf} $3 == 1
-      StrCpy $0 "0.$0"
-    ${Else}
-      ; This should never happen
-      System::Int64Op $0 / 10
-      Pop $0
-    ${EndIf}
   ${EndIf}
 
   SendMessage $LabelFreeSpace ${WM_SETTEXT} 0 "STR:$0 $1"
-
 FunctionEnd
 
 Function OnChange_DirRequest
   Pop $0
   System::Call 'user32::GetWindowTextW(i $DirRequest, w .r0, i ${NSIS_MAX_STRLEN})'
   StrCpy $1 "$0" 1 ; the first character
   ${If} "$1" == "$\""
     StrCpy $1 "$0" "" -1 ; the last character
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -546,69 +546,16 @@ ConvertActorsToBlobs(IDBDatabase* aDatab
 
       file->mFile.swap(blob);
       file->mFileInfo.swap(fileInfo);
     }
   }
 }
 
 void
-DispatchSuccessEvent(ResultHelper* aResultHelper,
-                     nsIDOMEvent* aEvent = nullptr)
-{
-  MOZ_ASSERT(aResultHelper);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DispatchSuccessEvent",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsRefPtr<IDBRequest> request = aResultHelper->Request();
-  MOZ_ASSERT(request);
-  request->AssertIsOnOwningThread();
-
-  nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
-
-  nsCOMPtr<nsIDOMEvent> successEvent;
-  if (!aEvent) {
-    successEvent = CreateGenericEvent(request,
-                                      nsDependentString(kSuccessEventType),
-                                      eDoesNotBubble,
-                                      eNotCancelable);
-    if (NS_WARN_IF(!successEvent)) {
-      return;
-    }
-
-    aEvent = successEvent;
-  }
-
-  request->SetResultCallback(aResultHelper);
-
-  MOZ_ASSERT(aEvent);
-  MOZ_ASSERT_IF(transaction, transaction->IsOpen());
-
-  bool dummy;
-  nsresult rv = request->DispatchEvent(aEvent, &dummy);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  MOZ_ASSERT_IF(transaction,
-                transaction->IsOpen() || transaction->IsAborted());
-
-  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
-  MOZ_ASSERT(internalEvent);
-
-  if (transaction &&
-      transaction->IsOpen() &&
-      internalEvent->mFlags.mExceptionHasBeenRisen) {
-    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
-  }
-}
-
-void
 DispatchErrorEvent(IDBRequest* aRequest,
                    nsresult aErrorCode,
                    IDBTransaction* aTransaction = nullptr,
                    nsIDOMEvent* aEvent = nullptr)
 {
   MOZ_ASSERT(aRequest);
   aRequest->AssertIsOnOwningThread();
   MOZ_ASSERT(NS_FAILED(aErrorCode));
@@ -657,16 +604,74 @@ DispatchErrorEvent(IDBRequest* aRequest,
     if (internalEvent->mFlags.mExceptionHasBeenRisen) {
       transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
     } else if (doDefault) {
       transaction->Abort(request);
     }
   }
 }
 
+void
+DispatchSuccessEvent(ResultHelper* aResultHelper,
+                     nsIDOMEvent* aEvent = nullptr)
+{
+  MOZ_ASSERT(aResultHelper);
+
+  PROFILER_LABEL("IndexedDB",
+                 "DispatchSuccessEvent",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsRefPtr<IDBRequest> request = aResultHelper->Request();
+  MOZ_ASSERT(request);
+  request->AssertIsOnOwningThread();
+
+  nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
+
+  if (transaction && transaction->IsAborted()) {
+    DispatchErrorEvent(request, transaction->AbortCode(), transaction);
+    return;
+  }
+
+  nsCOMPtr<nsIDOMEvent> successEvent;
+  if (!aEvent) {
+    successEvent = CreateGenericEvent(request,
+                                      nsDependentString(kSuccessEventType),
+                                      eDoesNotBubble,
+                                      eNotCancelable);
+    if (NS_WARN_IF(!successEvent)) {
+      return;
+    }
+
+    aEvent = successEvent;
+  }
+
+  request->SetResultCallback(aResultHelper);
+
+  MOZ_ASSERT(aEvent);
+  MOZ_ASSERT_IF(transaction, transaction->IsOpen());
+
+  bool dummy;
+  nsresult rv = request->DispatchEvent(aEvent, &dummy);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  MOZ_ASSERT_IF(transaction,
+                transaction->IsOpen() || transaction->IsAborted());
+
+  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+  MOZ_ASSERT(internalEvent);
+
+  if (transaction &&
+      transaction->IsOpen() &&
+      internalEvent->mFlags.mExceptionHasBeenRisen) {
+    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+  }
+}
+
 } // anonymous namespace
 
 /*******************************************************************************
  * Local class implementations
  ******************************************************************************/
 
 void
 PermissionRequestMainProcessHelper::OnPromptComplete(
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -184,16 +184,23 @@ public:
 
   bool
   IsAborted() const
   {
     AssertIsOnOwningThread();
     return NS_FAILED(mAbortCode);
   }
 
+  nsresult
+  AbortCode() const
+  {
+    AssertIsOnOwningThread();
+    return mAbortCode;
+  }
+
   void
   GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
 
   // 'Get' prefix is to avoid name collisions with the enum
   Mode
   GetMode() const
   {
     AssertIsOnOwningThread();
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -1664,16 +1664,17 @@ MediaCache::NoteSeek(MediaCacheStream* a
     // Any played block that is entirely after the start of the seeked-over
     // range must be converted.
     int32_t blockIndex =
       (aStream->mStreamOffset + BLOCK_SIZE - 1)/BLOCK_SIZE;
     int32_t endIndex =
       std::min<int64_t>((aOldOffset + BLOCK_SIZE - 1)/BLOCK_SIZE,
              aStream->mBlocks.Length());
     while (blockIndex < endIndex) {
+      MOZ_ASSERT(endIndex > 0);
       int32_t cacheBlockIndex = aStream->mBlocks[endIndex - 1];
       if (cacheBlockIndex >= 0) {
         BlockOwner* bo = GetBlockOwner(cacheBlockIndex, aStream);
         NS_ASSERTION(bo, "Stream doesn't own its blocks?");
         if (bo->mClass == PLAYED_BLOCK) {
           aStream->mPlayedBlocks.RemoveBlock(cacheBlockIndex);
           bo->mClass = READAHEAD_BLOCK;
           // Adding this as the first block is sure to be OK since
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -413,24 +413,20 @@ MediaSource::QueueAsyncSimpleEvent(const
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
   NS_DispatchToMainThread(event);
 }
 
 void
 MediaSource::DurationChange(double aOldDuration, double aNewDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("MediaSource(%p)::DurationChange(aNewDuration=%f)", this, aNewDuration);
+  MSE_DEBUG("MediaSource(%p)::DurationChange(aOldDuration=%f, aNewDuration=%f)", this, aOldDuration, aNewDuration);
 
   if (aNewDuration < aOldDuration) {
-    ErrorResult rv;
-    mSourceBuffers->Remove(aNewDuration, aOldDuration, rv);
-    if (rv.Failed()) {
-      return;
-    }
+    mSourceBuffers->RangeRemoval(aNewDuration, aOldDuration);
   }
   // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
 }
 
 void
 MediaSource::NotifyEvicted(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -183,20 +183,29 @@ SourceBuffer::Remove(double aStart, doub
     return;
   }
   if (IsNaN(mMediaSource->Duration()) ||
       aStart < 0 || aStart > mMediaSource->Duration() ||
       aEnd <= aStart || IsNaN(aEnd)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
-  if (mUpdating || mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
+  if (mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
+  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
+    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
+  }
+  RangeRemoval(aStart, aEnd);
+}
+
+void
+SourceBuffer::RangeRemoval(double aStart, double aEnd)
+{
   StartUpdating();
   /// TODO: Run coded frame removal algorithm.
 
   // Run the final step of the coded frame removal algorithm asynchronously
   // to ensure the SourceBuffer's updating flag transition behaves as
   // required by the spec.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
   NS_DispatchToMainThread(event);
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -103,16 +103,19 @@ public:
   void Ended();
 
   // Evict data in the source buffer in the given time range.
   void Evict(double aStart, double aEnd);
 
   double GetBufferedStart();
   double GetBufferedEnd();
 
+  // Runs the range removal algorithm as defined by the MSE spec.
+  void RangeRemoval(double aStart, double aEnd);
+
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
 private:
   ~SourceBuffer();
 
   friend class AsyncEventRunner<SourceBuffer>;
--- a/dom/media/mediasource/SourceBufferList.cpp
+++ b/dom/media/mediasource/SourceBufferList.cpp
@@ -103,25 +103,22 @@ SourceBufferList::AnyUpdating()
     if (mSourceBuffers[i]->Updating()) {
       return true;
     }
   }
   return false;
 }
 
 void
-SourceBufferList::Remove(double aStart, double aEnd, ErrorResult& aRv)
+SourceBufferList::RangeRemoval(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBufferList(%p)::Remove(aStart=%f, aEnd=%f", this, aStart, aEnd);
+  MSE_DEBUG("SourceBufferList(%p)::RangeRemoval(aStart=%f, aEnd=%f", this, aStart, aEnd);
   for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
-    mSourceBuffers[i]->Remove(aStart, aEnd, aRv);
-    if (aRv.Failed()) {
-      return;
-    }
+    mSourceBuffers[i]->RangeRemoval(aStart, aEnd);
   }
 }
 
 void
 SourceBufferList::Evict(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("SourceBufferList(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
--- a/dom/media/mediasource/SourceBufferList.h
+++ b/dom/media/mediasource/SourceBufferList.h
@@ -61,19 +61,18 @@ public:
   void Clear();
 
   // True if list has zero entries.
   bool IsEmpty();
 
   // Returns true if updating is true on any SourceBuffers in the list.
   bool AnyUpdating();
 
-  // Calls Remove(aStart, aEnd) on each SourceBuffer in the list.  Aborts on
-  // first error, with result returned in aRv.
-  void Remove(double aStart, double aEnd, ErrorResult& aRv);
+  // Runs the range removal steps from the MSE specification on each SourceBuffer.
+  void RangeRemoval(double aStart, double aEnd);
 
   // Mark all SourceBuffers input buffers as ended.
   void Ended();
 
   // Evicts data for the given time range from each SourceBuffer in the list.
   void Evict(double aStart, double aEnd);
 
   // Returns the highest end time of any of the Sourcebuffers.
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -4,16 +4,18 @@ support-files =
   mediasource.js
   seek.webm seek.webm^headers^
   seek_lowres.webm seek_lowres.webm^headers^
 
 [test_MediaSource.html]
 [test_MediaSource_disabled.html]
 [test_BufferedSeek.html]
 [test_BufferingWait.html]
+[test_EndOfStream.html]
+skip-if = (toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187
 [test_FrameSelection.html]
 [test_HaveMetadataUnbufferedSeek.html]
 [test_LoadedMetadataFired.html]
 [test_SeekableAfterEndOfStream.html]
 [test_SeekableAfterEndOfStreamSplit.html]
 [test_SeekableBeforeEndOfStream.html]
 [test_SeekableBeforeEndOfStreamSplit.html]
 [test_SplitAppendDelay.html]
--- a/dom/media/mediasource/test/test_BufferedSeek.html
+++ b/dom/media/mediasource/test/test_BufferedSeek.html
@@ -7,24 +7,32 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer));
       sb.addEventListener("updateend", function () {
-        ms.endOfStream()
+        updateCount++;
+        /* Ensure that we endOfStream on the first update event only as endOfStream can
+           raise more if the duration of the last buffered range and the intial duration
+           differ. See bug 1065207 */
+        if (updateCount == 1) {
+          ms.endOfStream();
+        };
       });
     });
 
     var target = 2;
 
     v.addEventListener("loadedmetadata", function () {
       if (v.currentTime != target &&
           v.buffered.length &&
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_EndOfStream.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>MSE: endOfStream call after an appendBuffer</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+runWithMSE(function () {
+  var ms = new MediaSource();
+
+  var v = document.createElement("video");
+  v.src = URL.createObjectURL(ms);
+  document.body.appendChild(v);
+
+  ms.addEventListener("sourceopen", function () {
+    var sb = ms.addSourceBuffer("video/webm");
+
+    fetchWithXHR("seek.webm", function (arrayBuffer) {
+      sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 88966));
+      var count = 0;
+      sb.addEventListener("updateend", function () {
+        ++count;
+        if (count == 1) {
+          setTimeout(function() {
+                       var fail = false;
+                       try {
+                         ms.endOfStream();
+                       } catch (e) {
+                         fail = true;
+                       }
+                       ok(!fail, "MediaSource.endOfStream succeeded");
+                       SimpleTest.finish();
+                     }, 0);
+        }
+      });
+    });
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/mediasource/test/test_FrameSelection.html
+++ b/dom/media/mediasource/test/test_FrameSelection.html
@@ -7,16 +7,18 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       // Append entire file covering range [0, 4].
       sb.appendBuffer(new Uint8Array(arrayBuffer));
     });
@@ -29,23 +31,23 @@ runWithMSE(function (ms, v) {
     v.addEventListener("loadedmetadata", function () {
       is(v.currentTime, 0, "currentTime has correct initial value");
       is(v.videoWidth, 320, "videoWidth has correct initial value");
       is(v.videoHeight, 240, "videoHeight has correct initial value");
 
       fetchWithXHR("seek_lowres.webm", function (arrayBuffer) {
         // Append initialization segment.
         sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 438));
-        var first = true;
         sb.addEventListener("updateend", function () {
-          if (first) {
+          updateCount++;
+          if (updateCount == 1) {
             // Append media segment covering range [2, 4].
             sb.appendBuffer(new Uint8Array(arrayBuffer, 51003));
-            first = false;
-          } else {
+          }
+          else if (updateCount == 2) {
             ms.endOfStream();
             target = targets.shift();
             v.currentTime = target.currentTime;
           }
         });
       });
     });
 
--- a/dom/media/mediasource/test/test_MediaSource.html
+++ b/dom/media/mediasource/test/test_MediaSource.html
@@ -52,17 +52,22 @@ runWithMSE(function () {
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer));
       is(sb.updating, true, "SourceBuffer.updating is expected value after appendBuffer");
     });
 
     sb.addEventListener("update", function () {
       is(sb.updating, false, "SourceBuffer.updating is expected value in update event");
       updateCount++;
-      ms.endOfStream();
+      /* Ensure that we endOfStream on the first update event only as endOfStream can
+         raise more if the duration of the last buffered range and the intial duration
+         differ. See bug 1065207 */
+      if (updateCount == 1) {
+        ms.endOfStream();
+      }
     });
 
     sb.addEventListener("updatestart", function () {
       updatestartCount++;
     });
 
     sb.addEventListener("updateend", function () {
       is(sb.updating, false, "SourceBuffer.updating is expected value in updateend event");
@@ -79,19 +84,20 @@ runWithMSE(function () {
   v.addEventListener("loadedmetadata", function () {
     loadedmetadataCount++;
   });
 
   v.addEventListener("ended", function () {
     // XXX: Duration should be exactly 4.0, see bug 1065207.
     ok(Math.abs(v.duration - 4) <= 0.002, "Video has correct duration");
     ok(Math.abs(v.currentTime - 4) <= 0.002, "Video has played to end");
-    is(updateCount, 1, "update event received");
-    is(updateendCount, 1, "updateend event received");
-    is(updatestartCount, 1, "updatestart event received");
+    // XXX: 2 update events can be received dueto duration differences, see bug 1065207.
+    ok(updateCount == 1 || updateCount == 2, "update event received");
+    ok(updateendCount == 1 || updateendCount == 2, "updateend event received");
+    ok(updatestartCount == 1 || updatestartCount == 2, "updatestart event received");
     is(loadedmetadataCount, 1, "loadedmetadata event received");
     v.parentNode.removeChild(v);
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
--- a/dom/media/mediasource/test/test_SeekableAfterEndOfStream.html
+++ b/dom/media/mediasource/test/test_SeekableAfterEndOfStream.html
@@ -7,24 +7,32 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer));
       sb.addEventListener("updateend", function () {
-        ms.endOfStream()
+        updateCount++;
+        /* Ensure that we endOfStream on the first update event only as endOfStream can
+           raise more if the duration of the last buffered range and the intial duration
+           differ. See bug 1065207 */
+        if (updateCount == 1) {
+          ms.endOfStream();
+        };
       });
     });
 
     var target = 2;
 
     v.addEventListener("loadedmetadata", function () {
       ok(v.seekable.length, "Resource is seekable");
       ok(v.seekable.length &&
--- a/dom/media/mediasource/test/test_SeekableAfterEndOfStreamSplit.html
+++ b/dom/media/mediasource/test/test_SeekableAfterEndOfStreamSplit.html
@@ -19,17 +19,17 @@ runWithMSE(function (ms, v) {
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 25223));
       var updateCount = 0;
       sb.addEventListener("updateend", function () {
         updateCount++;
         if (updateCount == 1) {
           sb.appendBuffer(new Uint8Array(arrayBuffer, 25223));
         }
-        else {
+        else if (updateCount == 2) {
           ms.endOfStream();
         }
       });
     });
 
     var target = 2;
 
     v.addEventListener("loadedmetadata", function () {
--- a/dom/media/mediasource/test/test_SplitAppend.html
+++ b/dom/media/mediasource/test/test_SplitAppend.html
@@ -7,28 +7,30 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
-      var first = true;
       sb.addEventListener("updateend", function () {
-        if (first) {
+        updateCount++;
+        if (updateCount == 1) {
           sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
-          first = false;
-        } else {
+        }
+        else if (updateCount == 2) {
           ms.endOfStream();
         }
       });
       v.play();
     });
   });
 
   v.addEventListener("ended", function () {
--- a/dom/media/mediasource/test/test_SplitAppendDelay.html
+++ b/dom/media/mediasource/test/test_SplitAppendDelay.html
@@ -7,30 +7,32 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
-      var first = true;
       sb.addEventListener("updateend", function () {
-        if (first) {
+        updateCount++;
+        if (updateCount == 1) {
           window.setTimeout(function () {
             sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
-            first = false;
           }, 1000);
-        } else {
+        }
+        else if (updateCount == 2) {
           ms.endOfStream();
         }
       });
       v.play();
     });
   });
 
   v.addEventListener("ended", function () {
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -116,16 +116,25 @@ function PlayFragmented(test, elem, toke
   return new Promise(function(resolve, reject) {
     var ms = new MediaSource();
     elem.src = URL.createObjectURL(ms);
 
     var sb;
     var curFragment = 0;
 
     function addNextFragment() {
+      /* We can get another updateevent as a result of calling ms.endOfStream() if
+         the highest end time of our source buffers is different from that of the
+         media source duration. Due to bug 1065207 this can happen because of
+         inaccuracies in the frame duration calculations. Check if we are already
+         "ended" and ignore the update event */
+      if (ms.readyState == "ended") {
+        return;
+      }
+
       if (curFragment >= test.fragments.length) {
         Log(token, "addNextFragment() end of stream");
         ms.endOfStream();
         resolve();
         return;
       }
 
       var fragmentFile = test.fragments[curFragment++];
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginHelperQt.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#include "PluginHelperQt.h"
+#include <QtCore/QCoreApplication>
+#include <QtCore/QEventLoop>
+
+static const int kMaxtimeToProcessEvents = 30;
+
+bool
+PluginHelperQt::AnswerProcessSomeEvents()
+{
+    QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginHelperQt.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#ifndef PluginHelperQt_h_
+#define PluginHelperQt_h_
+
+class PluginHelperQt
+{
+public:
+  static bool AnswerProcessSomeEvents();
+};
+
+#endif // PluginHelperQt_h_
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -1,19 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: sw=4 ts=4 et :
  * 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/. */
 
 #ifdef MOZ_WIDGET_QT
-// Must be included first to avoid conflicts.
-#include <QtCore/QCoreApplication>
-#include <QtCore/QEventLoop>
-#include "NestedLoopTimer.h"
+#include "PluginHelperQt.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
@@ -1641,23 +1638,21 @@ PluginModuleParent::ContentsScaleFactorC
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->ContentsScaleFactorChanged(aContentsScaleFactor);
 }
 #endif // #if defined(XP_MACOSX)
 
 #if defined(MOZ_WIDGET_QT)
-static const int kMaxtimeToProcessEvents = 30;
 bool
 PluginModuleParent::AnswerProcessSomeEvents()
 {
     PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
-    QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
-
+    PluginHelperQt::AnswerProcessSomeEvents();
     PLUGIN_LOG_DEBUG(("... quitting mini nested loop"));
 
     return true;
 }
 
 #elif defined(XP_MACOSX)
 bool
 PluginModuleParent::AnswerProcessSomeEvents()
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -65,18 +65,19 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
     EXPORTS.mozilla.plugins += [
         'PluginInterposeOSX.h',
     ]
 
 if CONFIG['MOZ_ENABLE_QT']:
     GENERATED_SOURCES += [
         'moc_NestedLoopTimer.cpp',
     ]
-    UNIFIED_SOURCES += [
+    SOURCES += [
         'NestedLoopTimer.cpp',
+        'PluginHelperQt.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'BrowserStreamChild.cpp',
     'BrowserStreamParent.cpp',
     'ChildAsyncCall.cpp',
     'ChildTimer.cpp',
     'PluginBackgroundDestroyer.cpp',
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -1759,17 +1759,17 @@ public:
   {
   }
 
   NS_IMETHOD Run()
   {
     // Note that we don't need to check mDispatchInputEvent here.  We need
     // to check it only when the editor requests to dispatch the input event.
 
-    if (!mTarget->IsInDoc()) {
+    if (!mTarget->IsInComposedDoc()) {
       return NS_OK;
     }
 
     nsCOMPtr<nsIPresShell> ps = mEditor->GetPresShell();
     if (!ps) {
       return NS_OK;
     }
 
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -846,16 +846,26 @@ DrawTargetD2D1::factory()
   }
 
   RadialGradientEffectD2D1::Register(mFactory);
 
   return mFactory;
 }
 
 void
+DrawTargetD2D1::CleanupD2D()
+{
+  if (mFactory) {
+    RadialGradientEffectD2D1::Unregister(mFactory);
+    mFactory->Release();
+    mFactory = nullptr;
+  }
+}
+
+void
 DrawTargetD2D1::MarkChanged()
 {
   if (mSnapshot) {
     if (mSnapshot->hasOneRef()) {
       // Just destroy it, since no-one else knows about it.
       mSnapshot = nullptr;
     } else {
       mSnapshot->DrawTargetWillChange();
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -604,16 +604,21 @@ Factory::CreateDrawTargetForD3D11Texture
   return nullptr;
 }
 
 void
 Factory::SetDirect3D11Device(ID3D11Device *aDevice)
 {
   mD3D11Device = aDevice;
 
+  if (mD2D1Device) {
+    mD2D1Device->Release();
+    mD2D1Device = nullptr;
+  }
+
   RefPtr<ID2D1Factory1> factory = D2DFactory1();
 
   RefPtr<IDXGIDevice> device;
   aDevice->QueryInterface((IDXGIDevice**)byRef(device));
   factory->CreateDevice(device, &mD2D1Device);
 }
 
 ID3D11Device*
@@ -651,17 +656,22 @@ uint64_t
 Factory::GetD2DVRAMUsageSourceSurface()
 {
   return DrawTargetD2D::mVRAMUsageSS;
 }
 
 void
 Factory::D2DCleanup()
 {
+  if (mD2D1Device) {
+    mD2D1Device->Release();
+    mD2D1Device = nullptr;
+  }
   DrawTargetD2D::CleanupD2D();
+  DrawTargetD2D1::CleanupD2D();
 }
 
 #endif // XP_WIN
 
 #ifdef USE_SKIA_GPU
 TemporaryRef<DrawTarget>
 Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
                                            const IntSize &aSize,
--- a/gfx/2d/RadialGradientEffectD2D1.cpp
+++ b/gfx/2d/RadialGradientEffectD2D1.cpp
@@ -279,16 +279,22 @@ RadialGradientEffectD2D1::Register(ID2D1
   HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect);
 
   if (FAILED(hr)) {
     gfxWarning() << "Failed to register radial gradient effect.";
   }
   return hr;
 }
 
+void
+RadialGradientEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
+{
+  aFactory->UnregisterEffect(CLSID_RadialGradientEffect);
+}
+
 HRESULT __stdcall
 RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
 {
   *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
   (*aEffectImpl)->AddRef();
 
   return S_OK;
 }
--- a/gfx/2d/RadialGradientEffectD2D1.h
+++ b/gfx/2d/RadialGradientEffectD2D1.h
@@ -67,16 +67,17 @@ public:
 
   // ID2D1TransformNode
   IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; }
 
   // ID2D1DrawTransform
   IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo);
 
   static HRESULT Register(ID2D1Factory1* aFactory);
+  static void Unregister(ID2D1Factory1* aFactory);
   static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
 
   HRESULT SetStopCollection(IUnknown *aStopCollection);
   IUnknown *GetStopCollection() const { return mStopCollection; }
 
 private:
   TemporaryRef<ID2D1ResourceTexture> CreateGradientTexture();
 
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -84,27 +84,31 @@ if CONFIG['MOZ_ENABLE_SKIA']:
     ]
 
 # Are we targeting x86 or x64?  If so, build SSE2 files.
 if CONFIG['INTEL_ARCHITECTURE']:
     # VC2005 doesn't support _mm_castsi128_ps, so SSE2 is turned off
     if CONFIG['_MSC_VER'] != '1400':
         SOURCES += [
             'BlurSSE2.cpp',
-            'convolverSSE2.cpp',
             'FilterProcessingSSE2.cpp',
             'ImageScalingSSE2.cpp',
         ]
+        if CONFIG['MOZ_ENABLE_SKIA']:
+            SOURCES += [
+                'convolverSSE2.cpp',
+            ]
         DEFINES['USE_SSE2'] = True
         # The file uses SSE2 intrinsics, so it needs special compile flags on some
         # compilers.
         SOURCES['BlurSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
         SOURCES['FilterProcessingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
         SOURCES['ImageScalingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
-        SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+        if CONFIG['MOZ_ENABLE_SKIA']:
+            SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
 
 UNIFIED_SOURCES += [
     'Blur.cpp',
     'DataSourceSurface.cpp',
     'DataSurfaceHelpers.cpp',
     'DrawEventRecorder.cpp',
     'DrawTarget.cpp',
     'DrawTargetCairo.cpp',
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -397,29 +397,16 @@ APZCTreeManager::PrepareAPZCForLayer(con
       apzc->AddHitTestRegions(EventRegions(unobscured));
       APZCTM_LOG("Adding region %s to visible region of APZC %p\n", Stringify(unobscured).c_str(), apzc);
     }
   }
 
   return apzc;
 }
 
-static EventRegions
-EventRegionsFor(const LayerMetricsWrapper& aLayer)
-{
-  // This is a workaround for bug 1082594. We should be able to replace this
-  // with just a call to aLayer.GetEventRegions() once that bug is fixed.
-  if (aLayer.IsScrollInfoLayer()) {
-    EventRegions regions(ParentLayerIntRect::ToUntyped(RoundedIn(aLayer.Metrics().mCompositionBounds)));
-    regions.mDispatchToContentHitRegion = regions.mHitRegion;
-    return regions;
-  }
-  return aLayer.GetEventRegions();
-}
-
 AsyncPanZoomController*
 APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
                                              const LayerMetricsWrapper& aLayer,
                                              uint64_t aLayersId,
                                              const gfx::Matrix4x4& aAncestorTransform,
                                              AsyncPanZoomController* aParent,
                                              AsyncPanZoomController* aNextSibling,
                                              const nsIntRegion& aObscured)
@@ -492,17 +479,17 @@ APZCTreeManager::UpdatePanZoomController
     next = UpdatePanZoomControllerTree(aState, child, childLayersId,
                                        ancestorTransform, aParent, next,
                                        obscured);
 
     // Each layer obscures its previous siblings, so we augment the obscured
     // region as we loop backwards through the children.
     nsIntRegion childRegion;
     if (gfxPrefs::LayoutEventRegionsEnabled()) {
-      childRegion = EventRegionsFor(child).mHitRegion;
+      childRegion = child.GetEventRegions().mHitRegion;
     } else {
       childRegion = child.GetVisibleRegion();
     }
     childRegion.Transform(gfx::To3DMatrix(child.GetTransform()));
     if (child.GetClipRect()) {
       childRegion.AndWith(*child.GetClipRect());
     }
 
@@ -526,17 +513,17 @@ APZCTreeManager::UpdatePanZoomController
     // When we compute the unobscured regions below, we subtract off the
     // |obscured| region, but it would also be ok to do this before the above
     // loop. At that point |obscured| would only have the uncles' hit regions
     // and not the children. The reason this is ok is again because of the way
     // we do hit-testing (where the deepest APZC is used) it doesn't matter if
     // we count the children as obscuring the parent or not.
 
     EventRegions unobscured;
-    unobscured.Sub(EventRegionsFor(aLayer), obscured);
+    unobscured.Sub(aLayer.GetEventRegions(), obscured);
     APZCTM_LOG("Picking up unobscured hit region %s from layer %p\n", Stringify(unobscured).c_str(), aLayer.GetLayer());
 
     // Take the hit region of the |aLayer|'s subtree (which has already been
     // transformed into the coordinate space of |aLayer|) and...
     EventRegions subtreeEventRegions = aState.mEventRegions.LastElement();
     aState.mEventRegions.RemoveElementAt(aState.mEventRegions.Length() - 1);
     // ... combine it with the hit region for this layer, and then ...
     subtreeEventRegions.OrWith(unobscured);
--- a/gfx/thebes/gfxQtPlatform.cpp
+++ b/gfx/thebes/gfxQtPlatform.cpp
@@ -2,17 +2,16 @@
  * 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/. */
 
 #include <QPixmap>
 #include <QWindow>
 #ifdef MOZ_X11
 #include <qpa/qplatformnativeinterface.h>
-#include <qpa/qplatformintegration.h>
 #endif
 #include <QGuiApplication>
 #include <QScreen>
 
 #include "gfxQtPlatform.h"
 
 #include "gfxFontconfigUtils.h"
 
--- a/ipc/chromium/src/base/message_pump_qt.cc
+++ b/ipc/chromium/src/base/message_pump_qt.cc
@@ -10,17 +10,16 @@
 
 #include "base/message_pump_qt.h"
 
 #include <fcntl.h>
 #include <limits>
 #include <math.h>
 
 #include "base/eintr_wrapper.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/platform_thread.h"
 
 namespace {
 // Cached QEvent user type, registered for our event system
 static int sPokeEvent;
 }  // namespace
 
new file mode 100644
--- /dev/null
+++ b/js/ipc/CPOWTimer.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=80:
+ *
+ * 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/. */
+
+#include "jsfriendapi.h"
+#include "xpcprivate.h"
+#include "CPOWTimer.h"
+
+CPOWTimer::~CPOWTimer() {
+    /* This is a best effort to find the compartment responsible for this CPOW call */
+    xpc::CompartmentPrivate* compartment = xpc::CompartmentPrivate::Get(js::GetObjectCompartment(mozilla::dom::GetIncumbentGlobal()
+                                                                                                 ->GetGlobalJSObject()));
+    PRIntervalTime time = PR_IntervalNow() - startInterval;
+    compartment->CPOWTime += time;
+}
new file mode 100644
--- /dev/null
+++ b/js/ipc/CPOWTimer.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=80:
+ *
+ * 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/. */
+
+#ifndef CPOWTIMER_H
+#define CPOWTIMER_H
+
+#include "prinrval.h"
+
+class JSObject;
+
+class MOZ_STACK_CLASS CPOWTimer {
+  public:
+    CPOWTimer(): startInterval(PR_IntervalNow()) {}
+    ~CPOWTimer();
+
+  private:
+    PRIntervalTime startInterval;
+};
+
+#endif
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WrapperOwner.h"
 #include "JavaScriptLogging.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "jsfriendapi.h"
 #include "xpcprivate.h"
+#include "CPOWTimer.h"
 #include "WrapperFactory.h"
 
 #include "nsIRemoteTagService.h"
 
 using namespace js;
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
@@ -129,17 +130,20 @@ const char CPOWProxyHandler::family = 0;
 const CPOWProxyHandler CPOWProxyHandler::singleton;
 
 #define FORWARD(call, args)                                             \
     WrapperOwner *owner = OwnerOf(proxy);                               \
     if (!owner->active()) {                                             \
         JS_ReportError(cx, "cannot use a CPOW whose process is gone");  \
         return false;                                                   \
     }                                                                   \
-    return owner->call args;
+    {                                                                   \
+        CPOWTimer timer;                                                \
+        return owner->call args;                                        \
+    }
 
 bool
 CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                         MutableHandle<JSPropertyDescriptor> desc) const
 {
     FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
 }
 
--- a/js/ipc/moz.build
+++ b/js/ipc/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 UNIFIED_SOURCES += [
+    'CPOWTimer.cpp',
     'JavaScriptChild.cpp',
     'JavaScriptParent.cpp',
     'JavaScriptShared.cpp',
     'WrapperAnswer.cpp',
     'WrapperOwner.cpp',
 ]
 
 IPDL_SOURCES += [
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -47,33 +47,45 @@ class ChunkPool
     Chunk *head_;
     size_t count_;
 
   public:
     ChunkPool() : head_(nullptr), count_(0) {}
 
     size_t count() const { return count_; }
 
-    /* Must be called with the GC lock taken. */
-    inline Chunk *get(JSRuntime *rt);
+    Chunk *head() { MOZ_ASSERT(head_); return head_; }
+    Chunk *pop();
+    void push(Chunk *chunk);
+    Chunk *remove(Chunk *chunk);
 
-    /* Must be called either during the GC or with the GC lock taken. */
-    inline void put(Chunk *chunk);
+#ifdef DEBUG
+    bool contains(Chunk *chunk) const;
+    bool verify() const;
+#endif
 
-    class Enum {
+    // Pool mutation does not invalidate an Iter unless the mutation
+    // is of the Chunk currently being visited by the Iter.
+    class Iter {
       public:
-        explicit Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.head_) {}
-        bool empty() { return !*chunkp; }
-        Chunk *front();
-        inline void popFront();
-        inline void removeAndPopFront();
+        explicit Iter(ChunkPool &pool) : current_(pool.head_) {}
+        bool done() const { return !current_; }
+        void next();
+        Chunk *get() const { return current_; }
+        operator Chunk *() const { return get(); }
+        Chunk *operator->() const { return get(); }
       private:
-        ChunkPool &pool;
-        Chunk **chunkp;
+        Chunk *current_;
     };
+
+  private:
+    // ChunkPool controls external resources with interdependencies on the
+    // JSRuntime and related structs, so must not be copied.
+    ChunkPool(const ChunkPool &) MOZ_DELETE;
+    ChunkPool operator=(const ChunkPool &) MOZ_DELETE;
 };
 
 // Performs extra allocation off the main thread so that when memory is
 // required on the main thread it will already be available and waiting.
 class BackgroundAllocTask : public GCParallelTask
 {
     // Guarded by the GC lock.
     JSRuntime *runtime;
@@ -458,20 +470,21 @@ class GCRuntime
         mode = m;
         marker.setGCMode(mode);
     }
 
     inline void updateOnFreeArenaAlloc(const ChunkInfo &info);
     inline void updateOnArenaFree(const ChunkInfo &info);
 
     GCChunkSet::Range allChunks() { return chunkSet.all(); }
-    Chunk **getAvailableChunkList();
     void moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock);
     bool hasChunk(Chunk *chunk) { return chunkSet.has(chunk); }
+    ChunkPool &availableChunks(const AutoLockGC &lock) { return availableChunks_; }
     ChunkPool &emptyChunks(const AutoLockGC &lock) { return emptyChunks_; }
+    const ChunkPool &availableChunks(const AutoLockGC &lock) const { return availableChunks_; }
     const ChunkPool &emptyChunks(const AutoLockGC &lock) const { return emptyChunks_; }
 
 #ifdef JS_GC_ZEAL
     void startVerifyPreBarriers();
     bool endVerifyPreBarriers();
     void startVerifyPostBarriers();
     bool endVerifyPostBarriers();
     void finishVerifier();
@@ -624,18 +637,18 @@ class GCRuntime
 
     /*
      * Doubly-linked lists of chunks from user and system compartments. The GC
      * allocates its arenas from the corresponding list and when all arenas
      * in the list head are taken, then the chunk is removed from the list.
      * During the GC when all arenas in a chunk become free, that chunk is
      * removed from the list and scheduled for release.
      */
-    js::gc::Chunk *availableChunkListHead;
-    js::gc::ChunkPool emptyChunks_;
+    ChunkPool availableChunks_;
+    ChunkPool emptyChunks_;
 
     js::RootedValueMap rootsHash;
 
     size_t maxMallocBytes;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -741,17 +741,17 @@ struct ChunkTrailer
 
 static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t) + sizeof(uint64_t),
               "ChunkTrailer size is incorrect.");
 
 /* The chunk header (located at the end of the chunk to preserve arena alignment). */
 struct ChunkInfo
 {
     Chunk           *next;
-    Chunk           **prevp;
+    Chunk           *prev;
 
     /* Free arenas are linked together with aheader.next. */
     ArenaHeader     *freeArenasHead;
 
 #if JS_BITS_PER_WORD == 32
     /*
      * Calculating sizes and offsets is simpler if sizeof(ChunkInfo) is
      * architecture-independent.
@@ -940,49 +940,29 @@ struct Chunk
     bool unused() const {
         return info.numArenasFree == ArenasPerChunk;
     }
 
     bool hasAvailableArenas() const {
         return info.numArenasFree != 0;
     }
 
-    inline void addToAvailableList(JSRuntime *rt);
-    inline void insertToAvailableList(Chunk **insertPoint);
-    inline void removeFromAvailableList();
-
     ArenaHeader *allocateArena(JSRuntime *rt, JS::Zone *zone, AllocKind kind,
                                const AutoLockGC &lock);
 
     enum ArenaDecommitState { IsCommitted = false, IsDecommitted = true };
     void releaseArena(JSRuntime *rt, ArenaHeader *aheader, const AutoLockGC &lock,
                       ArenaDecommitState state = IsCommitted);
     void recycleArena(ArenaHeader *aheader, SortedArenaList &dest, AllocKind thingKind,
                       size_t thingsPerArena);
 
     static Chunk *allocate(JSRuntime *rt);
 
     void decommitAllArenas(JSRuntime *rt);
 
-    /*
-     * Assuming that the info.prevp points to the next field of the previous
-     * chunk in a doubly-linked list, get that chunk.
-     */
-    Chunk *getPrevious() {
-        MOZ_ASSERT(info.prevp);
-        return fromPointerToNext(info.prevp);
-    }
-
-    /* Get the chunk from a pointer to its info.next field. */
-    static Chunk *fromPointerToNext(Chunk **nextFieldPtr) {
-        uintptr_t addr = reinterpret_cast<uintptr_t>(nextFieldPtr);
-        MOZ_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next));
-        return reinterpret_cast<Chunk *>(addr - offsetof(Chunk, info.next));
-    }
-
   private:
     inline void init(JSRuntime *rt);
 
     /* Search for a decommitted arena to allocate. */
     unsigned findDecommittedArenaOffset();
     ArenaHeader* fetchNextDecommittedArena();
 
     void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1101576.js
@@ -0,0 +1,14 @@
+// Random chosen test: js/src/jit-test/tests/ion/bug928423.js
+o = {
+        a: 1,
+            b: 1
+}
+print(1);
+for (var x = 0; x < 2; x++) {
+    print(2);
+    o["a1".substr(0, 1)]
+    o["b1".substr(0, 1)]
+}
+print(3);
+// jsfunfuzz
+"a" + "b"
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6053,16 +6053,18 @@ static const VMFunction SubstringKernelI
 
 bool CodeGenerator::visitSubstr(LSubstr *lir)
 {
     Register string = ToRegister(lir->string());
     Register begin = ToRegister(lir->begin());
     Register length = ToRegister(lir->length());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
+    Register temp2 = ToRegister(lir->temp2());
+    Register temp3 = ToRegister(lir->temp3());
     Address stringFlags(string, JSString::offsetOfFlags());
 
     Label isLatin1, notInline, nonZero, isInlinedLatin1;
 
     // For every edge case use the C++ variant.
     // Note: we also use this upon allocation failure in newGCString and
     // newGCFatInlineString. To squeeze out even more performance those failures
     // can be handled by allocate in ool code and returning to jit code to fill
@@ -6097,31 +6099,33 @@ bool CodeGenerator::visitSubstr(LSubstr 
 
     masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT),
                       &isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
                      Address(output, JSString::offsetOfFlags()));
         masm.computeEffectiveAddress(stringStorage, temp);
         BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
-        masm.computeEffectiveAddress(chars, begin);
+        masm.computeEffectiveAddress(chars, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
-        CopyStringChars(masm, temp, begin, length, string, sizeof(char16_t), sizeof(char16_t));
+        CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
+        masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store16(Imm32(0), Address(temp, 0));
         masm.jump(done);
     }
     masm.bind(&isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
                      Address(output, JSString::offsetOfFlags()));
-        masm.computeEffectiveAddress(stringStorage, temp);
+        masm.computeEffectiveAddress(stringStorage, temp2);
         static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
-        masm.addPtr(temp, begin);
+        masm.addPtr(begin, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
-        CopyStringChars(masm, temp, begin, length, string, sizeof(char), sizeof(char));
+        CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
+        masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store8(Imm32(0), Address(temp, 0));
         masm.jump(done);
     }
 
     // Handle other cases with a DependentString.
     masm.bind(&notInline);
     masm.newGCString(output, temp, slowPath);
     masm.store32(length, Address(output, JSString::offsetOfLength()));
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3445,41 +3445,49 @@ class LStringSplit : public LCallInstruc
     const LAllocation *separator() {
         return getOperand(1);
     }
     const MStringSplit *mir() const {
         return mir_->toStringSplit();
     }
 };
 
-class LSubstr : public LInstructionHelper<1, 3, 1>
+class LSubstr : public LInstructionHelper<1, 3, 3>
 {
   public:
     LIR_HEADER(Substr)
 
     LSubstr(const LAllocation &string, const LAllocation &begin, const LAllocation &length,
-            const LDefinition &temp)
+            const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3)
     {
         setOperand(0, string);
         setOperand(1, begin);
         setOperand(2, length);
         setTemp(0, temp);
+        setTemp(1, temp2);
+        setTemp(2, temp3);
     }
     const LAllocation *string() {
         return getOperand(0);
     }
     const LAllocation *begin() {
         return getOperand(1);
     }
     const LAllocation *length() {
         return getOperand(2);
     }
     const LDefinition *temp() {
         return getTemp(0);
     }
+    const LDefinition *temp2() {
+        return getTemp(1);
+    }
+    const LDefinition *temp3() {
+        return getTemp(2);
+    }
     const MStringSplit *mir() const {
         return mir_->toStringSplit();
     }
 };
 
 // Convert a 32-bit integer to a double.
 class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2179,20 +2179,25 @@ LIRGenerator::visitStringReplace(MString
                                                       useRegisterAtStart(ins->pattern()),
                                                       useRegisterOrConstantAtStart(ins->replacement()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitSubstr(MSubstr *ins)
 {
-    LSubstr *lir = new (alloc()) LSubstr(useFixed(ins->string(), CallTempReg1),
+    // The last temporary need to be a register that can handle 8bit moves, but
+    // there is no way to signal that to register allocator, except to give a
+    // fixed temporary that is able to do this.
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
-                                         temp());
+                                         temp(),
+                                         temp(),
+                                         tempFixed(CallTempReg1));
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
         // If the function has a singleton type, this instruction will only be
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -29,16 +29,17 @@ UNIFIED_SOURCES += [
     'testException.cpp',
     'testExternalStrings.cpp',
     'testFindSCCs.cpp',
     'testForOfIterator.cpp',
     'testFreshGlobalEvalRedefinition.cpp',
     'testFuncCallback.cpp',
     'testFunctionProperties.cpp',
     'testGCAllocator.cpp',
+    'testGCChunkPool.cpp',
     'testGCExactRooting.cpp',
     'testGCFinalizeCallback.cpp',
     'testGCHeapPostBarriers.cpp',
     'testGCMarking.cpp',
     'testGCOutOfMemory.cpp',
     'testGCStoreBufferRemoval.cpp',
     'testHashTable.cpp',
     'testHashTableInit.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testGCChunkPool.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+* vim: set ts=8 sts=4 et sw=4 tw=99:
+*/
+/* 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/. */
+
+#include "mozilla/Move.h"
+
+#include "gc/GCRuntime.h"
+#include "gc/Heap.h"
+
+#include "jsapi-tests/tests.h"
+
+BEGIN_TEST(testGCChunkPool)
+{
+    const int N = 10;
+    js::gc::ChunkPool pool;
+
+    // Create.
+    for (int i = 0; i < N; ++i) {
+        js::gc::Chunk *chunk = js::gc::Chunk::allocate(rt);
+        CHECK(chunk);
+        pool.push(chunk);
+    }
+    MOZ_ASSERT(pool.verify());
+
+    // Iterate.
+    uint32_t i = 0;
+    for (js::gc::ChunkPool::Iter iter(pool); !iter.done(); iter.next(), ++i)
+        CHECK(iter.get());
+    CHECK(i == pool.count());
+    MOZ_ASSERT(pool.verify());
+
+    // Push/Pop.
+    for (int i = 0; i < N; ++i) {
+        js::gc::Chunk *chunkA = pool.pop();
+        js::gc::Chunk *chunkB = pool.pop();
+        js::gc::Chunk *chunkC = pool.pop();
+        pool.push(chunkA);
+        pool.push(chunkB);
+        pool.push(chunkC);
+    }
+    MOZ_ASSERT(pool.verify());
+
+    // Remove.
+    js::gc::Chunk *chunk = nullptr;
+    int offset = N / 2;
+    for (js::gc::ChunkPool::Iter iter(pool); !iter.done(); iter.next(), --offset) {
+        if (offset == 0) {
+            chunk = pool.remove(iter.get());
+            break;
+        }
+    }
+    CHECK(chunk);
+    MOZ_ASSERT(!pool.contains(chunk));
+    MOZ_ASSERT(pool.verify());
+    pool.push(chunk);
+
+    // Destruct.
+    js::AutoLockGC lock(rt);
+    for (js::gc::ChunkPool::Iter iter(pool); !iter.done();) {
+        js::gc::Chunk *chunk = iter.get();
+        iter.next();
+        pool.remove(chunk);
+        js::gc::UnmapPages(chunk, js::gc::ChunkSize);
+    }
+
+    return true;
+}
+END_TEST(testGCChunkPool)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -658,106 +658,136 @@ AllocChunk(JSRuntime *rt)
 }
 
 static inline void
 FreeChunk(JSRuntime *rt, Chunk *p)
 {
     UnmapPages(static_cast<void *>(p), ChunkSize);
 }
 
-/* Must be called with the GC lock taken. */
-inline Chunk *
-ChunkPool::get(JSRuntime *rt)
-{
-    Chunk *chunk = head_;
-    if (!chunk) {
-        MOZ_ASSERT(!count_);
+Chunk *
+ChunkPool::pop()
+{
+    MOZ_ASSERT(bool(head_) == bool(count_));
+    if (!count_)
         return nullptr;
-    }
-
-    MOZ_ASSERT(count_);
-    head_ = chunk->info.next;
-    --count_;
-    return chunk;
-}
-
-/* Must be called either during the GC or with the GC lock taken. */
-inline void
-ChunkPool::put(Chunk *chunk)
-{
+    return remove(head_);
+}
+
+void
+ChunkPool::push(Chunk *chunk)
+{
+    MOZ_ASSERT(!chunk->info.next);
+    MOZ_ASSERT(!chunk->info.prev);
+
     chunk->info.age = 0;
     chunk->info.next = head_;
+    if (head_)
+        head_->info.prev = chunk;
     head_ = chunk;
-    count_++;
-}
-
-inline Chunk *
-ChunkPool::Enum::front()
-{
-    Chunk *chunk = *chunkp;
-    MOZ_ASSERT_IF(chunk, pool.count() != 0);
+    ++count_;
+
+    MOZ_ASSERT(verify());
+}
+
+Chunk *
+ChunkPool::remove(Chunk *chunk)
+{
+    MOZ_ASSERT(count_ > 0);
+    MOZ_ASSERT(contains(chunk));
+
+    if (head_ == chunk)
+        head_ = chunk->info.next;
+    if (chunk->info.prev)
+        chunk->info.prev->info.next = chunk->info.next;
+    if (chunk->info.next)
+        chunk->info.next->info.prev = chunk->info.prev;
+    chunk->info.next = chunk->info.prev = nullptr;
+    --count_;
+
+    MOZ_ASSERT(verify());
     return chunk;
 }
 
-inline void
-ChunkPool::Enum::popFront()
-{
-    MOZ_ASSERT(!empty());
-    chunkp = &front()->info.next;
-}
-
-inline void
-ChunkPool::Enum::removeAndPopFront()
-{
-    MOZ_ASSERT(!empty());
-    *chunkp = front()->info.next;
-    --pool.count_;
+#ifdef DEBUG
+bool
+ChunkPool::contains(Chunk *chunk) const
+{
+    verify();
+    for (Chunk *cursor = head_; cursor; cursor = cursor->info.next) {
+        if (cursor == chunk)
+            return true;
+    }
+    return false;
+}
+
+bool
+ChunkPool::verify() const
+{
+    MOZ_ASSERT(bool(head_) == bool(count_));
+    uint32_t count = 0;
+    for (Chunk *cursor = head_; cursor; cursor = cursor->info.next, ++count) {
+        MOZ_ASSERT_IF(cursor->info.prev, cursor->info.prev->info.next == cursor);
+        MOZ_ASSERT_IF(cursor->info.next, cursor->info.next->info.prev == cursor);
+    }
+    MOZ_ASSERT(count_ == count);
+    return true;
+}
+#endif
+
+void
+ChunkPool::Iter::next()
+{
+    MOZ_ASSERT(!done());
+    current_ = current_->info.next;
 }
 
 Chunk *
 GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock)
 {
     /*
      * Return old empty chunks to the system while preserving the order of
      * other chunks in the list. This way, if the GC runs several times
      * without emptying the list, the older chunks will stay at the tail
      * and are more likely to reach the max age.
      */
     Chunk *freeList = nullptr;
     unsigned freeChunkCount = 0;
-    for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); ) {
-        Chunk *chunk = e.front();
+    for (ChunkPool::Iter iter(emptyChunks(lock)); !iter.done();) {
+        Chunk *chunk = iter.get();
+        iter.next();
+
         MOZ_ASSERT(chunk->unused());
         MOZ_ASSERT(!chunkSet.has(chunk));
         if (freeChunkCount >= tunables.maxEmptyChunkCount() ||
             (freeChunkCount >= tunables.minEmptyChunkCount() &&
              (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
-            e.removeAndPopFront();
+            emptyChunks(lock).remove(chunk);
             prepareToFreeChunk(chunk->info);
             chunk->info.next = freeList;
             freeList = chunk;
         } else {
             /* Keep the chunk but increase its age. */
             ++freeChunkCount;
             ++chunk->info.age;
-            e.popFront();
         }
     }
     MOZ_ASSERT(emptyChunks(lock).count() <= tunables.maxEmptyChunkCount());
     MOZ_ASSERT_IF(shrinkBuffers, emptyChunks(lock).count() <= tunables.minEmptyChunkCount());
     return freeList;
 }
 
 static void
 FreeChunkPool(JSRuntime *rt, ChunkPool &pool)
 {
-    for (ChunkPool::Enum e(pool); !e.empty();) {
-        Chunk *chunk = e.front();
-        e.removeAndPopFront();
+    for (ChunkPool::Iter iter(pool); !iter.done();) {
+        Chunk *chunk = iter.get();
+        iter.next();
+        pool.remove(chunk);
         MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
         FreeChunk(rt, chunk);
     }
     MOZ_ASSERT(pool.count() == 0);
 }
 
 void
 GCRuntime::freeEmptyChunks(JSRuntime *rt, const AutoLockGC &lock)
@@ -782,17 +812,17 @@ Chunk::allocate(JSRuntime *rt)
     if (!chunk)
         return nullptr;
     chunk->init(rt);
     rt->gc.stats.count(gcstats::STAT_NEW_CHUNK);
     return chunk;
 }
 
 /* Must be called with the GC lock taken. */
-inline void
+void
 GCRuntime::releaseChunk(Chunk *chunk)
 {
     MOZ_ASSERT(chunk);
     prepareToFreeChunk(chunk->info);
     FreeChunk(rt, chunk);
 }
 
 inline void
@@ -835,64 +865,25 @@ Chunk::init(JSRuntime *rt)
     /*
      * Decommit the arenas. We do this after poisoning so that if the OS does
      * not have to recycle the pages, we still get the benefit of poisoning.
      */
     decommitAllArenas(rt);
 
     /* Initialize the chunk info. */
     info.age = 0;
+    info.next = nullptr;
+    info.prev = nullptr;
     info.trailer.storeBuffer = nullptr;
     info.trailer.location = ChunkLocationBitTenuredHeap;
     info.trailer.runtime = rt;
 
     /* The rest of info fields are initialized in pickChunk. */
 }
 
-inline Chunk **
-GCRuntime::getAvailableChunkList()
-{
-    return &availableChunkListHead;
-}
-
-inline void
-Chunk::addToAvailableList(JSRuntime *rt)
-{
-    insertToAvailableList(rt->gc.getAvailableChunkList());
-}
-
-inline void
-Chunk::insertToAvailableList(Chunk **insertPoint)
-{
-    MOZ_ASSERT(hasAvailableArenas());
-    MOZ_ASSERT(!info.prevp);
-    MOZ_ASSERT(!info.next);
-    info.prevp = insertPoint;
-    Chunk *insertBefore = *insertPoint;
-    if (insertBefore) {
-        MOZ_ASSERT(insertBefore->info.prevp == insertPoint);
-        insertBefore->info.prevp = &info.next;
-    }
-    info.next = insertBefore;
-    *insertPoint = this;
-}
-
-inline void
-Chunk::removeFromAvailableList()
-{
-    MOZ_ASSERT(info.prevp);
-    *info.prevp = info.next;
-    if (info.next) {
-        MOZ_ASSERT(info.next->info.prevp == &info.next);
-        info.next->info.prevp = info.prevp;
-    }
-    info.prevp = nullptr;
-    info.next = nullptr;
-}
-
 /*
  * Search for and return the next decommitted Arena. Our goal is to keep
  * lastDecommittedArenaOffset "close" to a free arena. We do this by setting
  * it to the most recently freed arena when we free, and forcing it to
  * the last alloc + 1 when we allocate.
  */
 uint32_t
 Chunk::findDecommittedArenaOffset()
@@ -950,17 +941,17 @@ Chunk::fetchNextFreeArena(JSRuntime *rt)
 ArenaHeader *
 Chunk::allocateArena(JSRuntime *rt, Zone *zone, AllocKind thingKind, const AutoLockGC &lock)
 {
     ArenaHeader *aheader = info.numArenasFreeCommitted > 0
                            ? fetchNextFreeArena(rt)
                            : fetchNextDecommittedArena();
     aheader->init(zone, thingKind);
     if (MOZ_UNLIKELY(!hasAvailableArenas()))
-        removeFromAvailableList();
+        rt->gc.availableChunks(lock).remove(this);
     return aheader;
 }
 
 inline void
 GCRuntime::updateOnArenaFree(const ChunkInfo &info)
 {
     ++numArenasFreeCommitted;
 }
@@ -1001,36 +992,36 @@ Chunk::releaseArena(JSRuntime *rt, Arena
     if (state == IsCommitted) {
         aheader->setAsNotAllocated();
         addArenaToFreeList(rt, aheader);
     } else {
         addArenaToDecommittedList(rt, aheader);
     }
 
     if (info.numArenasFree == 1) {
-        MOZ_ASSERT(!info.prevp);
+        MOZ_ASSERT(!info.prev);
         MOZ_ASSERT(!info.next);
-        addToAvailableList(rt);
+        rt->gc.availableChunks(lock).push(this);
     } else if (!unused()) {
-        MOZ_ASSERT(info.prevp);
+        MOZ_ASSERT(rt->gc.availableChunks(lock).contains(this));
     } else {
         MOZ_ASSERT(unused());
-        removeFromAvailableList();
+        rt->gc.availableChunks(lock).remove(this);
         decommitAllArenas(rt);
         rt->gc.moveChunkToFreePool(this, lock);
     }
 }
 
 void
 GCRuntime::moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock)
 {
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(chunkSet.has(chunk));
     chunkSet.remove(chunk);
-    emptyChunks(lock).put(chunk);
+    emptyChunks(lock).push(chunk);
 }
 
 inline bool
 GCRuntime::wantBackgroundAllocation(const AutoLockGC &lock) const
 {
     // To minimize memory waste, we do not want to run the background chunk
     // allocation if we already have some empty chunks or when the runtime has
     // a small heap size (and therefore likely has a small growth rate).
@@ -1074,22 +1065,20 @@ class js::gc::AutoMaybeStartBackgroundAl
             runtime->gc.startBackgroundAllocTaskIfIdle();
     }
 };
 
 Chunk *
 GCRuntime::pickChunk(const AutoLockGC &lock,
                      AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
 {
-    Chunk **listHeadp = getAvailableChunkList();
-    Chunk *chunk = *listHeadp;
-    if (chunk)
-        return chunk;
-
-    chunk = emptyChunks(lock).get(rt);
+    if (availableChunks(lock).count())
+        return availableChunks(lock).head();
+
+    Chunk *chunk = emptyChunks(lock).pop();
     if (!chunk) {
         chunk = Chunk::allocate(rt);
         if (!chunk)
             return nullptr;
         MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     }
 
     MOZ_ASSERT(chunk->unused());
@@ -1106,19 +1095,17 @@ GCRuntime::pickChunk(const AutoLockGC &l
      */
     GCChunkSet::AddPtr p = chunkSet.lookupForAdd(chunk);
     MOZ_ASSERT(!p);
     if (!chunkSet.add(p, chunk)) {
         releaseChunk(chunk);
         return nullptr;
     }
 
-    chunk->info.prevp = nullptr;
-    chunk->info.next = nullptr;
-    chunk->addToAvailableList(rt);
+    availableChunks(lock).push(chunk);
 
     return chunk;
 }
 
 ArenaHeader *
 GCRuntime::allocateArena(Chunk *chunk, Zone *zone, AllocKind thingKind, const AutoLockGC &lock)
 {
     MOZ_ASSERT(chunk->hasAvailableArenas());
@@ -1163,17 +1150,16 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     systemZone(nullptr),
 #ifdef JSGC_GENERATIONAL
     nursery(rt),
     storeBuffer(rt, nursery),
 #endif
     stats(rt),
     marker(rt),
     usage(nullptr),
-    availableChunkListHead(nullptr),
     maxMallocBytes(0),
     numArenasFreeCommitted(0),
     verifyPreData(nullptr),
     verifyPostData(nullptr),
     chunkAllocationSinceLastGC(false),
     nextFullGCTime(0),
     lastGCTime(0),
     mode(JSGC_MODE_INCREMENTAL),
@@ -1407,17 +1393,23 @@ GCRuntime::finish()
             for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
                 js_delete(comp.get());
             js_delete(zone.get());
         }
     }
 
     zones.clear();
 
-    availableChunkListHead = nullptr;
+    for (ChunkPool::Iter iter(availableChunks_); !iter.done();) {
+        Chunk *chunk = iter.get();
+        iter.next();
+        MOZ_ASSERT(chunkSet.has(chunk));
+        availableChunks_.remove(chunk);
+    }
+
     if (chunkSet.initialized()) {
         for (GCChunkSet::Range r(chunkSet.all()); !r.empty(); r.popFront())
             releaseChunk(r.front());
         chunkSet.clear();
     }
 
     FreeChunkPool(rt, emptyChunks_);
 
@@ -3402,42 +3394,44 @@ GCRuntime::maybePeriodicFullGC()
 }
 
 // Do all possible decommit immediately from the current thread without
 // releasing the GC lock or allocating any memory.
 void
 GCRuntime::decommitAllWithoutUnlocking(const AutoLockGC &lock)
 {
     MOZ_ASSERT(emptyChunks(lock).count() == 0);
-    for (Chunk *chunk = *getAvailableChunkList(); chunk; chunk = chunk->info.next) {
+    for (ChunkPool::Iter chunk(availableChunks(lock)); !chunk.done(); chunk.next()) {
         for (size_t i = 0; i < ArenasPerChunk; ++i) {
             if (chunk->decommittedArenas.get(i) || chunk->arenas[i].aheader.allocated())
                 continue;
 
             if (MarkPagesUnused(&chunk->arenas[i], ArenaSize)) {
                 chunk->info.numArenasFreeCommitted--;
                 chunk->decommittedArenas.set(i);
             }
         }
     }
+    MOZ_ASSERT(availableChunks(lock).verify());
 }
 
 void
 GCRuntime::decommitArenas(const AutoLockGC &lock)
 {
     // Verify that all entries in the empty chunks pool are decommitted.
-    for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); e.popFront())
-        MOZ_ASSERT(e.front()->info.numArenasFreeCommitted == 0);
+    for (ChunkPool::Iter chunk(emptyChunks(lock)); !chunk.done(); chunk.next())
+        MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
 
     // Build a Vector of all current available Chunks. Since we release the
     // gc lock while doing the decommit syscall, it is dangerous to iterate
     // the available list directly, as concurrent operations can modify it.
     mozilla::Vector<Chunk *> toDecommit;
-    for (Chunk *chunk = availableChunkListHead; chunk; chunk = chunk->info.next) {
-        if (!toDecommit.append(chunk)) {
+    MOZ_ASSERT(availableChunks(lock).verify());
+    for (ChunkPool::Iter iter(availableChunks(lock)); !iter.done(); iter.next()) {
+        if (!toDecommit.append(iter.get())) {
             // The OOM handler does a full, immediate decommit, so there is
             // nothing more to do here in any case.
             return onOutOfMallocMemory(lock);
         }
     }
 
     // Start at the tail and stop before the first chunk: we allocate from the
     // head and don't want to thrash with the mutator.
@@ -3457,16 +3451,17 @@ GCRuntime::decommitArenas(const AutoLock
             chunk->releaseArena(rt, aheader, lock, Chunk::ArenaDecommitState(ok));
 
             // FIXME Bug 1095620: add cancellation support when this becomes
             // a ParallelTask.
             if (/* cancel_ || */ !ok)
                 return;
         }
     }
+    MOZ_ASSERT(availableChunks(lock).verify());
 }
 
 void
 GCRuntime::expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock)
 {
 #ifdef JSGC_FJGENERATIONAL
     rt->threadPool.pruneChunkCache();
 #endif
@@ -3649,17 +3644,17 @@ BackgroundAllocTask::run()
     while (!cancel_ && runtime->gc.wantBackgroundAllocation(lock)) {
         Chunk *chunk;
         {
             AutoUnlockGC unlock(runtime);
             chunk = Chunk::allocate(runtime);
             if (!chunk)
                 break;
         }
-        chunkPool_.put(chunk);
+        chunkPool_.push(chunk);
     }
 }
 
 void
 GCHelperState::startBackgroundSweep()
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -364,22 +364,27 @@ class DebugScript
      * When non-zero, compile script in single-step mode. The top bit is set and
      * cleared by setStepMode, as used by JSD. The lower bits are a count,
      * adjusted by changeStepModeCount, used by the Debugger object. Only
      * when the bit is clear and the count is zero may we compile the script
      * without single-step support.
      */
     uint32_t        stepMode;
 
-    /* Number of breakpoint sites at opcodes in the script. */
+    /*
+     * Number of breakpoint sites at opcodes in the script. This is the number
+     * of populated entries in DebugScript::breakpoints, below.
+     */
     uint32_t        numSites;
 
     /*
-     * Array with all breakpoints installed at opcodes in the script, indexed
-     * by the offset of the opcode into the script.
+     * Breakpoints set in our script. For speed and simplicity, this array is
+     * parallel to script->code(): the BreakpointSite for the opcode at
+     * script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
+     * this array's true length is script->length().
      */
     BreakpointSite  *breakpoints[1];
 };
 
 typedef HashMap<JSScript *,
                 DebugScript *,
                 DefaultHasher<JSScript *>,
                 SystemAllocPolicy> DebugScriptMap;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -236,16 +236,17 @@ class Debugger::FrameRange
             entry = dbg->frames.lookup(frame);
             if (entry)
                 break;
             nextDebugger++;
         }
     }
 };
 
+
 /*** Breakpoints *********************************************************************************/
 
 BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc)
   : script(script), pc(pc), enabledCount(0)
 {
     MOZ_ASSERT(!script->hasBreakpointsAt(pc));
     JS_INIT_CLIST(&breakpoints);
 }
@@ -338,16 +339,17 @@ Breakpoint::nextInDebugger()
 
 Breakpoint *
 Breakpoint::nextInSite()
 {
     JSCList *link = JS_NEXT_LINK(&siteLinks);
     return (link == &site->breakpoints) ? nullptr : fromSiteLinks(link);
 }
 
+
 /*** Debugger hook dispatch **********************************************************************/
 
 Debugger::Debugger(JSContext *cx, NativeObject *dbg)
   : object(dbg),
     uncaughtExceptionHook(nullptr),
     enabled(true),
     trackingAllocationSites(false),
     allocationSamplingProbability(1.0),
@@ -2963,18 +2965,21 @@ Debugger::addDebuggeeGlobal(JSContext *c
             return false;
         }
 
         debuggeeCompartment->setObjectMetadataCallback(SavedStacksMetadataCallback);
         setMetadataCallback = true;
     }
 
     /*
-     * Each debugger-debuggee relation must be stored in up to three places.
-     * JSCompartment::addDebuggee enables debug mode if needed.
+     * For global to become this js::Debugger's debuggee:
+     * - global must be in this->debuggees,
+     * - this js::Debugger must be in global->getDebuggers(), and
+     * - JSCompartment::isDebuggee()'s bit must be set.
+     * All three indications must be kept consistent.
      */
     AutoCompartment ac(cx, global);
     GlobalObject::DebuggerVector *v = GlobalObject::getOrCreateDebuggers(cx, global);
     if (!v || !v->append(this)) {
         js_ReportOutOfMemory(cx);
     } else {
         if (!debuggees.put(global)) {
             js_ReportOutOfMemory(cx);
@@ -5809,16 +5814,17 @@ static const JSPropertySpec DebuggerFram
 };
 
 static const JSFunctionSpec DebuggerFrame_methods[] = {
     JS_FN("eval", DebuggerFrame_eval, 1, 0),
     JS_FN("evalWithBindings", DebuggerFrame_evalWithBindings, 1, 0),
     JS_FS_END
 };
 
+
 /*** Debugger.Object *****************************************************************************/
 
 static void
 DebuggerObject_trace(JSTracer *trc, JSObject *obj)
 {
     /*
      * There is a barrier on private pointers, so the Unbarriered marking
      * is okay.
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3624,16 +3624,17 @@ public:
     };
 
     explicit CompartmentPrivate(JSCompartment *c)
         : wantXrays(false)
         , writeToGlobalPrototype(false)
         , skipWriteToGlobalPrototype(false)
         , universalXPConnectEnabled(false)
         , forcePermissiveCOWs(false)
+        , CPOWTime(0)
         , skipCOWCallableChecks(false)
         , scriptability(c)
         , scope(nullptr)
     {
         MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
         mozilla::PodArrayZero(wrapperDenialWarnings);
     }
 
@@ -3677,16 +3678,19 @@ public:
     // This is only ever set during mochitest runs when enablePrivilege is called.
     // It allows the SpecialPowers scope to waive the normal chrome security
     // wrappers and expose properties directly to content. This lets us avoid a
     // bunch of overhead and complexity in our SpecialPowers automation glue.
     //
     // Using it in production is inherently unsafe.
     bool forcePermissiveCOWs;
 
+    // A running count of how much time we've spent processing CPOWs.
+    PRIntervalTime               CPOWTime;
+
     // Disables the XPConnect security checks that deny access to callables and
     // accessor descriptors on COWs. Do not use this unless you are bholley.
     bool skipCOWCallableChecks;
 
     // Whether we've emitted a warning about a property that was filtered out
     // by a security wrapper. See XrayWrapper.cpp.
     bool wrapperDenialWarnings[WrapperDenialTypeCount];
 
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -873,18 +873,17 @@ RestyleManager::ProcessRestyledFrames(ns
     aChangeList.ChangeAt(index, &changeData);
     if (changeData->mFrame) {
       propTable->Delete(changeData->mFrame, ChangeListProperty());
     }
 
 #ifdef DEBUG
     // reget frame from content since it may have been regenerated...
     if (changeData->mContent) {
-      if (!nsAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent) &&
-          !nsTransitionManager::ContentOrAncestorHasTransition(changeData->mContent)) {
+      if (!css::CommonAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent)) {
         nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
         if (frame) {
           DebugVerifyStyleTree(frame);
         }
       }
     } else if (!changeData->mFrame ||
                changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
       NS_WARNING("Unable to test style tree integrity -- no content node "
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2885,16 +2885,22 @@ nsDisplayLayerEventRegions::AddFrame(nsD
   } else {
     mHitRegion.Or(mHitRegion, borderBox);
   }
   if (aBuilder->GetAncestorHasTouchEventHandler()) {
     mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
   }
 }
 
+void
+nsDisplayLayerEventRegions::AddInactiveScrollPort(const nsRect& aRect)
+{
+  mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aRect);
+}
+
 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
                                nsIFrame* aCaretFrame)
   : nsDisplayItem(aBuilder, aCaretFrame)
   , mCaret(aBuilder->GetCaret())
   , mBounds(aBuilder->GetCaretRect() + ToReferenceFrame())
 {
   MOZ_COUNT_CTOR(nsDisplayCaret);
 }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -2578,16 +2578,21 @@ public:
   }
 
   NS_DISPLAY_DECL_NAME("LayerEventRegions", TYPE_LAYER_EVENT_REGIONS)
 
   // Indicate that aFrame's border-box contributes to the event regions for
   // this layer. aFrame must have the same reference frame as mFrame.
   void AddFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
 
+  // Indicate that an inactive scrollframe's scrollport should be added to the
+  // dispatch-to-content region, to ensure that APZ lets content create a
+  // displayport.
+  void AddInactiveScrollPort(const nsRect& aRect);
+
   const nsRegion& HitRegion() { return mHitRegion; }
   const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; }
   const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; }
 
 private:
   // Relative to aFrame's reference frame.
   // These are the points that are definitely in the hit region.
   nsRegion mHitRegion;
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2947,16 +2947,27 @@ ScrollFrameHelper::BuildDisplayList(nsDi
       nsLayoutUtils::WantSubAPZC() &&
       WantAsyncScroll() &&
       // If we are the root scroll frame for the display root then we don't need a scroll
       // info layer to make a ComputeFrameMetrics call for us as
       // nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us.
       (!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
   }
 
+  if (aBuilder->IsPaintingToWindow() &&
+      !mShouldBuildScrollableLayer &&
+      shouldBuildLayer)
+  {
+    if (nsDisplayLayerEventRegions *eventRegions = aBuilder->GetLayerEventRegions()) {
+      // Make sure that APZ will dispatch events back to content so we can
+      // create a displayport for this frame.
+      eventRegions->AddInactiveScrollPort(mScrollPort + aBuilder->ToReferenceFrame(mOuter));
+    }
+  }
+
   mScrollParentID = aBuilder->GetCurrentScrollParentId();
 
   nsDisplayListCollection scrolledContent;
   {
     // Note that setting the current scroll parent id here means that positioned children
     // of this scroll info layer will pick up the scroll info layer as their scroll handoff
     // parent. This is intentional because that is what happens for positioned children
     // of scroll layers, and we want to maintain consistent behaviour between scroll layers
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1442,16 +1442,49 @@ nsDisplayImage::GetLayerState(nsDisplayL
   mImage->GetImageContainer(aManager, getter_AddRefs(container));
   if (!container) {
     return LAYER_NONE;
   }
 
   return LAYER_ACTIVE;
 }
 
+
+/* virtual */ nsRegion
+nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+                                bool* aSnap)
+{
+  *aSnap = true;
+  bool animated;
+  if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated &&
+      mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
+    // OK, the entire region painted by the image is opaque. But what is that
+    // region? It's the image's "dest rect" (the rect where a full copy of
+    // the image is mapped), clipped to the container's content box (which is
+    // what GetBounds() returns). So, we grab those rects and intersect them.
+    const nsRect frameContentBox = GetBounds(aSnap);
+
+    // Note: To get the "dest rect", we have to provide the "constraint rect"
+    // (which is the content-box, with the effects of fragmentation undone).
+    nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
+    nsRect constraintRect(frameContentBox.TopLeft(),
+                          imageFrame->mComputedSize);
+    constraintRect.y -= imageFrame->GetContinuationOffset();
+
+    const nsRect destRect =
+      nsLayoutUtils::ComputeObjectDestRect(constraintRect,
+                                           imageFrame->mIntrinsicSize,
+                                           imageFrame->mIntrinsicRatio,
+                                           imageFrame->StylePosition());
+
+    return nsRegion(destRect.Intersect(frameContentBox));
+  }
+  return nsRegion();
+}
+
 already_AddRefed<Layer>
 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
                            LayerManager* aManager,
                            const ContainerLayerParameters& aParameters)
 {
   nsRefPtr<ImageContainer> container;
   nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
   NS_ENSURE_SUCCESS(rv, nullptr);
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -394,30 +394,24 @@ public:
   nsRect GetBounds(bool* aSnap)
   {
     *aSnap = true;
 
     nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
     return imageFrame->GetInnerArea() + ToReferenceFrame();
   }
 
-  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+                           bool* aSnap) MOZ_OVERRIDE
   {
     return GetBounds(aSnap);
   }
 
-  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap)
-  {
-    *aSnap = true;
-    bool animated;
-    if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated && mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
-      return nsRegion(GetBounds(aSnap));
-    }
-    return nsRegion();
-  }
+  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+                                   bool* aSnap) MOZ_OVERRIDE;
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE;
 
   /**
    * Configure an ImageLayer for this display item.
    * Set the required filter and scaling transform.
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-1-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        padding: 4px;
+        width: 32px;
+        height: 32px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is kept from filling the container via 'object-fit'. This
+     is an interesting case because, by default, images fill their container,
+     which means we can often optimize away the background completely. BUT, if
+     "object-fit" prevents the image from filling its container, we can't
+     optimize away the background; it need to be painted in the uncovered area.
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        object-fit: none;
+        width: 40px;
+        height: 40px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-2-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .fakeBackground {
+        background: salmon;
+        height: 3in;
+        width: 32px;
+      }
+
+      img.test {
+        width: 32px;
+        height: 32px;
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <div class="fakeBackground"></div>
+    <img class="test" src="blue-32x32.png">
+    <div class="fakeBackground"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is kept from filling the container via 'object-fit' (and
+     the img element is fragmented). This is an interesting case because, by
+     default, images fill their container, which means we can often optimize
+     away the background completely. BUT, if "object-fit" prevents the image
+     from filling its container, we can't optimize away the background; it need
+     to be painted in the uncovered area.
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      img.test {
+        background: salmon;
+        object-fit: none;
+        width: 32px;
+        /* We make the height 6in larger than the image's intrinsic height,
+         * which gives us the following happy results:
+         *  (1) the <img> will split over several 3in tall reftest-print cards
+         *      (so, we get to test fragmentation).
+         *  (2) the image pixels end up on the second fragment (not the first),
+         *      so we get to test image-data painting on later fragments.
+         *  (3) the reference case can easily match us using a simple img
+         *      with 3in-tall divs before & after it.
+         */
+        height: calc(32px + 6in);
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <img class="test" src="blue-32x32.png">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-1-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        padding-top: 5px;
+        padding-left: 5px;
+        width: 27px;
+        height: 27px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is offset from the container via 'object-position'. This is
+     an interesting case because, by default, images fill their container,
+     which means we can often optimize away the background completely. BUT, if
+     "object-position" offsets the image from its container's content-box, we
+     can't optimize away the background; it need to be painted in the uncovered
+     area.
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        object-position: 5px 5px;
+        width: 32px;
+        height: 32px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-2-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      img.test {
+        background: salmon;
+        padding-left: 10px;
+        padding-top: 20px;
+        width: 22px;
+        height: calc(5in - 20px);
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <img class="test" src="blue-32x32.png">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is offset from the container via 'object-position' (and
+     the img element is fragmented). This is an interesting case because, by
+     default, images fill their container, which means we can often optimize
+     away the background completely. BUT, if "object-position" offsets the
+     image from its container's content-box, we can't optimize away the
+     background; it need to be painted in the uncovered area.
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      img.test {
+        background: salmon;
+        object-position: 10px 20px;
+        width: 32px;
+        height: 5in;
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <img class="test" src="blue-32x32.png">
+  </body>
+</html>
--- a/layout/reftests/image/reftest.list
+++ b/layout/reftests/image/reftest.list
@@ -6,16 +6,22 @@ fuzzy-if(Android,8,30) == background-ima
 skip-if(B2G&&browserIsRemote) == image-zoom-1.html image-zoom-1-ref.html
 skip-if(B2G&&browserIsRemote) == image-zoom-2.html image-zoom-1-ref.html
 == invalid-url-image-1.html invalid-url-image-1-ref.html
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1a.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1b.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1c.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1d.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 
+# Tests for "object-fit" & "object-position"
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-with-background-1.html image-object-fit-with-background-1-ref.html
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-with-background-2.html image-object-fit-with-background-2-ref.html
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-1.html image-object-position-with-background-1-ref.html
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-2.html image-object-position-with-background-2-ref.html
+
 # Tests for image-orientation used with 'from-image' (note that all
 # image-orientation tests are fuzzy because the JPEG images do not perfectly
 # reproduce blocks of solid color, even at maximum quality):
 fuzzy(2,5) == image-orientation-from-image.html?none     image-orientation-ref.html?0
 fuzzy(1,1) == image-orientation-from-image.html?0        image-orientation-ref.html?0
 fuzzy(1,1) == image-orientation-from-image.html?90       image-orientation-ref.html?90
 fuzzy(1,1) == image-orientation-from-image.html?180      image-orientation-ref.html?180
 fuzzy(1,1) == image-orientation-from-image.html?270      image-orientation-ref.html?270
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -17,16 +17,17 @@
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsDisplayList.h"
 #include "mozilla/MemoryReporting.h"
 #include "RestyleManager.h"
+#include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
 
 
 using mozilla::layers::Layer;
 using mozilla::dom::AnimationPlayer;
 using mozilla::dom::Animation;
 
@@ -181,16 +182,60 @@ CommonAnimationManager::HasAttributeDepe
 }
 
 /* virtual */ bool
 CommonAnimationManager::MediumFeaturesChanged(nsPresContext* aPresContext)
 {
   return false;
 }
 
+/* virtual */ void
+CommonAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
+{
+  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
+                    "pres context mismatch");
+  nsIStyleRule *rule =
+    GetAnimationRule(aData->mElement,
+                     nsCSSPseudoElements::ePseudo_NotPseudoElement);
+  if (rule) {
+    aData->mRuleWalker->Forward(rule);
+  }
+}
+
+/* virtual */ void
+CommonAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
+{
+  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
+                    "pres context mismatch");
+  if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
+      aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
+    return;
+  }
+
+  // FIXME: Do we really want to be the only thing keeping a
+  // pseudo-element alive?  I *think* the non-animation restyle should
+  // handle that, but should add a test.
+  nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
+  if (rule) {
+    aData->mRuleWalker->Forward(rule);
+  }
+}
+
+/* virtual */ void
+CommonAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
+{
+}
+
+#ifdef MOZ_XUL
+/* virtual */ void
+CommonAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
+{
+}
+#endif
+
 /* virtual */ size_t
 CommonAnimationManager::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mElementCollections
   //
   // The following members are not measured
@@ -252,16 +297,116 @@ CommonAnimationManager::ExtractComputedV
                         StyleAnimationValue::eUnit_Enumerated,
                       "unexpected unit");
     aComputedValue.SetIntValue(aComputedValue.GetIntValue(),
                                StyleAnimationValue::eUnit_Visibility);
   }
   return result;
 }
 
+AnimationPlayerCollection*
+CommonAnimationManager::GetAnimationPlayers(dom::Element *aElement,
+                                            nsCSSPseudoElements::Type aPseudoType,
+                                            bool aCreateIfNeeded)
+{
+  if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
+    // Early return for the most common case.
+    return nullptr;
+  }
+
+  nsIAtom *propName;
+  if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
+    propName = GetAnimationsAtom();
+  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
+    propName = GetAnimationsBeforeAtom();
+  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
+    propName = GetAnimationsAfterAtom();
+  } else {
+    NS_ASSERTION(!aCreateIfNeeded,
+                 "should never try to create transitions for pseudo "
+                 "other than :before or :after");
+    return nullptr;
+  }
+  AnimationPlayerCollection* collection =
+    static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
+  if (!collection && aCreateIfNeeded) {
+    // FIXME: Consider arena-allocating?
+    collection =
+      new AnimationPlayerCollection(aElement, propName, this);
+    nsresult rv =
+      aElement->SetProperty(propName, collection,
+                            &AnimationPlayerCollection::PropertyDtor, false);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("SetProperty failed");
+      delete collection;
+      return nullptr;
+    }
+    if (propName == nsGkAtoms::animationsProperty ||
+        propName == nsGkAtoms::transitionsProperty) {
+      aElement->SetMayHaveAnimations();
+    }
+
+    AddElementCollection(collection);
+  }
+
+  return collection;
+}
+
+nsIStyleRule*
+CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
+                                         nsCSSPseudoElements::Type aPseudoType)
+{
+  NS_ABORT_IF_FALSE(
+    aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
+    aPseudoType == nsCSSPseudoElements::ePseudo_before ||
+    aPseudoType == nsCSSPseudoElements::ePseudo_after,
+    "forbidden pseudo type");
+
+  if (!mPresContext->IsDynamic()) {
+    // For print or print preview, ignore animations.
+    return nullptr;
+  }
+
+  AnimationPlayerCollection* collection =
+    GetAnimationPlayers(aElement, aPseudoType, false);
+  if (!collection) {
+    return nullptr;
+  }
+
+  RestyleManager* restyleManager = mPresContext->RestyleManager();
+  if (restyleManager->SkipAnimationRules()) {
+    // During the non-animation part of processing restyles, we don't
+    // add the animation rule.
+
+    if (collection->mStyleRule && restyleManager->PostAnimationRestyles()) {
+      collection->PostRestyleForAnimation(mPresContext);
+    }
+
+    return nullptr;
+  }
+
+  // Animations should already be refreshed, but transitions may not be.
+  // Note that this is temporary, we would like both animations and transitions
+  // to both be refreshed by this point.
+  if (IsAnimationManager()) {
+    NS_WARN_IF_FALSE(!collection->mNeedsRefreshes ||
+                     collection->mStyleRuleRefreshTime ==
+                       mPresContext->RefreshDriver()->MostRecentRefresh(),
+                     "should already have refreshed style rule");
+  } else {
+    // FIXME: Remove this assignment.  See bug 1061364.
+    collection->mNeedsRefreshes = true;
+    collection->EnsureStyleRuleFor(
+      mPresContext->RefreshDriver()->MostRecentRefresh(),
+      EnsureStyleRule_IsNotThrottled);
+  }
+
+  return collection->mStyleRule;
+}
+
 /* static */ const CommonAnimationManager::LayerAnimationRecord
   CommonAnimationManager::sLayerAnimationInfo[] =
     { { eCSSProperty_transform,
         nsDisplayItem::TYPE_TRANSFORM,
         nsChangeHint_UpdateTransformLayer },
       { eCSSProperty_opacity,
         nsDisplayItem::TYPE_OPACITY,
         nsChangeHint_UpdateOpacityLayer } };
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -48,45 +48,67 @@ public:
 
   // nsIStyleRuleProcessor (parts)
   virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual nsRestyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) MOZ_OVERRIDE;
+  virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
+  virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
+  virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
+#ifdef MOZ_XUL
+  virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
+#endif
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
 
   /**
    * Notify the manager that the pres context is going away.
    */
   void Disconnect();
 
   // Tell the restyle tracker about all the styles that we're currently
   // animating, so that it can update the animation rule for these
   // elements.
   void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker);
 
-  virtual AnimationPlayerCollection*
+  AnimationPlayerCollection*
   GetAnimationPlayers(dom::Element *aElement,
                       nsCSSPseudoElements::Type aPseudoType,
-                      bool aCreateIfNeeded) = 0;
+                      bool aCreateIfNeeded);
+
+  // Returns true if aContent or any of its ancestors has an animation
+  // or transition.
+  static bool ContentOrAncestorHasAnimation(nsIContent* aContent) {
+    do {
+      if (aContent->GetProperty(nsGkAtoms::animationsProperty) ||
+          aContent->GetProperty(nsGkAtoms::transitionsProperty)) {
+        return true;
+      }
+    } while ((aContent = aContent->GetParent()));
+
+    return false;
+  }
 
   // Notify this manager that one of its collections of animation players,
   // has been updated.
   void NotifyCollectionUpdated(AnimationPlayerCollection& aCollection);
 
   enum FlushFlags {
     Can_Throttle,
     Cannot_Throttle
   };
 
+  nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
+                                 nsCSSPseudoElements::Type aPseudoType);
+
   static bool ExtractComputedValueForTransition(
                   nsCSSProperty aProperty,
                   nsStyleContext* aStyleContext,
                   mozilla::StyleAnimationValue& aComputedValue);
 
   // For CSS properties that may be animated on a separate layer, represents
   // a record of the corresponding layer type and change hint.
   struct LayerAnimationRecord {
@@ -109,16 +131,24 @@ protected:
 
   void AddElementCollection(AnimationPlayerCollection* aCollection);
   void ElementCollectionRemoved() { CheckNeedsRefresh(); }
   void RemoveAllElementCollections();
 
   // Check to see if we should stop or start observing the refresh driver
   void CheckNeedsRefresh();
 
+  virtual nsIAtom* GetAnimationsAtom() = 0;
+  virtual nsIAtom* GetAnimationsBeforeAtom() = 0;
+  virtual nsIAtom* GetAnimationsAfterAtom() = 0;
+
+  virtual bool IsAnimationManager() {
+    return false;
+  }
+
   // When this returns a value other than nullptr, it also,
   // as a side-effect, notifies the ActiveLayerTracker.
   static AnimationPlayerCollection*
   GetAnimationsForCompositor(nsIContent* aContent,
                              nsIAtom* aElementProperty,
                              nsCSSProperty aProperty);
 
   PRCList mElementCollections;
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -6,17 +6,16 @@
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
 
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleAnimationValue.h"
 
 #include "nsPresContext.h"
-#include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
 #include "nsCSSRules.h"
 #include "RestyleManager.h"
 #include "nsLayoutUtils.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include <math.h>
@@ -193,107 +192,16 @@ nsAnimationManager::QueueEvents(Animatio
   for (size_t playerIdx = aCollection->mPlayers.Length(); playerIdx-- != 0; ) {
     CSSAnimationPlayer* player =
       aCollection->mPlayers[playerIdx]->AsCSSAnimationPlayer();
     MOZ_ASSERT(player, "Expected a collection of CSS Animation players");
     player->QueueEvents(aEventsToDispatch);
   }
 }
 
-AnimationPlayerCollection*
-nsAnimationManager::GetAnimationPlayers(dom::Element *aElement,
-                                        nsCSSPseudoElements::Type aPseudoType,
-                                        bool aCreateIfNeeded)
-{
-  if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
-    // Early return for the most common case.
-    return nullptr;
-  }
-
-  nsIAtom *propName;
-  if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
-    propName = nsGkAtoms::animationsProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
-    propName = nsGkAtoms::animationsOfBeforeProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
-    propName = nsGkAtoms::animationsOfAfterProperty;
-  } else {
-    NS_ASSERTION(!aCreateIfNeeded,
-                 "should never try to create transitions for pseudo "
-                 "other than :before or :after");
-    return nullptr;
-  }
-  AnimationPlayerCollection* collection =
-    static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
-  if (!collection && aCreateIfNeeded) {
-    // FIXME: Consider arena-allocating?
-    collection =
-      new AnimationPlayerCollection(aElement, propName, this);
-    nsresult rv =
-      aElement->SetProperty(propName, collection,
-                            &AnimationPlayerCollection::PropertyDtor, false);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("SetProperty failed");
-      delete collection;
-      return nullptr;
-    }
-    if (propName == nsGkAtoms::animationsProperty) {
-      aElement->SetMayHaveAnimations();
-    }
-
-    AddElementCollection(collection);
-  }
-
-  return collection;
-}
-
-/* virtual */ void
-nsAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-  nsIStyleRule *rule =
-    GetAnimationRule(aData->mElement,
-                     nsCSSPseudoElements::ePseudo_NotPseudoElement);
-  if (rule) {
-    aData->mRuleWalker->Forward(rule);
-  }
-}
-
-/* virtual */ void
-nsAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-  if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
-      aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
-    return;
-  }
-
-  // FIXME: Do we really want to be the only thing keeping a
-  // pseudo-element alive?  I *think* the non-animation restyle should
-  // handle that, but should add a test.
-  nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
-  if (rule) {
-    aData->mRuleWalker->Forward(rule);
-  }
-}
-
-/* virtual */ void
-nsAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
-{
-}
-
-#ifdef MOZ_XUL
-/* virtual */ void
-nsAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
-{
-}
-#endif
-
 /* virtual */ size_t
 nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mPendingEvents
@@ -724,57 +632,16 @@ nsAnimationManager::BuildSegment(Infalli
   } else {
     tf = &aAnimation.GetTimingFunction();
   }
   segment.mTimingFunction.Init(*tf);
 
   return true;
 }
 
-nsIStyleRule*
-nsAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
-                                     nsCSSPseudoElements::Type aPseudoType)
-{
-  NS_ABORT_IF_FALSE(
-    aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
-    aPseudoType == nsCSSPseudoElements::ePseudo_before ||
-    aPseudoType == nsCSSPseudoElements::ePseudo_after,
-    "forbidden pseudo type");
-
-  if (!mPresContext->IsDynamic()) {
-    // For print or print preview, ignore animations.
-    return nullptr;
-  }
-
-  AnimationPlayerCollection* collection =
-    GetAnimationPlayers(aElement, aPseudoType, false);
-  if (!collection) {
-    return nullptr;
-  }
-
-  RestyleManager* restyleManager = mPresContext->RestyleManager();
-  if (restyleManager->SkipAnimationRules()) {
-    // During the non-animation part of processing restyles, we don't
-    // add the animation rule.
-
-    if (collection->mStyleRule && restyleManager->PostAnimationRestyles()) {
-      collection->PostRestyleForAnimation(mPresContext);
-    }
-
-    return nullptr;
-  }
-
-  NS_WARN_IF_FALSE(!collection->mNeedsRefreshes ||
-                   collection->mStyleRuleRefreshTime ==
-                     mPresContext->RefreshDriver()->MostRecentRefresh(),
-                   "should already have refreshed style rule");
-
-  return collection->mStyleRule;
-}
-
 /* virtual */ void
 nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime)
 {
   NS_ABORT_IF_FALSE(mPresContext,
                     "refresh driver should not notify additional observers "
                     "after pres context has been destroyed");
   if (!mPresContext->GetPresShell()) {
     // Someone might be keeping mPresContext alive past the point
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -156,40 +156,23 @@ public:
 
   static mozilla::AnimationPlayerCollection*
   GetAnimationsForCompositor(nsIContent* aContent, nsCSSProperty aProperty)
   {
     return mozilla::css::CommonAnimationManager::GetAnimationsForCompositor(
       aContent, nsGkAtoms::animationsProperty, aProperty);
   }
 
-  // Returns true if aContent or any of its ancestors has an animation.
-  static bool ContentOrAncestorHasAnimation(nsIContent* aContent) {
-    do {
-      if (aContent->GetProperty(nsGkAtoms::animationsProperty)) {
-        return true;
-      }
-    } while ((aContent = aContent->GetParent()));
-
-    return false;
-  }
-
   void UpdateStyleAndEvents(mozilla::AnimationPlayerCollection* aEA,
                             mozilla::TimeStamp aRefreshTime,
                             mozilla::EnsureStyleRuleFlags aFlags);
   void QueueEvents(mozilla::AnimationPlayerCollection* aEA,
                    mozilla::EventArray &aEventsToDispatch);
 
   // nsIStyleRuleProcessor (parts)
-  virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
-#ifdef MOZ_XUL
-  virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
-#endif
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
 
   // nsARefreshObserver
   virtual void WillRefresh(mozilla::TimeStamp aTime) MOZ_OVERRIDE;
 
@@ -218,22 +201,29 @@ public:
    */
   void DispatchEvents() {
     // Fast-path the common case: no events
     if (!mPendingEvents.IsEmpty()) {
       DoDispatchEvents();
     }
   }
 
-  virtual mozilla::AnimationPlayerCollection*
-  GetAnimationPlayers(mozilla::dom::Element *aElement,
-                      nsCSSPseudoElements::Type aPseudoType,
-                      bool aCreateIfNeeded) MOZ_OVERRIDE;
-  nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
-                                 nsCSSPseudoElements::Type aPseudoType);
+protected:
+  virtual nsIAtom* GetAnimationsAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::animationsProperty;
+  }
+  virtual nsIAtom* GetAnimationsBeforeAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::animationsOfBeforeProperty;
+  }
+  virtual nsIAtom* GetAnimationsAfterAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::animationsOfAfterProperty;
+  }
+  virtual bool IsAnimationManager() MOZ_OVERRIDE {
+    return true;
+  }
 
 private:
   void BuildAnimations(nsStyleContext* aStyleContext,
                        mozilla::dom::Element* aTarget,
                        mozilla::dom::AnimationTimeline* aTimeline,
                        mozilla::AnimationPlayerPtrArray& aAnimations);
   bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>&
                       aSegments,
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -1445,18 +1445,21 @@ nsStyleSet::RuleNodeWithReplacement(Elem
             }
           }
           break;
         }
         case eRestyle_CSSTransitions: {
           if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
               aPseudoType == nsCSSPseudoElements::ePseudo_before ||
               aPseudoType == nsCSSPseudoElements::ePseudo_after) {
-            PresContext()->TransitionManager()->
-              WalkTransitionRule(aElement, aPseudoType, &ruleWalker);
+            nsIStyleRule* rule = PresContext()->TransitionManager()->
+              GetAnimationRule(aElement, aPseudoType);
+            if (rule) {
+              ruleWalker.ForwardOnPossiblyCSSRule(rule);
+            }
           }
           break;
         }
         case eRestyle_SVGAttrAnimations: {
           MOZ_ASSERT(aReplacements & (eRestyle_ChangeAnimationPhase |
                                       eRestyle_ChangeAnimationPhaseDescendants),
                      "don't know how to do this level without phase change");
 
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -431,17 +431,17 @@ nsTransitionManager::ConsiderStartingTra
   // transition that the current value rounds to the final value.  In
   // this case, we'll end up with shouldAnimate as false (because
   // there's no value change), but we need to return early here rather
   // than cancel the running transition because shouldAnimate is false!
   MOZ_ASSERT(!oldPT || oldPT->Properties()[0].mSegments.Length() == 1,
              "Should have one animation property segment for a transition");
   if (haveCurrentTransition && haveValues &&
       oldPT->Properties()[0].mSegments[0].mToValue == endValue) {
-    // WalkTransitionRule already called RestyleForAnimation.
+    // GetAnimationRule already called RestyleForAnimation.
     return;
   }
 
   nsPresContext *presContext = aNewStyleContext->PresContext();
 
   if (!shouldAnimate) {
     if (haveCurrentTransition) {
       // We're in the middle of a transition, and just got a non-transition
@@ -455,17 +455,17 @@ nsTransitionManager::ConsiderStartingTra
       players.RemoveElementAt(currentIndex);
       aElementTransitions->UpdateAnimationGeneration(mPresContext);
 
       if (players.IsEmpty()) {
         aElementTransitions->Destroy();
         // |aElementTransitions| is now a dangling pointer!
         aElementTransitions = nullptr;
       }
-      // WalkTransitionRule already called RestyleForAnimation.
+      // GetAnimationRule already called RestyleForAnimation.
     }
     return;
   }
 
   const nsTimingFunction &tf = aTransition.GetTimingFunction();
   float delay = aTransition.GetDelay();
   float duration = aTransition.GetDuration();
   if (duration < 0.0) {
@@ -574,143 +574,20 @@ nsTransitionManager::ConsiderStartingTra
   }
   aElementTransitions->UpdateAnimationGeneration(mPresContext);
   aElementTransitions->PostRestyleForAnimation(presContext);
 
   *aStartedAny = true;
   aWhichStarted->AddProperty(aProperty);
 }
 
-AnimationPlayerCollection*
-nsTransitionManager::GetAnimationPlayers(
-  dom::Element *aElement,
-  nsCSSPseudoElements::Type aPseudoType,
-  bool aCreateIfNeeded)
-{
-  if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
-    // Early return for the most common case.
-    return nullptr;
-  }
-
-  nsIAtom *propName;
-  if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
-    propName = nsGkAtoms::transitionsProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
-    propName = nsGkAtoms::transitionsOfBeforeProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
-    propName = nsGkAtoms::transitionsOfAfterProperty;
-  } else {
-    NS_ASSERTION(!aCreateIfNeeded,
-                 "should never try to create transitions for pseudo "
-                 "other than :before or :after");
-    return nullptr;
-  }
-  AnimationPlayerCollection* collection =
-    static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
-  if (!collection && aCreateIfNeeded) {
-    // FIXME: Consider arena-allocating?
-    collection = new AnimationPlayerCollection(aElement, propName, this);
-    nsresult rv =
-      aElement->SetProperty(propName, collection,
-                            &AnimationPlayerCollection::PropertyDtor, false);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("SetProperty failed");
-      delete collection;
-      return nullptr;
-    }
-    if (propName == nsGkAtoms::transitionsProperty) {
-      aElement->SetMayHaveAnimations();
-    }
-
-    AddElementCollection(collection);
-  }
-
-  return collection;
-}
-
 /*
  * nsIStyleRuleProcessor implementation
  */
 
-void
-nsTransitionManager::WalkTransitionRule(dom::Element* aElement,
-                                        nsCSSPseudoElements::Type aPseudoType,
-                                        nsRuleWalker* aRuleWalker)
-{
-  AnimationPlayerCollection* collection =
-    GetAnimationPlayers(aElement, aPseudoType, false);
-  if (!collection) {
-    return;
-  }
-
-  if (!mPresContext->IsDynamic()) {
-    // For print or print preview, ignore animations.
-    return;
-  }
-
-  RestyleManager* restyleManager = mPresContext->RestyleManager();
-  if (restyleManager->SkipAnimationRules()) {
-    // If we're processing a normal style change rather than one from
-    // animation, don't add the transition rule.  This allows us to
-    // compute the new style value rather than having the transition
-    // override it, so that we can start transitioning differently.
-
-    if (restyleManager->PostAnimationRestyles()) {
-      // We need to immediately restyle with animation
-      // after doing this.
-      collection->PostRestyleForAnimation(mPresContext);
-    }
-    return;
-  }
-
-  collection->mNeedsRefreshes = true;
-  collection->EnsureStyleRuleFor(
-    mPresContext->RefreshDriver()->MostRecentRefresh(),
-    EnsureStyleRule_IsNotThrottled);
-
-  if (collection->mStyleRule) {
-    aRuleWalker->Forward(collection->mStyleRule);
-  }
-}
-
-/* virtual */ void
-nsTransitionManager::RulesMatching(ElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-  WalkTransitionRule(aData->mElement,
-                     nsCSSPseudoElements::ePseudo_NotPseudoElement,
-                     aData->mRuleWalker);
-}
-
-/* virtual */ void
-nsTransitionManager::RulesMatching(PseudoElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-
-  // Note:  If we're the only thing keeping a pseudo-element frame alive
-  // (per ProbePseudoStyleContext), we still want to keep it alive, so
-  // this is ok.
-  WalkTransitionRule(aData->mElement, aData->mPseudoType,
-                     aData->mRuleWalker);
-}
-
-/* virtual */ void
-nsTransitionManager::RulesMatching(AnonBoxRuleProcessorData* aData)
-{
-}
-
-#ifdef MOZ_XUL
-/* virtual */ void
-nsTransitionManager::RulesMatching(XULTreeRuleProcessorData* aData)
-{
-}
-#endif
-
 /* virtual */ size_t
 nsTransitionManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
 }
 
 /* virtual */ size_t
 nsTransitionManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -95,33 +95,16 @@ public:
     : mozilla::css::CommonAnimationManager(aPresContext)
     , mInAnimationOnlyStyleUpdate(false)
   {
   }
 
   typedef mozilla::AnimationPlayerCollection AnimationPlayerCollection;
 
   static AnimationPlayerCollection*
-  GetTransitions(nsIContent* aContent) {
-    return static_cast<AnimationPlayerCollection*>
-      (aContent->GetProperty(nsGkAtoms::transitionsProperty));
-  }
-
-  // Returns true if aContent or any of its ancestors has a transition.
-  static bool ContentOrAncestorHasTransition(nsIContent* aContent) {
-    do {
-      if (GetTransitions(aContent)) {
-        return true;
-      }
-    } while ((aContent = aContent->GetParent()));
-
-    return false;
-  }
-
-  static AnimationPlayerCollection*
   GetAnimationsForCompositor(nsIContent* aContent, nsCSSProperty aProperty)
   {
     return mozilla::css::CommonAnimationManager::GetAnimationsForCompositor(
       aContent, nsGkAtoms::transitionsProperty, aProperty);
   }
 
   /**
    * StyleContextChanged
@@ -143,40 +126,36 @@ public:
     StyleContextChanged(mozilla::dom::Element *aElement,
                         nsStyleContext *aOldStyleContext,
                         nsStyleContext *aNewStyleContext);
 
   void SetInAnimationOnlyStyleUpdate(bool aInAnimationOnlyUpdate) {
     mInAnimationOnlyStyleUpdate = aInAnimationOnlyUpdate;
   }
 
-  // nsIStyleRuleProcessor (parts)
-  virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
-#ifdef MOZ_XUL
-  virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
-#endif
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
 
   // nsARefreshObserver
   virtual void WillRefresh(mozilla::TimeStamp aTime) MOZ_OVERRIDE;
 
   void FlushTransitions(FlushFlags aFlags);
 
-  virtual AnimationPlayerCollection*
-  GetAnimationPlayers(mozilla::dom::Element *aElement,
-                      nsCSSPseudoElements::Type aPseudoType,
-                      bool aCreateIfNeeded) MOZ_OVERRIDE;
-  void WalkTransitionRule(mozilla::dom::Element* aElement,
-                          nsCSSPseudoElements::Type aPseudoType,
-                          nsRuleWalker* aRuleWalker);
+protected:
+  virtual nsIAtom* GetAnimationsAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::transitionsProperty;
+  }
+  virtual nsIAtom* GetAnimationsBeforeAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::transitionsOfBeforeProperty;
+  }
+  virtual nsIAtom* GetAnimationsAfterAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::transitionsOfAfterProperty;
+  }
 
 private:
   void
   ConsiderStartingTransition(nsCSSProperty aProperty,
                              const mozilla::StyleTransition& aTransition,
                              mozilla::dom::Element* aElement,
                              AnimationPlayerCollection*& aElementTransitions,
                              nsStyleContext* aOldStyleContext,
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -459,16 +459,24 @@ status_t MPEG4Extractor::readMetaData() 
     if (mInitCheck != NO_INIT) {
         return mInitCheck;
     }
 
     off64_t offset = 0;
     status_t err;
     while (!mFirstTrack) {
         err = parseChunk(&offset, 0);
+        // The parseChunk function returns UNKNOWN_ERROR to skip
+        // some boxes we don't want to handle. Filter that error
+        // code but return others so e.g. I/O errors propagate.
+        if (err != OK && err != (status_t) UNKNOWN_ERROR) {
+          ALOGW("Error %d parsing chuck at offset %lld looking for first track",
+              err, (long long)offset);
+          break;
+        }
     }
 
     if (mInitCheck == OK) {
         if (mHasVideo) {
             mFileMetaData->setCString(
                     kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
         } else {
             mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
--- a/media/mtransport/test/buffered_stun_socket_unittest.cpp
+++ b/media/mtransport/test/buffered_stun_socket_unittest.cpp
@@ -37,28 +37,16 @@ MtransportTestUtils *test_utils;
 static uint8_t kStunMessage[] = {
   0x00, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42,
   0x9b, 0x90, 0xbe, 0x2c, 0xae, 0x1a, 0x0c, 0xa8,
   0xa0, 0xd6, 0x8b, 0x08, 0x80, 0x28, 0x00, 0x04,
   0xdb, 0x35, 0x5f, 0xaa
 };
 static size_t kStunMessageLen = sizeof(kStunMessage);
 
-class DummySocket;
-
-// Temporary whitelist for refcounted class dangerously exposing its destructor.
-// Filed bug 1028140 to address this class.
-namespace mozilla {
-template<>
-struct HasDangerousPublicDestructor<DummySocket>
-{
-  static const bool value = true;
-};
-}
-
 class DummySocket : public NrSocketBase {
  public:
   DummySocket()
       : writable_(UINT_MAX),
         write_buffer_(nullptr),
         readable_(UINT_MAX),
         read_buffer_(nullptr),
         cb_(nullptr),
@@ -202,16 +190,18 @@ class DummySocket : public NrSocketBase 
     }
 
     return self_;
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket);
 
  private:
+  ~DummySocket() {}
+
   DISALLOW_COPY_ASSIGN(DummySocket);
 
   size_t writable_;  // Amount we allow someone to write.
   ScopedDeletePtr<DataBuffer> write_buffer_;
   size_t readable_;   // Amount we allow someone to read.
   ScopedDeletePtr<DataBuffer> read_buffer_;
 
   NR_async_cb cb_;
@@ -225,17 +215,17 @@ class BufferedStunSocketTest : public ::
       : dummy_(nullptr),
         test_socket_(nullptr) {}
 
   ~BufferedStunSocketTest() {
     nr_socket_destroy(&test_socket_);
   }
 
   void SetUp() {
-    ScopedDeletePtr<DummySocket> dummy(new DummySocket());
+    nsRefPtr<DummySocket> dummy(new DummySocket());
 
     int r = nr_socket_buffered_stun_create(
         dummy->get_nr_socket(),
         kStunMessageLen,
         &test_socket_);
     ASSERT_EQ(0, r);
     dummy_ = dummy.forget();  // Now owned by test_socket_.
 
@@ -246,17 +236,17 @@ class BufferedStunSocketTest : public ::
     r = nr_socket_connect(test_socket_,
                           &remote_addr_);
     ASSERT_EQ(0, r);
   }
 
   nr_socket *socket() { return test_socket_; }
 
  protected:
-  DummySocket *dummy_;
+  nsRefPtr<DummySocket> dummy_;
   nr_socket *test_socket_;
   nr_transport_addr remote_addr_;
 };
 
 
 TEST_F(BufferedStunSocketTest, TestCreate) {
 }
 
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -2732,17 +2732,17 @@ public class BrowserApp extends GeckoApp
 
             return true;
         }
 
         bookmark.setEnabled(!AboutPages.isAboutReader(tab.getURL()));
         bookmark.setVisible(!GeckoProfile.get(this).inGuestMode());
         bookmark.setCheckable(true);
         bookmark.setChecked(tab.isBookmark());
-        bookmark.setIcon(tab.isBookmark() ? R.drawable.ic_menu_bookmark_remove : R.drawable.ic_menu_bookmark_add);
+        bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
 
         back.setEnabled(tab.canDoBack());
         forward.setEnabled(tab.canDoForward());
         desktopMode.setChecked(tab.getDesktopMode());
         desktopMode.setIcon(tab.getDesktopMode() ? R.drawable.ic_menu_desktop_mode_on : R.drawable.ic_menu_desktop_mode_off);
 
         String url = tab.getURL();
         if (AboutPages.isAboutReader(url)) {
@@ -2831,16 +2831,32 @@ public class BrowserApp extends GeckoApp
             exitGuestMode.setVisible(true);
         } else {
             enterGuestMode.setVisible(true);
         }
 
         return true;
     }
 
+    private int resolveBookmarkIconID(final boolean isBookmark) {
+        if (NewTabletUI.isEnabled(this) && HardwareUtils.isLargeTablet()) {
+            if (isBookmark) {
+                return R.drawable.new_tablet_ic_menu_bookmark_remove;
+            } else {
+                return R.drawable.new_tablet_ic_menu_bookmark_add;
+            }
+        }
+
+        if (isBookmark) {
+            return R.drawable.ic_menu_bookmark_remove;
+        } else {
+            return R.drawable.ic_menu_bookmark_add;
+        }
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         Tab tab = null;
         Intent intent = null;
 
         final int itemId = item.getItemId();
 
         // Track the menu action. We don't know much about the context, but we can use this to determine
@@ -2852,21 +2868,21 @@ public class BrowserApp extends GeckoApp
         }
 
         if (itemId == R.id.bookmark) {
             tab = Tabs.getInstance().getSelectedTab();
             if (tab != null) {
                 if (item.isChecked()) {
                     Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "bookmark");
                     tab.removeBookmark();
-                    item.setIcon(R.drawable.ic_menu_bookmark_add);
+                    item.setIcon(resolveBookmarkIconID(false));
                 } else {
                     Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "bookmark");
                     tab.addBookmark();
-                    item.setIcon(R.drawable.ic_menu_bookmark_remove);
+                    item.setIcon(resolveBookmarkIconID(true));
                 }
             }
             return true;
         }
 
         if (itemId == R.id.share) {
             shareCurrentUrl();
             return true;
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -223,16 +223,17 @@ public class GeckoAppShell
     private static Sensor gAccelerometerSensor;
     private static Sensor gLinearAccelerometerSensor;
     private static Sensor gGyroscopeSensor;
     private static Sensor gOrientationSensor;
     private static Sensor gProximitySensor;
     private static Sensor gLightSensor;
 
     private static final String GECKOREQUEST_RESPONSE_KEY = "response";
+    private static final String GECKOREQUEST_ERROR_KEY = "error";
 
     /*
      * Keep in sync with constants found here:
      * http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl
     */
     static public final int WPL_STATE_START = 0x00000001;
     static public final int WPL_STATE_STOP = 0x00000010;
     static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
@@ -429,17 +430,17 @@ public class GeckoAppShell
     public static void sendRequestToGecko(final GeckoRequest request) {
         final String responseMessage = "Gecko:Request" + request.getId();
 
         EventDispatcher.getInstance().registerGeckoThreadListener(new NativeEventListener() {
             @Override
             public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
                 EventDispatcher.getInstance().unregisterGeckoThreadListener(this, event);
                 if (!message.has(GECKOREQUEST_RESPONSE_KEY)) {
-                    request.onError();
+                    request.onError(message.getObject(GECKOREQUEST_ERROR_KEY));
                     return;
                 }
                 request.onResponse(message.getObject(GECKOREQUEST_RESPONSE_KEY));
             }
         }, responseMessage);
 
         sendEventToGecko(GeckoEvent.createBroadcastEvent(request.getName(), request.getData()));
     }
--- a/mobile/android/base/menu/GeckoMenuInflater.java
+++ b/mobile/android/base/menu/GeckoMenuInflater.java
@@ -2,16 +2,17 @@
  * 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/. */
 
 package org.mozilla.gecko.menu;
 
 import java.io.IOException;
 
 import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.NewTabletUI;
 import org.mozilla.gecko.R;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -127,22 +128,30 @@ public class GeckoMenuInflater extends M
         item.title = a.getText(R.styleable.MenuItem_android_title);
         item.checkable = a.getBoolean(R.styleable.MenuItem_android_checkable, false);
         item.checked = a.getBoolean(R.styleable.MenuItem_android_checked, false);
         item.visible = a.getBoolean(R.styleable.MenuItem_android_visible, true);
         item.enabled = a.getBoolean(R.styleable.MenuItem_android_enabled, true);
         item.hasSubMenu = false;
 
         // TODO: (bug 1058909) Remove this branch when we remove old tablet. We do this to
-        // avoid using a new menu resource for new tablet (which only has a new reload button).
-        if (item.id == R.id.reload && NewTabletUI.isEnabled(mContext)) {
-            item.iconRes = R.drawable.new_tablet_ic_menu_reload;
+        // avoid using a new menu resource for new tablet.
+        final int iconResID;
+        if (!NewTabletUI.isEnabled(mContext)) {
+            iconResID = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
         } else {
-            item.iconRes = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
+            if (item.id == R.id.reload) {
+                iconResID = R.drawable.new_tablet_ic_menu_reload;
+            } else if (HardwareUtils.isLargeTablet() && item.id == R.id.bookmark) {
+                iconResID = R.drawable.new_tablet_ic_menu_bookmark_add;
+            } else {
+                iconResID = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
+            }
         }
+        item.iconRes = iconResID;
 
         if (Versions.feature11Plus) {
             item.showAsAction = a.getInt(R.styleable.MenuItem_android_showAsAction, 0);
         }
 
         a.recycle();
     }
 
index 802af44f1596d5162feab910ad868a03fa145c30..5bd9a95a161377502d0e64865b5e3e74a414a55f
GIT binary patch
literal 775
zc%17D@N?(olHy`uVBq!ia0vp^(m*WB!3-pGf}dA0FfdvN_=LCuxp7Gu(TVBt$?0*4
z8F4@|3B-;~0;06&gfx(X#B^ihG>9xv+#w+WB$%8Lo0t-xlmXNZRPW&602GN!N&{;L
zGSVU9AO?s8DT_@`1?fsk4^2n_GUDS?ViQ0LVq;@N6BB`ofhZy=38W@DEh;$~s3j&T
zDJmfyXiGfU90UnNKz2+_8pvUZ88I=*V2jd2!5}^!XkA)-a$0O+ny+tKXeh)BAeR7T
zA$p;PKphPBa&!vVmW;T#xEP4<z-EHTWUxyT)BTc?fLcB=>D>ngL`F%FUob<fR>|D0
zC$=vv)@7W)ntmsPgK4(Y-3N9oOH2R!z0mOD@P-2~V;7i6d^QgHCnkFSm(;aB`OWI8
zkFMU^bLIWMFFS8ZmY1pQILOASJ)<R$i$CYxBIcFfCr$maY+ixP%-_P%=brz3dUAOr
z?+yDIUBIXl_H=O!sbE}t;iZ{VqR6q2x~(!^9U2QgUd(7;a%Sdp$JBYY-|JbwNuK+b
zwO9V{zJJ%_&PUDu7thQYzu5l5l{-Q?s<SV$nQv)7^r6LC$z^VAL3Fvus(JS!<nLUQ
zKOW!7xI>ro_=5`tTD*x{1tT6UmTZntTd-y08e2oZd7Yk_bsIALCRwVM?__DnoWEpd
zqz8B7S<hMg&KH<&ESf%T+QKb14Wc)-)@ZFbvDT$3`_47b{O0I+U%2iF&HlBGb=%r}
z>jc+e`O^uztG{I1yR2)rh&VYVWm!SSgvT7yzI^12^gDl!F(zix*&R)9chyYZqZ^*U
zJxMvpb#GLUO}XfbO^!i#>}|YOUU?P&@k>l?Wo`BUAAf$SUFYZVsYx@|mbC#!34^Dr
KpUXO@geCxrZ$pm&
index cc01566a8da04aeeb3b6488b713beac498feb69e..20678853b8465345d5c76ab85bd3bd38d4934b5c
GIT binary patch
literal 555
zc%17D@N?(olHy`uVBq!ia0vp^LO?9S!3-p~d(N!@QuhLULR^8ggM&kKbV@{GVrXb+
zTvD2+r)O+ZT6}UwTvB>`a=Jr80+0kDAU7!^CLsl=CN2>ylAIBpkP1?nm=PD3;p>|g
z2LwP_uwJ0aKs9k7Eg-!>Eip-{@j#VH86YlD3Zx8bOKdFASfFWOy@?>H*u=D`1h7<6
zS`0*4B3M~sT6}y;ELbEaF*P<RJt`>)WM+I?Y$C`IpkAQ)F$oz!hyAp!DhIm5vn0qb
zn1Mw`maU#W`_P{w95r_LAK6xg|N3kCW3CyOu;YS%jOX8Y%=8Z~T(my_c3|#T&5yd*
zVy5M(?kb4>S*aLroUqTzL}{<G(~Zwp9VQ;RG9PH@W=|K#kP61qQ`du990c4Rh6%D9
z<w=tXJd+e~=HS`td;eRvy-3u%vC3Bb^4EG(quo1yvifgvt^J#z*<!#o%{6R=YTVMy
z+}h?@X4?;J_4W1Sdh_PpTX`8dP46<EXD(arT+%E^TYBJ<WlY4*B@4B8gzY?dQTL8!
zW!>Z{1^RF09u#)XQ#f@?g!z2lo*TiAzq0=C-CO_SuZhE#uXF$WVQRfK|6uD+@wJyr
lgT5ZQdY=7o(UksDABN1kTIIZZCxMa!gQu&X%Q~loCIHb%<d*;d
index 92e88fd92c9eb9c8e6c189c1ac5c77c5a5e61163..1f22612cef7a301a43a33433a2e3a132b084654c
GIT binary patch
literal 995
zc%17D@N?(olHy`uVBq!ia0vp^DnP8k!3-o9%B;A^z`)oa;1l8s<i;hX1tcUyC#J_F
zq{k<v#l@w>#>N80<CD{)qM~AxQ{$3>;^}co8SzOOu}SHn2?;<g(a|X&!Q_m1uxvy^
z22c>F5vUA|(nCW-V-qtX5)(lRk~3mrl4F6o5>miiplRumk!fHPK<wDq6o-U_*u>O$
zuwJMk$>|Ob4spp~!MHf68<I1Ojnm>1!O9?d!GdvdX|c&^@rmg`DG)a?4Qvj`t{AAx
zGk{Ktj*bSIlL&G%&@b^I1hgn4CMh*84&)cGXH(;nQo$ONfDQvWH31kb5VxhpfJ_EC
zFD5Y+7)sG#?*P39atzo3U^l?L07mi2AjibVr^Y5gf)PZ<g53tzl>re1I{Wy1lhr^&
zTS|ibf*I-sv|Qcv#F%OXW7mHAxF%VmN@o7=Uo#adxZA(=vX#l6KciA?@#5<jk^7G)
zDRQ{?JUQ?(izVrm@bw3)SkwRIpHBTD*O!>{H-6=@Bhhygc=+D?U(MJvadxoTuC0D9
zAJjE-HPoJ$8og<<VszeKuwh1IW%#+h8#i|z)b`!IK5*gwo1b^gId*Pw2r#MCd%8G=
zR50$ne6yZAQR3*w%a_VHuC7|<%Bw1-tGP9}IU>r(SG9>{qkdAn$D8iY?Y}GRChs}F
zNAi8@e)H$g%Bwf5*p=`yZqt_K+s`*|>Is;Z9=2x5i+EFmjvHC4GBVB@Onq}g<6oq=
z@Y-m5H}A4${+uP9X1&HopXpl8%6M(I?3SPK*LMrLU98n5X5YEG+4?=d@7tT%?)OYI
zzrD3@m{I>B!06P3q+bS8_9<I^>JO5c-B@OG;UZ_(<jRcYA<hXg+YGpJ9n6npb{Std
z$9AQ_FRe`A)Eyn+g1Bc)Q+lonh^OvKXeo0(b2_F;Q1^y`+C`B$dfp)|!Jdn>3$(mT
zME$O-%xbt?+Qk*KsL3VN;_iYSoYSL4w%*=wXD9b!FT;9%&hPRc6mQ9~ifmPy^1<+=
z%&mC_VRcdMALiLEuV|e1%X^#8zEgYo?)S`a-|OoArSJboyQY<P23L>Uh^!afaJV3u
z&2y@n?pgmAUw(RD6KP$4f5JNJKmQ&-U;ppYd;4PUfPYL4f6QuQPw)Q@Od1THu6{1-
HoD!M<@M5Rs
index 107c77e8c3feb7d213a4f5c39fff08b76f16a475..2eb454333e88ae0ba338647ccb0bfe6b86fdfc54
GIT binary patch
literal 1323
zc%17D@N?(olHy`uVBq!ia0vp^W<YGo!3-q5&T3dOFfe`%@Ck7Ra^sRRKxATiR8&-4
zQhHosT3k{Zh>@HTpOg`sm;n@tO-PSTO!G@h0;x>SaBy&lOG*W+Nr$KelCg>Dp`oEb
zc0^JVP&__4JvKHLB$AXKnwSVA<AExH2F9iU1%a|a5~u~LH#yzd7-(rm3`j4~-t@?X
zw19*Jpj1pUNIV{FAjC#6BO?wdo}3z=m=1O!NF+WfH6CbMaz<=2*b8wPF^Op)jbM92
zL(`(8qhpiOKx&dQqN7uQ;;}#m%&9=n#wDc1K=dYp?Mlpmr~xTRg4+vZfWjvxCOIDL
zDF^}wMFzw!FoHW0Xbw;TC@^DSApn$(N=S{2Oo>fS2C0n8h>J@FsRa8SgyKPd0ed9F
z)6)}d8pP!wi$W6;A`%nh!M22kCPyTIT;-6E0CZstFs72gmIBo$Wkg1%#U!PI-3(G3
z5A;!T#trqo8o-ELRubeF%+Sg>LC`Y3wrkqN>J(pdUZ&YBo>Q;<`+c~Md8yRvw_<GT
zM0>w~Q{>#v)BO2Q8|wk}YiBhCP6T``IX@}?!^X);iDliJ{r<k|b2Kyh`l*SZulUS1
znJo#<zqp@9c~^vdnH!h-BI8J4d1r^pluPrkuXvf(@ZjL?8Cl_L9_jDZ+-V@L^F&(h
z@q+2Mv{!|`VV`C5W8Xaq*|5_>3O6}wJhGpaz7h#ud+2B3otL|}F)%RMd%8G=R50E>
z|1-QNQ0Dl@^F3`LSy8jHf}*^Pl0^)+M6A;6_G#IqBBWLA6e)Bn$m!dgiRbUXlsR5o
zHShVo^jH60eZBmBPW8R#HG2Qz^<Ferb1oEl9=S`WuF@&#Qr8oa8<oK;Ph7fWY*cHK
zlKQ;j&$RC`r*58VvGMv8yIMg+B<NIEm-mX0SsSJ5r><RSs~s=BVDW=p0u7(OZb=uN
z+LL+MEhKl5?QNN7OkUe_mg;`qRdYpQQ`ReiNZz$sueYh@-QT!*pQ4O*cF%<+D=Wh}
z{{JX9Z=bu#w%Rg3{&Zc0OLX?snZFsH%jz^Oc{@SN+NXK_QSMgdw?)?qOZHi@O?|97
zWwV%+pTpA4bBn}mJr{CoEpG}*S*}^K+~HwQ|Ee|ZPC_D4E1R^EotBAjlF@po(^@=X
z?sBOr%`XSf-SuZ*A$*<nMy38GPQ6ZT@v^zoWgm8(c(ibFNQ&u&iK?4cU%05UDf@z<
z!lsZHJM=a=zSyC-X;M+f4$p^TCLsY5O?`<IUGtosZCkA`6ijdP%h=(rXu#zD{q6=S
z-k^#P0(^44y}uK`&S+EjUubg6*N*$(A`Kts1ST%w$tpKL$cUMmoP68tJO9Vu1a_Tc
znUhRY<6hNG{5Z+UUnFf|_?8*#@?<LC`c^-#jJ9#xT>d^WEY#D}z{xXBTF0*DiBih(
zgKAO7Sw+t|Jo+b+nDaMoMU7kn&*7H^0Uw@f%d*617KDAvoy2v&x?W-1%$o}tbhGNu
wwQb<YJh?V@KijA0&ps{Nyu7aZ&xF5>>%5IlnD(VS115e3Pgg&ebxsLQ0FSdbYXATM
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..79aea57bc797602fbb01191f315abfa28ff388a8
GIT binary patch
literal 897
zc%17D@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z6#fV2lj#32|ir0|y5OW8*Z3goM!0
zP+#9PAQ>GU9RT7c$Hm1(My7=(CdS6bMn<NDCL}~8B}FACM<is##->Cjqyfq3#Ps<1
z)VR2msD#wG<mA}Iw78`7n8eiR_>7o@jJTw<*u<2Wg!I^i^qAzdIH2OBjF_a1*rbg3
z#Pqo2)Yzo-xWu&h#I)EXpe#r*HW8#NE-3>E0o9~KNTBw(<g|DoI~gpVoB=WcBnVaj
zK_GTUd@>RP*{Gy+un3TZm;hqLLl|HK<Dn)%>;fr9Vt@^V+YV+!%mG`M5f4Vo1VrM1
zAzM@u<QL4q$im9O$;HDbAS5gzDkd%=C9j~Up{b><XJBY<X=Ux;=;Yz$9TFCukeHO5
zl9`=TSzX`I)Y{(B-8*68<SEnUEm*i{@ygZfHf-Fqb=&scdk-8ueB{{i6DQAHx^nf}
z{fAGUK6~-%_1pI!KYjl4?fZ|PzkdJCEz?{8jN(*J7sn6@$<l*=t%DO8jz3&K{ifn!
zk!81>G^b2oGQBrRb;*Vk(LIa&_-;M>{Qv)j%j=$7J%4!XUG(*e``c`<eiys9a@{)b
zAG1SjBYPsR7HS{Z@ggKwKe1SN)2fr76DuUn-3~ond?P>OMtk@w#X2tKOB{bPC$C=l
z{$qFl{nG(CQ>3~BEMsO>PRg2eaeqO;u~}0LJ3}oaUW5y3--zxx*`fQ6HNrJF;fXri
z<RX=JwNDb#yZ$oY_*l}so%_!o<*)OYq6=e%?=#hXJ-OpGbE}wT6jwvNf9QASguRgq
zpOq?joR-?bV5j|sb%#vIi$u;o5v!(8%9fipEW04}<jE;#zlAb26SAe}oqX|fq9Nbz
zqQ7lDR}AA<o+<Z`{O~k`Ju3Nfx<vZrjbRE4J=px*`ZxBu`#)$@N{(=B_!=_tddSpg
zLY+H>Li0r;kL{?sue;c~zIEdo55^x;CQXfE+^1#qbp5iY9_xPv|9UTfT&M8~r+(;0
PP)_o6^>bP0l+XkKjlzI7
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..67654c5d76800a5bcd2844bd2aa17224679a12cf
GIT binary patch
literal 641
zc%17D@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TGp96eCTp7S1G&I!LH_b08DLOhj
zG&C(LDk?TMHZ&n2CMGQ|E+Z;AIU*q=Dk&)@DJecaH98?JK0YNjDLpzNH6}4NIzA&l
zIXyNpB`z@|COIuWF+DChJuV3-k^y2TrNx5DxTK6&kaz}ADjrHg89<G(U~XJ8R2D1_
zLh(>7U}a!7R2SF^h%AUq28lytp@zT}Be)<o5ItD7#tP`?fRZ4;U<O7`E?z!<K`{wQ
zX?X=Db!}Z^6H_Z|J13uj$gJG5%G$d6rndHup5Fe6ljbg5vUJ(<HJi8X+I#fa$;-Fy
zK7R7z&D(eHKYsfB<?FZaKYsrD^VfgTaYdlnZ#`WcLnI_?4|aw%IWVwZm>w1pm=PHn
zb*oEvi*}@1(h|?^_y6}V>oj(Elh`9}{At~6_ie$SWM=m8f7;xjb6g}RB|;`ZV!gxr
z2@|<9p4zw_3Vk6Hu&c@KUGJsoos!Su{=R$K9z6Ma`{KX7tQ=vNc~_X$<VC$G>eIV5
zXEE>Q<36^+8*I`(=y0gckE-LGxVAy+pxTnTg28ULXRK=R;m{7a;j>}kuGB-kA+l;E
z3qH+!;GAf1)M3q;DMm@5UVrVDY9;Zy9ZNs$t?uk5)nO@8xBj|<h~9opi;k@xc4CTa
ot%WzWM!fx-Dc<$y?}E1a`m$5~mNi|Q4}(&Yr>mdKI;Vst00M{w+W-In
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0098b76bd26ab501d846fee8e477e60691979e4b
GIT binary patch
literal 629
zc%17D@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQae*%0$Tp3`%!NDOkG&DLoIv^n-
zDk>^8G&wRdEi^GPHa0dQDJd>4BQ7p2A|WF-HYGkj4M;{Oq{YRh#3d)kB&NnEr^hCy
zM1#oGnB=tBg!H(?^q8a!AQ_vK9-o*I2PBiz<C4;1LF_azJ0liM#sj5*DuD>XMIyln
zq7j7RLE>Qb$q;UO9GD9xq1xk;;VR+W41_X>Y%;{yjPLbHF+iUOmjw9*GcYo-aPkQV
z3W<n{NlD8oC@O2}>KR(uIXSy~`-VivWE2*al$KRBHnp|)Ph7NN<E9<EcJDoW<k*Q*
zXU?9#aOv9h+js9hc=+V$i<ht8ynFxo>(9UclAk431C77x>Eak7A<24>)yavGfptUT
zb|$4);Tv~v;I`ga`+a}6G{f%Qz?eD7!_aW~i3~%6(edLm*%)M&Y7{Xs_&nJavg@44
zg2mTv+}Qp5tW5FGz4!0uT(OE#J6WE+eDb-u*UBfwWT>rHR&BU+BJ0wX;;H>o3|srR
zR7vhQSuMKY*Sy<|61&bvv#!|lG*LpL_MDBu{ON`b%~u+$_!w=L%`cky&xWz&@7Axm
zfB)os&HP`=xXLp6m(Qg0OE>p0r%hh6cgF?4Ns$MNK5f0{`~6n<CMJ!z?d**Q-(F|>
YulGTn@4_zYbWqaqboFyt=akR{0H)6buK)l5
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..112c5af885ac1c6e9fd445f97dbd6453b01a771c
GIT binary patch
literal 436
zc%17D@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQa^8<WBT!FNwr>C!PT4-o!Kte)f
zWLjioN@zktL}FrGTxx7=N_>1uOk!$ua(ZlHYFtukY(hE^#V4i4C8ozFr^P0v$AP5M
zfMi@!Mm&fOW+VaCWW*(b*bs3D8J`Rh1d9W?Anh4o5{f`7fogz)V6-(LX%o<iCM7|B
z!3^wNJOU!(60&NV##YYmo?gCwA)y&rdF2g_&FxdxtlPM4``-P>Pn<q?<<{-{4<A2!
z@$U2Af82jw0QFDsba4!kkW4+;D%9k_z;eOW@sNYg8-_q0r<?!(o8?>VQM}h&WV0yQ
z$L~U!YJP@f;EY8togVhJDS8`+y!rX<i<ZXT^SfJlUEXvEY*22H+pKzx@$td7cE*D>
zo1SGZN?95b#65L_SI7PqucofB08JAQzHAu}xz?JkXEZ7%)O_0gl6mjf_AiP%e<n6e
d+IK#N!G7;@&83%Le^zDyg}<wx%Q~loCII;Wt+fCE
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9cf55fb750949e3209eca683c740a9a5efb82d71
GIT binary patch
literal 1108
zc%17D@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^T<Z2>+Zt_&~`
z8XD^0;9zW=24W;607+k85IZ0tAu=*8HZ~<bJ}o9DIVvF?$c|1-kB?7{OHPhSOp8rQ
zk4{L5Nl1xLPLEDbk4s980jfy^ksx+*T3ljAY(jc`QfgdcI*=QioED#y0c6JmxgZA6
zgp9c4RG>LPMm&g2k4pxUV2z1saUe6HBv1>8Oiqsn8Vl3~R0%W(!T=eY4rYK10g@nd
zKqjX{xIn3RkQ7J+Y#LZu5=0!%g|Z=v;~)k?HG<VZOhYmji3GU>f*=ae%#4Sb3F0Or
ztN@ZOHH$w1<Dju5$S;_Ik%^gwm5qawi$_32QCUS*Q%l>>$i%|d&e_G)-NVz{$Jft4
zAT%sIGCDpXDLFMQBP%;6FTb?BqOz*7rM;`WXVT;;Q>RT|uyE1hB}<pBTC;Y;#x2`+
z?%KU)-~Iy!4;?;n`pnsL=PzBpa`oD++xH(ndiwn3>yMwleEs(Q$IoBC|NQ;;|LV<t
zVDfnH>Eak7A=!Jd+djCE;n>6PZ*tD43QqFU-Fj)4L@bBsE``<@rRgDU9kWh%bk+X<
zT$i3Rf3ESidG0&4#Kp9}%D<5~k|SCst-PpdO=?f*d@jz(Mc%D7im4}q>Ua<Qu@MNX
zo8J(?5fKn4-k^FqiplBJb7p~wN(a)P7Rp6@_x@M<V3S{JFXLJxIfl>MihgQI-M(LQ
zm({G<sA=|rdyjUXlTNR-3cGZ}P~x|1)bb!J&)d;8w=YI0xeBFtPtjbe<G9VmrSKBp
zwPV$@vP;%1+nHYTwqeVTv_hWk?W>ti39jJU&i{Bu=vTgDJ^{-)&LzAF^A`VZpw?Zw
z=#gj7UvHyze-F;kxWw6|X7DdA{M1?Io{bGfEXNG^W|zG26G}d!&;Osp?bAG?s6w8b
zMH|}-Y(l1)$uJj59{A{@7b{V?^N8WRQ;%5<RHth`n7Zd3)83+&tnW_0mb{=eUF^Zm
zJ@=Tu7OiA=d-|1EWBTGPt5=8g6(xkq?W?^pDbd|LK{@O6ji&qC?wn&Np5&yxWKE08
zSw_*KY=&PU*1pF|*8XZ(T~y6d!moC6lFS=6xzle|CENf0ZBN-V?Y2nf;;WZ-241o|
z^_JZ|?e&x(^TQM87kNMGhz)de=5fCiWOko#!?hI6?yFy9Z+twtG`HIF)%14zU3=~>
bQ~uBJO8v`%CefG*P+{Qd>gTe~DWM4f=D69O
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5b9299437465db3a539517136cc476c003c476ac
GIT binary patch
literal 744
zc%17D@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*HX>F!BZXgt#)mKxk;FLqbAmLPB(O
zN<?B}L_$VvY)VX0QgmW^Oj3GWa&l}^dUQf+Y+_nWLPlItT5MuUOhP&k#U-W2B&WrL
z$+)D9SfF51YJ5^gJV;|kTykn$Vp=Rv9B4>l22gukQhF>%J4iu1NMi;_1i}E3=`fTL
zpA3?UO9r8MFp2{!hLG`4@r-zwNE}Fg21pPr5)alJ4-<iCkB2D(tAVi7<6-2@!_u>W
zVG&sp<QL4q$i&7WAtS4xsIH}NXku#a;OOM);py!c5Ev9378#wClA4i|S5#b9+1b<E
zH*xZu`Ae3sTD^9|rd_-D9XNR8<e9VQE?m5P<?4<5kDtGO|Ka1O&)>fP{Qc+eKjy>C
zz+h)I@N{tuk&x^?$gO-RfPw8o{%4PSODA|}d41+_?V7NtLvGdk|NjLejql#PTgLbA
zbGTMrMWEX04+(RXXBU(opXkn-$8^Q@!7`7#lbO<2o8%n6Y07(^YmUyPEzIiEO|FKX
zILrS-olVp9IH&&Q4Ti#27VAqiUR>AbGq3rvblsb|clHIo+FLU#BCs)IvYg4{j0=<5
zyuK`HX*ILW&}V4mi!Ei6;Qan#k$9WlCs+4)L5U=0D~TfsUHqD+J;_d8L7E&!Q%)Rk
zTCB?(=6TEOkQa!-dQ0oTDz|9U2#wfVue1byYCLS#@aR-<;q+2bICbfYwcV>rQ(86r
z4;z@XpPBbH^MrHA(;H`nN<N3ntakcxP`+Pr#<cD~9~m#IpS&K+I{Df=&pM|mc~`$L
gPuTkPf>-hz>36ea{QsTQE&-)hPgg&ebxsLQ0C0^k#{d8T
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fdf1ae32a3586ecb9b45bb8c690272868607b24b
GIT binary patch
literal 1579
zc%17D@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2I}#X;^)4C~IxykuZtVhQjGab<vk
z(9lo^2M1&0G$6y%)6*d#0mz7rjRlf^NlBrhX#oidadB~xk!hici9qql$du58gqWDL
zh@_;5#KgF`w1|X^sHCL$__Uaq<hZ!hn53lW#Ps<1)aZn?n56Wmgw&YCwAiHdxTN&x
z_>8!uw3vjH_~i81#8e<VHZdhSIXxx;!cC1$NRLTQi%ZOiON21eW0TY3lQLqHQ{zCU
zWyAx`Nlc3aazSQ7xEb*v5fB4N#wMl314V#bkl{cnC?gi=B#<D80V2WdIIyeY62T<M
zA_xO)EIKy>q&_(v#s*2nLl_W2kZBN=AU4D<m^jpGxE6FSj2#b^g4hDJ3(k&5H~`9y
zg9}0ogj<~qbqv%Qa7&Ys-H@@*{`g&B3Rqnd<QL4q$i&RT%Er#Y$<4#dFCZ);Dkd%=
zDJ3hXprov#rmm%}t8ZXrVrgw-YiIB1<m~G1>E-R?9}p5677-O47oU`pnwFlKm6Kmk
zSW;S6Syf%v*wozG*51+8-8*^8)M?Xa%$zlQ{(?n|mn>bja@Fd!>o;!RvUS_`-Fx=#
z+kfETp~FXx9y@;G)af&4&tJH7<?6NTH*ej&cmLs|$4{O<d-3x1o44;ie){_D$IoBC
z|NQ;;_4m?h1_ow1PZ!4!3CXz!yPbnmW!N5k&oPeGGCi_{!O+L`bhJh$<J7{F4uYq=
z`2+&Jq+C^JO<Fngk-_Ilr<?>5wNBit-*mrx-{Uf~eTU8Fv2(BNKKg2d$5l?Fz+TtL
zUmcGw2I(9V-(s2|$n^ixw*t;j;oirepP2ogPxd?WiR*G|R~6#}yG~EneD`a@d@Zfe
z&=5Pvx^;nfHm!*k*{=H}S$nF~g0@rcCyujru`?=sXh=HZ^v*0Ovms-_Ch48OrzY8O
z2+Azq(Ku)A@*;t4%^_t<;r2m3;s?SEd_SreZK>;fBq4KrMTp{T^X*sK^=+1Ysxg&W
zJwJHnpT{nbuZ0?L?!AA((B#|{IhC&lPfsot?v)o&TDfQ6>+hUtGqhcUUdkBT?7R{h
z&$r-%jjvKc=Y=OVisuUVZS20A{czK^-;X4oTen-pK2o0eZ5FR~!iFSg-|7?<zv&ij
zD|uU`w~I%gIHYy&o`l?@GU44TUH_bzXz#RE%EISu55MKdxeLog7EQjD$!)p8FZgv^
z($^sCOhxV2DqG*{-uUzP%ApflK5y0SWxVSamdQlQEI0chHEG&kx3?T(B`voC1oYO&
zFTK{rx+QS;Hkr&YzToWE<zZPDnNlhq=VzV$aKY#8e1T`0yWH1uy7^vtx1Y87QTrC(
zLm88{UEH1+V&(Rft7lz6WegktQ+<u9ge%U5zxDR82#fqq+%o@EQN_vyo0#3KSL|We
zuU@gH@uHNL{9$kHqb4>!!7@$L-)pQpe7LmBuHt9+B5}>x=E_T_KVsWsvozzNb}I7|
z2g$h1g)^Gk1*XPcaFX3DvRPI!WA_S?gKH*vaZY*AsA;u~k7ePMfSXL4{Wa(PWcZQg
z{?aJwHS>dNw~vx%`hr{-bGnV3OQah#j&3)QUhv}ZE=MVzb<Xlf&l~Lay;%85b#fk`
z@yn+x{kp6TKJ8D~Rb8!{{XTZt?2m_^$S(VtAJ3q)LI0S^QiGeG)0gWyS~Q)VT=G*u
z&DW<(Ua#Twsr*Mizl7A@mYgXORdfz~&LbOtcuDWJBmAlc8ZW&sTw|N;8*(n}fb-Ev
qt*HjTw9GEXce3WZn%`ln{Ip&{dXq-3-*Gum8Sm-p=d#Wzp$P!v$f834
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d2baeed443d989754b3312c85f2b3b792dcf472d
GIT binary patch
literal 1123
zc%17D@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bYNg$To~XJ;>rL6p`oD;4i3h~X&^>I
z0+5W2jRlf^NlDSs(V?Mf0SO6FQBk3x$#HRUk&$UY*~rM0(1e7zxQw{Cw1|X^sHCK*
zg!H($)Y#aR*kquxl(^*N*rfE>#I(4i^thz7_+%hEB|14hCLujODJ?cJH7+SNJ}EUW
zF+DapEj}p&%t(z(NQ;57W0Nv~Cd4JC#ezgK;*!&WC>CfGkPS977Q_IV3?$=#YJln!
z)4>d&uJm{aBN@U4ks#BOK<325#gm}Qz#<U68S!9cU=pk#8D?fOgbPyu=ElLea7H{-
zFe4tW1!8G3)OM)e<P3-#;=%3#8JnCA6M>TVb$&Gfqo=DR$S;_Ik%^gwm5qawn}?T=
zUqDbuSVT-*LRwBiNmWf#N6*m2+|tI*$=TJ--P125G%P$KDmo@MAvq;AJuAPUu&A`G
zqNcW?skyal!o;c5X3d?qaM9u=OP8%)vu^!{jhnaZ*t758k)tP0o<4K--1!R^FI~QN
z{l=}^cke%Z^7Pq@m#^QvefRmx*Y7`m{r>a!pZ!OvAHXE^+|$J|L_%`z!QOc8L<aT`
z_seGcn5s_NvxT=)bn_nVu2z+$u2Z~BJ)MlWB_~Dx{g<`*UGclT^QGsD@9URMii!0-
zwD_8D(Pq(kKg1$G9b?yjq?6q)t{?gB9DC{V71r#f##i=!cfMLEb<X*!2(P@!ip~p9
zn6B6?KH0@4>b^arOi)YivXXSeo@YvH^&Iax->Ttqx^|H1?K+2R4BAi19M3)E4coQw
zpsPmJ&wt(dBDX3`uap+ayKGTTf8Ue7{l&A>SN5Juc9i(G?QYGMAG5i*@-N%wCNgYY
z>{=Bhv-T*9t@Lp#+vwv=w$ToA<{fRctGd!Ysjli$|0c&OgO1#Pf?CoaotO6pUYx<^
zTrkN?t=VAZtYu9vwIW*5G;_^ZX3whhnzi1sb(gAF<f>+e!$zt)<_;?iy~A(1S!8Hk
za^1-3_v+;;Ilo0EqOFttc6lB0>{=I8FlpIplQ}6bw3yW^cbz)yI78@=KzD!>OA@D-
zhr=5))jLf$3{-iXbNb58t7n+AZ#0cc^<&s58MW4N#Vj%Hw;I-;XNg@E$Xs~xdN$*W
zn$CWc%tjA(Ui;wc1fd<jX14Kllmvc%ez5Mwn|D7Q@0NCaDY`v5jicrLzobZgm!v=G
yYFzB>Y+O2>jw`Ic|9zSH=D~#kyX(FG|1s$%JfE;z-TN-6pzw6{b6Mw<&;$Vcved%>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/drawable/new_tablet_ic_menu_bookmark_add.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+        android:src="@null"/>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/drawable/new_tablet_ic_menu_bookmark_remove.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+        android:src="@null"/>
--- a/mobile/android/base/resources/values-large-v11/styles.xml
+++ b/mobile/android/base/resources/values-large-v11/styles.xml
@@ -102,25 +102,20 @@
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
         <item name="android:scaleType">center</item>
         <item name="android:background">@drawable/new_tablet_action_bar_button</item>
 
         <!-- layout_width/height doesn't work here, likely because it's
              an ImageButton, so we use padding instead.
 
-             Because the curve of the reload button is lower than the end of the arrow and thus
-             the top of the view, it is perceived to be slighly lower than the other buttons.
-             Thus we use padding to fill a slightly smaller height than the toolbar and let
-             android:gravity center the image with a slight offset (.5dp higher) instead. Note that
-             this offset also affects the pressed state, but it's hardly noticeable and thus negligible.
-
-             Note: the above change is done on a generic style and thus any other items added
-             to the menu bar may appear to be offset. -->
-        <item name="android:paddingTop">20dp</item>
+             Notes:
+                 * The bookmarks star is larger than the reload button
+                 * The reload button contains whitespace at the top of the image to lower it -->
+        <item name="android:paddingTop">19dp</item>
         <item name="android:paddingBottom">21dp</item>
         <item name="android:paddingLeft">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
         <item name="android:paddingRight">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
     </style>
 
     <style name="Widget.BookmarksListView" parent="Widget.HomeListView">
         <item name="android:scrollbarStyle">outsideOverlay</item>
     </style>
--- a/mobile/android/base/tests/testGeckoRequest.java
+++ b/mobile/android/base/tests/testGeckoRequest.java
@@ -91,17 +91,17 @@ public class testGeckoRequest extends UI
 
         GeckoAppShell.sendRequestToGecko(new GeckoRequest(REQUEST_EXCEPTION_EVENT, null) {
             @Override
             public void onResponse(NativeJSObject nativeJSObject) {
                 responseReceived.set(true);
             }
 
             @Override
-            public void onError() {
+            public void onError(NativeJSObject error) {
                 errorReceived.set(true);
             }
         });
 
         WaitHelper.waitFor("Received error for listener with exception", new Condition() {
             @Override
             public boolean isSatisfied() {
                 return errorReceived.get();
--- a/mobile/android/base/util/GeckoRequest.java
+++ b/mobile/android/base/util/GeckoRequest.java
@@ -1,8 +1,11 @@
+/* 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/. */
 package org.mozilla.gecko.util;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import org.mozilla.gecko.mozglue.RobocopTarget;
@@ -72,20 +75,20 @@ public abstract class GeckoRequest {
      * @param nativeJSObject The response data from Gecko
      */
     @RobocopTarget
     public abstract void onResponse(NativeJSObject nativeJSObject);
 
     /**
      * Callback executed when the request fails.
      *
-     * In general, this should not be overridden since there's no way to differentiate between
-     * expected errors and logic errors in JS. If the Gecko-side request handler wants to send a
-     * recoverable error to Java, it should include any error data in the response object that the
-     * {@link #onResponse(NativeJSObject)} callback can handle as necessary.
+     * By default, an exception is thrown. This should be overridden if the
+     * GeckoRequest is able to recover from the error.
      *
      * @throws RuntimeException
      */
     @RobocopTarget
-    public void onError() {
-        throw new RuntimeException("Unhandled error for GeckoRequest: " + name);
+    public void onError(NativeJSObject error) {
+        final String message = error.optString("message", "<no message>");
+        final String stack = error.optString("stack", "<no stack>");
+        throw new RuntimeException("Unhandled error for GeckoRequest " + name + ": " + message + "\nJS stack:\n" + stack);
     }
-}
\ No newline at end of file
+}
--- a/mobile/android/modules/Messaging.jsm
+++ b/mobile/android/modules/Messaging.jsm
@@ -138,29 +138,31 @@ let requestHandler = {
     delete this._listeners[aMessage];
     Services.obs.removeObserver(this, aMessage);
   },
 
   observe: Task.async(function* (aSubject, aTopic, aData) {
     let wrapper = JSON.parse(aData);
     let listener = this._listeners[aTopic];
 
-    // A null response indicates an error. If an error occurs in the callback
-    // below, the response will remain null, and Java will fire onError for
-    // this request.
-    let response = null;
-
     try {
-      let result = yield listener(wrapper.data);
-      if (typeof result !== "object" || result === null) {
+      let response = yield listener(wrapper.data);
+      if (typeof response !== "object" || response === null) {
         throw new Error("Gecko request listener did not return an object");
       }
-      response = result;
+
+      Messaging.sendRequest({
+        type: "Gecko:Request" + wrapper.id,
+        response: response
+      });
     } catch (e) {
-      Cu.reportError(e);
-    }
+      Cu.reportError("Error in Messaging handler for " + aTopic + ": " + e);
 
-    Messaging.sendRequest({
-      type: "Gecko:Request" + wrapper.id,
-      response: response
-    });
+      Messaging.sendRequest({
+        type: "Gecko:Request" + wrapper.id,
+        error: {
+          message: e.message || (e && e.toString()),
+          stack: e.stack || Components.stack.formattedStack,
+        }
+      });
+    }
   })
 };
--- a/testing/config/mozharness/android_arm_config.py
+++ b/testing/config/mozharness/android_arm_config.py
@@ -1,40 +1,107 @@
 # 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/.
 
 config = {
     "suite_definitions": {
         "mochitest": {
             "run_filename": "runtestsremote.py",
+            "testsdir": "mochitest",
+            "options": ["--autorun", "--close-when-done", "--dm_trans=sut",
+                "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s",
+                "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s",
+                "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
+                "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
+                "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
+                "--quiet", "--log-raw=%(raw_log_file)s",
+                "--total-chunks=16",
+                "--run-only-tests=android23.json"
+            ],
+        },
+        "mochitest-gl": {
+            "run_filename": "runtestsremote.py",
+            "testsdir": "mochitest",
             "options": ["--autorun", "--close-when-done", "--dm_trans=sut",
                 "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s",
                 "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s",
                 "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
                 "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
                 "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
-                "--quiet", "--log-raw=%(raw_log_file)s"
+                "--quiet", "--log-raw=%(raw_log_file)s",
+                "--total-chunks=2",
+                "--test-manifest=gl.json"
+            ],
+        },
+        "robocop": {
+            "run_filename": "runtestsremote.py",
+            "testsdir": "mochitest",
+            "options": ["--autorun", "--close-when-done", "--dm_trans=sut",
+                "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s",
+                "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s",
+                "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
+                "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
+                "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
+                "--quiet", "--log-raw=%(raw_log_file)s",
+                "--total-chunks=4",
+                "--robocop-path=../..",
+                "--robocop-ids=fennec_ids.txt",
+                "--robocop=robocop.ini",
             ],
         },
         "reftest": {
             "run_filename": "remotereftest.py",
+            "testsdir": "reftest",
             "options": [ "--app=%(app)s", "--ignore-window-size",
                 "--bootstrap",
                 "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s",
                 "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s",
                 "--devicePort=%(device_port)s", "--http-port=%(http_port)s",
                 "--ssl-port=%(ssl_port)s", "--httpd-path", "reftest/components",
                 "--symbols-path=%(symbols_path)s",
+                "--total-chunks=16",
+                "tests/layout/reftests/reftest.list",
+            ],
+        },
+        "crashtest": {
+            "run_filename": "remotereftest.py",
+            "testsdir": "reftest",
+            "options": [ "--app=%(app)s", "--ignore-window-size",
+                "--bootstrap",
+                "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s",
+                "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s",
+                "--devicePort=%(device_port)s", "--http-port=%(http_port)s",
+                "--ssl-port=%(ssl_port)s", "--httpd-path", "reftest/components",
+                "--symbols-path=%(symbols_path)s",
+                "--total-chunks=2",
+                "tests/testing/crashtest/crashtests.list"
+            ],
+        },
+        "jsreftest": {
+            "run_filename": "remotereftest.py",
+            "testsdir": "reftest",
+            "options": [ "--app=%(app)s", "--ignore-window-size",
+                "--bootstrap",
+                "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s",
+                "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s",
+                "--devicePort=%(device_port)s", "--http-port=%(http_port)s",
+                "--ssl-port=%(ssl_port)s", "--httpd-path", "reftest/components",
+                "--symbols-path=%(symbols_path)s",
+                "../jsreftest/tests/jstests.list",
+                "--total-chunks=6",
+                "--extra-profile-file=jsreftest/tests/user.js",
             ],
         },
         "xpcshell": {
             "run_filename": "remotexpcshelltests.py",
+            "testsdir": "xpcshell",
             "options": ["--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
                 "--xre-path=%(xre_path)s", "--testing-modules-dir=%(modules_dir)s",
                 "--apk=%(installer_path)s", "--no-logfiles",
                 "--symbols-path=%(symbols_path)s",
                 "--manifest=tests/xpcshell.ini",
                 "--log-raw=%(raw_log_file)s",
+                "--total-chunks=3",
             ],
         },
     }, # end suite_definitions
 }
--- a/testing/marionette/client/marionette/tests/unit/test_findelement.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement.py
@@ -1,16 +1,16 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
 from marionette import HTMLElement
 from by import By
-from errors import NoSuchElementException
+from errors import NoSuchElementException, InvalidSelectorException
 
 
 class TestElements(MarionetteTestCase):
     def test_id(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('mozLink');")
         found_el = self.marionette.find_element(By.ID, "mozLink")
@@ -149,8 +149,13 @@ class TestElements(MarionetteTestCase):
         self.assertFalse(el.id in [found_el.id for found_el in found_els])
 
     def test_finding_active_element_returns_element(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         fbody = self.marionette.find_element(By.TAG_NAME, 'body')
         abody = self.marionette.get_active_element()
         self.assertEqual(fbody, abody)
+
+    def test_throws_error_when_trying_to_use_invalid_selector_type(self):
+        test_html = self.marionette.absolute_url("test.html")
+        self.marionette.navigate(test_html)
+        self.assertRaises(InvalidSelectorException, self.marionette.find_element, "Brie Search Type", "doesn't matter")
--- a/testing/marionette/marionette-elements.js
+++ b/testing/marionette/marionette-elements.js
@@ -288,17 +288,17 @@ ElementManager.prototype = {
    * @return nsIDOMElement or list of nsIDOMElements
    *        Returns the element(s) by calling the on_success function.
    */
   find: function EM_find(win, values, searchTimeout, on_success, on_error, all, command_id) {
     let startTime = values.time ? values.time : new Date().getTime();
     let startNode = (values.element != undefined) ?
                     this.getKnownElement(values.element, win) : win.document;
     if (this.elementStrategies.indexOf(values.using) < 0) {
-      throw new ElementException("No such strategy.", 17, null);
+      throw new ElementException("No such strategy.", 32, null);
     }
     let found = all ? this.findElements(values.using, values.value, win.document, startNode) :
                       this.findElement(values.using, values.value, win.document, startNode);
     let type = Object.prototype.toString.call(found);
     let isArrayLike = ((type == '[object Array]') || (type == '[object HTMLCollection]') || (type == '[object NodeList]'));
     if (found == null || (isArrayLike && found.length <= 0)) {
       if (!searchTimeout || new Date().getTime() - startTime > searchTimeout) {
         if (all) {
--- a/testing/mochitest/redirect.html
+++ b/testing/mochitest/redirect.html
@@ -12,18 +12,29 @@
         detail: {
           "data": aURL + location.search,
           "type": "loadURI"
         }
       });
       document.dispatchEvent(event);
     }
 
+    function redirectToHarness()
+    {
+      redirect("chrome://mochikit/content/harness.xul");
+    }
+
     function onLoad() {
-      redirect("chrome://mochikit/content/harness.xul");
+      // Wait for MozAfterPaint, since the listener in browser-test.js is not
+      // added until then.
+      window.addEventListener("MozAfterPaint", function testOnMozAfterPaint() {
+        window.removeEventListener("MozAfterPaint", testOnMozAfterPaint);
+        setTimeout(redirectToHarness, 0);
+      });
+
     }
   </script>
 </head>
 
 <body onload="onLoad();">
 redirecting...
 </body>
 </html>
--- a/testing/web-platform/meta/media-source/mediasource-remove.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-remove.html.ini
@@ -1,14 +1,11 @@
 [mediasource-remove.html]
   type: testharness
   expected: TIMEOUT
-  [Test remove transitioning readyState from \'ended\' to \'open\'.]
-    expected: FAIL
-
   [Test removing all appended data.]
     expected: FAIL
 
   [Test removing beginning of appended data.]
     expected: FAIL
 
   [Test removing the middle of appended data.]
     expected: FAIL
--- a/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
+++ b/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
@@ -22,16 +22,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/GMPInstallManager.jsm");
 
 const URI_EXTENSION_STRINGS    = "chrome://mozapps/locale/extensions/extensions.properties";
 const STRING_TYPE_NAME         = "type.%ID%.name";
 
 const SEC_IN_A_DAY              = 24 * 60 * 60;
 
 const EME_PREF_ENABLED         = "media.eme.enabled";
+const NS_GRE_BIN_DIR           = "GreBinD";
 const CLEARKEY_PLUGIN_ID       = "gmp-clearkey";
 const CLEARKEY_VERSION         = "0.1";
 
 const OPENH264_PLUGIN_ID       = "gmp-gmpopenh264";
 const OPENH264_PREF_BRANCH     = "media." + OPENH264_PLUGIN_ID + ".";
 const OPENH264_PREF_ENABLED    = "enabled";
 const OPENH264_PREF_VERSION    = "version";
 const OPENH264_PREF_LASTUPDATE = "lastUpdate";
@@ -278,17 +279,18 @@ let OpenH264Provider = {
         gmpService.addPluginDirectory(this.gmpPath);
       } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
         this._log.warn("startup() - adding gmp directory failed with " + e.name + " - sandboxing not available?");
       }
     }
 
     if (Preferences.get(EME_PREF_ENABLED, false)) {
       try {
-        gmpService.addPluginDirectory(OS.Path.join(OS.Constants.Path.libDir,
+        let greBinDir = Services.dirsvc.get(NS_GRE_BIN_DIR, Ci.nsILocalFile);
+        gmpService.addPluginDirectory(OS.Path.join(greBinDir.path,
                                                    CLEARKEY_PLUGIN_ID,
                                                    CLEARKEY_VERSION));
       } catch (e) {
         this._log.warn("startup() - adding clearkey CDM failed", e);
       }
     }
 
     let telemetry = {};
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -79,16 +79,19 @@ if CONFIG['MOZ_ENABLE_GTK']:
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     UNIFIED_SOURCES += [
         'android/nsAndroidHandlerApp.cpp',
         'android/nsExternalSharingAppService.cpp',
         'android/nsExternalURLHandlerService.cpp',
         'android/nsMIMEInfoAndroid.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt':
+    SOURCES += [
+        'unix/nsMIMEInfoQt.cpp',
+    ]
     UNIFIED_SOURCES += [
         'unix/nsGNOMERegistry.cpp',
         'unix/nsMIMEInfoUnix.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     UNIFIED_SOURCES += [
         'win/nsMIMEInfoWin.cpp',
     ]
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsMIMEInfoQt.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#ifdef MOZ_WIDGET_QT
+#include <QDesktopServices>
+#include <QUrl>
+#include <QString>
+#include <QStringList>
+#endif
+
+#include "nsMIMEInfoQt.h"
+#include "nsIURI.h"
+#include "nsStringGlue.h"
+
+nsresult
+nsMIMEInfoQt::LoadUriInternal(nsIURI * aURI)
+{
+#ifdef MOZ_WIDGET_QT
+  nsAutoCString spec;
+  aURI->GetAsciiSpec(spec);
+  if (QDesktopServices::openUrl(QUrl(spec.get()))) {
+    return NS_OK;
+  }
+#endif
+
+  return NS_ERROR_FAILURE;
+}
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsMIMEInfoQt.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#ifndef nsMIMEInfoQt_h_
+#define nsMIMEInfoQt_h_
+
+#include "nsCOMPtr.h"
+
+class nsIURI;
+
+class nsMIMEInfoQt
+{
+public:
+  static nsresult LoadUriInternal(nsIURI * aURI);
+};
+
+#endif // nsMIMEInfoQt_h_
--- a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
+++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
@@ -1,47 +1,43 @@
 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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/. */
 
 #ifdef MOZ_WIDGET_QT
-#include <QDesktopServices>
-#include <QUrl>
-#include <QString>
 #if (MOZ_ENABLE_CONTENTACTION)
 #include <contentaction/contentaction.h>
 #include "nsContentHandlerApp.h"
 #endif
 #endif
 
 #include "nsMIMEInfoUnix.h"
 #include "nsGNOMERegistry.h"
 #include "nsIGIOService.h"
 #include "nsNetCID.h"
 #include "nsIIOService.h"
 #include "nsIGnomeVFSService.h"
 #include "nsAutoPtr.h"
 #ifdef MOZ_ENABLE_DBUS
 #include "nsDBusHandlerApp.h"
 #endif
+#ifdef MOZ_WIDGET_QT
+#include "nsMIMEInfoQt.h"
+#endif
 
 nsresult
 nsMIMEInfoUnix::LoadUriInternal(nsIURI * aURI)
 {
   nsresult rv = nsGNOMERegistry::LoadURL(aURI);
 
 #ifdef MOZ_WIDGET_QT
   if (NS_FAILED(rv)) {
-    nsAutoCString spec;
-    aURI->GetAsciiSpec(spec);
-    if (QDesktopServices::openUrl(QUrl(spec.get()))) {
-      rv = NS_OK;
-    }
+    rv = nsMIMEInfoQt::LoadUriInternal(aURI);
   }
 #endif
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsMIMEInfoUnix::GetHasDefaultHandler(bool *_retval)
--- a/widget/qt/moz.build
+++ b/widget/qt/moz.build
@@ -37,17 +37,17 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/widget',
 ]
 
 if CONFIG['MOZ_X11']:
     LOCAL_INCLUDES += [
-        '../shared/x11',
+        '../x11',
     ]
 
 #DEFINES['DEBUG_WIDGETS'] = True
 
 if CONFIG['OS_ARCH'] == 'Linux':
     DEFINES['_BSD_SOURCE'] = True
 
 if CONFIG['OS_ARCH'] == 'SunOS' and not CONFIG['GNU_CC']:
--- a/widget/qt/nsPrintSettingsQt.cpp
+++ b/widget/qt/nsPrintSettingsQt.cpp
@@ -286,33 +286,27 @@ static const QPrinter::PageSize indexToQ
     QPrinter::B4, QPrinter::B6, QPrinter::B7, QPrinter::B8, QPrinter::B9,
     QPrinter::C5E, QPrinter::Comm10E, QPrinter::DLE, QPrinter::Folio,
     QPrinter::Ledger, QPrinter::Tabloid
 };
 
 NS_IMETHODIMP
 nsPrintSettingsQt::GetPaperName(char16_t** aPaperName)
 {
-    PR_STATIC_ASSERT(sizeof(indexToPaperName)/
-        sizeof(char*) == QPrinter::NPageSize);
-    PR_STATIC_ASSERT(sizeof(indexToQtPaperEnum)/
-        sizeof(QPrinter::PageSize) == QPrinter::NPageSize);
-
     QPrinter::PaperSize size = mQPrinter->paperSize();
     QString name(indexToPaperName[size]);
-    *aPaperName = ToNewUnicode(nsDependentString
-        ((const char16_t*)name.constData()));
+    *aPaperName = ToNewUnicode(nsDependentString((const char16_t*)name.constData()));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintSettingsQt::SetPaperName(const char16_t* aPaperName)
 {
     QString ref((QChar*)aPaperName, NS_strlen(aPaperName));
-    for (uint32_t i = 0; i < QPrinter::NPageSize; i++)
+    for (uint32_t i = 0; i < sizeof(indexToPaperName)/sizeof(char*); i++)
     {
         if (ref == QString(indexToPaperName[i])) {
             mQPrinter->setPageSize(indexToQtPaperEnum[i]);
             return NS_OK;
         }
     }
     return NS_ERROR_FAILURE;
 }