Merge m-c to fig
authorLucas Rocha <lucasr@mozilla.com>
Tue, 20 Aug 2013 09:49:52 +0100
changeset 143567 9d9eab84d2e7b1a067fa4613ee5c949056c2d44a
parent 143566 1efcc5f4d314f361183bab9498e4d60c0bf0914c (current diff)
parent 143136 bb025b6949e88dc58b47bd614b49d1e5f20141d3 (diff)
child 143568 e9c576ab368957705df4aee014863c407cf47f32
push id25130
push userlrocha@mozilla.com
push dateWed, 21 Aug 2013 09:41:27 +0000
treeherdermozilla-central@b2486721572e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.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 m-c to fig
gfx/thebes/gfxReusableSurfaceWrapper.cpp
js/xpconnect/tests/mochitest/file_bug790732.html
layout/mathml/nsMathMLmsubFrame.cpp
layout/mathml/nsMathMLmsubFrame.h
layout/mathml/nsMathMLmsubsupFrame.cpp
layout/mathml/nsMathMLmsubsupFrame.h
layout/mathml/nsMathMLmsupFrame.cpp
layout/mathml/nsMathMLmsupFrame.h
layout/reftests/css-invalid/fieldset/fieldset-ref.html
layout/reftests/css-valid/fieldset/fieldset-ref.html
security/manager/pki/resources/content/editsslcert.xul
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "a7d496de30d0a7612b31de99b01b5a4e2b749728", 
+    "revision": "78decf03b39ded18b914d57f4f3152d6f1b4c56a", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -800,17 +800,16 @@ pref("urlclassifier.gethashtables", "goo
 
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
 pref("urlclassifier.max-complete-age", 2700);
 #endif
 
 pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/");
-pref("browser.mixedcontent.warning.infoURL", "http://support.mozilla.org/1/%APP%/%VERSION%/%OS%/%LOCALE%/mixed-content/");
 
 pref("browser.EULA.version", 3);
 pref("browser.rights.version", 3);
 pref("browser.rights.3.shown", false);
 
 #ifdef DEBUG
 // Don't show the about:rights notification in debug builds.
 pref("browser.rights.override", true);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6632,17 +6632,17 @@ var gIdentityHandler = {
    * to the user to override mixed content blocking
    */
   showMixedContentDoorhanger : function() {
     // If we've already got an active notification, bail out to avoid showing it repeatedly.
     if (PopupNotifications.getNotification("mixed-content-blocked", gBrowser.selectedBrowser))
       return;
 
     let helplink = document.getElementById("mixed-content-blocked-helplink");
-    helplink.href = Services.urlFormatter.formatURLPref("browser.mixedcontent.warning.infoURL");
+    helplink.setAttribute("onclick", "openHelpLink('mixed-content');");
 
     let brandBundle = document.getElementById("bundle_brand");
     let brandShortName = brandBundle.getString("brandShortName");
     let messageString = gNavigatorBundle.getFormattedString("mixedContentBlocked.message", [brandShortName]);
     let action = {
       label: gNavigatorBundle.getString("mixedContentBlocked.keepBlockingButton.label"),
       accessKey: gNavigatorBundle.getString("mixedContentBlocked.keepBlockingButton.accesskey"),
       callback: function() { /* NOP */ }
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -375,21 +375,20 @@ function gatherTextUnder ( root )
   var node = root.firstChild;
   var depth = 1;
   while ( node && depth > 0 ) {
     // See if this node is text.
     if ( node.nodeType == Node.TEXT_NODE ) {
       // Add this text to our collection.
       text += " " + node.data;
     } else if ( node instanceof HTMLImageElement) {
-      // If it has an alt= attribute, use that.
+      // If it has an "alt" attribute, add that.
       var altText = node.getAttribute( "alt" );
       if ( altText && altText != "" ) {
-        text = altText;
-        break;
+        text += " " + altText;
       }
     }
     // Find next node to test.
     // First, see if this node has children.
     if ( node.hasChildNodes() ) {
       // Go to first child.
       node = node.firstChild;
       depth++;
@@ -399,20 +398,18 @@ function gatherTextUnder ( root )
         node = node.parentNode;
         depth--;
       }
       if ( node.nextSibling ) {
         node = node.nextSibling;
       }
     }
   }
-  // Strip leading whitespace.
-  text = text.replace( /^\s+/, "" );
-  // Strip trailing whitespace.
-  text = text.replace( /\s+$/, "" );
+  // Strip leading and tailing whitespace.
+  text = text.trim();
   // Compress remaining whitespace.
   text = text.replace( /\s+/g, " " );
   return text;
 }
 
 function getShellService()
 {
   var shell = null;
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -783,27 +783,27 @@ function DownloadsPlacesView(aRichListBo
 
   this._searchTerm = "";
 
   this._active = aActive;
 
   // Register as a downloads view. The places data will be initialized by
   // the places setter.
   this._initiallySelectedElement = null;
-  let downloadsData = DownloadsCommon.getData(window.opener || window);
-  downloadsData.addView(this);
+  this._downloadsData = DownloadsCommon.getData(window.opener || window);
+  this._downloadsData.addView(this);
 
   // Get the Download button out of the attention state since we're about to
   // view all downloads.
   DownloadsCommon.getIndicatorData(window).attention = false;
 
   // Make sure to unregister the view if the window is closed.
   window.addEventListener("unload", function() {
     window.controllers.removeController(this);
-    downloadsData.removeView(this);
+    this._downloadsData.removeView(this);
     this.result = null;
   }.bind(this), true);
   // Resizing the window may change items visibility.
   window.addEventListener("resize", function() {
     this._ensureVisibleElementsAreActive();
   }.bind(this), true);
 }
 
@@ -1443,21 +1443,17 @@ DownloadsPlacesView.prototype = {
         break;
       case "cmd_selectAll":
         this._richlistbox.selectAll();
         break;
       case "cmd_paste":
         this._downloadURLFromClipboard();
         break;
       case "downloadsCmd_clearDownloads":
-        if (PrivateBrowsingUtils.isWindowPrivate(window)) {
-          Services.downloads.cleanUpPrivate();
-        } else {
-          Services.downloads.cleanUp();
-        }
+        this._downloadsData.removeFinished();
         if (this.result) {
           Cc["@mozilla.org/browser/download-history;1"]
             .getService(Ci.nsIDownloadHistory)
             .removeAllDownloads();
         }
         // There may be no selection or focus change as a result
         // of these change, and we want the command updated immediately.
         goUpdateCommand("downloadsCmd_clearDownloads");
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -132,17 +132,21 @@ const DownloadsPanel = {
     }
     this._state = this.kStateHidden;
 
     window.addEventListener("unload", this.onWindowUnload, false);
 
     // Ensure that the Download Manager service is running.  This resumes
     // active downloads if required.  If there are downloads to be shown in the
     // panel, starting the service will make us load their data asynchronously.
-    Services.downloads;
+    if (DownloadsCommon.useJSTransfer) {
+      DownloadsCommon.initializeAllDataLinks();
+    } else {
+      Services.downloads;
+    }
 
     // Now that data loading has eventually started, load the required XUL
     // elements and initialize our views.
     DownloadsCommon.log("Ensuring DownloadsPanel overlay loaded.");
     DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay,
                                                function DP_I_callback() {
       DownloadsViewController.initialize();
       DownloadsCommon.log("Attaching DownloadsView...");
@@ -1339,21 +1343,17 @@ const DownloadsViewController = {
     // ancestors of the focused element.
     return !!element;
   },
 
   isCommandEnabled: function DVC_isCommandEnabled(aCommand)
   {
     // Handle commands that are not selection-specific.
     if (aCommand == "downloadsCmd_clearList") {
-      if (PrivateBrowsingUtils.isWindowPrivate(window)) {
-        return Services.downloads.canCleanUpPrivate;
-      } else {
-        return Services.downloads.canCleanUp;
-      }
+      return DownloadsCommon.getData(window).canRemoveFinished;
     }
 
     // Other commands are selection-specific.
     let element = DownloadsView.richListBox.selectedItem;
     return element &&
            new DownloadsViewItemController(element).isCommandEnabled(aCommand);
   },
 
@@ -1390,21 +1390,17 @@ const DownloadsViewController = {
 
   /**
    * This object contains one key for each command that operates regardless of
    * the currently selected item in the list.
    */
   commands: {
     downloadsCmd_clearList: function DVC_downloadsCmd_clearList()
     {
-      if (PrivateBrowsingUtils.isWindowPrivate(window)) {
-        Services.downloads.cleanUpPrivate();
-      } else {
-        Services.downloads.cleanUp();
-      }
+      DownloadsCommon.getData(window).removeFinished();
     }
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsViewItemController
 
 /**
--- a/browser/components/downloads/src/DownloadsCommon.jsm
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -632,25 +632,29 @@ DownloadsDataCtor.prototype = {
    *        this because getService isn't available for us when this method is
    *        called, and we must ensure to register our listeners before the
    *        getService call for the Download Manager returns.
    */
   initializeDataLink: function DD_initializeDataLink(aDownloadManagerService)
   {
     // Start receiving real-time events.
     if (DownloadsCommon.useJSTransfer) {
-      let promiseList = this._isPrivate ? Downloads.getPrivateDownloadList()
-                                        : Downloads.getPublicDownloadList();
-      promiseList.then(list => list.addView(this)).then(null, Cu.reportError);
+      if (!this._dataLinkInitialized) {
+        let promiseList = this._isPrivate ? Downloads.getPrivateDownloadList()
+                                          : Downloads.getPublicDownloadList();
+        promiseList.then(list => list.addView(this)).then(null, Cu.reportError);
+        this._dataLinkInitialized = true;
+      }
     } else {
       aDownloadManagerService.addPrivacyAwareListener(this);
       Services.obs.addObserver(this, "download-manager-remove-download-guid",
                                false);
     }
   },
+  _dataLinkInitialized: false,
 
   /**
    * Stops receiving events for current downloads and cancels any pending read.
    */
   terminateDataLink: function DD_terminateDataLink()
   {
     if (DownloadsCommon.useJSTransfer) {
       Cu.reportError("terminateDataLink not applicable with useJSTransfer");
@@ -659,16 +663,56 @@ DownloadsDataCtor.prototype = {
 
     this._terminateDataAccess();
 
     // Stop receiving real-time events.
     Services.obs.removeObserver(this, "download-manager-remove-download-guid");
     Services.downloads.removeListener(this);
   },
 
+  /**
+   * True if there are finished downloads that can be removed from the list.
+   */
+  get canRemoveFinished()
+  {
+    if (DownloadsCommon.useJSTransfer) {
+      for (let [, dataItem] of Iterator(this.dataItems)) {
+        if (dataItem && !dataItem.inProgress) {
+          return true;
+        }
+      }
+      return false;
+    } else {
+      if (this._isPrivate) {
+        return Services.downloads.canCleanUpPrivate;
+      } else {
+        return Services.downloads.canCleanUp;
+      }
+    }
+  },
+
+  /**
+   * Asks the back-end to remove finished downloads from the list.
+   */
+  removeFinished: function DD_removeFinished()
+  {
+    if (DownloadsCommon.useJSTransfer) {
+      let promiseList = this._isPrivate ? Downloads.getPrivateDownloadList()
+                                        : Downloads.getPublicDownloadList();
+      promiseList.then(list => list.removeFinished())
+                 .then(null, Cu.reportError);
+    } else {
+      if (this._isPrivate) {
+        Services.downloads.cleanUpPrivate();
+      } else {
+        Services.downloads.cleanUp();
+      }
+    }
+  },
+
   //////////////////////////////////////////////////////////////////////////////
   //// Integration with the asynchronous Downloads back-end
 
   onDownloadAdded: function (aDownload)
   {
     let dataItem = new DownloadsDataItem(aDownload);
     this._downloadToDataItemMap.set(aDownload, dataItem);
     this.dataItems[dataItem.downloadGuid] = dataItem;
--- a/browser/metro/base/content/ContextCommands.js
+++ b/browser/metro/base/content/ContextCommands.js
@@ -88,35 +88,36 @@ var ContextCommands = {
     }
 
     if (target.localName == "browser") {
       // content
       let x = ContextMenuUI.popupState.x;
       let y = ContextMenuUI.popupState.y;
       let json = {x: x, y: y, command: "paste" };
       target.messageManager.sendAsyncMessage("Browser:ContextCommand", json);
-      SelectionHelperUI.closeEditSession();
     } else {
       // chrome
       CommandUpdater.doCommand("cmd_paste");
       target.focus();
     }
+    SelectionHelperUI.closeEditSession();
   },
 
   pasteAndGo: function cc_pasteAndGo() {
     let target = ContextMenuUI.popupState.target;
     target.editor.selectAll();
     target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
     BrowserUI.goToURI();
   },
 
   select: function cc_select() {
     SelectionHelperUI.openEditSession(ContextMenuUI.popupState.target,
                                       ContextMenuUI.popupState.xPos,
-                                      ContextMenuUI.popupState.yPos);
+                                      ContextMenuUI.popupState.yPos,
+                                      true);
   },
 
   selectAll: function cc_selectAll() {
     let target = ContextMenuUI.popupState.target;
     if (target.localName == "browser") {
       // content
       let x = ContextMenuUI.popupState.xPos;
       let y = ContextMenuUI.popupState.yPos;
--- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
+++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
@@ -288,17 +288,17 @@ var ContextMenuHandler = {
       elem = elem.parentNode;
     }
 
     // Over arching text tests
     if (isText) {
       // If this is text and has a selection, we want to bring
       // up the copy option on the context menu.
       let selection = targetWindow.getSelection();
-      if (selection && selection.toString().length > 0) {
+      if (selection && this._tapInSelection(selection, aX, aY)) {
         state.string = targetWindow.getSelection().toString();
         state.types.push("copy");
         state.types.push("selected-text");
       } else {
         // Add general content text if this isn't anything specific
         if (state.types.indexOf("image") == -1 &&
             state.types.indexOf("media") == -1 &&
             state.types.indexOf("video") == -1 &&
@@ -318,16 +318,30 @@ var ContextMenuHandler = {
       if (this._types[i].handler(state, popupNode))
         state.types.push(this._types[i].name);
 
     this._previousState = state;
 
     sendAsyncMessage("Content:ContextMenu", state);
   },
 
+  _tapInSelection: function (aSelection, aX, aY) {
+    if (!aSelection || !aSelection.rangeCount) {
+      return false;
+    }
+    for (let idx = 0; idx < aSelection.rangeCount; idx++) {
+      let range = aSelection.getRangeAt(idx);
+      let rect = range.getBoundingClientRect();
+      if (Util.pointWithinDOMRect(aX, aY, rect)) {
+        return true;
+      }
+    }
+    return false;
+  },
+
   _getLinkURL: function ch_getLinkURL(aLink) {
     let href = aLink.href;
     if (href)
       return href;
 
     href = aLink.getAttributeNS(kXLinkNamespace, "href");
     if (!href || !href.match(/\S/)) {
       // Without this we try to save as the current doc,
--- a/browser/metro/base/content/contenthandlers/SelectionHandler.js
+++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js
@@ -51,29 +51,35 @@ var SelectionHandler = {
 
   /*************************************************
    * Browser event handlers
    */
 
   /*
    * Selection start event handler
    */
-  _onSelectionStart: function _onSelectionStart(aX, aY) {
+  _onSelectionStart: function _onSelectionStart(aJson) {
     // Init content window information
-    if (!this._initTargetInfo(aX, aY)) {
+    if (!this._initTargetInfo(aJson.xPos, aJson.yPos)) {
       this._onFail("failed to get target information");
       return;
     }
 
+    // for context menu select command, which doesn't trigger
+    // form input focus changes.
+    if (aJson.setFocus && this._targetIsEditable) {
+      this._targetElement.focus();
+    }
+
     // Clear any existing selection from the document
     let selection = this._contentWindow.getSelection();
     selection.removeAllRanges();
 
     // Set our initial selection, aX and aY should be in client coordinates.
-    let framePoint = this._clientPointToFramePoint({ xPos: aX, yPos: aY });
+    let framePoint = this._clientPointToFramePoint({ xPos: aJson.xPos, yPos: aJson.yPos });
     if (!this._domWinUtils.selectAtPoint(framePoint.xPos, framePoint.yPos,
                                          Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
       this._onFail("failed to set selection at point");
       return;
     }
 
     // Update the position of our selection monocles
     this._updateSelectionUI("start", true, true);
@@ -493,17 +499,17 @@ var SelectionHandler = {
 
   receiveMessage: function sh_receiveMessage(aMessage) {
     if (this._debugEvents && aMessage.name != "Browser:SelectionMove") {
       Util.dumpLn("SelectionHandler:", aMessage.name);
     }
     let json = aMessage.json;
     switch (aMessage.name) {
       case "Browser:SelectionStart":
-        this._onSelectionStart(json.xPos, json.yPos);
+        this._onSelectionStart(json);
         break;
 
       case "Browser:SelectionAttach":
         this._onSelectionAttach(json.xPos, json.yPos);
         break;
 
       case "Browser:CaretAttach":
         this._onCaretAttach(json.xPos, json.yPos);
--- a/browser/metro/base/content/downloads.js
+++ b/browser/metro/base/content/downloads.js
@@ -190,25 +190,23 @@ var Downloads = {
 
     let buttons = [
       {
         isDefault: true,
         label: tryAgainButtonText,
         accessKey: "",
         callback: function() {
           Downloads.manager.retryDownload(aDownload.id);
-          Downloads._downloadProgressIndicator.reset();
         }
       },
       {
         label: cancelButtonText,
         accessKey: "",
         callback: function() {
           Downloads.cancelDownload(aDownload);
-          Downloads._downloadProgressIndicator.reset();
         }
       }
     ];
     this.showNotification("download-failed", message, buttons,
       this._notificationBox.PRIORITY_WARNING_HIGH);
   },
 
   _showDownloadCompleteNotification: function (aDownload) {
@@ -218,17 +216,16 @@ var Downloads = {
     let buttons = [
       {
         label: showInFilesButtonText,
         accessKey: "",
         callback: function() {
           let fileURI = aDownload.target;
           let file = Downloads._getLocalFile(fileURI);
           file.reveal();
-          Downloads._downloadProgressIndicator.reset();
         }
       }
     ];
 
     if (this._downloadCount > 1) {
       message = PluralForm.get(this._downloadCount,
                                Strings.browser.GetStringFromName("alertMultipleDownloadsComplete"))
                                .replace("#1", this._downloadCount)
@@ -239,17 +236,16 @@ var Downloads = {
         [aDownload.displayName], 1);
 
       buttons.unshift({
         isDefault: true,
         label: runButtonText,
         accessKey: "",
         callback: function() {
           Downloads.openDownload(aDownload);
-          Downloads._downloadProgressIndicator.reset();
         }
       });
     }
     this.showNotification("download-complete", message, buttons,
       this._notificationBox.PRIORITY_WARNING_MEDIUM);
   },
 
   _showDownloadCompleteToast: function (aDownload) {
@@ -373,17 +369,16 @@ var Downloads = {
 
       let buttons = [
         {
           isDefault: false,
           label: cancelButtonText,
           accessKey: "",
           callback: function() {
             Downloads.cancelDownloads();
-            Downloads._downloadProgressIndicator.reset();
           }
         }
       ];
 
       this._progressNotification =
         this.showNotification("download-progress", message, buttons,
         this._notificationBox.PRIORITY_WARNING_LOW);
     } else {
@@ -431,25 +426,27 @@ var Downloads = {
         }
 
         this._runDownloadBooleanMap.delete(download.targetFile.path);
         if (this._downloadsInProgress == 0) {
           if (this._downloadCount > 1 || !runAfterDownload) {
             this._showDownloadCompleteToast(download);
             this._showDownloadCompleteNotification(download);
           }
+          this._downloadProgressIndicator.reset();
           this._progressNotificationInfo.clear();
           this._downloadCount = 0;
           this._notificationBox.removeNotification(this._progressNotification);
           this._progressNotification = null;
         }
         break;
       case "dl-failed":
         download = aSubject.QueryInterface(Ci.nsIDownload);
         this._showDownloadFailedNotification(download);
+        this._downloadProgressIndicator.reset();
         break;
       case "dl-request":
         setTimeout(function() {
           ContextUI.displayNavbar();
         }, 1000);
         break;
     }
   },
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectionHelperUI.js
@@ -375,41 +375,44 @@ var SelectionHelperUI = {
     });
 
     this._sendAsyncMessage("Browser:SelectionHandlerPing", { id: this._pingCount });
     return deferred.promise;
   },
 
   /*
    * openEditSession
-   * 
+   *
    * Attempts to select underlying text at a point and begins editing
    * the section.
    *
    * @param aMsgTarget - Browser or chrome message target
    * @param aX, aY - Browser relative client coordinates.
+   * @param aSetFocus - (optional) For form inputs, requests that the focus
+   * be set to the element.
    */
-  openEditSession: function openEditSession(aMsgTarget, aX, aY) {
+  openEditSession: function openEditSession(aMsgTarget, aX, aY, aSetFocus) {
     if (!aMsgTarget || this.isActive)
       return;
     this._init(aMsgTarget);
     this._setupDebugOptions();
-
+    let setFocus = aSetFocus || false;
     // Send this over to SelectionHandler in content, they'll message us
     // back with information on the current selection. SelectionStart
     // takes client coordinates.
     this._sendAsyncMessage("Browser:SelectionStart", {
+      setFocus: setFocus,
       xPos: aX,
       yPos: aY
     });
   },
 
   /*
    * attachEditSession
-   * 
+   *
    * Attaches to existing selection and begins editing.
    *
    * @param aMsgTarget - Browser or chrome message target
    * @param aX, aY - Browser relative client coordinates.
    */
   attachEditSession: function attachEditSession(aMsgTarget, aX, aY) {
     if (!aMsgTarget || this.isActive)
       return;
--- a/browser/metro/base/tests/mochiperf/browser_layers_01.js
+++ b/browser/metro/base/tests/mochiperf/browser_layers_01.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 function test() {
   let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
   Services.scriptloader.loadSubScript(testDir + "/perfhelpers.js", this);
-  requestLongerTimeout(5);
   runTests();
 }
 
 gTests.push({
   desc: "rotating divs",
   run: function run() {
     yield addTab(chromeRoot + "res/divs_test.html", true);
 
--- a/browser/metro/base/tests/mochiperf/browser_miscgfx_01.js
+++ b/browser/metro/base/tests/mochiperf/browser_miscgfx_01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 function test() {
   let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
   Services.scriptloader.loadSubScript(testDir + "/perfhelpers.js", this);
-  requestLongerTimeout(5);
+  requestLongerTimeout(2);
   runTests();
 }
 
 function tapRadius() {
   let dpi = Util.displayDPI;
   return Services.prefs.getIntPref("ui.dragThresholdX") / 240 * dpi; // 27.999998728434246
 }
 
--- a/browser/metro/base/tests/mochitest/browser_selection_basic.html
+++ b/browser/metro/base/tests/mochitest/browser_selection_basic.html
@@ -17,16 +17,26 @@ rabbit-hole under the hedge.
 <br>
 In another moment down went Alice after it, never once considering how in the world she
 was to get out again.
 <br>
 <br>
 The rabbit-hole went straight on like a tunnel for some way, and then dipped suddenly
 down, so suddenly that Alice had not a moment to think about stopping herself before she
 found herself falling down a very deep well.
+<div id="seldiv" style="width:300px; height:100px;">The rabbit-hole went straight on like a tunnel for some way, and then dipped suddenly
+down, so suddenly that Alice had not a moment to think about stopping herself before she
+found herself falling down a very deep well.
+</div>
+<br />
+<br />
+<br />
+<br />
+<br />
+<div id="emptydiv" style="width:300px; height:100px; border: solid 1px black;"></div>
 <br>
 <br>
 <br>
 <br>
 <br>
 <br>
 <br>
 <br>
--- a/browser/metro/base/tests/mochitest/browser_selection_basic.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js
@@ -279,22 +279,25 @@ gTests.push({
 
     is(SelectionHelperUI.isActive, true, "selection active");
 
     // scroll page
     sendTouchDrag(gWindow,
                   400,
                   400,
                   400,
-                  200);
+                  350);
 
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
+    // cancel fling from scroll above
+    TouchModule.cancelPending();
+
     // active state - should be disabled after a page scroll
     is(SelectionHelperUI.isActive, false, "selection inactive");
   },
   tearDown: function tearDown() {
     EventUtils.synthesizeKey("VK_HOME", {}, gWindow);
     emptyClipboard();
     if (gWindow)
       clearSelection(gWindow);
@@ -342,17 +345,41 @@ gTests.push({
 
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
   },
   tearDown: setUpAndTearDown,
 });
 
+gTests.push({
+  desc: "bug 903737 - right click targeting",
+  setUp: setUpAndTearDown,
+  run: function test() {
+    yield hideContextUI();
+    let range = gWindow.document.createRange();
+    range.selectNode(gWindow.document.getElementById("seldiv"));
+    gWindow.getSelection().addRange(range);
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(gWindow, gWindow.document.getElementById("seldiv"));
+    yield promise;
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    let emptydiv = gWindow.document.getElementById("emptydiv");
+    let coords = logicalCoordsForElement(emptydiv);
+    InputSourceHelper.isPrecise = true;
+    sendContextMenuClick(coords.x, coords.y);
+    yield waitForCondition(function () {
+      return ContextUI.tabbarVisible;
+    });
+    yield hideContextUI();
+  },
+  tearDown: setUpAndTearDown,
+});
+
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
-
-  requestLongerTimeout(3);
   runTests();
 }
--- a/browser/metro/base/tests/mochitest/browser_selection_frame_content.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_frame_content.js
@@ -129,17 +129,17 @@ gTests.push({
     yield waitForCondition(function () {
         return SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
     is(SelectionHelperUI.isActive, true, "selection active");
     is(getTrimmedSelection(gFrame).toString(), "started", "selection test");
 
     let promise = waitForEvent(document, "popupshown");
-    sendContextMenuClick(527, 188);
+    sendContextMenuClickToSelection(gFrame.contentDocument.defaultView);
 
     yield promise;
     ok(promise && !(promise instanceof Error), "promise error");
     ok(ContextMenuUI._menuPopup.visible, "is visible");
 
     let menuItem = document.getElementById("context-copy");
     ok(menuItem, "menu item exists");
     ok(!menuItem.hidden, "menu item visible");
@@ -211,12 +211,10 @@ gTests.push({
   },
 });
 
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
-
-  requestLongerTimeout(3);
   runTests();
 }
--- a/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.js
@@ -152,12 +152,10 @@ gTests.push({
   },
 });
 
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
-
-  requestLongerTimeout(3);
   runTests();
 }
--- a/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.js
@@ -246,15 +246,12 @@ gTests.push({
   },
 });
 
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
-
   // XXX need this until bugs 886624 and 859742 are fully resolved
   setDevPixelEqualToPx();
-
-  requestLongerTimeout(3);
   runTests();
 }
--- a/browser/metro/base/tests/mochitest/browser_selection_inputs.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_inputs.js
@@ -54,17 +54,17 @@ gTests.push({
   },
 });
 
 gTests.push({
   desc: "basic text input selection",
   setUp: setUpAndTearDown,
   tearDown: setUpAndTearDown,
   run: function test() {
-    gInput.focus();
+    gInput.blur();
     gInput.selectionStart = gInput.selectionEnd = 0;
 
     let promise = waitForEvent(document, "popupshown");
     sendContextMenuClick(200, 17);
     yield promise;
 
     checkContextUIMenuItemVisibility(["context-select",
                                       "context-select-all"]);
@@ -76,16 +76,17 @@ gTests.push({
     sendElementTap(gWindow, menuItem);
     yield popupPromise;
 
     yield waitForCondition(function () {
         return SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
     is(getTrimmedSelection(gInput).toString(), "went", "selection test");
+    is(gWindow.document.activeElement, gInput, "input focused");
   },
 });
 
 gTests.push({
   desc: "drag left to scroll",
   setUp: setUpAndTearDown,
   tearDown: setUpAndTearDown,
   run: function test() {
@@ -193,15 +194,12 @@ gTests.push({
   },
 });
 
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
-
   // XXX need this until bugs 886624 and 859742 are fully resolved
   setDevPixelEqualToPx();
-
-  requestLongerTimeout(3);
   runTests();
 }
--- a/browser/metro/base/tests/mochitest/browser_selection_textarea.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_textarea.js
@@ -107,12 +107,10 @@ gTests.push({
   },
 });
 
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
-
-  requestLongerTimeout(3);
   runTests();
 }
--- a/browser/metro/base/tests/mochitest/browser_selection_urlbar.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_urlbar.js
@@ -81,61 +81,69 @@ gTests.push({
     ok(ContextMenuUI._menuPopup.visible, "is visible");
     let paste = document.getElementById("context-paste");
     ok(!paste.hidden, "paste item is visible");
 
     sendElementTap(window, paste);
     ok(edit.popup.popupOpen, "bug: popup should be showing");
 
     clearSelection(edit);
+    yield waitForCondition(function () {
+      return !SelectionHelperUI.isSelectionUIVisible;
+    });
   }
 });
 
 gTests.push({
   desc: "bug 895284 - tap selection",
   run: function() {
     gWindow = window;
 
     yield showNavBar();
     let edit = document.getElementById("urlbar-edit");
     edit.value = "wikipedia.org";
     edit.select();
 
     let editCoords = logicalCoordsForElement(edit);
     SelectionHelperUI.attachEditSession(ChromeSelectionHandler, editCoords.x, editCoords.y);
-
     ok(SelectionHelperUI.isSelectionUIVisible, "selection enabled");
 
     let selection = edit.QueryInterface(Components.interfaces.nsIDOMXULTextBoxElement)
                         .editor.selection;
     let rects = selection.getRangeAt(0).getClientRects();
     let midX = Math.ceil(((rects[0].right - rects[0].left) * .5) + rects[0].left);
     let midY = Math.ceil(((rects[0].bottom - rects[0].top) * .5) + rects[0].top);
 
     sendTap(window, midX, midY);
 
     ok(SelectionHelperUI.isCaretUIVisible, "caret browsing enabled");
 
     clearSelection(edit);
+    yield waitForCondition(function () {
+      return !SelectionHelperUI.isSelectionUIVisible;
+    });
   }
 });
 
 gTests.push({
   desc: "bug 894713 - blur shuts down selection handling",
   run: function() {
     gWindow = window;
     yield showNavBar();
     let edit = document.getElementById("urlbar-edit");
     edit.value = "wikipedia.org";
     edit.select();
     let editCoords = logicalCoordsForElement(edit);
     SelectionHelperUI.attachEditSession(ChromeSelectionHandler, editCoords.x, editCoords.y);
     edit.blur();
     ok(!SelectionHelperUI.isSelectionUIVisible, "selection no longer enabled");
     clearSelection(edit);
+    yield waitForCondition(function () {
+      return !SelectionHelperUI.isSelectionUIVisible;
+    });
   }
 });
 
 function getClipboardCondition(aExpected) {
   return () => aExpected == SpecialPowers.getClipboardData("text/unicode");
 }
 
 gTests.push({
--- a/browser/metro/base/tests/mochitest/head.js
+++ b/browser/metro/base/tests/mochitest/head.js
@@ -578,16 +578,36 @@ function sendContextMenuClick(aX, aY) {
   let mainwin = mediator.getMostRecentWindow("navigator:browser");
   let utils = mainwin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIDOMWindowUtils);
   utils.sendMouseEvent("contextmenu", aX, aY, 2, 1, 0, true,
                         1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
 }
 
 /*
+ * sendContextMenuClickToSelection - simulates a press-hold touch input event
+ * selected text in a window.
+ */
+function sendContextMenuClickToSelection(aWindow) {
+  let selection = aWindow.getSelection();
+  if (!selection || !selection.rangeCount) {
+    ok(false, "no selection to tap!");
+    return;
+  }
+  let range = selection.getRangeAt(0);
+  let rect = range.getBoundingClientRect();
+  let x = rect.left + (rect.width / 2);
+  let y = rect.top + (rect.height / 2);
+  let utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                      .getInterface(Components.interfaces.nsIDOMWindowUtils);
+  utils.sendMouseEventToWindow("contextmenu", x, y, 2, 1, 0, true,
+                                1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
+}
+
+/*
  * sendContextMenuClickToWindow - simulates a press-hold touch input event.
  *
  * @param aWindow window used to retrieve dom window utils, and the
  * target window for the event.
  * @param aX, aY logical coordinates of the event relative to aWindow.
  */
 function sendContextMenuClickToWindow(aWindow, aX, aY) {
   let utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -30,16 +30,17 @@ pref("metro.debug.selection.dumpEvents",
 
 // Enable tab-modal prompts
 pref("prompts.tab_modal.enabled", true);
 
 
 // Enable off main thread compositing
 pref("layers.offmainthreadcomposition.enabled", true);
 pref("layers.async-pan-zoom.enabled", false);
+pref("layers.componentalpha.enabled", false);
 pref("gfx.axis.fling_friction", "0.002");
 
 // Enable Microsoft TSF support by default for imes.
 pref("intl.enable_tsf_support", true);
 
 pref("general.autoScroll", true);
 pref("general.smoothScroll", true);
 pref("general.smoothScroll.durationToIntervalRatio", 200);
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -287,24 +287,39 @@ documenttab[selected] .documenttab-selec
   outline: 0 !important;
 }
 
 .selection-overlay-hidden {
   display: none;
 }
 
 .selectionhandle {
-  list-style-image: url("chrome://browser/skin/images/selection-monocle.png");
-  border: 0px solid gray;
+  background-image: url("chrome://browser/skin/images/selection-monocle.png");
+  background-repeat: no-repeat;
+  background-size: 18px 18px;
   padding: 0px;
-  margin-top: -30px;
-  margin-left: -18px;
+  margin-top: -19px;
+  margin-left: -9px;
   pointer-events: auto;
 }
 
+@media (min-resolution: @min_res_140pc@) {
+  /* Load 140% image when scaled by 140% */
+  .selectionhandle {
+    background-image: url("chrome://browser/skin/images/selection-monocle@1.4x.png");
+  }
+}
+
+@media (min-resolution: @min_res_180pc@) {
+  /* Load 180% image when scaled by 180% */
+  .selectionhandle {
+    background-image: url("chrome://browser/skin/images/selection-monocle@1.8x.png");
+  }
+}
+
 /* content scrollbars */
 .scroller {
   opacity: 0;
   background-color: rgba(0, 0, 0, 0.4) !important;
   -moz-border-top-colors: none !important;
   -moz-border-bottom-colors: none !important;
   -moz-border-right-colors: none !important;
   -moz-border-left-colors: none !important;
index 63d7cb67f4df03ec90725a3df05353fd62b5b115..b4754ffb97dbe8a652d0c60a921e93343e180841
GIT binary patch
literal 339
zc$@)K0j&OsP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0003RNkl<Zc-pO$
zOA3Q95Qd9F4<YzCgWK-<IE2vNB<e9-blcKiMsT6-TiP4wLO0X=kb%&~8cGL#3^S82
zF`3E2^E{JWshw0VbyUig&o#|4L6RtnZt_besaWz2wDf)df@B*7L2$*K50HeMI4KN6
zO4F3evZSu-AdKNdjA10_oZmrv4#sgzbzRehA%+}%cLxT!a+L?~1c`IjG!4#*cmD(f
zpf%Hl5M*h{)pxZ4NU#IvU}?zJK?0zH9pgA!gN9+yLB-D?+@ucr>#uFtfjY4?<m#X;
zU;k5;KdyEb(zdPf`k{kmL1LlFg5(xC>n)OH8C6w9ecwYE!)Iubzc;P<8M#jZ5Sx2O
lbU9KhRT_noP(H^r=LHF)dhK^8LpuNf002ovPDHLkV1jL6k~07R
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f5b7130b9117c8dd62fd531c428443d5f1db721
GIT binary patch
literal 394
zc$@)>0d@X~P)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&0003|Nkl<Zc-p;|
zO=^Td5QVey5XQ5N19}1_u4b7Cvq&~>5^@s-y^A{~S?ET{+(S15m3ojsQ?xy;GB$iP
zc2)W4>YwTd*L7`pWFjVG-v9b1jN8PUj1Kd{d@*DIcKA6<MGnjlLkqYtzOBeBb7g1+
zSH#~EIqQOxB%!h_sjh44x(+?QF+L}Do)bC98H8a-P1BH}!45y&jC?8L$Tc{fPBcvu
zSv2^;Ukc%Wjw<r3Is`#L<2cfS27km!p;Q(5R*mKqnNugX_rHjz8sa#n4Gr;9$b(3v
z4(55jMFa6tB!Wn0Ox5<3Wf1vP2Zv$UqJelR^0CiM<eC&kafdb8VRvcIN!zxo1Q0iO
zPC&ZDiQnh$0>0)JzLh%I=WgNS;(Ah56`_vweUBdB#(Hw^%J^nm<9+6`qQ)QByjD`N
oy>-Bzu6eCUJSNt>yF8otUqx%j+Oo|vZ~y=R07*qoM6N<$f^PY-oB#j-
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..229c9b019ace4d5ae745db2c2a81b0f0831c9def
GIT binary patch
literal 541
zc$@(p0^<FNP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80005xNkl<Zc-qaE
zKW@S>6vite<q!-lRas#qMG7P01Q8dYoq!QWj=;pf8z7OSh%=PlgYIpo4#4pGKFLVP
zvYQ0Eq)+-uD1Q0;McB{JH^pKxD-QF(G?_rq6wjQjdTA3~F;U2TGNb`xp~!V@7$`6u
zX2(!QcH-T_r3P-9SB4CM#$8qeb>@R1OF*lh)_})+>%=OR3U#|(n$2buMG-BROB#>I
z)a&&yrZpiny(9xA-Ix2mPwVxXj>jWu0%OQQ?r)2<q%%-pwyGBgg8>~52TB{r9gRjh
z6I;xsGSE@IFqur~(wI)CIujk)fXnPuUv3!C&}~uo+&~~F)oL}`@AqUF(14c8L~w2(
zk`ux(BwMjstyBi0lL3eMlD8UGw55QSYKvk{2L{TrJM3x80ZnMjlwqJH51`0c4rr<j
zv|yku55#dymIGB&WuTqAf!zOs+!x{1wqdneRc^yP3sHQvQtWwNVkvf+_rExdhQpz1
za?Ir_k7JuVVzF32L&ej-Y)C*!*Y`IX4ccrrNi7+2kejI`lX*;PO8fmj&F6DeOn~2b
zmZmAKr)|r9;g&1`EkEnU<o>iK40(f=$<^%1;A-{^4V#zKNGjVh7<)0hoF?NiWpRoI
ff)<`#ay|b6x~0~WMy?HW00000NkvXXu0mjfIm_}e
--- a/browser/metro/theme/jar.mn
+++ b/browser/metro/theme/jar.mn
@@ -92,16 +92,18 @@ chrome.jar:
   skin/images/play-hdpi.png                 (images/play-hdpi.png)
   skin/images/pause-hdpi.png                (images/pause-hdpi.png)
   skin/images/mute-hdpi.png                 (images/mute-hdpi.png)
   skin/images/unmute-hdpi.png               (images/unmute-hdpi.png)
   skin/images/fullscreen-hdpi.png           (images/fullscreen-hdpi.png)
   skin/images/exitfullscreen-hdpi.png       (images/exitfullscreen-hdpi.png)
   skin/images/scrubber-hdpi.png             (images/scrubber-hdpi.png)
   skin/images/selection-monocle.png         (images/selection-monocle.png)
+  skin/images/selection-monocle@1.4x.png    (images/selection-monocle@1.4x.png)
+  skin/images/selection-monocle@1.8x.png    (images/selection-monocle@1.8x.png)
   skin/images/appbar-icons.png              (images/appbar-icons.png)
   skin/images/pinned-hdpi.png               (images/pinned-hdpi.png)
   skin/images/tile-selected-check-hdpi.png  (images/tile-selected-check-hdpi.png)
   skin/images/plus-34.png                   (images/plus-34.png)
   skin/images/plus-24.png                   (images/plus-24.png)
   skin/images/progresscircle.png            (images/progresscircle.png)
 
   skin/images/overlay-back.png              (images/overlay-back.png)
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -23,16 +23,19 @@ const CSP_VIOLATION_TOPIC = "csp-on-viol
 // Needed to support CSP 1.0 spec and our original CSP implementation - should
 // be removed when our original implementation is deprecated.
 const CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT = "csp_type_xmlhttprequest_spec_compliant";
 const CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT = "csp_type_websocket_spec_compliant";
 
 const WARN_FLAG = Ci.nsIScriptError.warningFlag;
 const ERROR_FLAG = Ci.nsIScriptError.ERROR_FLAG;
 
+// The cutoff length of content location in creating CSP cache key.
+const CSP_CACHE_URI_CUTOFF_SIZE = 512;
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/CSPUtils.jsm");
 
 /* ::::: Policy Parsing & Data structures :::::: */
 
 function ContentSecurityPolicy() {
   CSPdebug("CSP CREATED");
@@ -491,29 +494,48 @@ ContentSecurityPolicy.prototype = {
         // need to lie if we are testing in report-only mode
         return this._reportOnlyMode;
       }
     }
     return true;
   },
 
   /**
+   * Creates a cache key from content location and content type.
+   */
+  _createCacheKey:
+  function (aContentLocation, aContentType) {
+    if (aContentType != Ci.nsIContentPolicy.TYPE_SCRIPT &&
+        aContentLocation.scheme == "data") {
+      // For non-script data: URI, use ("data:", aContentType) as the cache key.
+      return aContentLocation.scheme + ":" + aContentType;
+    }
+
+    let uri = aContentLocation.spec;
+    if (uri.length > CSP_CACHE_URI_CUTOFF_SIZE) {
+      // Don't cache for a URI longer than the cutoff size.
+      return null;
+    }
+    return uri + "!" + aContentType;
+  },
+
+  /**
    * Delegate method called by the service when sub-elements of the protected
    * document are being loaded.  Given a bit of information about the request,
    * decides whether or not the policy is satisfied.
    */
   shouldLoad:
   function csp_shouldLoad(aContentType,
                           aContentLocation,
                           aRequestOrigin,
                           aContext,
                           aMimeTypeGuess,
                           aOriginalUri) {
-    let key = aContentLocation.spec + "!" + aContentType;
-    if (this._cache[key]) {
+    let key = this._createCacheKey(aContentLocation, aContentType);
+    if (key && this._cache[key]) {
       return this._cache[key];
     }
 
 #ifndef MOZ_B2G
     // Try to remove as much as possible from the hot path on b2g.
     CSPdebug("shouldLoad location = " + aContentLocation.asciiSpec);
     CSPdebug("shouldLoad content type = " + aContentType);
 #endif
@@ -577,18 +599,20 @@ ContentSecurityPolicy.prototype = {
         }
 
         this._asyncReportViolation(aContentLocation, aOriginalUri, violatedPolicy);
       } catch(e) {
         CSPdebug('---------------- ERROR: ' + e);
       }
     }
 
-    let ret = this._cache[key] =
-      (this._reportOnlyMode ? Ci.nsIContentPolicy.ACCEPT : res);
+    let ret = (this._reportOnlyMode ? Ci.nsIContentPolicy.ACCEPT : res);
+    if (key) {
+      this._cache[key] = ret;
+    }
     return ret;
   },
 
   shouldProcess:
   function csp_shouldProcess(aContentType,
                              aContentLocation,
                              aRequestOrigin,
                              aContext,
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -610,16 +610,18 @@ MOCHITEST_FILES_C= \
 		bug803225_test_mailto.html \
 		test_mixed_content_blocker_frameNavigation.html \
 		file_mixed_content_frameNavigation.html \
 		file_mixed_content_frameNavigation_innermost.html \
 		file_mixed_content_frameNavigation_grandchild.html \
 		file_mixed_content_frameNavigation_secure.html \
 		file_mixed_content_frameNavigation_secure_grandchild.html \
 		file_mixed_content_frameNavigation_blankTarget.html \
+		file_bug902350.html \
+		file_bug902350_frame.html \
 		test_bug789856.html \
 		file_bug804395.jar \
 		test_bug804395.html \
 		test_bug809003.html \
 		test_bug810494.html \
 		test_bug819051.html \
 		bug819051.sjs \
 		test_textnode_split_in_selection.html \
@@ -700,12 +702,13 @@ MOCHITEST_FILES_PARTS = $(foreach s,A B 
 
 # Disabled for frequent failures (bug 841505, bug 842344, etc)
 #		test_XHR_timeout.html \
 #		test_XHR_timeout.js \
 #		file_XHR_timeout.sjs \
 
 MOCHITEST_BROWSER_FILES = \
 		browser_bug593387.js \
+		browser_bug902350.js \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/browser_bug902350.js
@@ -0,0 +1,65 @@
+/*
+ * Mixed Content Block frame navigates for target="_top" - Test for Bug 902350
+ */
+
+
+const PREF_ACTIVE = "security.mixed_content.block_active_content";
+const gHttpTestRoot = "https://example.com/tests/content/base/test/";
+var origBlockActive;
+var gTestBrowser = null;
+
+registerCleanupFunction(function() {
+  // Set preferences back to their original values
+  Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
+});
+
+function MixedTestsCompleted() {
+  gBrowser.removeCurrentTab();
+  window.focus();
+  finish();
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
+
+  Services.prefs.setBoolPref(PREF_ACTIVE, true);
+
+  var newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  gTestBrowser = gBrowser.selectedBrowser;
+  newTab.linkedBrowser.stop()
+
+  gTestBrowser.addEventListener("load", MixedTest1A, true);
+  var url = gHttpTestRoot + "file_bug902350.html";
+  gTestBrowser.contentWindow.location = url;
+}
+
+// Need to capture 2 loads, one for the main page and one for the iframe
+function MixedTest1A() {
+  gTestBrowser.removeEventListener("load", MixedTest1A, true);
+  gTestBrowser.addEventListener("load", MixedTest1B, true);
+}
+
+// Find the iframe and click the link in it
+function MixedTest1B() {
+  gTestBrowser.removeEventListener("load", MixedTest1B, true);
+  gTestBrowser.addEventListener("load", MixedTest1C, true);
+  var frame = content.document.getElementById("testing_frame");
+  var topTarget = frame.contentWindow.document.getElementById("topTarget");
+  topTarget.click();
+
+  // The link click should have caused a load and should not invoke the Mixed Content Blocker
+  var notification = PopupNotifications.getNotification("mixed-content-blocked", gTestBrowser);
+  ok(!notification, "Mixed Content Doorhanger appears when trying to navigate top");
+
+}
+
+function MixedTest1C() {
+  gTestBrowser.removeEventListener("load", MixedTest1C, true);
+  ok(gTestBrowser.contentWindow.location == "http://example.com/", "Navigating to insecure domain through target='_top' failed.")
+  MixedTestsCompleted();
+}
+
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_bug902350.html
@@ -0,0 +1,19 @@
+<DOCTYPE HTML>
+<html>
+<!--
+Test for Mixed Content Blocker target="_top" frame navigation
+https://bugzilla.mozilla.org/show_bug.cgi?id=902350
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 902350</title>
+</head>
+
+<body>
+  <div id="framediv">
+    <iframe src="https://example.com/tests/content/base/test/file_bug902350_frame.html" id="testing_frame"></iframe>
+  </div>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_bug902350_frame.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Opening link with _top target from an https iframe.
+https://bugzilla.mozilla.org/show_bug.cgi?id=902350
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<a href="http://example.com/" id="topTarget" target="_top">Go to http site</a>
+</body>
+</html>
--- a/content/events/src/Touch.cpp
+++ b/content/events/src/Touch.cpp
@@ -1,26 +1,83 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* 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/dom/Touch.h"
 
+#include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/TouchBinding.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
 #include "nsDOMTouchEvent.h"
-#include "nsGUIEvent.h"
 #include "nsPresContext.h"
 
 namespace mozilla {
 namespace dom {
 
-/* static */ bool
+Touch::Touch(mozilla::dom::EventTarget* aTarget,
+             int32_t aIdentifier,
+             int32_t aPageX,
+             int32_t aPageY,
+             int32_t aScreenX,
+             int32_t aScreenY,
+             int32_t aClientX,
+             int32_t aClientY,
+             int32_t aRadiusX,
+             int32_t aRadiusY,
+             float aRotationAngle,
+             float aForce)
+{
+  SetIsDOMBinding();
+  mTarget = aTarget;
+  mIdentifier = aIdentifier;
+  mPagePoint = CSSIntPoint(aPageX, aPageY);
+  mScreenPoint = nsIntPoint(aScreenX, aScreenY);
+  mClientPoint = CSSIntPoint(aClientX, aClientY);
+  mRefPoint = nsIntPoint(0, 0);
+  mPointsInitialized = true;
+  mRadius.x = aRadiusX;
+  mRadius.y = aRadiusY;
+  mRotationAngle = aRotationAngle;
+  mForce = aForce;
+
+  mChanged = false;
+  mMessage = 0;
+  nsJSContext::LikelyShortLivingObjectCreated();
+}
+
+Touch::Touch(int32_t aIdentifier,
+             nsIntPoint aPoint,
+             nsIntPoint aRadius,
+             float aRotationAngle,
+             float aForce)
+{
+  SetIsDOMBinding();
+  mIdentifier = aIdentifier;
+  mPagePoint = CSSIntPoint(0, 0);
+  mScreenPoint = nsIntPoint(0, 0);
+  mClientPoint = CSSIntPoint(0, 0);
+  mRefPoint = aPoint;
+  mPointsInitialized = false;
+  mRadius = aRadius;
+  mRotationAngle = aRotationAngle;
+  mForce = aForce;
+
+  mChanged = false;
+  mMessage = 0;
+  nsJSContext::LikelyShortLivingObjectCreated();
+}
+
+Touch::~Touch()
+{
+}
+
+ /* static */ bool
 Touch::PrefEnabled()
 {
   return nsDOMTouchEvent::PrefEnabled();
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(Touch, mTarget)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Touch)
@@ -55,16 +112,22 @@ Touch::InitializePoints(nsPresContext* a
   mPagePoint = nsDOMEvent::GetPageCoords(
     aPresContext, aEvent, LayoutDeviceIntPoint::FromUntyped(mRefPoint),
     mClientPoint);
   mScreenPoint = nsDOMEvent::GetScreenCoords(aPresContext, aEvent,
     LayoutDeviceIntPoint::FromUntyped(mRefPoint));
   mPointsInitialized = true;
 }
 
+void
+Touch::SetTarget(mozilla::dom::EventTarget* aTarget)
+{
+  mTarget = aTarget;
+}
+
 bool
 Touch::Equals(Touch* aTouch)
 {
   return mRefPoint == aTouch->mRefPoint &&
          mForce == aTouch->Force() &&
          mRotationAngle == aTouch->RotationAngle() &&
          mRadius.x == aTouch->RadiusX() &&
          mRadius.y == aTouch->RadiusY();
--- a/content/events/src/Touch.h
+++ b/content/events/src/Touch.h
@@ -1,27 +1,28 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* 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 mozilla_dom_Touch_h
 #define mozilla_dom_Touch_h
 
-#include "nsString.h"
-#include "nsTArray.h"
 #include "mozilla/Attributes.h"
-#include "nsJSEnvironment.h"
 #include "nsWrapperCache.h"
-#include "mozilla/dom/EventTarget.h"
 #include "Units.h"
 
+class nsPresContext;
+class nsEvent;
+
 namespace mozilla {
 namespace dom {
 
+class EventTarget;
+
 class Touch MOZ_FINAL : public nsISupports
                       , public nsWrapperCache
 {
 public:
   static bool PrefEnabled();
 
   Touch(mozilla::dom::EventTarget* aTarget,
         int32_t aIdentifier,
@@ -29,65 +30,32 @@ public:
         int32_t aPageY,
         int32_t aScreenX,
         int32_t aScreenY,
         int32_t aClientX,
         int32_t aClientY,
         int32_t aRadiusX,
         int32_t aRadiusY,
         float aRotationAngle,
-        float aForce)
-    {
-      SetIsDOMBinding();
-      mTarget = aTarget;
-      mIdentifier = aIdentifier;
-      mPagePoint = CSSIntPoint(aPageX, aPageY);
-      mScreenPoint = nsIntPoint(aScreenX, aScreenY);
-      mClientPoint = CSSIntPoint(aClientX, aClientY);
-      mRefPoint = nsIntPoint(0, 0);
-      mPointsInitialized = true;
-      mRadius.x = aRadiusX;
-      mRadius.y = aRadiusY;
-      mRotationAngle = aRotationAngle;
-      mForce = aForce;
-
-      mChanged = false;
-      mMessage = 0;
-      nsJSContext::LikelyShortLivingObjectCreated();
-    }
+        float aForce);
   Touch(int32_t aIdentifier,
         nsIntPoint aPoint,
         nsIntPoint aRadius,
         float aRotationAngle,
-        float aForce)
-    {
-      SetIsDOMBinding();
-      mIdentifier = aIdentifier;
-      mPagePoint = CSSIntPoint(0, 0);
-      mScreenPoint = nsIntPoint(0, 0);
-      mClientPoint = CSSIntPoint(0, 0);
-      mRefPoint = aPoint;
-      mPointsInitialized = false;
-      mRadius = aRadius;
-      mRotationAngle = aRotationAngle;
-      mForce = aForce;
+        float aForce);
 
-      mChanged = false;
-      mMessage = 0;
-      nsJSContext::LikelyShortLivingObjectCreated();
-    }
+  ~Touch();
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Touch)
 
   void InitializePoints(nsPresContext* aPresContext, nsEvent* aEvent);
 
-  void SetTarget(mozilla::dom::EventTarget *aTarget)
-  {
-    mTarget = aTarget;
-  }
+  void SetTarget(mozilla::dom::EventTarget* aTarget);
+
   bool Equals(Touch* aTouch);
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
   EventTarget* GetParentObject() { return mTarget; }
 
   // WebIDL
   int32_t Identifier() const { return mIdentifier; }
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -5,32 +5,37 @@
 
 #ifndef nsDOMEvent_h__
 #define nsDOMEvent_h__
 
 #include "mozilla/Attributes.h"
 #include "nsIDOMEvent.h"
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
-#include "nsIDOMEventTarget.h"
 #include "nsPIDOMWindow.h"
 #include "nsPoint.h"
 #include "nsGUIEvent.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
-#include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/EventBinding.h"
 #include "nsIScriptGlobalObject.h"
 #include "Units.h"
 
 class nsIContent;
+class nsIDOMEventTarget;
 class nsPresContext;
 struct JSContext;
 class JSObject;
 
+namespace mozilla {
+namespace dom {
+class EventTarget;
+}
+}
+
 // Dummy class so we can cast through it to get from nsISupports to
 // nsDOMEvent subclasses with only two non-ambiguous static casts.
 class nsDOMEventBase : public nsIDOMEvent
 {
 };
 
 class nsDOMEvent : public nsDOMEventBase,
                    public nsWrapperCache
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -456,16 +456,17 @@ nsEventDispatcher::Dispatch(nsISupports*
   // event dispatching is finished.
   nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
 
   nsTArray<nsEventTargetChainItem> chain(128);
 
   // Create the event target chain item for the event target.
   nsEventTargetChainItem* targetEtci =
     nsEventTargetChainItem::Create(chain, target->GetTargetForEventTargetChain());
+  MOZ_ASSERT(&chain[0] == targetEtci);
   if (!targetEtci->IsValid()) {
     nsEventTargetChainItem::DestroyLast(chain, targetEtci);
     return NS_ERROR_FAILURE;
   }
 
   // Make sure that nsIDOMEvent::target and nsIDOMEvent::originalTarget
   // point to the last item in the chain.
   if (!aEvent->target) {
@@ -503,24 +504,26 @@ nsEventDispatcher::Dispatch(nsISupports*
                                     isInAnon);
   targetEtci->PreHandleEvent(preVisitor);
 
   if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) {
     // Event target couldn't handle the event. Try to propagate to chrome.
     nsEventTargetChainItem::DestroyLast(chain, targetEtci);
     targetEtci = EventTargetChainItemForChromeTarget(chain, content);
     NS_ENSURE_STATE(targetEtci);
+    MOZ_ASSERT(&chain[0] == targetEtci);
     targetEtci->PreHandleEvent(preVisitor);
   }
   if (preVisitor.mCanHandle) {
     // At least the original target can handle the event.
     // Setting the retarget to the |target| simplifies retargeting code.
     nsCOMPtr<EventTarget> t = do_QueryInterface(aEvent->target);
     targetEtci->SetNewTarget(t);
     nsEventTargetChainItem* topEtci = targetEtci;
+    targetEtci = nullptr;
     while (preVisitor.mParentTarget) {
       EventTarget* parentTarget = preVisitor.mParentTarget;
       nsEventTargetChainItem* parentEtci =
         nsEventTargetChainItem::Create(chain, preVisitor.mParentTarget, topEtci);
       if (!parentEtci->IsValid()) {
         nsEventTargetChainItem::DestroyLast(chain, parentEtci);
         rv = NS_ERROR_FAILURE;
         break;
@@ -546,17 +549,17 @@ nsEventDispatcher::Dispatch(nsISupports*
           nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
           if (disabledTarget) {
             parentEtci = EventTargetChainItemForChromeTarget(chain,
                                                              disabledTarget,
                                                              topEtci);
             if (parentEtci) {
               parentEtci->PreHandleEvent(preVisitor);
               if (preVisitor.mCanHandle) {
-                targetEtci->SetNewTarget(parentTarget);
+                chain[0].SetNewTarget(parentTarget);
                 topEtci = parentEtci;
                 continue;
               }
             }
           }
         }
         break;
       }
@@ -586,18 +589,16 @@ nsEventDispatcher::Dispatch(nsISupports*
         }
       }
     }
   }
 
   // Note, nsEventTargetChainItem objects are deleted when the chain goes out of
   // the scope.
 
-  targetEtci = nullptr;
-
   aEvent->mFlags.mIsBeingDispatched = false;
   aEvent->mFlags.mDispatchedAtLeastOnce = true;
 
   if (!externalDOMEvent && preVisitor.mDOMEvent) {
     // An nsDOMEvent was created while dispatching the event.
     // Duplicate private data if someone holds a pointer to it.
     nsrefcnt rc = 0;
     NS_RELEASE2(preVisitor.mDOMEvent, rc);
--- a/content/events/src/nsEventListenerManager.h
+++ b/content/events/src/nsEventListenerManager.h
@@ -3,17 +3,16 @@
  * 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 nsEventListenerManager_h__
 #define nsEventListenerManager_h__
 
 #include "jsapi.h"
 #include "mozilla/dom/EventListenerBinding.h"
-#include "mozilla/dom/EventTarget.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsCxPusher.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "nsGUIEvent.h"
@@ -33,16 +32,18 @@ class nsCxPusher;
 class nsIEventListenerInfo;
 
 struct nsListenerStruct;
 class nsEventListenerManager;
 
 namespace mozilla {
 namespace dom {
 
+class EventTarget;
+
 typedef CallbackObjectHolder<EventListener, nsIDOMEventListener>
   EventListenerHolder;
 
 struct EventListenerFlags
 {
   friend struct ::nsListenerStruct;
   friend class  ::nsEventListenerManager;
 private:
--- a/content/events/src/nsEventListenerService.h
+++ b/content/events/src/nsEventListenerService.h
@@ -3,17 +3,16 @@
  * 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 nsEventListenerService_h__
 #define nsEventListenerService_h__
 #include "nsIEventListenerService.h"
 #include "nsAutoPtr.h"
 #include "nsIDOMEventListener.h"
-#include "nsIDOMEventTarget.h"
 #include "nsString.h"
 #include "nsCycleCollectionParticipant.h"
 #include "jsapi.h"
 #include "mozilla/Attributes.h"
 
 
 class nsEventListenerInfo : public nsIEventListenerInfo
 {
--- a/content/html/content/public/nsIFormControl.h
+++ b/content/html/content/public/nsIFormControl.h
@@ -10,16 +10,17 @@ class nsIDOMHTMLFormElement;
 class nsPresState;
 class nsString;
 class nsIFormProcessor;
 class nsFormSubmission;
 
 namespace mozilla {
 namespace dom {
 class Element;
+class HTMLFieldSetElement;
 } // namespace dom
 } // namespace mozilla
 
 enum FormControlsTypes {
   NS_FORM_FIELDSET = 1,
   NS_FORM_LABEL,
   NS_FORM_OUTPUT,
   NS_FORM_SELECT,
@@ -83,16 +84,22 @@ PR_STATIC_ASSERT((uint32_t)eInputElement
  */
 class nsIFormControl : public nsISupports
 {
 public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFORMCONTROL_IID)
 
   /**
+   * Get the fieldset for this form control.
+   * @return the fieldset
+   */
+  virtual mozilla::dom::HTMLFieldSetElement *GetFieldSet() = 0;
+
+  /**
    * Get the form for this form control.
    * @return the form
    */
   virtual mozilla::dom::Element *GetFormElement() = 0;
 
   /**
    * Set the form for this form control.
    * @param aForm the form.  This must not be null.
--- a/content/html/content/src/HTMLFieldSetElement.cpp
+++ b/content/html/content/src/HTMLFieldSetElement.cpp
@@ -12,22 +12,23 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(FieldSet)
 
 namespace mozilla {
 namespace dom {
 
 HTMLFieldSetElement::HTMLFieldSetElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLFormElement(aNodeInfo)
   , mElements(nullptr)
   , mFirstLegend(nullptr)
+  , mInvalidElementsCount(0)
 {
   // <fieldset> is always barred from constraint validation.
   SetBarredFromConstraintValidation(true);
 
-  // We start out enabled
-  AddStatesSilently(NS_EVENT_STATE_ENABLED);
+  // We start out enabled and valid.
+  AddStatesSilently(NS_EVENT_STATE_ENABLED | NS_EVENT_STATE_VALID);
 }
 
 HTMLFieldSetElement::~HTMLFieldSetElement()
 {
   uint32_t length = mDependentElements.Length();
   for (uint32_t i = 0; i < length; ++i) {
     mDependentElements[i]->ForgetFieldSet(this);
   }
@@ -206,16 +207,42 @@ HTMLFieldSetElement::RemoveChildAt(uint3
   nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify);
 
   if (firstLegendHasChanged) {
     NotifyElementsForFirstLegendChange(aNotify);
   }
 }
 
 void
+HTMLFieldSetElement::AddElement(nsGenericHTMLFormElement* aElement)
+{
+  mDependentElements.AppendElement(aElement);
+
+  // We need to update the validity of the fieldset.
+  nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
+  if (cvElmt &&
+      cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
+    UpdateValidity(false);
+  }
+}
+
+void
+HTMLFieldSetElement::RemoveElement(nsGenericHTMLFormElement* aElement)
+{
+  mDependentElements.RemoveElement(aElement);
+
+  // We need to update the validity of the fieldset.
+  nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
+  if (cvElmt &&
+      cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
+    UpdateValidity(true);
+  }
+}
+
+void
 HTMLFieldSetElement::NotifyElementsForFirstLegendChange(bool aNotify)
 {
   /**
    * NOTE: this could be optimized if only call when the fieldset is currently
    * disabled.
    * This should also make sure that mElements is set when we happen to be here.
    * However, this method shouldn't be called very often in normal use cases.
    */
@@ -226,16 +253,56 @@ HTMLFieldSetElement::NotifyElementsForFi
 
   uint32_t length = mElements->Length(true);
   for (uint32_t i = 0; i < length; ++i) {
     static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
       ->FieldSetFirstLegendChanged(aNotify);
   }
 }
 
+void
+HTMLFieldSetElement::UpdateValidity(bool aElementValidity)
+{
+  if (aElementValidity) {
+    --mInvalidElementsCount;
+  } else {
+    ++mInvalidElementsCount;
+  }
+
+  MOZ_ASSERT(mInvalidElementsCount >= 0);
+
+  // The fieldset validity has just changed if:
+  // - there are no more invalid elements ;
+  // - or there is one invalid elmement and an element just became invalid.
+  if (!mInvalidElementsCount || (mInvalidElementsCount == 1 && !aElementValidity)) {
+    UpdateState(true);
+  }
+
+  // We should propagate the change to the fieldset parent chain.
+  if (mFieldSet) {
+    mFieldSet->UpdateValidity(aElementValidity);
+  }
+
+  return;
+}
+
+nsEventStates
+HTMLFieldSetElement::IntrinsicState() const
+{
+  nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
+
+  if (mInvalidElementsCount) {
+    state |= NS_EVENT_STATE_INVALID;
+  } else {
+    state |= NS_EVENT_STATE_VALID;
+  }
+
+  return state;
+}
+
 JSObject*
 HTMLFieldSetElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLFieldSetElementBinding::Wrap(aCx, aScope, this);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/HTMLFieldSetElement.h
+++ b/content/html/content/src/HTMLFieldSetElement.h
@@ -50,23 +50,19 @@ public:
   NS_IMETHOD_(uint32_t) GetType() const MOZ_OVERRIDE { return NS_FORM_FIELDSET; }
   NS_IMETHOD Reset() MOZ_OVERRIDE;
   NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission) MOZ_OVERRIDE;
   virtual bool IsDisabledForEvents(uint32_t aMessage) MOZ_OVERRIDE;
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   const nsIContent* GetFirstLegend() const { return mFirstLegend; }
 
-  void AddElement(nsGenericHTMLFormElement* aElement) {
-    mDependentElements.AppendElement(aElement);
-  }
+  void AddElement(nsGenericHTMLFormElement* aElement);
 
-  void RemoveElement(nsGenericHTMLFormElement* aElement) {
-    mDependentElements.RemoveElement(aElement);
-  }
+  void RemoveElement(nsGenericHTMLFormElement* aElement);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLFieldSetElement,
                                            nsGenericHTMLFormElement)
 
   // WebIDL
   bool Disabled() const
   {
     return GetBoolAttr(nsGkAtoms::disabled);
@@ -92,16 +88,31 @@ public:
   // XPCOM Validity is OK for us
 
   // XPCOM GetValidationMessage is OK for us
 
   // XPCOM CheckValidity is OK for us
 
   // XPCOM SetCustomValidity is OK for us
 
+  virtual nsEventStates IntrinsicState() const;
+
+
+  /*
+   * This method will update the fieldset's validity.  This method has to be
+   * called by fieldset elements whenever their validity state or status regarding
+   * constraint validation changes.
+   *
+   * @note If an element becomes barred from constraint validation, it has to
+   * be considered as valid.
+   *
+   * @param aElementValidityState the new validity state of the element
+   */
+  void UpdateValidity(bool aElementValidityState);
+
 protected:
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
 private:
 
   /**
    * Notify all elements (in mElements) that the first legend of the fieldset
@@ -115,15 +126,22 @@ private:
 
   // listed form controls elements.
   nsRefPtr<nsContentList> mElements;
 
   // List of elements which have this fieldset as first fieldset ancestor.
   nsTArray<nsGenericHTMLFormElement*> mDependentElements;
 
   nsIContent* mFirstLegend;
+
+  /**
+   * Number of invalid and candidate for constraint validation
+   * elements in the fieldSet the last time UpdateValidity has been called.
+   *
+   * @note Should only be used by UpdateValidity() and IntrinsicState()!
+   */
+  int32_t mInvalidElementsCount;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_HTMLFieldSetElement_h */
-
--- a/content/html/content/src/HTMLTrackElement.cpp
+++ b/content/html/content/src/HTMLTrackElement.cpp
@@ -57,16 +57,19 @@ NS_NewHTMLTrackElement(already_AddRefed<
   }
 
   return new mozilla::dom::HTMLTrackElement(aNodeInfo);
 }
 
 namespace mozilla {
 namespace dom {
 
+// The default value for kKindTable is "subtitles"
+static const char* kKindTableDefaultString = kKindTable->tag;
+
 /** HTMLTrackElement */
 HTMLTrackElement::HTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
   , mReadyState(NONE)
 {
 #ifdef PR_LOGGING
   if (!gTrackElementLog) {
     gTrackElementLog = PR_NewLogModule("nsTrackElement");
@@ -86,16 +89,22 @@ NS_IMPL_RELEASE_INHERITED(HTMLTrackEleme
 NS_IMPL_CYCLE_COLLECTION_INHERITED_4(HTMLTrackElement, nsGenericHTMLElement,
                                      mTrack, mChannel, mMediaParent,
                                      mLoadListener)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLTrackElement)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 void
+HTMLTrackElement::GetKind(DOMString& aKind) const
+{
+  GetEnumAttr(nsGkAtoms::kind, kKindTableDefaultString, aKind);
+}
+
+void
 HTMLTrackElement::OnChannelRedirect(nsIChannel* aChannel,
                                     nsIChannel* aNewChannel,
                                     uint32_t aFlags)
 {
   NS_ASSERTION(aChannel == mChannel, "Channels should match!");
   mChannel = aNewChannel;
 }
 
@@ -125,65 +134,37 @@ HTMLTrackElement::Track()
 }
 
 void
 HTMLTrackElement::CreateTextTrack()
 {
   nsString label, srcLang;
   GetSrclang(srcLang);
   GetLabel(label);
-  mTrack = new TextTrack(OwnerDoc()->GetParentObject(), Kind(), label, srcLang);
+
+  TextTrackKind kind;
+  if (const nsAttrValue* value = GetParsedAttr(nsGkAtoms::kind)) {
+    kind = static_cast<TextTrackKind>(value->GetEnumValue());
+  } else {
+    kind = TextTrackKind::Subtitles;
+  }
+
+  mTrack = new TextTrack(OwnerDoc()->GetParentObject(), kind, label, srcLang);
 
   if (mMediaParent) {
     mMediaParent->AddTextTrack(mTrack);
   }
 }
 
-TextTrackKind
-HTMLTrackElement::Kind() const
-{
-  const nsAttrValue* value = GetParsedAttr(nsGkAtoms::kind);
-  if (!value) {
-    return TextTrackKind::Subtitles;
-  }
-  return static_cast<TextTrackKind>(value->GetEnumValue());
-}
-
-static EnumEntry
-StringFromKind(TextTrackKind aKind)
-{
-  return TextTrackKindValues::strings[static_cast<int>(aKind)];
-}
-
-void
-HTMLTrackElement::SetKind(TextTrackKind aKind, ErrorResult& aError)
-{
-  const EnumEntry& string = StringFromKind(aKind);
-  nsAutoString kind;
-
-  kind.AssignASCII(string.value, string.length);
-  SetHTMLAttr(nsGkAtoms::kind, kind, aError);
-}
-
 bool
 HTMLTrackElement::ParseAttribute(int32_t aNamespaceID,
                                  nsIAtom* aAttribute,
                                  const nsAString& aValue,
                                  nsAttrValue& aResult)
 {
-  // Map html attribute string values to TextTrackKind enums.
-  static const nsAttrValue::EnumTable kKindTable[] = {
-    { "subtitles", static_cast<int16_t>(TextTrackKind::Subtitles) },
-    { "captions", static_cast<int16_t>(TextTrackKind::Captions) },
-    { "descriptions", static_cast<int16_t>(TextTrackKind::Descriptions) },
-    { "chapters", static_cast<int16_t>(TextTrackKind::Chapters) },
-    { "metadata", static_cast<int16_t>(TextTrackKind::Metadata) },
-    { 0 }
-  };
-
   if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::kind) {
     // Case-insensitive lookup, with the first element as the default.
     return aResult.ParseEnumValue(aValue, kKindTable, false, kKindTable);
   }
 
   // Otherwise call the generic implementation.
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID,
                                               aAttribute,
--- a/content/html/content/src/HTMLTrackElement.h
+++ b/content/html/content/src/HTMLTrackElement.h
@@ -16,32 +16,45 @@
 #include "nsIDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIHttpChannel.h"
 
 namespace mozilla {
 namespace dom {
 
+// Map html attribute string values to TextTrackKind enums.
+static const nsAttrValue::EnumTable kKindTable[] = {
+  { "subtitles", static_cast<int16_t>(TextTrackKind::Subtitles) },
+  { "captions", static_cast<int16_t>(TextTrackKind::Captions) },
+  { "descriptions", static_cast<int16_t>(TextTrackKind::Descriptions) },
+  { "chapters", static_cast<int16_t>(TextTrackKind::Chapters) },
+  { "metadata", static_cast<int16_t>(TextTrackKind::Metadata) },
+  { 0 }
+};
+
 class WebVTTLoadListener;
 
 class HTMLTrackElement MOZ_FINAL : public nsGenericHTMLElement
 {
 public:
   HTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~HTMLTrackElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLTrackElement,
                                            nsGenericHTMLElement)
 
   // HTMLTrackElement WebIDL
-  TextTrackKind Kind() const;
-  void SetKind(TextTrackKind aKind, ErrorResult& aError);
+  void GetKind(DOMString& aKind) const;
+  void SetKind(const nsAString& aKind, ErrorResult& aError)
+  {
+    SetHTMLAttr(nsGkAtoms::kind, aKind, aError);
+  }
 
   void GetSrc(DOMString& aSrc) const
   {
     GetHTMLURIAttr(nsGkAtoms::src, aSrc);
   }
   void SetSrc(const nsAString& aSrc, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::src, aSrc, aError);
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2100,16 +2100,22 @@ nsGenericHTMLFormElement::ClearForm(bool
 }
 
 Element*
 nsGenericHTMLFormElement::GetFormElement()
 {
   return mForm;
 }
 
+HTMLFieldSetElement*
+nsGenericHTMLFormElement::GetFieldSet()
+{
+  return mFieldSet;
+}
+
 nsresult
 nsGenericHTMLFormElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   NS_ENSURE_ARG_POINTER(aForm);
   NS_IF_ADDREF(*aForm = mForm);
   return NS_OK;
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1270,16 +1270,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   nsINode* GetParentObject() const;
 
   virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
   virtual void SaveSubtreeState() MOZ_OVERRIDE;
 
   // nsIFormControl
+  virtual mozilla::dom::HTMLFieldSetElement* GetFieldSet();
   virtual mozilla::dom::Element* GetFormElement() MOZ_OVERRIDE;
   mozilla::dom::HTMLFormElement* GetForm() const
   {
     return mForm;
   }
   virtual void SetForm(nsIDOMHTMLFormElement* aForm) MOZ_OVERRIDE;
   virtual void ClearForm(bool aRemoveFromForm) MOZ_OVERRIDE;
 
--- a/content/html/content/src/nsIConstraintValidation.cpp
+++ b/content/html/content/src/nsIConstraintValidation.cpp
@@ -3,22 +3,26 @@
  * 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 "nsIConstraintValidation.h"
 
 #include "nsAString.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/HTMLFormElement.h"
+#include "mozilla/dom/HTMLFieldSetElement.h"
 #include "mozilla/dom/ValidityState.h"
 #include "nsIFormControl.h"
 #include "nsContentUtils.h"
 
 const uint16_t nsIConstraintValidation::sContentSpecifiedMaxLengthMessage = 256;
 
+using namespace mozilla;
+using namespace mozilla::dom;
+
 nsIConstraintValidation::nsIConstraintValidation()
   : mValidityBitField(0)
   // By default, all elements are subjects to constraint validation.
   , mBarredFromConstraintValidation(false)
 {
 }
 
 nsIConstraintValidation::~nsIConstraintValidation()
@@ -126,26 +130,30 @@ nsIConstraintValidation::SetValidityStat
   bool previousValidity = IsValid();
 
   if (aValue) {
     mValidityBitField |= aState;
   } else {
     mValidityBitField &= ~aState;
   }
 
-  // Inform the form element if our validity has changed.
+  // Inform the form and fieldset elements if our validity has changed.
   if (previousValidity != IsValid() && IsCandidateForConstraintValidation()) {
     nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
     NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
 
-    mozilla::dom::HTMLFormElement* form =
-      static_cast<mozilla::dom::HTMLFormElement*>(formCtrl->GetFormElement());
+    HTMLFormElement* form =
+      static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
     if (form) {
       form->UpdateValidity(IsValid());
     }
+    HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
+      if (fieldSet) {
+      fieldSet->UpdateValidity(IsValid());
+    }
   }
 }
 
 void
 nsIConstraintValidation::SetCustomValidity(const nsAString& aError)
 {
   mCustomValidity.Assign(aError);
   SetValidityState(VALIDITY_STATE_CUSTOM_ERROR, !mCustomValidity.IsEmpty());
@@ -153,25 +161,29 @@ nsIConstraintValidation::SetCustomValidi
 
 void
 nsIConstraintValidation::SetBarredFromConstraintValidation(bool aBarred)
 {
   bool previousBarred = mBarredFromConstraintValidation;
 
   mBarredFromConstraintValidation = aBarred;
 
-  // Inform the form element if our status regarding constraint validation
-  // is going to change.
+  // Inform the form and fieldset elements if our status regarding constraint
+  // validation is going to change.
   if (!IsValid() && previousBarred != mBarredFromConstraintValidation) {
     nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
     NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
 
-    mozilla::dom::HTMLFormElement* form =
-      static_cast<mozilla::dom::HTMLFormElement*>(formCtrl->GetFormElement());
+    // If the element is going to be barred from constraint validation, we can
+    // inform the form and fieldset that we are now valid. Otherwise, we are now
+    // invalid.
+    HTMLFormElement* form =
+      static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
     if (form) {
-      // If the element is going to be barred from constraint validation,
-      // we can inform the form that we are now valid.
-      // Otherwise, we are now invalid.
       form->UpdateValidity(aBarred);
     }
+    HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
+    if (fieldSet) {
+      fieldSet->UpdateValidity(aBarred);
+    }
   }
 }
 
--- a/content/html/content/test/forms/test_validation.html
+++ b/content/html/content/test/forms/test_validation.html
@@ -84,18 +84,18 @@ function checkConstraintValidationAPIDef
   ok(element.validity.valid, "The element should be valid by default");
 
   ok(element.checkValidity(), "The element should be valid by default");
 }
 
 function checkDefaultPseudoClass()
 {
   is(window.getComputedStyle(document.getElementById('f'), null)
-       .getPropertyValue('background-color'), "rgb(0, 0, 0)",
-     "Nor :valid and :invalid should apply");
+       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+     ":valid should apply");
 
   is(window.getComputedStyle(document.getElementById('o'), null)
        .getPropertyValue('background-color'), "rgb(0, 255, 0)",
      ":valid should apply");
 
   is(window.getComputedStyle(document.getElementById('obj'), null)
        .getPropertyValue('background-color'), "rgb(0, 0, 0)",
      "Nor :valid and :invalid should apply");
@@ -222,27 +222,34 @@ function checkCustomError(element, isBar
        "When the element has a custom validity message, validation message should return it");
   } else {
     is(element.validationMessage, "",
        "An element barred from constraint validation can't have a validation message");
   }
   ok(element.validity.customError, "The element should suffer from a custom error");
   ok(!element.validity.valid, "The element should not be valid with a custom error");
 
-  is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
-     isBarred ? "rgb(0, 0, 0)" : "rgb(255, 0, 0)",
-     ":invalid pseudo-classs should apply");
+  if (element.tagName == "FIELDSET") {
+    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
+       isBarred ? "rgb(0, 255, 0)" : "rgb(255, 0, 0)",
+       ":invalid pseudo-classs should apply" + element.tagName);
+  }
+  else {
+    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
+       isBarred ? "rgb(0, 0, 0)" : "rgb(255, 0, 0)",
+       ":invalid pseudo-classs should apply" + element.tagName);
+  }
 
   element.setCustomValidity("");
   is(element.validationMessage, "", "The element should not have a validation message when reseted");
   ok(!element.validity.customError, "The element should not suffer anymore from a custom error");
   ok(element.validity.valid, "The element should now be valid");
 
   is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
-     isBarred ? "rgb(0, 0, 0)" : "rgb(0, 255, 0)",
+     isBarred && element.tagName != "FIELDSET" ? "rgb(0, 0, 0)" : "rgb(0, 255, 0)",
      ":valid pseudo-classs should apply");
 }
 
 function checkCheckValidity(element)
 {
   element.setCustomValidity("message");
   ok(!element.checkValidity(), "checkValidity() should return false when the element is not valid");
 
--- a/content/html/content/test/test_track.html
+++ b/content/html/content/test/test_track.html
@@ -15,24 +15,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
   function() {
-    /* TODO:: See https://bugzilla.mozilla.org/show_bug.cgi?id=880064 and https://www.w3.org/Bugs/Public/show_bug.cgi?id=22221
     reflectLimitedEnumerated({
       element: document.createElement("track"),
       attribute: "kind",
       validValues: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
       invalidValues: ["foo", "bar", "\u0000", "null", "", "subtitle", "caption", "description", "chapter", "meta"],
       defaultValue: "subtitles"
-    });*/
+    });
     // Default attribute
     reflectBoolean({
       element: document.createElement("track"),
       attribute: "default"
     });
     // Label attribute
     reflectString({
       element: document.createElement("track"),
@@ -50,35 +49,15 @@ SpecialPowers.pushPrefEnv({"set": [["med
       element: document.createElement("track"),
       attribute: "srclang",
       otherValues: ["foo", "bar", "\u0000", "null", ""]
     });
 
     var track = document.createElement("track");
     is(track.readyState, 0, "Default ready state should be 0 (NONE).");
 
-    // Following are manual track.kind tests until the reflect.js problems get
-    // cleared up above.
-    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=880064 and
-    //      https://www.w3.org/Bugs/Public/show_bug.cgi?id=22221
-    is(track.kind, "subtitles", "Default track kind should be subtitles.");
-
-    // Kind should not be able to be set to bogus value.
-    track.kind = "bogus";
-    is(track.kind, "subtitles", "Track kind should not be able to be set to a bogus value.");
-
-    checkKind("captions", "Kind should be set to captions.");
-    checkKind("descriptions", "Kind should be set to descriptions.");
-    checkKind("chapters", "Kind should be set to chapters.");
-    checkKind("metadata", "Kind should be set to metadata.");
-
-    function checkKind(kind, message) {
-      track.kind = kind;
-      is(track.kind, kind, message);
-    }
-
     SimpleTest.finish();
   }
 );
 </script>
 </pre>
 </body>
 </html>
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8630,29 +8630,45 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     if (mScriptGlobal)
         requestingElement = mScriptGlobal->GetFrameElementInternal();
 
     nsRefPtr<nsGlobalWindow> MMADeathGrip = mScriptGlobal;
 
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
     uint32_t contentType;
     bool isNewDocShell = false;
+    bool isTargetTopLevelDocShell = false;
     nsCOMPtr<nsIDocShell> targetDocShell;
     if (aWindowTarget && *aWindowTarget) {
         // Locate the target DocShell.
         nsCOMPtr<nsIDocShellTreeItem> targetItem;
         FindItemWithName(aWindowTarget, nullptr, this,
                          getter_AddRefs(targetItem));
 
         targetDocShell = do_QueryInterface(targetItem);
         // If the targetDocShell doesn't exist, then this is a new docShell
         // and we should consider this a TYPE_DOCUMENT load
         isNewDocShell = !targetDocShell;
-    }
-    if (IsFrame() && !isNewDocShell) {
+
+        // If the targetDocShell and the rootDocShell are the same, then the
+        // targetDocShell is the top level document and hence we should
+        // consider this TYPE_DOCUMENT
+        if (targetDocShell) {
+          nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+          targetDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+          NS_ASSERTION(sameTypeRoot, "No document shell root tree item from targetDocShell!");
+          nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(sameTypeRoot);
+          NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
+
+          if (targetDocShell == rootShell) {
+            isTargetTopLevelDocShell = true;
+          }
+        }
+    }
+    if (IsFrame() && !isNewDocShell && !isTargetTopLevelDocShell) {
         NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
         contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
     } else {
         contentType = nsIContentPolicy::TYPE_DOCUMENT;
     }
 
     nsISupports* context = requestingElement;
     if (!context) {
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -255,17 +255,27 @@ ConsoleAPI.prototype = {
    * @private
    * @param array aCall
    *        Array that holds information about the queued call.
    */
   _processQueuedCall: function CA__processQueuedItem(aCall)
   {
     let [method, args, meta] = aCall;
 
-    let frame = meta.stack[0];
+    let frame;
+    if (meta.stack.length) {
+      frame = meta.stack[0];
+    } else {
+      frame = {
+        filename: "",
+        lineNumber: 0,
+        functionName: "",
+      };
+    }
+
     let consoleEvent = {
       ID: this._outerID,
       innerID: this._innerID,
       level: method,
       filename: frame.filename,
       lineNumber: frame.lineNumber,
       functionName: frame.functionName,
       timeStamp: meta.timeStamp,
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -3572,21 +3572,27 @@ const InterfaceShimEntry kInterfaceShimM
   { "nsIDOMOfflineResourceList", "OfflineResourceList" },
   { "nsIDOMRange", "Range" },
   { "nsIDOMSVGLength", "SVGLength" },
   { "nsIDOMNodeFilter", "NodeFilter" },
   { "nsIDOMXPathNamespace", "XPathNamespace" },
   { "nsIDOMXPathResult", "XPathResult" } };
 
 static nsresult
-DefineComponentsShim(JSContext *cx, JS::HandleObject global)
+DefineComponentsShim(JSContext *cx, JS::HandleObject global, nsPIDOMWindow *win)
 {
   // Keep track of how often this happens.
   Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
 
+  // Warn once.
+  nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
+  if (doc) {
+    doc->WarnOnceAbout(nsIDocument::eComponents, /* asError = */ true);
+  }
+
   // Create a fake Components object.
   JS::Rooted<JSObject*> components(cx, JS_NewObject(cx, nullptr, nullptr, global));
   NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY);
   bool ok = JS_DefineProperty(cx, global, "Components", JS::ObjectValue(*components),
                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   // Create a fake interfaces object.
@@ -3632,24 +3638,25 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
   JS::RootedObject obj(cx, obj_);
   JS::RootedId id(cx, id_);
 
   if (!JSID_IS_STRING(id)) {
     return NS_OK;
   }
 
   MOZ_ASSERT(*_retval == true); // guaranteed by XPC_WN_Helper_NewResolve
-  if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
-    *objp = obj;
-    return DefineComponentsShim(cx, obj);
-  }
 
   nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
   MOZ_ASSERT(win->IsInnerWindow());
 
+  if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
+    *objp = obj;
+    return DefineComponentsShim(cx, obj, win);
+  }
+
   nsIScriptContext *my_context = win->GetContextInternal();
 
   // Don't resolve standard classes on XrayWrappers, only resolve them if we're
   // resolving on the real global object.
   if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
     bool did_resolve = false;
     bool ok = true;
     JS::Rooted<JS::Value> exn(cx, JSVAL_VOID);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2106,19 +2106,17 @@ CreateNativeGlobalForInner(JSContext* aC
     if (top->GetGlobalJSObject()) {
       options.zoneSpec = JS::SameZoneAs(top->GetGlobalJSObject());
     }
   }
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
 
   // Determine if we need the Components object.
-  bool componentsInContent = !Preferences::GetBool("dom.omit_components_in_content", true);
-  bool needComponents = componentsInContent ||
-                        nsContentUtils::IsSystemPrincipal(aPrincipal) ||
+  bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
   uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
 
   nsRefPtr<nsIXPConnectJSObjectHolder> jsholder;
   nsresult rv = xpc->InitClassesWithNewWrappedGlobal(
     aCx, ToSupports(aNewInner),
     aPrincipal, flags, options, getter_AddRefs(jsholder));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -184,16 +184,37 @@ nsJSUtils::CompileFunction(JSContext* aC
     ReportPendingException(aCx);
     return NS_ERROR_FAILURE;
   }
 
   *aFunctionObject = JS_GetFunctionObject(fun);
   return NS_OK;
 }
 
+class MOZ_STACK_CLASS AutoDontReportUncaught {
+  JSContext* mContext;
+  bool mWasSet;
+
+public:
+  AutoDontReportUncaught(JSContext* aContext) : mContext(aContext) {
+    MOZ_ASSERT(aContext);
+    uint32_t oldOptions = JS_GetOptions(mContext);
+    mWasSet = oldOptions & JSOPTION_DONT_REPORT_UNCAUGHT;
+    if (!mWasSet) {
+      JS_SetOptions(mContext, oldOptions | JSOPTION_DONT_REPORT_UNCAUGHT);
+    }
+  }
+  ~AutoDontReportUncaught() {
+    if (!mWasSet) {
+      JS_SetOptions(mContext,
+                    JS_GetOptions(mContext) & ~JSOPTION_DONT_REPORT_UNCAUGHT);
+    }
+  }
+};
+
 nsresult
 nsJSUtils::EvaluateString(JSContext* aCx,
                           const nsAString& aScript,
                           JS::Handle<JSObject*> aScopeObject,
                           JS::CompileOptions& aCompileOptions,
                           EvaluateOptions& aEvaluateOptions,
                           JS::Value* aRetValue)
 {
@@ -219,16 +240,23 @@ nsJSUtils::EvaluateString(JSContext* aCx
   aCompileOptions.setPrincipals(p);
 
   bool ok = false;
   nsresult rv = nsContentUtils::GetSecurityManager()->
                   CanExecuteScripts(aCx, nsJSPrincipals::get(p), &ok);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(ok, NS_OK);
 
+  mozilla::Maybe<AutoDontReportUncaught> dontReport;
+  if (!aEvaluateOptions.reportUncaught) {
+    // We need to prevent AutoLastFrameCheck from reporting and clearing
+    // any pending exceptions.
+    dontReport.construct(aCx);
+  }
+
   // Scope the JSAutoCompartment so that we can later wrap the return value
   // into the caller's cx.
   {
     JSAutoCompartment ac(aCx, aScopeObject);
 
     JS::RootedObject rootedScope(aCx, aScopeObject);
     ok = JS::Evaluate(aCx, rootedScope, aCompileOptions,
                       PromiseFlatString(aScript).get(),
--- a/dom/icc/tests/marionette/test_stk_display_text.js
+++ b/dom/icc/tests/marionette/test_stk_display_text.js
@@ -8,21 +8,23 @@ SpecialPowers.addPermission("mobileconne
 let icc = navigator.mozIccManager;
 ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
 
 function testDisplayText(command, expect) {
   log("STK CMD " + JSON.stringify(command));
   is(command.typeOfCommand, icc.STK_CMD_DISPLAY_TEXT, expect.name);
   is(command.options.text, expect.text, expect.name);
   is(command.commandQualifier, expect.commandQualifier, expect.name);
-  if (expect.userClear) {
-    is(command.options.userClear, expect.userClear, expect.name);
-  }
-  if (expect.isHighPriority) {
-    is(command.options.isHighPriority, expect.isHighPriority, expect.name);
+  is(command.options.userClear, expect.userClear, expect.name);
+  is(command.options.isHighPriority, expect.isHighPriority, expect.name);
+
+  let duration = command.options.duration;
+  if (duration) {
+    is(duration.timeUnit, expect.duration.timeUnit, expect.name);
+    is(duration.timeInterval, expect.duration.timeInterval, expect.name);
   }
 
   runNextTest();
 }
 
 let tests = [
   {command: "d01a8103012180820281028d0f04546f6f6c6b697420546573742031",
    func: testDisplayText,
@@ -56,35 +58,50 @@ let tests = [
             userClear: true}},
   {command: "d01a8103012180820281028d0f043c474f2d4241434b57415244533e",
    func: testDisplayText,
    expect: {name: "display_text_cmd_6",
             commandQualifier: 0x80,
             text: "<GO-BACKWARDS>",
             userClear: true}},
    {command: "d0248103012180820281028d1908041704140420041004120421042204120423041904220415",
+    func: testDisplayText,
+    expect: {name: "display_text_cmd_7",
+             commandQualifier: 0x80,
+             text: "ЗДРАВСТВУЙТЕ",
+             userClear: true}},
+   {command: "d0108103012180820281028d05084f60597d",
+    func: testDisplayText,
+    expect: {name: "display_text_cmd_8",
+             commandQualifier: 0x80,
+             text: "你好",
+             userClear: true}},
+   {command: "d0128103012180820281028d07080038003030eb",
+    func: testDisplayText,
+    expect: {name: "display_text_cmd_9",
+             commandQualifier: 0x80,
+             text: "80ル",
+             userClear: true}},
+   {command: "d0288103012180820281020d1d00d3309bfc06c95c301aa8e80259c3ec34b9ac07c9602f58ed159bb940",
+    func: testDisplayText,
+    expect: {name: "display_text_cmd_10",
+             commandQualifier: 0x80,
+             text: "Saldo 2.04 E. Validez 20/05/13. ",
+             userClear: true}},
+  {command: "d0198103012180820281028D0A043130205365636F6E648402010A",
    func: testDisplayText,
-   expect: {name: "display_text_cmd_7",
-            commandQualifier: 0x80,
-            text: "ЗДРАВСТВУЙТЕ",
-            userClear: true}},
-   {command: "d0108103012180820281028d05084f60597d",
-   func: testDisplayText,
-   expect: {name: "display_text_cmd_8",
+   expect: {name: "display_text_cmd_11",
             commandQualifier: 0x80,
-            text: "你好",
-            userClear: true}},
-   {command: "d0128103012180820281028d07080038003030eb",
-   func: testDisplayText,
-   expect: {name: "display_text_cmd_9",
-            commandQualifier: 0x80,
-            text: "80ル",
-            userClear: true}},
+            text: "10 Second",
+            userClear: true,
+            duration: {timeUnit: icc.STK_TIME_UNIT_SECOND,
+                       timeInterval: 0x0A}}},
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_get_inkey.js
+++ b/dom/icc/tests/marionette/test_stk_get_inkey.js
@@ -8,24 +8,24 @@ SpecialPowers.addPermission("mobileconne
 let icc = navigator.mozIccManager;
 ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
 
 function testGetInKey(command, expect) {
   log("STK CMD " + JSON.stringify(command));
   is(command.typeOfCommand, icc.STK_CMD_GET_INKEY, expect.name);
   is(command.commandQualifier, expect.commandQualifier, expect.name);
   is(command.options.text, expect.text, expect.name);
-  if (command.options.isAlphabet) {
-    is(command.options.isAlphabet, expect.isAlphabet, expect.name);
-  }
-  if (command.options.isUCS2) {
-    is(command.options.isUCS2, expect.isUCS2, expect.name);
-  }
-  if (command.options.isYesNoRequested) {
-    is(command.options.isYesNoRequested, expect.isYesNoRequested, expect.name);
+  is(command.options.isAlphabet, expect.isAlphabet, expect.name);
+  is(command.options.isUCS2, expect.isUCS2, expect.name);
+  is(command.options.isYesNoRequested, expect.isYesNoRequested, expect.name);
+
+  let duration = command.options.duration;
+  if (duration) {
+    is(duration.timeUnit, expect.duration.timeUnit, expect.name);
+    is(duration.timeInterval, expect.duration.timeInterval, expect.name);
   }
 
   runNextTest();
 }
 
 let tests = [
   {command: "d0158103012200820281828d0a04456e74657220222b22",
    func: testGetInKey,
@@ -93,18 +93,26 @@ let tests = [
             commandQualifier: 0x04,
             text: "Enter NO",
             isYesNoRequested: true}},
   {command: "d0198103012200820281828d0a043c4e4f2d49434f4e3e1e020001",
    func: testGetInKey,
    expect: {name: "get_inkey_cmd_13",
             commandQualifier: 0x00,
             text: "<NO-ICON>"}},
+  {command: "D0198103012200820281828D0A04456E74657220222B228402010A",
+   func: testGetInKey,
+   expect: {name: "get_inkey_cmd_14",
+            commandQualifier: 0x00,
+            text: "Enter \"+\"",
+            duration: {timeUnit: icc.STK_TIME_UNIT_SECOND,
+                       timeInterval: 0x0A}}},
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_get_input.js
+++ b/dom/icc/tests/marionette/test_stk_get_input.js
@@ -169,16 +169,17 @@ let tests = [
    func: testGetInput,
    expect: {name: "get_input_cmd_18",
             commandQualifier: 0x00,
             text: "<BASIC-ICON>",
             minLength: 0,
             maxLength: 10}},
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_launch_browser.js
+++ b/dom/icc/tests/marionette/test_stk_launch_browser.js
@@ -246,16 +246,17 @@ let tests = [
   {command: "d010810301150282028182310005038030eb",
    func: testLaunchBrowser,
    expect: {name: "launch_browser_cmd_38",
             commandQualifier: 0x02,
             url: "",
             text: "ル"}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_poll_off.js
+++ b/dom/icc/tests/marionette/test_stk_poll_off.js
@@ -18,16 +18,17 @@ function testPollOff(command, expect) {
 
 let tests = [
   {command: "d009810301040082028182",
    func: testPollOff,
    expect: {name: "pull_off_cmd_1",
             commandQualifier: 0x00}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_proactive_command.js
+++ b/dom/icc/tests/marionette/test_stk_proactive_command.js
@@ -3,25 +3,16 @@
 
 MARIONETTE_TIMEOUT = 30000;
 
 SpecialPowers.addPermission("mobileconnection", true, document);
 
 let icc = navigator.mozIccManager;
 ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
 
-function testDisplayTextGsm7BitEncoding(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_DISPLAY_TEXT);
-  is(cmd.options.userClear, true);
-  is(cmd.options.text, "Saldo 2.04 E. Validez 20/05/13. ");
-
-  runNextTest();
-}
-
 function testLocalInfoLocation(cmd) {
   log("STK CMD " + JSON.stringify(cmd));
   is(cmd.typeOfCommand, icc.STK_CMD_PROVIDE_LOCAL_INFO);
   is(cmd.commandNumber, 0x01);
   is(cmd.commandQualifier, icc.STK_LOCAL_INFO_LOCATION_INFO);
   is(cmd.options.localInfoType, icc.STK_LOCAL_INFO_LOCATION_INFO);
 
   runNextTest();
@@ -52,45 +43,16 @@ function testLocalInfoLanguage(cmd) {
   is(cmd.typeOfCommand, icc.STK_CMD_PROVIDE_LOCAL_INFO);
   is(cmd.commandNumber, 0x01);
   is(cmd.commandQualifier, icc.STK_LOCAL_INFO_LANGUAGE);
   is(cmd.options.localInfoType, icc.STK_LOCAL_INFO_LANGUAGE);
 
   runNextTest();
 }
 
-function isFirstMenuItemNull(cmd) {
-  return (cmd.options.items.length == 1 && !(cmd.options.items[0]));
-}
-
-function testInitialSetupMenu(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_SET_UP_MENU);
-  is(isFirstMenuItemNull(cmd), false);
-
-  runNextTest();
-}
-function testRemoveSetupMenu(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_SET_UP_MENU);
-  is(isFirstMenuItemNull(cmd), true);
-
-  runNextTest();
-}
-
-function testPollingOff(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_POLL_OFF);
-  is(cmd.commandNumber, 0x01);
-  is(cmd.commandQualifier, 0x00);
-  is(cmd.options, null);
-
-  runNextTest();
-}
-
 function testRefresh(cmd) {
   log("STK CMD " + JSON.stringify(cmd));
   is(cmd.typeOfCommand, icc.STK_CMD_REFRESH);
   is(cmd.commandNumber, 0x01);
   is(cmd.commandQualifier, 0x01);
   is(cmd.options, null);
 
   runNextTest();
@@ -125,91 +87,32 @@ function testTimerManagementGetCurrentVa
   is(cmd.commandNumber, 0x01);
   is(cmd.commandQualifier, icc.STK_TIMER_GET_CURRENT_VALUE);
   is(cmd.options.timerAction, icc.STK_TIMER_GET_CURRENT_VALUE);
   is(cmd.options.timerId, 0x08);
 
   runNextTest();
 }
 
-function testGetInKeyVariableTimeout(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_GET_INKEY);
-  is(cmd.options.duration.timeUnit, 0x01);
-  is(cmd.options.duration.timeInterval, 0x0A);
-
-  runNextTest();
-}
-
-function testSetupCall(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_SET_UP_CALL);
-  is(cmd.commandNumber, 0x01);
-  is(cmd.commandQualifier, 0x04);
-  is(cmd.options.address, "012340123456,1,2");
-  is(cmd.options.confirmMessage, "Disconnect");
-  is(cmd.options.callMessage, "Message");
-
-  runNextTest();
-}
-
-function testDisplayTextVariableTimeOut(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_DISPLAY_TEXT);
-  is(cmd.commandNumber, 0x01);
-  is(cmd.options.duration.timeUnit, icc.STK_TIME_UNIT_SECOND);
-  is(cmd.options.duration.timeInterval, 0x0A);
-
-  runNextTest();
-}
-
-function testSetUpCallVariableTimeOut(cmd) {
-  log("STK CMD " + JSON.stringify(cmd));
-  is(cmd.typeOfCommand, icc.STK_CMD_SET_UP_CALL);
-  is(cmd.commandNumber, 0x01);
-  is(cmd.options.duration.timeUnit, icc.STK_TIME_UNIT_SECOND);
-  is(cmd.options.duration.timeInterval, 0x0A);
-
-  runNextTest();
-}
-
 let tests = [
-  {command: "d0288103012180820281020d1d00d3309bfc06c95c301aa8e80259c3ec34b9ac07c9602f58ed159bb940",
-   func: testDisplayTextGsm7BitEncoding},
   {command: "d009810301260082028182",
    func: testLocalInfoLocation},
   {command: "d009810301260182028182",
    func: testLocalInfoImei},
   {command: "d009810301260382028182",
    func: testLocalInfoDate},
   {command: "d009810301260482028182",
    func: testLocalInfoLanguage},
-  {command: "D00D81030125008202818285008F00",
-   func: testRemoveSetupMenu},
-  {command:"D03B810301250082028182850C546F6F6C6B6974204D656E758F07014974656D20318F07024974656D20328F07034974656D20338F07044974656D2034",
-   func: testInitialSetupMenu},
-  {command: "d009810301040082028182",
-   func: testPollingOff},
-  {command: "d0108103010101820281829205013f002fe2",
-   func: testRefresh},
   {command: "d011810301270082028182a40101a503102030",
    func: testTimerManagementStart},
   {command: "d00c810301270182028182a40104",
    func: testTimerManagementDeactivate},
   {command: "d00c810301270282028182a40108",
    func: testTimerManagementGetCurrentValue},
-  {command: "d029810301100482028182050a446973636f6e6e6563748609811032042143651c2c05074d657373616765",
-   func: testSetupCall},
-  {command: "D0198103012200820281828D0A04456E74657220222B228402010A",
-   func: testGetInKeyVariableTimeout},
-  {command: "d0198103012180820281028D0A043130205365636F6E648402010A",
-   func: testDisplayTextVariableTimeOut},
-  {command: "d02281030110008202818385084E6F7420627573798609911032042143651C2C8402010A",
-   func: testSetUpCallVariableTimeOut},
-];
+ ];
 
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(cmd, func) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(cmd, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
--- a/dom/icc/tests/marionette/test_stk_refresh.js
+++ b/dom/icc/tests/marionette/test_stk_refresh.js
@@ -22,16 +22,17 @@ let tests = [
    expect: {name: "refresh_cmd_1",
             commandQualifier: 0x01}},
   {command: "d009810301010482028182",
    func: testRefresh,
    expect: {name: "refresh_cmd_2",
             commandQualifier: 0x04}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_select_item.js
+++ b/dom/icc/tests/marionette/test_stk_select_item.js
@@ -307,16 +307,17 @@ let tests = [
   {command: "d0348103012400820281828508820430a03832cb308f0901820430a03832cb318f0902820430a03832cb328f0903820430a03832cb33",
    func: testSelectItem,
    expect: {name: "select_item_cmd_48",
             commandQualifier: 0x00,
             title: "82ル0",
             items: [{identifier: 1, text: "82ル1"}, {identifier: 2, text: "82ル2"}, {identifier: 3, text: "82ル3"}]}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_send_dtmf.js
+++ b/dom/icc/tests/marionette/test_stk_send_dtmf.js
@@ -187,16 +187,17 @@ let tests = [
             text: "你好"}},
   {command: "d01281030114008202818385038030ebac02c1f2",
    func: testSendDTMF,
    expect: {name: "send_dtmf_cmd_34",
             commandQualifier: 0x00,
             text: "ル"}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_send_sms.js
+++ b/dom/icc/tests/marionette/test_stk_send_sms.js
@@ -237,16 +237,17 @@ let tests = [
             title: "81ル1"}},
   {command: "d0348103011300820281838508820430a03832cb3286099111223344556677f88b140100099110325476f84008080038003030eb0033",
    func: testSendSMS,
    expect: {name: "send_sms_cmd_43",
             commandQualifier: 0x00,
             title: "82ル2"}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_send_ss.js
+++ b/dom/icc/tests/marionette/test_stk_send_ss.js
@@ -197,16 +197,17 @@ let tests = [
             title: "你好"}},
   {command: "d02081030111008202818385038030eb891091aa120a214365870921436587a901fb",
    func: testSendSS,
    expect: {name: "send_ss_cmd_36",
             commandQualifier: 0x00,
             title: "ル"}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_send_ussd.js
+++ b/dom/icc/tests/marionette/test_stk_send_ussd.js
@@ -202,16 +202,17 @@ let tests = [
             title: "你好"}},
   {command: "d04981030112008202818385038030eb8a39f041e19058341e9149e592d9743ea151e9945ab55eb1596d2b2c1e93cbe6333aad5eb3dbee373c2e9fd3ebf63b3eaf6fc564335acd76c3e560",
    func: testSendUSSD,
    expect: {name: "send_ussd_cmd_37",
             commandQualifier: 0x00,
             title: "ル"}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_setup_call.js
+++ b/dom/icc/tests/marionette/test_stk_setup_call.js
@@ -7,250 +7,351 @@ SpecialPowers.addPermission("mobileconne
 
 let icc = navigator.mozIccManager;
 ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
 
 function testSetupCall(command, expect) {
   log("STK CMD " + JSON.stringify(command));
   is(command.typeOfCommand, icc.STK_CMD_SET_UP_CALL, expect.name);
   is(command.commandQualifier, expect.commandQualifier, expect.name);
-  if (command.options.confirmMessage) {
-    is(command.options.confirmMessage, expect.text, expect.name);
+  is(command.options.confirmMessage, expect.confirmMessage, expect.name);
+  is(command.options.address, expect.address, expect.name);
+  is(command.options.callMessage, expect.callMessage, expect.name);
+
+  let duration = command.options.duration;
+  if (duration) {
+    is(duration.timeUnit, expect.duration.timeUnit, expect.name);
+    is(duration.timeInterval, expect.duration.timeInterval, expect.name);
   }
 
   runNextTest();
 }
 
 let tests = [
   {command: "d01e81030110008202818385084e6f7420627573798609911032042143651c2c",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_1",
             commandQualifier: 0x00,
-            text: "Not busy"}},
+            confirmMessage: "Not busy",
+            address: "+012340123456,1,2"}},
   {command: "d01d81030110028202818385074f6e20686f6c648609911032042143651c2c",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_2",
             commandQualifier: 0x02,
-            text: "On hold"}},
+            confirmMessage: "On hold",
+            address: "+012340123456,1,2"}},
   {command: "d020810301100482028183850a446973636f6e6e6563748609911032042143651c2c",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_3",
             commandQualifier: 0x04,
-            text: "Disconnect"}},
+            confirmMessage: "Disconnect",
+            address: "+012340123456,1,2"}},
   {command: "d02b81030110008202818385114361706162696c69747920636f6e6669678609911032042143651c2c870201a0",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_4",
             commandQualifier: 0x00,
-            text: "Capability config"}},
+            confirmMessage: "Capability config",
+            address: "+012340123456,1,2"}},
   {command: "d01c81030110018202818386119110325476981032547698103254769810",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_5",
-            commandQualifier: 0x01}},
+            commandQualifier: 0x01,
+            address: "+01234567890123456789012345678901"}},
   {command: "d081fd8103011001820281838581ed54687265652074797065732061726520646566696e65643a202d2073657420757020612063616c6c2c20627574206f6e6c79206966206e6f742063757272656e746c792062757379206f6e20616e6f746865722063616c6c3b202d2073657420757020612063616c6c2c2070757474696e6720616c6c206f746865722063616c6c732028696620616e7929206f6e20686f6c643b202d2073657420757020612063616c6c2c20646973636f6e6e656374696e6720616c6c206f746865722063616c6c732028696620616e79292066697273742e20466f722065616368206f662074686573652074797065732c2086029110",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_6",
             commandQualifier: 0x01,
-            text: "Three types are defined: - set up a call, but only if not currently busy on another call; - set up a call, putting all other calls (if any) on hold; - set up a call, disconnecting all other calls (if any) first. For each of these types, "}},
+            confirmMessage: "Three types are defined: - set up a call, but only if not currently busy on another call; - set up a call, putting all other calls (if any) on hold; - set up a call, disconnecting all other calls (if any) first. For each of these types, ",
+            address: "+01"}},
   {command: "d02b810301100082028183850c43616c6c65642070617274798609911032042143651c2c880780509595959595",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_7",
             commandQualifier: 0x00,
-            text: "Called party"}},
+            confirmMessage: "Called party",
+            address: "+012340123456,1,2"}},
   {command: "d02281030110018202818385084475726174696f6e8609911032042143651c2c8402010a",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_8",
             commandQualifier: 0x01,
-            text: "Duration"}},
+            confirmMessage: "Duration",
+            address: "+012340123456,1,2",
+            duration: {timeUnit: icc.STK_TIME_UNIT_SECOND,
+                       timeInterval: 0x0A}}},
   {command: "d028810301100082028183850c434f4e4649524d4154494f4e8609911032042143651c2c850443414c4c",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_9",
             commandQualifier: 0x00,
-            text: "CONFIRMATION"}},
+            confirmMessage: "CONFIRMATION",
+            callMessage: "CALL",
+            address: "+012340123456,1,2"}},
   {command: "d03081030110008202818385165365742075702063616c6c2049636f6e20332e312e318609911032042143651c2c9e020101",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_10",
             commandQualifier: 0x00,
-            text: "Set up call Icon 3.1.1"}},
+            confirmMessage: "Set up call Icon 3.1.1",
+            address: "+012340123456,1,2"}},
   {command: "d03081030110008202818385165365742075702063616c6c2049636f6e20332e322e318609911032042143651c2c9e020001",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_11",
             commandQualifier: 0x00,
-            text: "Set up call Icon 3.2.1"}},
+            confirmMessage: "Set up call Icon 3.2.1",
+            address: "+012340123456,1,2"}},
   {command: "d03081030110008202818385165365742075702063616c6c2049636f6e20332e332e318609911032042143651c2c9e020102",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_12",
             commandQualifier: 0x00,
-            text: "Set up call Icon 3.3.1"}},
+            confirmMessage: "Set up call Icon 3.3.1",
+            address: "+012340123456,1,2"}},
   {command: "d04c81030110008202818385165365742075702063616c6c2049636f6e20332e342e318609911032042143651c2c9e02000185165365742075702063616c6c2049636f6e20332e342e329e020001",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_13",
             commandQualifier: 0x00,
-            text: "Set up call Icon 3.4.1"}},
+            confirmMessage: "Set up call Icon 3.4.1",
+            callMessage: "Set up call Icon 3.4.2",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e00b4d004000600b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_14",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_15",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e01b4d004000601b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_16",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_17",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e02b4d004000602b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_18",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_19",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e04b4d004000604b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_20",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_21",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_22",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 3"}},
+            confirmMessage: "CONFIRMATION 3",
+            callMessage: "CALL 3",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e08b4d004000608b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_23",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_24",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_25",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 3"}},
+            confirmMessage: "CONFIRMATION 3",
+            callMessage: "CALL 3",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e10b4d004000610b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_26",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_27",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_28",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 3"}},
+            confirmMessage: "CONFIRMATION 3",
+            callMessage: "CALL 3",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e20b4d004000620b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_29",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_30",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_31",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 3"}},
+            confirmMessage: "CONFIRMATION 3",
+            callMessage: "CALL 3",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e40b4d004000640b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_32",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_33",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_34",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 3"}},
+            confirmMessage: "CONFIRMATION 3",
+            callMessage: "CALL 3",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e80b4d004000680b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_35",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032d004000e00b4d004000600b4",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_36",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20338609911032042143651c2c850643414c4c2033",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_37",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 3"}},
+            confirmMessage: "CONFIRMATION 3",
+            callMessage: "CALL 3",
+            address: "+012340123456,1,2"}},
   {command: "d038810301100082028183850e434f4e4649524d4154494f4e20318609911032042143651c2c850643414c4c2031d004000e00b4d0040006004b",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_38",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 1"}},
+            confirmMessage: "CONFIRMATION 1",
+            callMessage: "CALL 1",
+            address: "+012340123456,1,2"}},
   {command: "d02c810301100082028183850e434f4e4649524d4154494f4e20328609911032042143651c2c850643414c4c2032",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_39",
             commandQualifier: 0x00,
-            text: "CONFIRMATION 2"}},
+            confirmMessage: "CONFIRMATION 2",
+            callMessage: "CALL 2",
+            address: "+012340123456,1,2"}},
   {command: "d02d810301100082028183851980041704140420041004120421042204120423041904220415860791103204214365",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_40",
             commandQualifier: 0x00,
-            text: "ЗДРАВСТВУЙТЕ"}},
+            confirmMessage: "ЗДРАВСТВУЙТЕ",
+            address: "+012340123456"}},
   {command: "d04c810301100082028183851b800417041404200410041204210422041204230419042204150031860791103204214365851b800417041404200410041204210422041204230419042204150032",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_41",
             commandQualifier: 0x00,
-            text: "ЗДРАВСТВУЙТЕ1"}},
+            confirmMessage: "ЗДРАВСТВУЙТЕ1",
+            callMessage: "ЗДРАВСТВУЙТЕ2",
+            address: "+012340123456"}},
   {command: "d0198103011000820281838505804e0d5fd9860791103204214365",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_42",
             commandQualifier: 0x00,
-            text: "不忙"}},
+            confirmMessage: "不忙",
+            address: "+012340123456"}},
   {command: "d022810301100082028183850580786e5b9a860791103204214365850780625375358bdd",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_43",
             commandQualifier: 0x00,
-            text: "确定"}},
+            confirmMessage: "确定",
+            callMessage: "打电话",
+            address: "+012340123456"}},
   {command: "d01781030110008202818385038030eb860791103204214365",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_44",
             commandQualifier: 0x00,
-            text: "ル"}},
+            confirmMessage: "ル",
+            address: "+012340123456"}},
   {command: "d02081030110008202818385058030eb003186079110320421436585058030eb0032",
    func: testSetupCall,
    expect: {name: "setup_call_cmd_45",
             commandQualifier: 0x00,
-            text: "ル1"}},
+            confirmMessage: "ル1",
+            callMessage:"ル2",
+            address: "+012340123456"}},
+  {command: "d029810301100482028182050a446973636f6e6e6563748609811032042143651c2c05074d657373616765",
+   func: testSetupCall,
+   expect: {name: "setup_call_cmd_46",
+            commandQualifier: 0x04,
+            confirmMessage: "Disconnect",
+            address: "012340123456,1,2",
+            callMessage: "Message"}},
+  {command: "d02281030110008202818385084E6F7420627573798609911032042143651C2C8402010A",
+   func: testSetupCall,
+   expect: {name: "setup_call_cmd_47",
+            commandQualifier: 0x00,
+             confirmMessage: "Not busy",
+             address: "+012340123456,1,2",
+            duration: {timeUnit: icc.STK_TIME_UNIT_SECOND,
+                       timeInterval: 0x0A}}},
+
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_setup_event_list.js
+++ b/dom/icc/tests/marionette/test_stk_setup_event_list.js
@@ -47,16 +47,17 @@ let tests = [
             eventList: null}},
   {command: "d00c810301050082028182990107",
    func: testSetupEventList,
    expect: {name: "setup_event_list_cmd_6",
             commandQualifier: 0x00,
             eventList: [7]}}
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_setup_idle_mode_text.js
+++ b/dom/icc/tests/marionette/test_stk_setup_idle_mode_text.js
@@ -190,16 +190,17 @@ let tests = [
             text: "你好"}},
   {command: "d0148103012800820281828d09080038003030eb0030",
    func: testSetupIdleModeText,
    expect: {name: "setup_idle_mode_text_cmd_35",
             commandQualifier: 0x00,
             text: "80ル0"}},
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/icc/tests/marionette/test_stk_setup_menu.js
+++ b/dom/icc/tests/marionette/test_stk_setup_menu.js
@@ -16,16 +16,35 @@ function testSetupMenu(command, expect) 
   for (let index in command.options.items) {
     is(command.options.items[index].identifier, expect.items[index].identifier, expect.name);
     is(command.options.items[index].text, expect.items[index].text, expect.name);
   }
 
   runNextTest();
 }
 
+function isFirstMenuItemNull(command) {
+  return (command.options.items.length == 1 && !(command.options.items[0]));
+}
+
+function testInitialSetupMenu(command) {
+  log("STK CMD " + JSON.stringify(command));
+  is(command.typeOfCommand, icc.STK_CMD_SET_UP_MENU);
+  is(isFirstMenuItemNull(command), false);
+
+  runNextTest();
+}
+function testRemoveSetupMenu(command) {
+  log("STK CMD " + JSON.stringify(command));
+  is(command.typeOfCommand, icc.STK_CMD_SET_UP_MENU);
+  is(isFirstMenuItemNull(command), true);
+
+  runNextTest();
+}
+
 let tests = [
   {command: "d03b810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034",
    func: testSetupMenu,
    expect: {name: "setup_menu_cmd_1",
             commandQualifier: 0x00,
             title: "Toolkit Menu",
             items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}},
   {command: "d023810301250082028182850c546f6f6c6b6974204d656e758f04114f6e658f041254776f",
@@ -202,19 +221,25 @@ let tests = [
             commandQualifier: 0x00,
             title: "80ル0",
             items: [{identifier: 1, text: "80ル1"}, {identifier: 2, text: "80ル2"}, {identifier: 3, text: "80ル3"}, {identifier: 4, text: "80ル4"}]}},
   {command: "d02c8103012500820281828509800038003030eb00308f0a11800038003030eb00358f0a12800038003030eb0036",
    func: testSetupMenu,
    expect: {name: "setup_menu_cmd_31",
             commandQualifier: 0x00,
             title: "80ル0",
-            items: [{identifier: 17, text: "80ル5"}, {identifier: 18, text: "80ル6"}]}}
+            items: [{identifier: 17, text: "80ル5"}, {identifier: 18, text: "80ル6"}]}},
+  {command: "D00D81030125008202818285008F00",
+   func: testRemoveSetupMenu},
+  {command:"D03B810301250082028182850C546F6F6C6B6974204D656E758F07014974656D20318F07024974656D20328F07034974656D20338F07044974656D2034",
+   func: testInitialSetupMenu},
+
 ];
 
+// TODO - Bug 843455: Import scripts for marionette tests.
 let pendingEmulatorCmdCount = 0;
 function sendStkPduToEmulator(command, func, expect) {
   ++pendingEmulatorCmdCount;
 
   runEmulatorCmd(command, function (result) {
     --pendingEmulatorCmdCount;
     is(result[0], "OK");
   });
--- a/dom/interfaces/traversal/nsIDOMNodeIterator.idl
+++ b/dom/interfaces/traversal/nsIDOMNodeIterator.idl
@@ -1,20 +1,18 @@
 /* -*- Mode: IDL; tab-width: 2; 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 "domstubs.idl"
 
-interface nsIDOMNodeIterator;
 interface nsIDOMNodeFilter;
 
-
 [scriptable, uuid(a86bdac9-ff89-4c94-8160-4fe86733bab8)]
 // Introduced in DOM Level 2, updated to DOM Level 4:
 interface nsIDOMNodeIterator : nsISupports
 {
   readonly attribute nsIDOMNode       root;
   readonly attribute unsigned long    whatToShow;
   readonly attribute nsIDOMNodeFilter filter;
   nsIDOMNode         nextNode()
--- a/dom/interfaces/traversal/nsIDOMTreeWalker.idl
+++ b/dom/interfaces/traversal/nsIDOMTreeWalker.idl
@@ -1,20 +1,18 @@
 /* -*- Mode: IDL; tab-width: 2; 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 "domstubs.idl"
 
-interface nsIDOMTreeWalker;
 interface nsIDOMNodeFilter;
 
-
 [scriptable, uuid(c4ffa159-237c-4dde-b0ba-20b9f9270cf6)]
 // Introduced in DOM Level 2:
 interface nsIDOMTreeWalker : nsISupports {
   readonly attribute nsIDOMNode       root;
   readonly attribute unsigned long    whatToShow;
   readonly attribute nsIDOMNodeFilter filter;
            attribute nsIDOMNode       currentNode;
                                         // raises(DOMException) on setting
--- a/dom/locales/en-US/chrome/mathml/mathml.properties
+++ b/dom/locales/en-US/chrome/mathml/mathml.properties
@@ -1,12 +1,13 @@
 # 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/.
 
+InvalidChild=Invalid markup: <%1$S> is not allowed as a child of <%2$S>.
 ChildCountIncorrect=Invalid markup: Incorrect number of children for <%1$S/> tag.
 DuplicateMprescripts=Invalid markup: More than one <mprescripts/> in <mmultiscripts/>.
 # LOCALIZATION NOTE:  The first child of <mmultiscript/> is the base, that is the element to which scripts are attached.
 NoBase=Invalid markup: Expected exactly one Base element in <mmultiscripts/>.  Found none.
 SubSupMismatch=Invalid markup: Incomplete subscript/superscript pair in <mmultiscripts/>.
 
 # LOCALIZATION NOTE:  When localizing the single quotes ('), follow the conventions in css.properties for your target locale.
 AttributeParsingError=Error in parsing the value '%1$S' for '%2$S' attribute of <%3$S/>.  Attribute ignored.
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -1876,23 +1876,25 @@ let RIL = {
       options.rilMessageType = "callError";
       options.errorMsg = errorMsg;
       this.sendChromeMessage(options);
     }).bind(this);
 
     if (this._isEmergencyNumber(options.number)) {
       this.dialEmergencyNumber(options, onerror);
     } else {
-      // TODO: Both dial() and sendMMI() functions should be unified at some
-      // point in the future. In the mean time we handle temporary CLIR MMI
-      // commands through the dial() function. Please see bug 889737.
-      let mmi = this._parseMMI(options.number);
-      if (mmi && this._isTemporaryModeCLIR(mmi)) {
-        options.number = mmi.dialNumber;
-        options.clirMode = this._getCLIRMode(mmi);
+      if (!this._isCdma) {
+        // TODO: Both dial() and sendMMI() functions should be unified at some
+        // point in the future. In the mean time we handle temporary CLIR MMI
+        // commands through the dial() function. Please see bug 889737.
+        let mmi = this._parseMMI(options.number);
+        if (mmi && this._isTemporaryModeCLIR(mmi)) {
+          options.number = mmi.dialNumber;
+          options.clirMode = this._getCLIRMode(mmi);
+        }
       }
       this.dialNonEmergencyNumber(options, onerror);
     }
   },
 
   dialNonEmergencyNumber: function dialNonEmergencyNumber(options, onerror) {
     if (this.radioState == GECKO_RADIOSTATE_OFF) {
       // Notify error in establishing the call without radio.
@@ -9546,72 +9548,24 @@ let CdmaPDUHelper = {
     }
 
     return result;
   }
 };
 
 let StkCommandParamsFactory = {
   createParam: function createParam(cmdDetails, ctlvs) {
-    let param;
-    switch (cmdDetails.typeOfCommand) {
-      case STK_CMD_REFRESH:
-        param = this.processRefresh(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_POLL_INTERVAL:
-        param = this.processPollInterval(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_POLL_OFF:
-        param = this.processPollOff(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_PROVIDE_LOCAL_INFO:
-        param = this.processProvideLocalInfo(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_SET_UP_EVENT_LIST:
-        param = this.processSetUpEventList(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_SET_UP_MENU:
-      case STK_CMD_SELECT_ITEM:
-        param = this.processSelectItem(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_DISPLAY_TEXT:
-        param = this.processDisplayText(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_SET_UP_IDLE_MODE_TEXT:
-        param = this.processSetUpIdleModeText(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_GET_INKEY:
-        param = this.processGetInkey(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_GET_INPUT:
-        param = this.processGetInput(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_SEND_SS:
-      case STK_CMD_SEND_USSD:
-      case STK_CMD_SEND_SMS:
-      case STK_CMD_SEND_DTMF:
-        param = this.processEventNotify(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_SET_UP_CALL:
-        param = this.processSetupCall(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_LAUNCH_BROWSER:
-        param = this.processLaunchBrowser(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_PLAY_TONE:
-        param = this.processPlayTone(cmdDetails, ctlvs);
-        break;
-      case STK_CMD_TIMER_MANAGEMENT:
-        param = this.processTimerManagement(cmdDetails, ctlvs);
-        break;
-      default:
-        if (DEBUG) debug("unknown proactive command");
-        break;
-    }
-    return param;
+    let method = StkCommandParamsFactory[cmdDetails.typeOfCommand];
+    if (typeof method != "function") {
+      if (DEBUG) {
+        debug("Unknown proactive command " + cmdDetails.typeOfCommand.toString(16));
+      }
+      return null;
+    }
+    return method.call(this, cmdDetails, ctlvs);
   },
 
   /**
    * Construct a param for Refresh.
    *
    * @param cmdDetails
    *        The value object of CommandDetails TLV.
    * @param ctlvs
@@ -10010,59 +9964,85 @@ let StkCommandParamsFactory = {
         COMPREHENSIONTLV_TAG_TIMER_VALUE, ctlvs);
     if (ctlv) {
       timer.timerValue = ctlv.value.timerValue;
     }
 
     return timer;
   }
 };
+StkCommandParamsFactory[STK_CMD_REFRESH] = function STK_CMD_REFRESH(cmdDetails, ctlvs) {
+  return this.processRefresh(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_POLL_INTERVAL] = function STK_CMD_POLL_INTERVAL(cmdDetails, ctlvs) {
+  return this.processPollInterval(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_POLL_OFF] = function STK_CMD_POLL_OFF(cmdDetails, ctlvs) {
+  return this.processPollOff(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_PROVIDE_LOCAL_INFO] = function STK_CMD_PROVIDE_LOCAL_INFO(cmdDetails, ctlvs) {
+  return this.processProvideLocalInfo(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SET_UP_EVENT_LIST] = function STK_CMD_SET_UP_EVENT_LIST(cmdDetails, ctlvs) {
+  return this.processSetUpEventList(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SET_UP_MENU] = function STK_CMD_SET_UP_MENU(cmdDetails, ctlvs) {
+  return this.processSelectItem(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SELECT_ITEM] = function STK_CMD_SELECT_ITEM(cmdDetails, ctlvs) {
+  return this.processSelectItem(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_DISPLAY_TEXT] = function STK_CMD_DISPLAY_TEXT(cmdDetails, ctlvs) {
+  return this.processDisplayText(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SET_UP_IDLE_MODE_TEXT] = function STK_CMD_SET_UP_IDLE_MODE_TEXT(cmdDetails, ctlvs) {
+  return this.processSetUpIdleModeText(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_GET_INKEY] = function STK_CMD_GET_INKEY(cmdDetails, ctlvs) {
+  return this.processGetInkey(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_GET_INPUT] = function STK_CMD_GET_INPUT(cmdDetails, ctlvs) {
+  return this.processGetInput(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SEND_SS] = function STK_CMD_SEND_SS(cmdDetails, ctlvs) {
+  return this.processEventNotify(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SEND_USSD] = function STK_CMD_SEND_USSD(cmdDetails, ctlvs) {
+  return this.processEventNotify(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SEND_SMS] = function STK_CMD_SEND_SMS(cmdDetails, ctlvs) {
+  return this.processEventNotify(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SEND_DTMF] = function STK_CMD_SEND_DTMF(cmdDetails, ctlvs) {
+  return this.processEventNotify(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_SET_UP_CALL] = function STK_CMD_SET_UP_CALL(cmdDetails, ctlvs) {
+  return this.processSetupCall(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_LAUNCH_BROWSER] = function STK_CMD_LAUNCH_BROWSER(cmdDetails, ctlvs) {
+  return this.processLaunchBrowser(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_PLAY_TONE] = function STK_CMD_PLAY_TONE(cmdDetails, ctlvs) {
+  return this.processPlayTone(cmdDetails, ctlvs);
+};
+StkCommandParamsFactory[STK_CMD_TIMER_MANAGEMENT] = function STK_CMD_TIMER_MANAGEMENT(cmdDetails, ctlvs) {
+  return this.processTimerManagement(cmdDetails, ctlvs);
+};
 
 let StkProactiveCmdHelper = {
   retrieve: function retrieve(tag, length) {
-    switch (tag) {
-      case COMPREHENSIONTLV_TAG_COMMAND_DETAILS:
-        return this.retrieveCommandDetails(length);
-      case COMPREHENSIONTLV_TAG_DEVICE_ID:
-        return this.retrieveDeviceId(length);
-      case COMPREHENSIONTLV_TAG_ALPHA_ID:
-        return this.retrieveAlphaId(length);
-      case COMPREHENSIONTLV_TAG_DURATION:
-        return this.retrieveDuration(length);
-      case COMPREHENSIONTLV_TAG_ADDRESS:
-        return this.retrieveAddress(length);
-      case COMPREHENSIONTLV_TAG_TEXT_STRING:
-        return this.retrieveTextString(length);
-      case COMPREHENSIONTLV_TAG_TONE:
-        return this.retrieveTone(length);
-      case COMPREHENSIONTLV_TAG_ITEM:
-        return this.retrieveItem(length);
-      case COMPREHENSIONTLV_TAG_ITEM_ID:
-        return this.retrieveItemId(length);
-      case COMPREHENSIONTLV_TAG_RESPONSE_LENGTH:
-        return this.retrieveResponseLength(length);
-      case COMPREHENSIONTLV_TAG_FILE_LIST:
-        return this.retrieveFileList(length);
-      case COMPREHENSIONTLV_TAG_DEFAULT_TEXT:
-        return this.retrieveDefaultText(length);
-      case COMPREHENSIONTLV_TAG_EVENT_LIST:
-        return this.retrieveEventList(length);
-      case COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER:
-        return this.retrieveTimerId(length);
-      case COMPREHENSIONTLV_TAG_TIMER_VALUE:
-        return this.retrieveTimerValue(length);
-      case COMPREHENSIONTLV_TAG_IMMEDIATE_RESPONSE:
-        return this.retrieveImmediaResponse(length);
-      case COMPREHENSIONTLV_TAG_URL:
-        return this.retrieveUrl(length);
-      default:
-        if (DEBUG) debug("StkProactiveCmdHelper: unknown tag " + tag.toString(16));
-        Buf.seekIncoming(length * PDU_HEX_OCTET_SIZE);
-        return null;
-    }
+    let method = StkProactiveCmdHelper[tag];
+    if (typeof method != "function") {
+      if (DEBUG) {
+        debug("Unknown comprehension tag " + tag.toString(16));
+      }
+      Buf.seekIncoming(length * PDU_HEX_OCTET_SIZE);
+      return null;
+    }
+    return method.call(this, length);
   },
 
   /**
    * Command Details.
    *
    * | Byte | Description         | Length |
    * |  1   | Command details Tag |   1    |
    * |  2   | Length = 03         |   1    |
@@ -10372,16 +10352,67 @@ let StkProactiveCmdHelper = {
       let ctlv = ctlvs[i];
       if ((ctlv.tag & ~COMPREHENSIONTLV_FLAG_CR) == tag) {
         return ctlv;
       }
     }
     return null;
   },
 };
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_COMMAND_DETAILS] = function COMPREHENSIONTLV_TAG_COMMAND_DETAILS(length) {
+  return this.retrieveCommandDetails(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_DEVICE_ID] = function COMPREHENSIONTLV_TAG_DEVICE_ID(length) {
+  return this.retrieveDeviceId(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_ALPHA_ID] = function COMPREHENSIONTLV_TAG_ALPHA_ID(length) {
+  return this.retrieveAlphaId(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_DURATION] = function COMPREHENSIONTLV_TAG_DURATION(length) {
+  return this.retrieveDuration(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_ADDRESS] = function COMPREHENSIONTLV_TAG_ADDRESS(length) {
+  return this.retrieveAddress(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_TEXT_STRING] = function COMPREHENSIONTLV_TAG_TEXT_STRING(length) {
+  return this.retrieveTextString(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_TONE] = function COMPREHENSIONTLV_TAG_TONE(length) {
+  return this.retrieveTone(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_ITEM] = function COMPREHENSIONTLV_TAG_ITEM(length) {
+  return this.retrieveItem(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_ITEM_ID] = function COMPREHENSIONTLV_TAG_ITEM_ID(length) {
+  return this.retrieveItemId(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_RESPONSE_LENGTH] = function COMPREHENSIONTLV_TAG_RESPONSE_LENGTH(length) {
+  return this.retrieveResponseLength(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_FILE_LIST] = function COMPREHENSIONTLV_TAG_FILE_LIST(length) {
+  return this.retrieveFileList(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_DEFAULT_TEXT] = function COMPREHENSIONTLV_TAG_DEFAULT_TEXT(length) {
+  return this.retrieveDefaultText(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_EVENT_LIST] = function COMPREHENSIONTLV_TAG_EVENT_LIST(length) {
+  return this.retrieveEventList(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER] = function COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER(length) {
+  return this.retrieveTimerId(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_TIMER_VALUE] = function COMPREHENSIONTLV_TAG_TIMER_VALUE(length) {
+  return this.retrieveTimerValue(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_IMMEDIATE_RESPONSE] = function COMPREHENSIONTLV_TAG_IMMEDIATE_RESPONSE(length) {
+  return this.retrieveImmediaResponse(length);
+};
+StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_URL] = function COMPREHENSIONTLV_TAG_URL(length) {
+  return this.retrieveUrl(length);
+};
 
 let ComprehensionTlvHelper = {
   /**
    * Decode raw data to a Comprehension-TLV.
    */
   decode: function decode() {
     let hlen = 0; // For header(tag field + length field) length.
     let temp = GsmPDUHelper.readHexOctet();
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -27,44 +27,16 @@ function newUint8Worker() {
     index += offset;
   };
 
   worker.debug = do_print;
 
   return worker;
 }
 
-function newUint8SupportOutgoingIndexWorker() {
-  let worker = newWorker();
-  let index = 4;          // index for read
-  let buf = [0, 0, 0, 0]; // Preserved parcel size
-
-  worker.Buf.writeUint8 = function (value) {
-    if (worker.Buf.outgoingIndex >= buf.length) {
-      buf.push(value);
-    } else {
-      buf[worker.Buf.outgoingIndex] = value;
-    }
-
-    worker.Buf.outgoingIndex++;
-  };
-
-  worker.Buf.readUint8 = function () {
-    return buf[index++];
-  };
-
-  worker.Buf.seekIncoming = function (offset) {
-    index += offset;
-  };
-
-  worker.debug = do_print;
-
-  return worker;
-}
-
 /**
  * Verify GsmPDUHelper#readICCUCS2String()
  */
 add_test(function test_read_icc_ucs2_string() {
   let worker = newUint8Worker();
   let helper = worker.GsmPDUHelper;
 
   // 0x80
@@ -506,36 +478,16 @@ add_test(function test_is_gsm_8bit_alpha
   do_check_eq(ICCUtilsHelper.isGsm8BitAlphabet(langTable), true);
   do_check_eq(ICCUtilsHelper.isGsm8BitAlphabet(langShiftTable), true);
   do_check_eq(ICCUtilsHelper.isGsm8BitAlphabet("\uaaaa"), false);
 
   run_next_test();
 });
 
 /**
- * Verify RIL.sendStkTerminalProfile
- */
-add_test(function test_send_stk_terminal_profile() {
-  let worker = newUint8Worker();
-  let ril = worker.RIL;
-  let buf = worker.Buf;
-
-  ril.sendStkTerminalProfile(STK_SUPPORTED_TERMINAL_PROFILE);
-
-  buf.seekIncoming(8);
-  let profile = buf.readString();
-  for (let i = 0; i < STK_SUPPORTED_TERMINAL_PROFILE.length; i++) {
-    do_check_eq(parseInt(profile.substring(2 * i, 2 * i + 2), 16),
-                STK_SUPPORTED_TERMINAL_PROFILE[i]);
-  }
-
-  run_next_test();
-});
-
-/**
  * Verify RIL.iccGetCardLockState("fdn")
  */
 add_test(function test_icc_get_card_lock_state_fdn() {
   let worker = newUint8Worker();
   let ril = worker.RIL;
   let buf = worker.Buf;
 
   buf.sendParcel = function () {
@@ -565,652 +517,16 @@ add_test(function test_icc_get_card_lock
     }
 
     run_next_test();
   };
 
   ril.iccGetCardLockState({lockType: "fdn"});
 });
 
-/**
- * Verify ComprehensionTlvHelper.writeLocationInfoTlv
- */
-add_test(function test_write_location_info_tlv() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let tlvHelper = worker.ComprehensionTlvHelper;
-
-  // Test with 2-digit mnc, and gsmCellId obtained from UMTS network.
-  let loc = {
-    mcc: "466",
-    mnc: "92",
-    gsmLocationAreaCode : 10291,
-    gsmCellId: 19072823
-  };
-  tlvHelper.writeLocationInfoTlv(loc);
-
-  let tag = pduHelper.readHexOctet();
-  do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
-                   COMPREHENSIONTLV_FLAG_CR);
-
-  let length = pduHelper.readHexOctet();
-  do_check_eq(length, 9);
-
-  let mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
-  do_check_eq(mcc_mnc, "46692");
-
-  let lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
-  do_check_eq(lac, 10291);
-
-  let cellId = (pduHelper.readHexOctet() << 24) |
-               (pduHelper.readHexOctet() << 16) |
-               (pduHelper.readHexOctet() << 8)  |
-               (pduHelper.readHexOctet());
-  do_check_eq(cellId, 19072823);
-
-  // Test with 1-digit mnc, and gsmCellId obtained from GSM network.
-  loc = {
-    mcc: "466",
-    mnc: "02",
-    gsmLocationAreaCode : 10291,
-    gsmCellId: 65534
-  };
-  tlvHelper.writeLocationInfoTlv(loc);
-
-  tag = pduHelper.readHexOctet();
-  do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
-                   COMPREHENSIONTLV_FLAG_CR);
-
-  length = pduHelper.readHexOctet();
-  do_check_eq(length, 7);
-
-  mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
-  do_check_eq(mcc_mnc, "46602");
-
-  lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
-  do_check_eq(lac, 10291);
-
-  cellId = (pduHelper.readHexOctet() << 8) | (pduHelper.readHexOctet());
-  do_check_eq(cellId, 65534);
-
-  // Test with 3-digit mnc, and gsmCellId obtained from GSM network.
-  loc = {
-    mcc: "466",
-    mnc: "222",
-    gsmLocationAreaCode : 10291,
-    gsmCellId: 65534
-  };
-  tlvHelper.writeLocationInfoTlv(loc);
-
-  tag = pduHelper.readHexOctet();
-  do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
-                   COMPREHENSIONTLV_FLAG_CR);
-
-  length = pduHelper.readHexOctet();
-  do_check_eq(length, 7);
-
-  mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
-  do_check_eq(mcc_mnc, "466222");
-
-  lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
-  do_check_eq(lac, 10291);
-
-  cellId = (pduHelper.readHexOctet() << 8) | (pduHelper.readHexOctet());
-  do_check_eq(cellId, 65534);
-
-  run_next_test();
-});
-
-/**
- * Verify ComprehensionTlvHelper.writeErrorNumber
- */
-add_test(function test_write_disconnecting_cause() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let tlvHelper = worker.ComprehensionTlvHelper;
-
-  tlvHelper.writeCauseTlv(RIL_ERROR_TO_GECKO_ERROR[ERROR_GENERIC_FAILURE]);
-  let tag = pduHelper.readHexOctet();
-  do_check_eq(tag, COMPREHENSIONTLV_TAG_CAUSE | COMPREHENSIONTLV_FLAG_CR);
-  let len = pduHelper.readHexOctet();
-  do_check_eq(len, 2);  // We have one cause.
-  let standard = pduHelper.readHexOctet();
-  do_check_eq(standard, 0x60);
-  let cause = pduHelper.readHexOctet();
-  do_check_eq(cause, 0x80 | ERROR_GENERIC_FAILURE);
-
-  run_next_test();
-});
-
-/**
- * Verify ComprehensionTlvHelper.getSizeOfLengthOctets
- */
-add_test(function test_get_size_of_length_octets() {
-  let worker = newUint8Worker();
-  let tlvHelper = worker.ComprehensionTlvHelper;
-
-  let length = 0x70;
-  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 1);
-
-  length = 0x80;
-  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 2);
-
-  length = 0x180;
-  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 3);
-
-  length = 0x18000;
-  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 4);
-
-  run_next_test();
-});
-
-/**
- * Verify ComprehensionTlvHelper.writeLength
- */
-add_test(function test_write_length() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let tlvHelper = worker.ComprehensionTlvHelper;
-
-  let length = 0x70;
-  tlvHelper.writeLength(length);
-  do_check_eq(pduHelper.readHexOctet(), length);
-
-  length = 0x80;
-  tlvHelper.writeLength(length);
-  do_check_eq(pduHelper.readHexOctet(), 0x81);
-  do_check_eq(pduHelper.readHexOctet(), length);
-
-  length = 0x180;
-  tlvHelper.writeLength(length);
-  do_check_eq(pduHelper.readHexOctet(), 0x82);
-  do_check_eq(pduHelper.readHexOctet(), (length >> 8) & 0xff);
-  do_check_eq(pduHelper.readHexOctet(), length & 0xff);
-
-  length = 0x18000;
-  tlvHelper.writeLength(length);
-  do_check_eq(pduHelper.readHexOctet(), 0x83);
-  do_check_eq(pduHelper.readHexOctet(), (length >> 16) & 0xff);
-  do_check_eq(pduHelper.readHexOctet(), (length >> 8) & 0xff);
-  do_check_eq(pduHelper.readHexOctet(), length & 0xff);
-
-  run_next_test();
-});
-
-/**
- * Verify Proactive Command : Refresh
- */
-add_test(function test_stk_proactive_command_refresh() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  let refresh_1 = [
-    0xD0,
-    0x10,
-    0x81, 0x03, 0x01, 0x01, 0x01,
-    0x82, 0x02, 0x81, 0x82,
-    0x92, 0x05, 0x01, 0x3F, 0x00, 0x2F, 0xE2];
-
-  for (let i = 0; i < refresh_1.length; i++) {
-    pduHelper.writeHexOctet(refresh_1[i]);
-  }
-
-  let berTlv = berHelper.decode(refresh_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, 0x01);
-  do_check_eq(tlv.value.commandQualifier, 0x01);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_FILE_LIST, ctlvs);
-  do_check_eq(tlv.value.fileList, "3F002FE2");
-
-  run_next_test();
-});
-
-/**
- * Verify Proactive Command : Play Tone
- */
-add_test(function test_stk_proactive_command_play_tone() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  let tone_1 = [
-    0xD0,
-    0x1B,
-    0x81, 0x03, 0x01, 0x20, 0x00,
-    0x82, 0x02, 0x81, 0x03,
-    0x85, 0x09, 0x44, 0x69, 0x61, 0x6C, 0x20, 0x54, 0x6F, 0x6E, 0x65,
-    0x8E, 0x01, 0x01,
-    0x84, 0x02, 0x01, 0x05];
-
-  for (let i = 0; i < tone_1.length; i++) {
-    pduHelper.writeHexOctet(tone_1[i]);
-  }
-
-  let berTlv = berHelper.decode(tone_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, 0x20);
-  do_check_eq(tlv.value.commandQualifier, 0x00);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
-  do_check_eq(tlv.value.identifier, "Dial Tone");
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TONE, ctlvs);
-  do_check_eq(tlv.value.tone, STK_TONE_TYPE_DIAL_TONE);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs);
-  do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND);
-  do_check_eq(tlv.value.timeInterval, 5);
-
-  run_next_test();
-});
-
-/**
- * Verify Proactive Command : Poll Interval
- */
-add_test(function test_stk_proactive_command_poll_interval() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  let poll_1 = [
-    0xD0,
-    0x0D,
-    0x81, 0x03, 0x01, 0x03, 0x00,
-    0x82, 0x02, 0x81, 0x82,
-    0x84, 0x02, 0x01, 0x14];
-
-  for (let i = 0; i < poll_1.length; i++) {
-    pduHelper.writeHexOctet(poll_1[i]);
-  }
-
-  let berTlv = berHelper.decode(poll_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, 0x03);
-  do_check_eq(tlv.value.commandQualifier, 0x00);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs);
-  do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND);
-  do_check_eq(tlv.value.timeInterval, 0x14);
-
-  run_next_test();
-});
-
-/**
- * Verify Proactive Command: Display Text
- */
-add_test(function test_read_septets_to_string() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  let display_text_1 = [
-    0xd0,
-    0x28,
-    0x81, 0x03, 0x01, 0x21, 0x80,
-    0x82, 0x02, 0x81, 0x02,
-    0x0d, 0x1d, 0x00, 0xd3, 0x30, 0x9b, 0xfc, 0x06, 0xc9, 0x5c, 0x30, 0x1a,
-    0xa8, 0xe8, 0x02, 0x59, 0xc3, 0xec, 0x34, 0xb9, 0xac, 0x07, 0xc9, 0x60,
-    0x2f, 0x58, 0xed, 0x15, 0x9b, 0xb9, 0x40,
-  ];
-
-  for (let i = 0; i < display_text_1.length; i++) {
-    pduHelper.writeHexOctet(display_text_1[i]);
-  }
-
-  let berTlv = berHelper.decode(display_text_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TEXT_STRING, ctlvs);
-  do_check_eq(tlv.value.textString, "Saldo 2.04 E. Validez 20/05/13. ");
-
-  run_next_test();
-});
-
-add_test(function test_stk_proactive_command_event_list() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  let event_1 = [
-    0xD0,
-    0x0F,
-    0x81, 0x03, 0x01, 0x05, 0x00,
-    0x82, 0x02, 0x81, 0x82,
-    0x99, 0x04, 0x00, 0x01, 0x02, 0x03];
-
-  for (let i = 0; i < event_1.length; i++) {
-    pduHelper.writeHexOctet(event_1[i]);
-  }
-
-  let berTlv = berHelper.decode(event_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, 0x05);
-  do_check_eq(tlv.value.commandQualifier, 0x00);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_EVENT_LIST, ctlvs);
-  do_check_eq(Array.isArray(tlv.value.eventList), true);
-  for (let i = 0; i < tlv.value.eventList.length; i++) {
-    do_check_eq(tlv.value.eventList[i], i);
-  }
-
-  run_next_test();
-});
-
-/**
- * Verify Proactive Command : Get Input
- */
-add_test(function test_stk_proactive_command_get_input() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-  let stkCmdHelper = worker.StkCommandParamsFactory;
-
-  let get_input_1 = [
-    0xD0,
-    0x1E,
-    0x81, 0x03, 0x01, 0x23, 0x8F,
-    0x82, 0x02, 0x81, 0x82,
-    0x8D, 0x05, 0x04, 0x54, 0x65, 0x78, 0x74,
-    0x91, 0x02, 0x01, 0x10,
-    0x17, 0x08, 0x04, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74];
-
-  for (let i = 0; i < get_input_1.length; i++) {
-    pduHelper.writeHexOctet(get_input_1[i]);
-  }
-
-  let berTlv = berHelper.decode(get_input_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_GET_INPUT);
-
-  let input = stkCmdHelper.createParam(tlv.value, ctlvs);
-  do_check_eq(input.text, "Text");
-  do_check_eq(input.isAlphabet, true);
-  do_check_eq(input.isUCS2, true);
-  do_check_eq(input.hideInput, true);
-  do_check_eq(input.isPacked, true);
-  do_check_eq(input.isHelpAvailable, true);
-  do_check_eq(input.minLength, 0x01);
-  do_check_eq(input.maxLength, 0x10);
-  do_check_eq(input.defaultText, "Default");
-
-  let get_input_2 = [
-    0xD0,
-    0x11,
-    0x81, 0x03, 0x01, 0x23, 0x00,
-    0x82, 0x02, 0x81, 0x82,
-    0x8D, 0x00,
-    0x91, 0x02, 0x01, 0x10,
-    0x17, 0x00];
-
-  for (let i = 0; i < get_input_2.length; i++) {
-    pduHelper.writeHexOctet(get_input_2[i]);
-  }
-
-  berTlv = berHelper.decode(get_input_2.length);
-  ctlvs = berTlv.value;
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_GET_INPUT);
-
-  input = stkCmdHelper.createParam(tlv.value, ctlvs);
-  do_check_eq(input.text, null);
-  do_check_eq(input.minLength, 0x01);
-  do_check_eq(input.maxLength, 0x10);
-  do_check_eq(input.defaultText, null);
-
-  run_next_test();
-});
-
-add_test(function test_spn_display_condition() {
-  let worker = newWorker({
-    postRILMessage: function fakePostRILMessage(data) {
-      // Do nothing
-    },
-    postMessage: function fakePostMessage(message) {
-      // Do nothing
-    }
-  });
-  let RIL = worker.RIL;
-  let ICCUtilsHelper = worker.ICCUtilsHelper;
-
-  // Test updateDisplayCondition runs before any of SIM file is ready.
-  do_check_eq(ICCUtilsHelper.updateDisplayCondition(), true);
-  do_check_eq(RIL.iccInfo.isDisplayNetworkNameRequired, true);
-  do_check_eq(RIL.iccInfo.isDisplaySpnRequired, false);
-
-  // Test with value.
-  function testDisplayCondition(iccDisplayCondition,
-                                iccMcc, iccMnc, plmnMcc, plmnMnc,
-                                expectedIsDisplayNetworkNameRequired,
-                                expectedIsDisplaySPNRequired,
-                                callback) {
-    RIL.iccInfoPrivate.spnDisplayCondition = iccDisplayCondition;
-    RIL.iccInfo = {
-      mcc: iccMcc,
-      mnc: iccMnc
-    };
-    RIL.operator = {
-      mcc: plmnMcc,
-      mnc: plmnMnc
-    };
-
-    do_check_eq(ICCUtilsHelper.updateDisplayCondition(), true);
-    do_check_eq(RIL.iccInfo.isDisplayNetworkNameRequired, expectedIsDisplayNetworkNameRequired);
-    do_check_eq(RIL.iccInfo.isDisplaySpnRequired, expectedIsDisplaySPNRequired);
-    do_timeout(0, callback);
-  };
-
-  function testDisplayConditions(func, caseArray, oncomplete) {
-    (function do_call(index) {
-      let next = index < (caseArray.length - 1) ? do_call.bind(null, index + 1) : oncomplete;
-      caseArray[index].push(next);
-      func.apply(null, caseArray[index]);
-    })(0);
-  }
-
-  testDisplayConditions(testDisplayCondition, [
-    [1, 123, 456, 123, 456, true, true],
-    [0, 123, 456, 123, 456, false, true],
-    [2, 123, 456, 123, 457, false, false],
-    [0, 123, 456, 123, 457, false, true],
-  ], run_next_test);
-});
-
-/**
- * Verify Proactive Command : More Time
- */
-add_test(function test_stk_proactive_command_more_time() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  let more_time_1 = [
-    0xD0,
-    0x09,
-    0x81, 0x03, 0x01, 0x02, 0x00,
-    0x82, 0x02, 0x81, 0x82];
-
-  for(let i = 0 ; i < more_time_1.length; i++) {
-    pduHelper.writeHexOctet(more_time_1[i]);
-  }
-
-  let berTlv = berHelper.decode(more_time_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_MORE_TIME);
-  do_check_eq(tlv.value.commandQualifier, 0x00);
-
-  run_next_test();
-});
-
-/**
- * Verify Proactive Command : Set Up Call
- */
-add_test(function test_stk_proactive_command_set_up_call() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-  let cmdFactory = worker.StkCommandParamsFactory;
-
-  let set_up_call_1 = [
-    0xD0,
-    0x29,
-    0x81, 0x03, 0x01, 0x10, 0x04,
-    0x82, 0x02, 0x81, 0x82,
-    0x05, 0x0A, 0x44, 0x69, 0x73, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74,
-    0x86, 0x09, 0x81, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C,
-    0x05, 0x07, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65];
-
-  for (let i = 0 ; i < set_up_call_1.length; i++) {
-    pduHelper.writeHexOctet(set_up_call_1[i]);
-  }
-
-  let berTlv = berHelper.decode(set_up_call_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_SET_UP_CALL);
-
-  let setupCall = cmdFactory.createParam(tlv.value, ctlvs);
-  do_check_eq(setupCall.address, "012340123456,1,2");
-  do_check_eq(setupCall.confirmMessage, "Disconnect");
-  do_check_eq(setupCall.callMessage, "Message");
-
-  run_next_test();
-});
-
-add_test(function test_read_pnn() {
-  let worker = newUint8Worker();
-  let helper = worker.GsmPDUHelper;
-  let record = worker.ICCRecordHelper;
-  let buf    = worker.Buf;
-  let io     = worker.ICCIOHelper;
-  let ril    = worker.RIL;
-
-  io.loadLinearFixedEF = function fakeLoadLinearFixedEF(options) {
-    let records = [
-      // Record 1 - fullName: 'Long1', shortName: 'Short1'
-      [0x43, 0x06, 0x85, 0xCC, 0xB7, 0xFB, 0x1C, 0x03,
-       0x45, 0x07, 0x86, 0x53, 0xF4, 0x5B, 0x4E, 0x8F, 0x01,
-       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
-      // Record 2 - fullName: 'Long2'
-      [0x43, 0x06, 0x85, 0xCC, 0xB7, 0xFB, 0x2C, 0x03,
-       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
-      // Record 3 - Unused bytes
-      [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
-    ];
-
-    // Fake get response
-    options.totalRecords = records.length;
-    options.recordSize = records[0].length;
-
-    options.p1 = options.p1 || 1;
-
-    let record = records[options.p1 - 1];
-
-    // Write data size
-    buf.writeUint32(record.length * 2);
-
-    // Write record
-    for (let i = 0; i < record.length; i++) {
-      helper.writeHexOctet(record[i]);
-    }
-
-    // Write string delimiter
-    buf.writeStringDelimiter(record.length * 2);
-
-    if (options.callback) {
-      options.callback(options);
-    }
-  };
-
-  io.loadNextRecord = function fakeLoadNextRecord(options) {
-    options.p1++;
-    io.loadLinearFixedEF(options);
-  };
-
-  record.readPNN();
-
-  do_check_eq(ril.iccInfoPrivate.PNN.length, 2);
-  do_check_eq(ril.iccInfoPrivate.PNN[0].fullName, "Long1");
-  do_check_eq(ril.iccInfoPrivate.PNN[0].shortName, "Short1");
-  do_check_eq(ril.iccInfoPrivate.PNN[1].fullName, "Long2");
-  do_check_eq(ril.iccInfoPrivate.PNN[1].shortName, undefined);
-
-  run_next_test();
-});
-
-add_test(function read_network_name() {
-  let worker = newUint8Worker();
-  let helper = worker.GsmPDUHelper;
-  let buf = worker.Buf;
-
-  // Returning length of byte.
-  function writeNetworkName(isUCS2, requireCi, name) {
-    let codingOctet = 0x80;
-    let len;
-    if (requireCi) {
-      codingOctet |= 0x08;
-    }
-
-    if (isUCS2) {
-      codingOctet |= 0x10;
-      len = name.length * 2;
-    } else {
-      let spare = (8 - (name.length * 7) % 8) % 8;
-      codingOctet |= spare;
-      len = Math.ceil(name.length * 7 / 8);
-    }
-    helper.writeHexOctet(codingOctet);
-
-    if (isUCS2) {
-      helper.writeUCS2String(name);
-    } else {
-      helper.writeStringAsSeptets(name, 0, 0, 0);
-    }
-
-    return len + 1; // codingOctet.
-  }
-
-  function testNetworkName(isUCS2, requireCi, name) {
-    let len = writeNetworkName(isUCS2, requireCi, name);
-    do_check_eq(helper.readNetworkName(len), name);
-  }
-
-  testNetworkName( true,  true, "Test Network Name1");
-  testNetworkName( true, false, "Test Network Name2");
-  testNetworkName(false,  true, "Test Network Name3");
-  testNetworkName(false, false, "Test Network Name4");
-
-  run_next_test();
-});
-
 add_test(function test_get_network_name_from_icc() {
   let worker = newUint8Worker();
   let RIL = worker.RIL;
   let ICCUtilsHelper = worker.ICCUtilsHelper;
 
   function testGetNetworkNameFromICC(operatorData, expectedResult) {
     let result = ICCUtilsHelper.getNetworkNameFromICC(operatorData.mcc,
                                                       operatorData.mnc,
@@ -1294,126 +610,16 @@ add_test(function test_get_network_name_
   // Current PLMN is not HPLMN, and according to LAC, we should get
   // the thrid PNN record.
   testGetNetworkNameFromICC({mcc: 321, mnc: 654, lac: 0x0001},
                             {longName: "PNN3Long", shortName: "PNN3Short"});
 
   run_next_test();
 });
 
-/**
- * Verify Proactive Command : Timer Management
- */
-add_test(function test_stk_proactive_command_timer_management() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  // Timer Management - Start
-  let timer_management_1 = [
-    0xD0,
-    0x11,
-    0x81, 0x03, 0x01, 0x27, 0x00,
-    0x82, 0x02, 0x81, 0x82,
-    0xA4, 0x01, 0x01,
-    0xA5, 0x03, 0x10, 0x20, 0x30
-  ];
-
-  for(let i = 0 ; i < timer_management_1.length; i++) {
-    pduHelper.writeHexOctet(timer_management_1[i]);
-  }
-
-  let berTlv = berHelper.decode(timer_management_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_TIMER_MANAGEMENT);
-  do_check_eq(tlv.value.commandQualifier, STK_TIMER_START);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER, ctlvs);
-  do_check_eq(tlv.value.timerId, 0x01);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TIMER_VALUE, ctlvs);
-  do_check_eq(tlv.value.timerValue, (0x01 * 60 * 60) + (0x02 * 60) + 0x03);
-
-  // Timer Management - Deactivate
-  let timer_management_2 = [
-    0xD0,
-    0x0C,
-    0x81, 0x03, 0x01, 0x27, 0x01,
-    0x82, 0x02, 0x81, 0x82,
-    0xA4, 0x01, 0x01
-  ];
-
-  for(let i = 0 ; i < timer_management_2.length; i++) {
-    pduHelper.writeHexOctet(timer_management_2[i]);
-  }
-
-  berTlv = berHelper.decode(timer_management_2.length);
-  ctlvs = berTlv.value;
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_TIMER_MANAGEMENT);
-  do_check_eq(tlv.value.commandQualifier, STK_TIMER_DEACTIVATE);
-
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER, ctlvs);
-  do_check_eq(tlv.value.timerId, 0x01);
-
-  run_next_test();
-});
-
-/**
- * Verify Proactive Command : Provide Local Information
- */
-add_test(function test_stk_proactive_command_provide_local_information() {
-  let worker = newUint8Worker();
-  let pduHelper = worker.GsmPDUHelper;
-  let berHelper = worker.BerTlvHelper;
-  let stkHelper = worker.StkProactiveCmdHelper;
-
-  // Verify IMEI
-  let local_info_1 = [
-    0xD0,
-    0x09,
-    0x81, 0x03, 0x01, 0x26, 0x01,
-    0x82, 0x02, 0x81, 0x82];
-
-  for (let i = 0; i < local_info_1.length; i++) {
-    pduHelper.writeHexOctet(local_info_1[i]);
-  }
-
-  let berTlv = berHelper.decode(local_info_1.length);
-  let ctlvs = berTlv.value;
-  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_PROVIDE_LOCAL_INFO);
-  do_check_eq(tlv.value.commandQualifier, STK_LOCAL_INFO_IMEI);
-
-  // Verify Date and Time Zone
-  let local_info_2 = [
-    0xD0,
-    0x09,
-    0x81, 0x03, 0x01, 0x26, 0x03,
-    0x82, 0x02, 0x81, 0x82];
-
-  for (let i = 0; i < local_info_2.length; i++) {
-    pduHelper.writeHexOctet(local_info_2[i]);
-  }
-
-  berTlv = berHelper.decode(local_info_2.length);
-  ctlvs = berTlv.value;
-  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
-  do_check_eq(tlv.value.commandNumber, 0x01);
-  do_check_eq(tlv.value.typeOfCommand, STK_CMD_PROVIDE_LOCAL_INFO);
-  do_check_eq(tlv.value.commandQualifier, STK_LOCAL_INFO_DATE_TIME_ZONE);
-
-  run_next_test();
-});
-
 add_test(function test_path_id_for_spid_and_spn() {
   let worker = newWorker({
     postRILMessage: function fakePostRILMessage(data) {
       // Do nothing
     },
     postMessage: function fakePostMessage(message) {
       // Do nothing
     }});
@@ -1489,269 +695,16 @@ add_test(function test_parse_pbr_tlvs() 
   do_check_eq(pbr.anr1.fileId, 0x4f12);
   do_check_eq(pbr.ccp1.fileId, 0x4F3D);
   do_check_eq(pbr.ext1.fileId, 0x4F4A);
 
   run_next_test();
 });
 
 /**
- * Verify Event Download Command : Location Status
- */
-add_test(function test_stk_event_download_location_status() {
-  let worker = newUint8SupportOutgoingIndexWorker();
-  let buf = worker.Buf;
-  let pduHelper = worker.GsmPDUHelper;
-
-  buf.sendParcel = function () {
-    // Type
-    do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND);
-
-    // Token : we don't care
-    this.readUint32();
-
-    // Data Size, 42 = 2 * (2 + TLV_DEVICE_ID_SIZE(4) +
-    //                      TLV_EVENT_LIST_SIZE(3) +
-    //                      TLV_LOCATION_STATUS_SIZE(3) +
-    //                      TLV_LOCATION_INFO_GSM_SIZE(9))
-    do_check_eq(this.readUint32(), 42);
-
-    // BER tag
-    do_check_eq(pduHelper.readHexOctet(), BER_EVENT_DOWNLOAD_TAG);
-
-    // BER length, 19 = TLV_DEVICE_ID_SIZE(4) +
-    //                  TLV_EVENT_LIST_SIZE(3) +
-    //                  TLV_LOCATION_STATUS_SIZE(3) +
-    //                  TLV_LOCATION_INFO_GSM_SIZE(9)
-    do_check_eq(pduHelper.readHexOctet(), 19);
-
-    // Device Identifies, Type-Length-Value(Source ID-Destination ID)
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 2);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
-
-    // Event List, Type-Length-Value
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_EVENT_LIST |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 1);
-    do_check_eq(pduHelper.readHexOctet(), STK_EVENT_TYPE_LOCATION_STATUS);
-
-    // Location Status, Type-Length-Value
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_LOCATION_STATUS |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 1);
-    do_check_eq(pduHelper.readHexOctet(), STK_SERVICE_STATE_NORMAL);
-
-    // Location Info, Type-Length-Value
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_LOCATION_INFO |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 7);
-
-    do_check_eq(pduHelper.readHexOctet(), 0x21); // MCC + MNC
-    do_check_eq(pduHelper.readHexOctet(), 0x63);
-    do_check_eq(pduHelper.readHexOctet(), 0x54);
-    do_check_eq(pduHelper.readHexOctet(), 0); // LAC
-    do_check_eq(pduHelper.readHexOctet(), 0);
-    do_check_eq(pduHelper.readHexOctet(), 0); // Cell ID
-    do_check_eq(pduHelper.readHexOctet(), 0);
-
-    run_next_test();
-  };
-
-  let event = {
-    eventType: STK_EVENT_TYPE_LOCATION_STATUS,
-    locationStatus: STK_SERVICE_STATE_NORMAL,
-    locationInfo: {
-      mcc: "123",
-      mnc: "456",
-      gsmLocationAreaCode: 0,
-      gsmCellId: 0
-    }
-  };
-  worker.RIL.sendStkEventDownload({
-    event: event
-  });
-});
-
-/**
- * Verify STK terminal response
- */
-add_test(function test_stk_terminal_response() {
-  let worker = newUint8SupportOutgoingIndexWorker();
-  let buf = worker.Buf;
-  let pduHelper = worker.GsmPDUHelper;
-
-  buf.sendParcel = function () {
-    // Type
-    do_check_eq(this.readUint32(), REQUEST_STK_SEND_TERMINAL_RESPONSE);
-
-    // Token : we don't care
-    this.readUint32();
-
-    // Data Size, 44 = 2 * (TLV_COMMAND_DETAILS_SIZE(5) +
-    //                      TLV_DEVICE_ID_SIZE(4) +
-    //                      TLV_RESULT_SIZE(3) +
-    //                      TEXT LENGTH(10))
-    do_check_eq(this.readUint32(), 44);
-
-    // Command Details, Type-Length-Value
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_COMMAND_DETAILS |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 3);
-    do_check_eq(pduHelper.readHexOctet(), 0x01);
-    do_check_eq(pduHelper.readHexOctet(), STK_CMD_PROVIDE_LOCAL_INFO);
-    do_check_eq(pduHelper.readHexOctet(), STK_LOCAL_INFO_NNA);
-
-    // Device Identifies, Type-Length-Value(Source ID-Destination ID)
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID);
-    do_check_eq(pduHelper.readHexOctet(), 2);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
-
-    // Result
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_RESULT |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 1);
-    do_check_eq(pduHelper.readHexOctet(), STK_RESULT_OK);
-
-    // Text
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_TEXT_STRING |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 8);
-    do_check_eq(pduHelper.readHexOctet(), STK_TEXT_CODING_GSM_7BIT_PACKED);
-    do_check_eq(pduHelper.readSeptetsToString(7, 0, PDU_NL_IDENTIFIER_DEFAULT,
-                PDU_NL_IDENTIFIER_DEFAULT), "Mozilla");
-
-    run_next_test();
-  };
-
-  let response = {
-    command: {
-      commandNumber: 0x01,
-      typeOfCommand: STK_CMD_PROVIDE_LOCAL_INFO,
-      commandQualifier: STK_LOCAL_INFO_NNA,
-      options: {
-        isPacked: true
-      }
-    },
-    input: "Mozilla",
-    resultCode: STK_RESULT_OK
-  };
-  worker.RIL.sendStkTerminalResponse(response);
-});
-
-/**
- * Verify Event Download Command : Language Selection
- */
-add_test(function test_stk_event_download_language_selection() {
-  let worker = newUint8SupportOutgoingIndexWorker();
-  let buf = worker.Buf;
-  let pduHelper = worker.GsmPDUHelper;
-
-  buf.sendParcel = function () {
-    // Type
-    do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND);
-
-    // Token : we don't care
-    this.readUint32();
-
-    // Data Size, 26 = 2 * (2 + TLV_DEVICE_ID_SIZE(4) +
-    //                      TLV_EVENT_LIST_SIZE(3) +
-    //                      TLV_LANGUAGE(4))
-    do_check_eq(this.readUint32(), 26);
-
-    // BER tag
-    do_check_eq(pduHelper.readHexOctet(), BER_EVENT_DOWNLOAD_TAG);
-
-    // BER length, 19 = TLV_DEVICE_ID_SIZE(4) +
-    //                  TLV_EVENT_LIST_SIZE(3) +
-    //                  TLV_LANGUAGE(4)
-    do_check_eq(pduHelper.readHexOctet(), 11);
-
-    // Device Identifies, Type-Length-Value(Source ID-Destination ID)
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 2);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
-
-    // Event List, Type-Length-Value
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_EVENT_LIST |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 1);
-    do_check_eq(pduHelper.readHexOctet(), STK_EVENT_TYPE_LANGUAGE_SELECTION);
-
-    // Language, Type-Length-Value
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_LANGUAGE);
-    do_check_eq(pduHelper.readHexOctet(), 2);
-    do_check_eq(pduHelper.read8BitUnpackedToString(2), "zh");
-
-    run_next_test();
-  };
-
-  let event = {
-    eventType: STK_EVENT_TYPE_LANGUAGE_SELECTION,
-    language: "zh"
-  };
-  worker.RIL.sendStkEventDownload({
-    event: event
-  });
-});
-
-/**
- * Verify Event Download Command : Idle Screen Available
- */
-add_test(function test_stk_event_download_idle_screen_available() {
-  let worker = newUint8SupportOutgoingIndexWorker();
-  let buf = worker.Buf;
-  let pduHelper = worker.GsmPDUHelper;
-
-  buf.sendParcel = function () {
-    // Type
-    do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND);
-
-    // Token : we don't care
-    this.readUint32();
-
-    // Data Size, 18 = 2 * (2 + TLV_DEVICE_ID_SIZE(4) + TLV_EVENT_LIST_SIZE(3))
-    do_check_eq(this.readUint32(), 18);
-
-    // BER tag
-    do_check_eq(pduHelper.readHexOctet(), BER_EVENT_DOWNLOAD_TAG);
-
-    // BER length, 7 = TLV_DEVICE_ID_SIZE(4) + TLV_EVENT_LIST_SIZE(3)
-    do_check_eq(pduHelper.readHexOctet(), 7);
-
-    // Device Identities, Type-Length-Value(Source ID-Destination ID)
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 2);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_DISPLAY);
-    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
-
-    // Event List, Type-Length-Value
-    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_EVENT_LIST |
-                                          COMPREHENSIONTLV_FLAG_CR);
-    do_check_eq(pduHelper.readHexOctet(), 1);
-    do_check_eq(pduHelper.readHexOctet(), STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE);
-
-    run_next_test();
-  };
-
-  let event = {
-    eventType: STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE
-  };
-  worker.RIL.sendStkEventDownload({
-    event: event
-  });
-});
-
-/**
  * Verify ICCIOHelper.loadLinearFixedEF with recordSize.
  */
 add_test(function test_load_linear_fixed_ef() {
   let worker = newUint8Worker();
   let ril = worker.RIL;
   let io = worker.ICCIOHelper;
 
   io.getResponse = function fakeGetResponse(options) {
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/tests/test_ril_worker_stk.js
@@ -0,0 +1,927 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
+
+function run_test() {
+  run_next_test();
+}
+
+/**
+ * Helper function.
+ */
+function newUint8Worker() {
+  let worker = newWorker();
+  let index = 0; // index for read
+  let buf = [];
+
+  worker.Buf.writeUint8 = function (value) {
+    buf.push(value);
+  };
+
+  worker.Buf.readUint8 = function () {
+    return buf[index++];
+  };
+
+  worker.Buf.seekIncoming = function (offset) {
+    index += offset;
+  };
+
+  worker.debug = do_print;
+
+  return worker;
+}
+
+function newUint8SupportOutgoingIndexWorker() {
+  let worker = newWorker();
+  let index = 4;          // index for read
+  let buf = [0, 0, 0, 0]; // Preserved parcel size
+
+  worker.Buf.writeUint8 = function (value) {
+    if (worker.Buf.outgoingIndex >= buf.length) {
+      buf.push(value);
+    } else {
+      buf[worker.Buf.outgoingIndex] = value;
+    }
+
+    worker.Buf.outgoingIndex++;
+  };
+
+  worker.Buf.readUint8 = function () {
+    return buf[index++];
+  };
+
+  worker.Buf.seekIncoming = function (offset) {
+    index += offset;
+  };
+
+  worker.debug = do_print;
+
+  return worker;
+}
+
+// Test RIL requests related to STK.
+
+/**
+ * Verify RIL.sendStkTerminalProfile
+ */
+add_test(function test_send_stk_terminal_profile() {
+  let worker = newUint8Worker();
+  let ril = worker.RIL;
+  let buf = worker.Buf;
+
+  ril.sendStkTerminalProfile(STK_SUPPORTED_TERMINAL_PROFILE);
+
+  buf.seekIncoming(8);
+  let profile = buf.readString();
+  for (let i = 0; i < STK_SUPPORTED_TERMINAL_PROFILE.length; i++) {
+    do_check_eq(parseInt(profile.substring(2 * i, 2 * i + 2), 16),
+                STK_SUPPORTED_TERMINAL_PROFILE[i]);
+  }
+
+  run_next_test();
+});
+
+/**
+ * Verify STK terminal response
+ */
+add_test(function test_stk_terminal_response() {
+  let worker = newUint8SupportOutgoingIndexWorker();
+  let buf = worker.Buf;
+  let pduHelper = worker.GsmPDUHelper;
+
+  buf.sendParcel = function () {
+    // Type
+    do_check_eq(this.readUint32(), REQUEST_STK_SEND_TERMINAL_RESPONSE);
+
+    // Token : we don't care
+    this.readUint32();
+
+    // Data Size, 44 = 2 * (TLV_COMMAND_DETAILS_SIZE(5) +
+    //                      TLV_DEVICE_ID_SIZE(4) +
+    //                      TLV_RESULT_SIZE(3) +
+    //                      TEXT LENGTH(10))
+    do_check_eq(this.readUint32(), 44);
+
+    // Command Details, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_COMMAND_DETAILS |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 3);
+    do_check_eq(pduHelper.readHexOctet(), 0x01);
+    do_check_eq(pduHelper.readHexOctet(), STK_CMD_PROVIDE_LOCAL_INFO);
+    do_check_eq(pduHelper.readHexOctet(), STK_LOCAL_INFO_NNA);
+
+    // Device Identifies, Type-Length-Value(Source ID-Destination ID)
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID);
+    do_check_eq(pduHelper.readHexOctet(), 2);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
+
+    // Result
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_RESULT |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 1);
+    do_check_eq(pduHelper.readHexOctet(), STK_RESULT_OK);
+
+    // Text
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_TEXT_STRING |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 8);
+    do_check_eq(pduHelper.readHexOctet(), STK_TEXT_CODING_GSM_7BIT_PACKED);
+    do_check_eq(pduHelper.readSeptetsToString(7, 0, PDU_NL_IDENTIFIER_DEFAULT,
+                PDU_NL_IDENTIFIER_DEFAULT), "Mozilla");
+
+    run_next_test();
+  };
+
+  let response = {
+    command: {
+      commandNumber: 0x01,
+      typeOfCommand: STK_CMD_PROVIDE_LOCAL_INFO,
+      commandQualifier: STK_LOCAL_INFO_NNA,
+      options: {
+        isPacked: true
+      }
+    },
+    input: "Mozilla",
+    resultCode: STK_RESULT_OK
+  };
+  worker.RIL.sendStkTerminalResponse(response);
+});
+
+// Test ComprehensionTlvHelper
+
+/**
+ * Verify ComprehensionTlvHelper.writeLocationInfoTlv
+ */
+add_test(function test_write_location_info_tlv() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let tlvHelper = worker.ComprehensionTlvHelper;
+
+  // Test with 2-digit mnc, and gsmCellId obtained from UMTS network.
+  let loc = {
+    mcc: "466",
+    mnc: "92",
+    gsmLocationAreaCode : 10291,
+    gsmCellId: 19072823
+  };
+  tlvHelper.writeLocationInfoTlv(loc);
+
+  let tag = pduHelper.readHexOctet();
+  do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
+                   COMPREHENSIONTLV_FLAG_CR);
+
+  let length = pduHelper.readHexOctet();
+  do_check_eq(length, 9);
+
+  let mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
+  do_check_eq(mcc_mnc, "46692");
+
+  let lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
+  do_check_eq(lac, 10291);
+
+  let cellId = (pduHelper.readHexOctet() << 24) |
+               (pduHelper.readHexOctet() << 16) |
+               (pduHelper.readHexOctet() << 8)  |
+               (pduHelper.readHexOctet());
+  do_check_eq(cellId, 19072823);
+
+  // Test with 1-digit mnc, and gsmCellId obtained from GSM network.
+  loc = {
+    mcc: "466",
+    mnc: "02",
+    gsmLocationAreaCode : 10291,
+    gsmCellId: 65534
+  };
+  tlvHelper.writeLocationInfoTlv(loc);
+
+  tag = pduHelper.readHexOctet();
+  do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
+                   COMPREHENSIONTLV_FLAG_CR);
+
+  length = pduHelper.readHexOctet();
+  do_check_eq(length, 7);
+
+  mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
+  do_check_eq(mcc_mnc, "46602");
+
+  lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
+  do_check_eq(lac, 10291);
+
+  cellId = (pduHelper.readHexOctet() << 8) | (pduHelper.readHexOctet());
+  do_check_eq(cellId, 65534);
+
+  // Test with 3-digit mnc, and gsmCellId obtained from GSM network.
+  loc = {
+    mcc: "466",
+    mnc: "222",
+    gsmLocationAreaCode : 10291,
+    gsmCellId: 65534
+  };
+  tlvHelper.writeLocationInfoTlv(loc);
+
+  tag = pduHelper.readHexOctet();
+  do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
+                   COMPREHENSIONTLV_FLAG_CR);
+
+  length = pduHelper.readHexOctet();
+  do_check_eq(length, 7);
+
+  mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
+  do_check_eq(mcc_mnc, "466222");
+
+  lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
+  do_check_eq(lac, 10291);
+
+  cellId = (pduHelper.readHexOctet() << 8) | (pduHelper.readHexOctet());
+  do_check_eq(cellId, 65534);
+
+  run_next_test();
+});
+
+/**
+ * Verify ComprehensionTlvHelper.writeErrorNumber
+ */
+add_test(function test_write_disconnecting_cause() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let tlvHelper = worker.ComprehensionTlvHelper;
+
+  tlvHelper.writeCauseTlv(RIL_ERROR_TO_GECKO_ERROR[ERROR_GENERIC_FAILURE]);
+  let tag = pduHelper.readHexOctet();
+  do_check_eq(tag, COMPREHENSIONTLV_TAG_CAUSE | COMPREHENSIONTLV_FLAG_CR);
+  let len = pduHelper.readHexOctet();
+  do_check_eq(len, 2);  // We have one cause.
+  let standard = pduHelper.readHexOctet();
+  do_check_eq(standard, 0x60);
+  let cause = pduHelper.readHexOctet();
+  do_check_eq(cause, 0x80 | ERROR_GENERIC_FAILURE);
+
+  run_next_test();
+});
+
+/**
+ * Verify ComprehensionTlvHelper.getSizeOfLengthOctets
+ */
+add_test(function test_get_size_of_length_octets() {
+  let worker = newUint8Worker();
+  let tlvHelper = worker.ComprehensionTlvHelper;
+
+  let length = 0x70;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 1);
+
+  length = 0x80;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 2);
+
+  length = 0x180;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 3);
+
+  length = 0x18000;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 4);
+
+  run_next_test();
+});
+
+/**
+ * Verify ComprehensionTlvHelper.writeLength
+ */
+add_test(function test_write_length() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let tlvHelper = worker.ComprehensionTlvHelper;
+
+  let length = 0x70;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), length);
+
+  length = 0x80;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), 0x81);
+  do_check_eq(pduHelper.readHexOctet(), length);
+
+  length = 0x180;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), 0x82);
+  do_check_eq(pduHelper.readHexOctet(), (length >> 8) & 0xff);
+  do_check_eq(pduHelper.readHexOctet(), length & 0xff);
+
+  length = 0x18000;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), 0x83);
+  do_check_eq(pduHelper.readHexOctet(), (length >> 16) & 0xff);
+  do_check_eq(pduHelper.readHexOctet(), (length >> 8) & 0xff);
+  do_check_eq(pduHelper.readHexOctet(), length & 0xff);
+
+  run_next_test();
+});
+
+// Test Proactive commands.
+
+/**
+ * Verify Proactive Command : Refresh
+ */
+add_test(function test_stk_proactive_command_refresh() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  let refresh_1 = [
+    0xD0,
+    0x10,
+    0x81, 0x03, 0x01, 0x01, 0x01,
+    0x82, 0x02, 0x81, 0x82,
+    0x92, 0x05, 0x01, 0x3F, 0x00, 0x2F, 0xE2];
+
+  for (let i = 0; i < refresh_1.length; i++) {
+    pduHelper.writeHexOctet(refresh_1[i]);
+  }
+
+  let berTlv = berHelper.decode(refresh_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, 0x01);
+  do_check_eq(tlv.value.commandQualifier, 0x01);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_FILE_LIST, ctlvs);
+  do_check_eq(tlv.value.fileList, "3F002FE2");
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command : Play Tone
+ */
+add_test(function test_stk_proactive_command_play_tone() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  let tone_1 = [
+    0xD0,
+    0x1B,
+    0x81, 0x03, 0x01, 0x20, 0x00,
+    0x82, 0x02, 0x81, 0x03,
+    0x85, 0x09, 0x44, 0x69, 0x61, 0x6C, 0x20, 0x54, 0x6F, 0x6E, 0x65,
+    0x8E, 0x01, 0x01,
+    0x84, 0x02, 0x01, 0x05];
+
+  for (let i = 0; i < tone_1.length; i++) {
+    pduHelper.writeHexOctet(tone_1[i]);
+  }
+
+  let berTlv = berHelper.decode(tone_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, 0x20);
+  do_check_eq(tlv.value.commandQualifier, 0x00);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
+  do_check_eq(tlv.value.identifier, "Dial Tone");
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TONE, ctlvs);
+  do_check_eq(tlv.value.tone, STK_TONE_TYPE_DIAL_TONE);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs);
+  do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND);
+  do_check_eq(tlv.value.timeInterval, 5);
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command : Poll Interval
+ */
+add_test(function test_stk_proactive_command_poll_interval() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  let poll_1 = [
+    0xD0,
+    0x0D,
+    0x81, 0x03, 0x01, 0x03, 0x00,
+    0x82, 0x02, 0x81, 0x82,
+    0x84, 0x02, 0x01, 0x14];
+
+  for (let i = 0; i < poll_1.length; i++) {
+    pduHelper.writeHexOctet(poll_1[i]);
+  }
+
+  let berTlv = berHelper.decode(poll_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, 0x03);
+  do_check_eq(tlv.value.commandQualifier, 0x00);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs);
+  do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND);
+  do_check_eq(tlv.value.timeInterval, 0x14);
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command: Display Text
+ */
+add_test(function test_read_septets_to_string() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  let display_text_1 = [
+    0xd0,
+    0x28,
+    0x81, 0x03, 0x01, 0x21, 0x80,
+    0x82, 0x02, 0x81, 0x02,
+    0x0d, 0x1d, 0x00, 0xd3, 0x30, 0x9b, 0xfc, 0x06, 0xc9, 0x5c, 0x30, 0x1a,
+    0xa8, 0xe8, 0x02, 0x59, 0xc3, 0xec, 0x34, 0xb9, 0xac, 0x07, 0xc9, 0x60,
+    0x2f, 0x58, 0xed, 0x15, 0x9b, 0xb9, 0x40,
+  ];
+
+  for (let i = 0; i < display_text_1.length; i++) {
+    pduHelper.writeHexOctet(display_text_1[i]);
+  }
+
+  let berTlv = berHelper.decode(display_text_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TEXT_STRING, ctlvs);
+  do_check_eq(tlv.value.textString, "Saldo 2.04 E. Validez 20/05/13. ");
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command: Set Up Event List.
+ */
+add_test(function test_stk_proactive_command_event_list() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  let event_1 = [
+    0xD0,
+    0x0F,
+    0x81, 0x03, 0x01, 0x05, 0x00,
+    0x82, 0x02, 0x81, 0x82,
+    0x99, 0x04, 0x00, 0x01, 0x02, 0x03];
+
+  for (let i = 0; i < event_1.length; i++) {
+    pduHelper.writeHexOctet(event_1[i]);
+  }
+
+  let berTlv = berHelper.decode(event_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, 0x05);
+  do_check_eq(tlv.value.commandQualifier, 0x00);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_EVENT_LIST, ctlvs);
+  do_check_eq(Array.isArray(tlv.value.eventList), true);
+  for (let i = 0; i < tlv.value.eventList.length; i++) {
+    do_check_eq(tlv.value.eventList[i], i);
+  }
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command : Get Input
+ */
+add_test(function test_stk_proactive_command_get_input() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+  let stkCmdHelper = worker.StkCommandParamsFactory;
+
+  let get_input_1 = [
+    0xD0,
+    0x1E,
+    0x81, 0x03, 0x01, 0x23, 0x8F,
+    0x82, 0x02, 0x81, 0x82,
+    0x8D, 0x05, 0x04, 0x54, 0x65, 0x78, 0x74,
+    0x91, 0x02, 0x01, 0x10,
+    0x17, 0x08, 0x04, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74];
+
+  for (let i = 0; i < get_input_1.length; i++) {
+    pduHelper.writeHexOctet(get_input_1[i]);
+  }
+
+  let berTlv = berHelper.decode(get_input_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_GET_INPUT);
+
+  let input = stkCmdHelper.createParam(tlv.value, ctlvs);
+  do_check_eq(input.text, "Text");
+  do_check_eq(input.isAlphabet, true);
+  do_check_eq(input.isUCS2, true);
+  do_check_eq(input.hideInput, true);
+  do_check_eq(input.isPacked, true);
+  do_check_eq(input.isHelpAvailable, true);
+  do_check_eq(input.minLength, 0x01);
+  do_check_eq(input.maxLength, 0x10);
+  do_check_eq(input.defaultText, "Default");
+
+  let get_input_2 = [
+    0xD0,
+    0x11,
+    0x81, 0x03, 0x01, 0x23, 0x00,
+    0x82, 0x02, 0x81, 0x82,
+    0x8D, 0x00,
+    0x91, 0x02, 0x01, 0x10,
+    0x17, 0x00];
+
+  for (let i = 0; i < get_input_2.length; i++) {
+    pduHelper.writeHexOctet(get_input_2[i]);
+  }
+
+  berTlv = berHelper.decode(get_input_2.length);
+  ctlvs = berTlv.value;
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_GET_INPUT);
+
+  input = stkCmdHelper.createParam(tlv.value, ctlvs);
+  do_check_eq(input.text, null);
+  do_check_eq(input.minLength, 0x01);
+  do_check_eq(input.maxLength, 0x10);
+  do_check_eq(input.defaultText, null);
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command : More Time
+ */
+add_test(function test_stk_proactive_command_more_time() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  let more_time_1 = [
+    0xD0,
+    0x09,
+    0x81, 0x03, 0x01, 0x02, 0x00,
+    0x82, 0x02, 0x81, 0x82];
+
+  for(let i = 0 ; i < more_time_1.length; i++) {
+    pduHelper.writeHexOctet(more_time_1[i]);
+  }
+
+  let berTlv = berHelper.decode(more_time_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_MORE_TIME);
+  do_check_eq(tlv.value.commandQualifier, 0x00);
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command : Set Up Call
+ */
+add_test(function test_stk_proactive_command_set_up_call() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+  let cmdFactory = worker.StkCommandParamsFactory;
+
+  let set_up_call_1 = [
+    0xD0,
+    0x29,
+    0x81, 0x03, 0x01, 0x10, 0x04,
+    0x82, 0x02, 0x81, 0x82,
+    0x05, 0x0A, 0x44, 0x69, 0x73, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74,
+    0x86, 0x09, 0x81, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C,
+    0x05, 0x07, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65];
+
+  for (let i = 0 ; i < set_up_call_1.length; i++) {
+    pduHelper.writeHexOctet(set_up_call_1[i]);
+  }
+
+  let berTlv = berHelper.decode(set_up_call_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_SET_UP_CALL);
+
+  let setupCall = cmdFactory.createParam(tlv.value, ctlvs);
+  do_check_eq(setupCall.address, "012340123456,1,2");
+  do_check_eq(setupCall.confirmMessage, "Disconnect");
+  do_check_eq(setupCall.callMessage, "Message");
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command : Timer Management
+ */
+add_test(function test_stk_proactive_command_timer_management() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  // Timer Management - Start
+  let timer_management_1 = [
+    0xD0,
+    0x11,
+    0x81, 0x03, 0x01, 0x27, 0x00,
+    0x82, 0x02, 0x81, 0x82,
+    0xA4, 0x01, 0x01,
+    0xA5, 0x03, 0x10, 0x20, 0x30
+  ];
+
+  for(let i = 0 ; i < timer_management_1.length; i++) {
+    pduHelper.writeHexOctet(timer_management_1[i]);
+  }
+
+  let berTlv = berHelper.decode(timer_management_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_TIMER_MANAGEMENT);
+  do_check_eq(tlv.value.commandQualifier, STK_TIMER_START);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER, ctlvs);
+  do_check_eq(tlv.value.timerId, 0x01);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TIMER_VALUE, ctlvs);
+  do_check_eq(tlv.value.timerValue, (0x01 * 60 * 60) + (0x02 * 60) + 0x03);
+
+  // Timer Management - Deactivate
+  let timer_management_2 = [
+    0xD0,
+    0x0C,
+    0x81, 0x03, 0x01, 0x27, 0x01,
+    0x82, 0x02, 0x81, 0x82,
+    0xA4, 0x01, 0x01
+  ];
+
+  for(let i = 0 ; i < timer_management_2.length; i++) {
+    pduHelper.writeHexOctet(timer_management_2[i]);
+  }
+
+  berTlv = berHelper.decode(timer_management_2.length);
+  ctlvs = berTlv.value;
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_TIMER_MANAGEMENT);
+  do_check_eq(tlv.value.commandQualifier, STK_TIMER_DEACTIVATE);
+
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER, ctlvs);
+  do_check_eq(tlv.value.timerId, 0x01);
+
+  run_next_test();
+});
+
+/**
+ * Verify Proactive Command : Provide Local Information
+ */
+add_test(function test_stk_proactive_command_provide_local_information() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let berHelper = worker.BerTlvHelper;
+  let stkHelper = worker.StkProactiveCmdHelper;
+
+  // Verify IMEI
+  let local_info_1 = [
+    0xD0,
+    0x09,
+    0x81, 0x03, 0x01, 0x26, 0x01,
+    0x82, 0x02, 0x81, 0x82];
+
+  for (let i = 0; i < local_info_1.length; i++) {
+    pduHelper.writeHexOctet(local_info_1[i]);
+  }
+
+  let berTlv = berHelper.decode(local_info_1.length);
+  let ctlvs = berTlv.value;
+  let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_PROVIDE_LOCAL_INFO);
+  do_check_eq(tlv.value.commandQualifier, STK_LOCAL_INFO_IMEI);
+
+  // Verify Date and Time Zone
+  let local_info_2 = [
+    0xD0,
+    0x09,
+    0x81, 0x03, 0x01, 0x26, 0x03,
+    0x82, 0x02, 0x81, 0x82];
+
+  for (let i = 0; i < local_info_2.length; i++) {
+    pduHelper.writeHexOctet(local_info_2[i]);
+  }
+
+  berTlv = berHelper.decode(local_info_2.length);
+  ctlvs = berTlv.value;
+  tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
+  do_check_eq(tlv.value.commandNumber, 0x01);
+  do_check_eq(tlv.value.typeOfCommand, STK_CMD_PROVIDE_LOCAL_INFO);
+  do_check_eq(tlv.value.commandQualifier, STK_LOCAL_INFO_DATE_TIME_ZONE);
+
+  run_next_test();
+});
+
+/**
+ * Verify Event Download Command : Location Status
+ */
+add_test(function test_stk_event_download_location_status() {
+  let worker = newUint8SupportOutgoingIndexWorker();
+  let buf = worker.Buf;
+  let pduHelper = worker.GsmPDUHelper;
+
+  buf.sendParcel = function () {
+    // Type
+    do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND);
+
+    // Token : we don't care
+    this.readUint32();
+
+    // Data Size, 42 = 2 * (2 + TLV_DEVICE_ID_SIZE(4) +
+    //                      TLV_EVENT_LIST_SIZE(3) +
+    //                      TLV_LOCATION_STATUS_SIZE(3) +
+    //                      TLV_LOCATION_INFO_GSM_SIZE(9))
+    do_check_eq(this.readUint32(), 42);
+
+    // BER tag
+    do_check_eq(pduHelper.readHexOctet(), BER_EVENT_DOWNLOAD_TAG);
+
+    // BER length, 19 = TLV_DEVICE_ID_SIZE(4) +
+    //                  TLV_EVENT_LIST_SIZE(3) +
+    //                  TLV_LOCATION_STATUS_SIZE(3) +
+    //                  TLV_LOCATION_INFO_GSM_SIZE(9)
+    do_check_eq(pduHelper.readHexOctet(), 19);
+
+    // Device Identifies, Type-Length-Value(Source ID-Destination ID)
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 2);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
+
+    // Event List, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_EVENT_LIST |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 1);
+    do_check_eq(pduHelper.readHexOctet(), STK_EVENT_TYPE_LOCATION_STATUS);
+
+    // Location Status, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_LOCATION_STATUS |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 1);
+    do_check_eq(pduHelper.readHexOctet(), STK_SERVICE_STATE_NORMAL);
+
+    // Location Info, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_LOCATION_INFO |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 7);
+
+    do_check_eq(pduHelper.readHexOctet(), 0x21); // MCC + MNC
+    do_check_eq(pduHelper.readHexOctet(), 0x63);
+    do_check_eq(pduHelper.readHexOctet(), 0x54);
+    do_check_eq(pduHelper.readHexOctet(), 0); // LAC
+    do_check_eq(pduHelper.readHexOctet(), 0);
+    do_check_eq(pduHelper.readHexOctet(), 0); // Cell ID
+    do_check_eq(pduHelper.readHexOctet(), 0);
+
+    run_next_test();
+  };
+
+  let event = {
+    eventType: STK_EVENT_TYPE_LOCATION_STATUS,
+    locationStatus: STK_SERVICE_STATE_NORMAL,
+    locationInfo: {
+      mcc: "123",
+      mnc: "456",
+      gsmLocationAreaCode: 0,
+      gsmCellId: 0
+    }
+  };
+  worker.RIL.sendStkEventDownload({
+    event: event
+  });
+});
+
+// Test Event Download commands.
+
+/**
+ * Verify Event Download Command : Language Selection
+ */
+add_test(function test_stk_event_download_language_selection() {
+  let worker = newUint8SupportOutgoingIndexWorker();
+  let buf = worker.Buf;
+  let pduHelper = worker.GsmPDUHelper;
+
+  buf.sendParcel = function () {
+    // Type
+    do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND);
+
+    // Token : we don't care
+    this.readUint32();
+
+    // Data Size, 26 = 2 * (2 + TLV_DEVICE_ID_SIZE(4) +
+    //                      TLV_EVENT_LIST_SIZE(3) +
+    //                      TLV_LANGUAGE(4))
+    do_check_eq(this.readUint32(), 26);
+
+    // BER tag
+    do_check_eq(pduHelper.readHexOctet(), BER_EVENT_DOWNLOAD_TAG);
+
+    // BER length, 19 = TLV_DEVICE_ID_SIZE(4) +
+    //                  TLV_EVENT_LIST_SIZE(3) +
+    //                  TLV_LANGUAGE(4)
+    do_check_eq(pduHelper.readHexOctet(), 11);
+
+    // Device Identifies, Type-Length-Value(Source ID-Destination ID)
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 2);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_ME);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
+
+    // Event List, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_EVENT_LIST |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 1);
+    do_check_eq(pduHelper.readHexOctet(), STK_EVENT_TYPE_LANGUAGE_SELECTION);
+
+    // Language, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_LANGUAGE);
+    do_check_eq(pduHelper.readHexOctet(), 2);
+    do_check_eq(pduHelper.read8BitUnpackedToString(2), "zh");
+
+    run_next_test();
+  };
+
+  let event = {
+    eventType: STK_EVENT_TYPE_LANGUAGE_SELECTION,
+    language: "zh"
+  };
+  worker.RIL.sendStkEventDownload({
+    event: event
+  });
+});
+
+/**
+ * Verify Event Download Command : Idle Screen Available
+ */
+add_test(function test_stk_event_download_idle_screen_available() {
+  let worker = newUint8SupportOutgoingIndexWorker();
+  let buf = worker.Buf;
+  let pduHelper = worker.GsmPDUHelper;
+
+  buf.sendParcel = function () {
+    // Type
+    do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND);
+
+    // Token : we don't care
+    this.readUint32();
+
+    // Data Size, 18 = 2 * (2 + TLV_DEVICE_ID_SIZE(4) + TLV_EVENT_LIST_SIZE(3))
+    do_check_eq(this.readUint32(), 18);
+
+    // BER tag
+    do_check_eq(pduHelper.readHexOctet(), BER_EVENT_DOWNLOAD_TAG);
+
+    // BER length, 7 = TLV_DEVICE_ID_SIZE(4) + TLV_EVENT_LIST_SIZE(3)
+    do_check_eq(pduHelper.readHexOctet(), 7);
+
+    // Device Identities, Type-Length-Value(Source ID-Destination ID)
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 2);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_DISPLAY);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
+
+    // Event List, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_EVENT_LIST |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 1);
+    do_check_eq(pduHelper.readHexOctet(), STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE);
+
+    run_next_test();
+  };
+
+  let event = {
+    eventType: STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE
+  };
+  worker.RIL.sendStkEventDownload({
+    event: event
+  });
+});
--- a/dom/system/gonk/tests/xpcshell.ini
+++ b/dom/system/gonk/tests/xpcshell.ini
@@ -11,8 +11,9 @@ tail =
 [test_ril_worker_cellbroadcast.js]
 [test_ril_worker_ruim.js]
 [test_ril_worker_cw.js]
 [test_ril_worker_clir.js]
 [test_ril_worker_clip.js]
 [test_ril_worker_ssn.js]
 [test_ril_worker_voiceprivacy.js]
 [test_ril_worker_ecm.js]
+[test_ril_worker_stk.js]
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -72,27 +72,53 @@ function testLocationData(aMessageObject
   is(aMessageObject.filename, gArgs[0].filename, "filename matches");
   is(aMessageObject.lineNumber, gArgs[0].lineNumber, "lineNumber matches");
   is(aMessageObject.functionName, gArgs[0].functionName, "functionName matches");
   is(aMessageObject.arguments.length, gArgs[0].arguments.length, "arguments.length matches");
   gArgs[0].arguments.forEach(function (a, i) {
     is(aMessageObject.arguments[i], a, "correct arg " + i);
   });
 
+  startNativeCallbackTest();
+}
+
+function startNativeCallbackTest() {
+  // Reset the observer function to cope with the fabricated test data.
+  ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
+    try {
+      testNativeCallback(aSubject.wrappedJSObject);
+    } catch (ex) {
+      // XXX Bug 906593 - Exceptions in this function currently aren't
+      // reported, because of some XPConnect weirdness, so report them manually
+      ok(false, "Exception thrown in CO_observe: " + ex);
+    }
+  };
+
+  let button = gWindow.document.getElementById("test-nativeCallback");
+  ok(button, "found #test-nativeCallback button");
+  EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
+}
+
+function testNativeCallback(aMessageObject) {
+  is(aMessageObject.level, "log", "expected level received");
+  is(aMessageObject.filename, "", "filename matches");
+  is(aMessageObject.lineNumber, 0, "lineNumber matches");
+  is(aMessageObject.functionName, "", "functionName matches");
+
   startGroupTest();
 }
 
 function startGroupTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
       testConsoleGroup(aSubject.wrappedJSObject);
     } catch (ex) {
-      // XXX Exceptions in this function currently aren't reported, because of
-      // some XPConnect weirdness, so report them manually
+      // XXX Bug 906593 - Exceptions in this function currently aren't
+      // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   let button = gWindow.document.getElementById("test-groups");
   ok(button, "found #test-groups button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
 
@@ -144,18 +170,18 @@ function startTraceTest() {
 }
 
 function startLocationTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
       testLocationData(aSubject.wrappedJSObject);
     } catch (ex) {
-      // XXX Exceptions in this function currently aren't reported, because of
-      // some XPConnect weirdness, so report them manually
+      // XXX Bug 906593 - Exceptions in this function currently aren't
+      // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   gLevel = "log";
   gArgs = [
     {filename: TEST_URI, lineNumber: 19, functionName: "foobar646025", arguments: ["omg", "o", "d"]}
   ];
 
@@ -254,18 +280,18 @@ function consoleAPISanityTest() {
 }
 
 function startTimeTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
       testConsoleTime(aSubject.wrappedJSObject);
     } catch (ex) {
-      // XXX Exceptions in this function currently aren't reported, because of
-      // some XPConnect weirdness, so report them manually
+      // XXX Bug 906593 - Exceptions in this function currently aren't
+      // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   gLevel = "time";
   gArgs = [
     {filename: TEST_URI, lineNumber: 23, functionName: "startTimer",
      arguments: ["foo"],
      timer: { name: "foo" },
@@ -297,18 +323,18 @@ function testConsoleTime(aMessageObject)
 }
 
 function startTimeEndTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
       testConsoleTimeEnd(aSubject.wrappedJSObject);
     } catch (ex) {
-      // XXX Exceptions in this function currently aren't reported, because of
-      // some XPConnect weirdness, so report them manually
+      // XXX Bug 906593 - Exceptions in this function currently aren't
+      // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   gLevel = "timeEnd";
   gArgs = [
     {filename: TEST_URI, lineNumber: 27, functionName: "stopTimer",
      arguments: ["foo"],
      timer: { name: "foo" },
@@ -344,18 +370,18 @@ function testConsoleTimeEnd(aMessageObje
 }
 
 function startEmptyTimerTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
       testEmptyTimer(aSubject.wrappedJSObject);
     } catch (ex) {
-      // XXX Exceptions in this function currently aren't reported, because of
-      // some XPConnect weirdness, so report them manually
+      // XXX Bug 906593 - Exceptions in this function currently aren't
+      // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
 
   let button = gWindow.document.getElementById("test-namelessTimer");
   ok(button, "found #test-namelessTimer button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
@@ -387,18 +413,18 @@ var ConsoleObserver = {
   destroy: function CO_destroy() {
     Services.obs.removeObserver(this, "console-api-log-event");
   },
 
   observe: function CO_observe(aSubject, aTopic, aData) {
     try {
       testConsoleData(aSubject.wrappedJSObject);
     } catch (ex) {
-      // XXX Exceptions in this function currently aren't reported, because of
-      // some XPConnect weirdness, so report them manually
+      // XXX Bug 906593 - Exceptions in this function currently aren't
+      // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   }
 };
 
 function getWindowId(aWindow)
 {
   return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
--- a/dom/tests/browser/test-console-api.html
+++ b/dom/tests/browser/test-console-api.html
@@ -41,21 +41,28 @@
         console.error(str);
       }
 
       function testGroups() {
         console.groupCollapsed("a", "group");
         console.group("b", "group");
         console.groupEnd("b", "group");
       }
+
+      function nativeCallback() {
+        SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, function() {
+          new Promise(r => r.resolve(42)).then(console.log);
+        });
+      }
     </script>
   </head>
   <body>
     <h1>Console API Test Page</h1>
     <button onclick="test();">Log stuff</button>
     <button id="test-trace" onclick="foobar585956a('omg');">Test trace</button>
     <button id="test-location" onclick="foobar646025('omg');">Test location</button>
+    <button id="test-nativeCallback" onclick="nativeCallback();">Test nativeCallback</button>
     <button id="test-groups" onclick="testGroups();">Test groups</button>
     <button id="test-time" onclick="startTimer('foo');">Test time</button>
     <button id="test-timeEnd" onclick="stopTimer('foo');">Test timeEnd</button>
     <button id="test-namelessTimer" onclick="namelessTimer();">Test namelessTimer</button>
   </body>
 </html>
--- a/dom/webidl/HTMLTrackElement.webidl
+++ b/dom/webidl/HTMLTrackElement.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-track-element
  */
 
 [Pref="media.webvtt.enabled"]
 interface HTMLTrackElement : HTMLElement {
   [SetterThrows, Pure]
-  attribute TextTrackKind kind;
+  attribute DOMString kind;
   [SetterThrows, Pure]
   attribute DOMString src;
   [SetterThrows, Pure]
   attribute DOMString srclang;
   [SetterThrows, Pure]
   attribute DOMString label;
   [SetterThrows, Pure]
   attribute boolean default;
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -179,16 +179,17 @@ AppendToString(nsACString& s, const Matr
 
 nsACString&
 AppendToString(nsACString& s, const Filter filter,
                const char* pfx, const char* sfx)
 {
   s += pfx;
 
   switch (filter) {
+    case FILTER_GOOD: s += "FILTER_GOOD"; break;
     case FILTER_LINEAR: s += "FILTER_LINEAR"; break;
     case FILTER_POINT: s += "FILTER_POINT"; break;
   }
   return s += sfx;
 }
 
 nsACString&
 AppendToString(nsACString& s, TextureFlags flags,
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -179,29 +179,33 @@ protected:
 private:
   const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
   Derived& AsDerived() { return *static_cast<Derived*>(this); }
 
   bool IsPlaceholder(Tile aTile) const { return aTile == AsDerived().GetPlaceholderTile(); }
 };
 
 class BasicTiledLayerBuffer;
+class SurfaceDescriptorTiles;
+class ISurfaceAllocator;
 
 // Shadow layers may implement this interface in order to be notified when a
 // tiled layer buffer is updated.
 class TiledLayerComposer
 {
 public:
   /**
    * Update the current retained layer with the updated layer data.
-   * The BasicTiledLayerBuffer is expected to be in the ReadLock state
-   * prior to this being called. aTiledBuffer is copy constructed and
-   * is retained until it has been uploaded/copyed and unlocked.
+   * It is expected that the tiles described by aTiledDescriptor are all in the
+   * ReadLock state, so that the locks can be adopted when recreating a
+   * BasicTiledLayerBuffer locally. This lock will be retained until the buffer
+   * has completed uploading.
    */
-  virtual void PaintedTiledLayerBuffer(const BasicTiledLayerBuffer* aTiledBuffer) = 0;
+  virtual void PaintedTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+                                       const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
 
   /**
    * If some part of the buffer is being rendered at a lower precision, this
    * returns that region. If it is not, an empty region will be returned.
    */
   virtual const nsIntRegion& GetValidLowPrecisionRegion() const = 0;
 };
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -9,21 +9,28 @@
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/CanvasClient.h"
 #include "mozilla/layers/ContentClient.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layers/SharedPlanarYCbCrImage.h"
 #include "GLContext.h"
 #include "BasicLayers.h" // for PaintContext
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
-#include "gfxReusableSurfaceWrapper.h"
 #include "gfxPlatform.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "gfx2DGlue.h"
 
+#ifdef MOZ_ANDROID_OMTC
+#  include "gfxReusableImageSurfaceWrapper.h"
+#  include "gfxImageSurface.h"
+#else
+#  include "gfxReusableSharedImageSurfaceWrapper.h"
+#  include "gfxSharedImageSurface.h"
+#endif
+
 #include <stdint.h>
 
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace layers {
 
 TextureClient::TextureClient(TextureFlags aFlags)
@@ -315,38 +322,43 @@ DeprecatedTextureClientShmem::EnsureAllo
     mContentType = aContentType;
     mSize = aSize;
 
     if (!mForwarder->AllocSurfaceDescriptor(gfxIntSize(mSize.width, mSize.height),
                                             mContentType, &mDescriptor)) {
       NS_WARNING("creating SurfaceDescriptor failed!");
     }
     if (mContentType == gfxASurface::CONTENT_COLOR_ALPHA) {
-      nsRefPtr<gfxContext> context = new gfxContext(GetSurface());
+      gfxASurface* surface = GetSurface();
+      if (!surface) {
+        return false;
+      }
+      nsRefPtr<gfxContext> context = new gfxContext(surface);
       context->SetColor(gfxRGBA(0, 0, 0, 0));
       context->SetOperator(gfxContext::OPERATOR_SOURCE);
       context->Paint();
     }
   }
   return true;
 }
 
 void
 DeprecatedTextureClientShmem::SetDescriptor(const SurfaceDescriptor& aDescriptor)
 {
-  if (IsSurfaceDescriptorValid(aDescriptor)) {
-    ReleaseResources();
-    mDescriptor = aDescriptor;
-  } else {
+  if (aDescriptor.type() == SurfaceDescriptor::Tnull_t) {
     EnsureAllocated(mSize, mContentType);
+    return;
   }
 
-  MOZ_ASSERT(!mSurface);
+  ReleaseResources();
+  mDescriptor = aDescriptor;
 
-  NS_ASSERTION(mDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc ||
+  MOZ_ASSERT(!mSurface);
+  NS_ASSERTION(mDescriptor.type() == SurfaceDescriptor::T__None ||
+               mDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc ||
                mDescriptor.type() == SurfaceDescriptor::TShmem ||
                mDescriptor.type() == SurfaceDescriptor::TMemoryImage ||
                mDescriptor.type() == SurfaceDescriptor::TRGBImage,
                "Invalid surface descriptor");
 }
 
 gfxASurface*
 DeprecatedTextureClientShmem::GetSurface()
@@ -370,16 +382,20 @@ DeprecatedTextureClientShmem::GetSurface
 gfx::DrawTarget*
 DeprecatedTextureClientShmem::LockDrawTarget()
 {
   if (mDrawTarget) {
     return mDrawTarget;
   }
 
   gfxASurface* surface = GetSurface();
+  if (!surface) {
+    return nullptr;
+  }
+
   mDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surface, mSize);
 
   return mDrawTarget;
 }
 
 void
 DeprecatedTextureClientShmem::Unlock()
 {
@@ -389,17 +405,21 @@ DeprecatedTextureClientShmem::Unlock()
 
   ShadowLayerForwarder::CloseDescriptor(mDescriptor);
 }
 
 gfxImageSurface*
 DeprecatedTextureClientShmem::LockImageSurface()
 {
   if (!mSurfaceAsImage) {
-    mSurfaceAsImage = GetSurface()->GetAsImageSurface();
+    gfxASurface* surface = GetSurface();
+    if (!surface) {
+      return nullptr;
+    }
+    mSurfaceAsImage = surface->GetAsImageSurface();
   }
 
   return mSurfaceAsImage.get();
 }
 
 DeprecatedTextureClientTile::DeprecatedTextureClientTile(const DeprecatedTextureClientTile& aOther)
   : DeprecatedTextureClient(aOther.mForwarder, aOther.mTextureInfo)
   , mSurface(aOther.mSurface)
@@ -412,23 +432,23 @@ void
 DeprecatedTextureClientShmemYCbCr::ReleaseResources()
 {
   GetForwarder()->DestroySharedSurface(&mDescriptor);
 }
 
 void
 DeprecatedTextureClientShmemYCbCr::SetDescriptor(const SurfaceDescriptor& aDescriptor)
 {
-  MOZ_ASSERT(aDescriptor.type() == SurfaceDescriptor::TYCbCrImage);
+  MOZ_ASSERT(aDescriptor.type() == SurfaceDescriptor::TYCbCrImage ||
+             aDescriptor.type() == SurfaceDescriptor::T__None);
 
   if (IsSurfaceDescriptorValid(mDescriptor)) {
     GetForwarder()->DestroySharedSurface(&mDescriptor);
   }
   mDescriptor = aDescriptor;
-  MOZ_ASSERT(IsSurfaceDescriptorValid(mDescriptor));
 }
 
 void
 DeprecatedTextureClientShmemYCbCr::SetDescriptorFromReply(const SurfaceDescriptor& aDescriptor)
 {
   MOZ_ASSERT(aDescriptor.type() == SurfaceDescriptor::TYCbCrImage);
   DeprecatedSharedPlanarYCbCrImage* shYCbCr = DeprecatedSharedPlanarYCbCrImage::FromSurfaceDescriptor(aDescriptor);
   if (shYCbCr) {
@@ -444,32 +464,44 @@ DeprecatedTextureClientShmemYCbCr::Ensur
                                          gfxASurface::gfxContentType aType)
 {
   NS_RUNTIMEABORT("not enough arguments to do this (need both Y and CbCr sizes)");
   return false;
 }
 
 
 DeprecatedTextureClientTile::DeprecatedTextureClientTile(CompositableForwarder* aForwarder,
-                                     const TextureInfo& aTextureInfo)
+                                                         const TextureInfo& aTextureInfo,
+                                                         gfxReusableSurfaceWrapper* aSurface)
   : DeprecatedTextureClient(aForwarder, aTextureInfo)
-  , mSurface(nullptr)
+  , mSurface(aSurface)
 {
   mTextureInfo.mDeprecatedTextureHostFlags = TEXTURE_HOST_TILED;
 }
 
 bool
 DeprecatedTextureClientTile::EnsureAllocated(gfx::IntSize aSize, gfxASurface::gfxContentType aType)
 {
   if (!mSurface ||
       mSurface->Format() != gfxPlatform::GetPlatform()->OptimalFormatForContent(aType)) {
+#ifdef MOZ_ANDROID_OMTC
+    // If we're using OMTC, we can save some cycles by not using shared
+    // memory. Using shared memory here is a small, but significant
+    // performance regression.
     gfxImageSurface* tmpTile = new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
                                                    gfxPlatform::GetPlatform()->OptimalFormatForContent(aType),
                                                    aType != gfxASurface::CONTENT_COLOR);
-    mSurface = new gfxReusableSurfaceWrapper(tmpTile);
+    mSurface = new gfxReusableImageSurfaceWrapper(tmpTile);
+#else
+    nsRefPtr<gfxSharedImageSurface> sharedImage =
+      gfxSharedImageSurface::CreateUnsafe(mForwarder,
+                                          gfxIntSize(aSize.width, aSize.height),
+                                          gfxPlatform::GetPlatform()->OptimalFormatForContent(aType));
+    mSurface = new gfxReusableSharedImageSurfaceWrapper(mForwarder, sharedImage);
+#endif
     mContentType = aType;
   }
   return true;
 }
 
 gfxImageSurface*
 DeprecatedTextureClientTile::LockImageSurface()
 {
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -469,17 +469,18 @@ public:
   virtual gfxASurface::gfxContentType GetContentType() MOZ_OVERRIDE { return gfxASurface::CONTENT_COLOR_ALPHA; }
 };
 
 class DeprecatedTextureClientTile : public DeprecatedTextureClient
 {
 public:
   DeprecatedTextureClientTile(const DeprecatedTextureClientTile& aOther);
   DeprecatedTextureClientTile(CompositableForwarder* aForwarder,
-                    const TextureInfo& aTextureInfo);
+                              const TextureInfo& aTextureInfo,
+                              gfxReusableSurfaceWrapper* aSurface = nullptr);
   ~DeprecatedTextureClientTile();
 
   virtual bool EnsureAllocated(gfx::IntSize aSize,
                                gfxASurface::gfxContentType aType) MOZ_OVERRIDE;
 
   virtual gfxImageSurface* LockImageSurface() MOZ_OVERRIDE;
 
   gfxReusableSurfaceWrapper* GetReusableSurfaceWrapper()
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -2,16 +2,17 @@
  * 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/layers/TiledContentClient.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/MathAlgorithms.h"
 #include "ClientTiledThebesLayer.h"
+#include "gfxReusableSharedImageSurfaceWrapper.h"
 
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
 #include "cairo.h"
 #include <sstream>
 using mozilla::layers::Layer;
 static void DrawDebugOverlay(gfxASurface* imgSurf, int x, int y)
 {
   gfxContext c(imgSurf);
@@ -68,27 +69,26 @@ TiledContentClient::TiledContentClient(C
   MOZ_COUNT_CTOR(TiledContentClient);
 
   mLowPrecisionTiledBuffer.SetResolution(gfxPlatform::GetLowPrecisionResolution());
 }
 
 void
 TiledContentClient::LockCopyAndWrite(TiledBufferType aType)
 {
-  // Create a heap copy owned and released by the compositor. This is needed
-  // since we're sending this over an async message and content needs to be
-  // be able to modify the tiled buffer in the next transaction.
-  // TODO: Remove me once Bug 747811 lands.
   BasicTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
     ? &mLowPrecisionTiledBuffer
     : &mTiledBuffer;
 
-  BasicTiledLayerBuffer* heapCopy = new BasicTiledLayerBuffer(buffer->DeepCopy());
+  // Take an extra ReadLock on behalf of the TiledContentHost. This extra
+  // reference will be adopted when the descriptor is opened by
+  // BasicTiledLayerTile::OpenDescriptor.
   buffer->ReadLock();
-  mForwarder->PaintedTiledLayerBuffer(this, heapCopy);
+
+  mForwarder->PaintedTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
   buffer->ClearPaintedRegion();
 }
 
 BasicTiledLayerBuffer::BasicTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer,
                                              ClientLayerManager* aManager)
   : mThebesLayer(aThebesLayer)
   , mManager(aManager)
   , mLastPaintOpaque(false)
@@ -108,16 +108,93 @@ BasicTiledLayerBuffer::GetContentType() 
   if (mThebesLayer->CanUseOpaqueSurface()) {
     return gfxASurface::CONTENT_COLOR;
   } else {
     return gfxASurface::CONTENT_COLOR_ALPHA;
   }
 }
 
 
+TileDescriptor
+BasicTiledLayerTile::GetTileDescriptor()
+{
+  gfxReusableSurfaceWrapper* surface = GetSurface();
+  switch (surface->GetType()) {
+  case gfxReusableSurfaceWrapper::TYPE_IMAGE :
+    return BasicTileDescriptor(uintptr_t(surface));
+
+  case gfxReusableSurfaceWrapper::TYPE_SHARED_IMAGE :
+    return BasicShmTileDescriptor(static_cast<gfxReusableSharedImageSurfaceWrapper*>(surface)->GetShmem());
+
+  default :
+    NS_NOTREACHED("Unhandled gfxReusableSurfaceWrapper type");
+    return PlaceholderTileDescriptor();
+  }
+}
+
+
+/* static */ BasicTiledLayerTile
+BasicTiledLayerTile::OpenDescriptor(ISurfaceAllocator *aAllocator, const TileDescriptor& aDesc)
+{
+  switch (aDesc.type()) {
+  case TileDescriptor::TBasicShmTileDescriptor : {
+    nsRefPtr<gfxReusableSurfaceWrapper> surface =
+      gfxReusableSharedImageSurfaceWrapper::Open(
+        aAllocator, aDesc.get_BasicShmTileDescriptor().reusableSurface());
+    return BasicTiledLayerTile(
+      new DeprecatedTextureClientTile(nullptr, TextureInfo(BUFFER_TILED), surface));
+  }
+
+  case TileDescriptor::TBasicTileDescriptor : {
+    nsRefPtr<gfxReusableSurfaceWrapper> surface =
+      reinterpret_cast<gfxReusableSurfaceWrapper*>(
+        aDesc.get_BasicTileDescriptor().reusableSurface());
+    surface->ReadUnlock();
+    return BasicTiledLayerTile(
+      new DeprecatedTextureClientTile(nullptr, TextureInfo(BUFFER_TILED), surface));
+  }
+
+  default :
+    NS_NOTREACHED("Unknown tile descriptor type!");
+    return nullptr;
+  }
+}
+
+SurfaceDescriptorTiles
+BasicTiledLayerBuffer::GetSurfaceDescriptorTiles()
+{
+  InfallibleTArray<TileDescriptor> tiles;
+
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    TileDescriptor tileDesc;
+    if (mRetainedTiles.SafeElementAt(i, GetPlaceholderTile()) == GetPlaceholderTile()) {
+      tileDesc = PlaceholderTileDescriptor();
+    } else {
+      tileDesc = mRetainedTiles[i].GetTileDescriptor();
+    }
+    tiles.AppendElement(tileDesc);
+  }
+  return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion,
+                                tiles, mRetainedWidth, mRetainedHeight,
+                                mResolution);
+}
+
+/* static */ BasicTiledLayerBuffer
+BasicTiledLayerBuffer::OpenDescriptor(ISurfaceAllocator *aAllocator,
+                                      const SurfaceDescriptorTiles& aDescriptor)
+{
+  return BasicTiledLayerBuffer(aAllocator,
+                               aDescriptor.validRegion(),
+                               aDescriptor.paintedRegion(),
+                               aDescriptor.tiles(),
+                               aDescriptor.retainedWidth(),
+                               aDescriptor.retainedHeight(),
+                               aDescriptor.resolution());
+}
+
 void
 BasicTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                    const nsIntRegion& aPaintRegion,
                                    LayerManager::DrawThebesLayerCallback aCallback,
                                    void* aCallbackData)
 {
   mCallback = aCallback;
   mCallbackData = aCallbackData;
@@ -458,25 +535,10 @@ BasicTiledLayerBuffer::ProgressiveUpdate
     aInvalidRegion.Sub(aInvalidRegion, regionToPaint);
   } while (repeat);
 
   // Return false if nothing has been drawn, or give what has been drawn
   // to the shadow layer to upload.
   return isBufferChanged;
 }
 
-BasicTiledLayerBuffer
-BasicTiledLayerBuffer::DeepCopy() const
-{
-  BasicTiledLayerBuffer result = *this;
-
-  for (size_t i = 0; i < result.mRetainedTiles.Length(); i++) {
-    if (result.mRetainedTiles[i].IsPlaceholderTile()) continue;
-
-    result.mRetainedTiles[i].mDeprecatedTextureClient =
-      new DeprecatedTextureClientTile(*result.mRetainedTiles[i].mDeprecatedTextureClient);
-  }
-
-  return result;
-}
-
 }
 }
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -4,20 +4,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_TILEDCONTENTCLIENT_H
 #define MOZILLA_GFX_TILEDCONTENTCLIENT_H
 
 #include "mozilla/layers/ContentClient.h"
 #include "TiledLayerBuffer.h"
 #include "gfxPlatform.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 
+class BasicTileDescriptor;
+
 /**
  * Represent a single tile in tiled buffer. The buffer keeps tiles,
  * each tile keeps a reference to a texture client. The texture client
  * is backed by a gfxReusableSurfaceWrapper that implements a
  * copy-on-write mechanism while locked. The tile should be
  * locked before being sent to the compositor and unlocked
  * as soon as it is uploaded to prevent a copy.
  * Ideal place to store per tile debug information.
@@ -28,16 +31,20 @@ struct BasicTiledLayerTile {
   TimeStamp        mLastUpdate;
 #endif
 
   // Placeholder
   BasicTiledLayerTile()
     : mDeprecatedTextureClient(nullptr)
   {}
 
+  BasicTiledLayerTile(DeprecatedTextureClientTile* aTextureClient)
+    : mDeprecatedTextureClient(aTextureClient)
+  {}
+
   BasicTiledLayerTile(const BasicTiledLayerTile& o) {
     mDeprecatedTextureClient = o.mDeprecatedTextureClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
     mLastUpdate = o.mLastUpdate;
 #endif
   }
   BasicTiledLayerTile& operator=(const BasicTiledLayerTile& o) {
     if (this == &o) return *this;
@@ -58,16 +65,19 @@ struct BasicTiledLayerTile {
 
   void ReadUnlock() {
     GetSurface()->ReadUnlock();
   }
   void ReadLock() {
     GetSurface()->ReadLock();
   }
 
+  TileDescriptor GetTileDescriptor();
+  static BasicTiledLayerTile OpenDescriptor(ISurfaceAllocator *aAllocator, const TileDescriptor& aDesc);
+
   gfxReusableSurfaceWrapper* GetSurface() {
     return mDeprecatedTextureClient->GetReusableSurfaceWrapper();
   }
 };
 
 /**
  * This struct stores all the data necessary to perform a paint so that it
  * doesn't need to be recalculated on every repeated transaction.
@@ -102,16 +112,39 @@ public:
   BasicTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer,
                         ClientLayerManager* aManager);
   BasicTiledLayerBuffer()
     : mThebesLayer(nullptr)
     , mManager(nullptr)
     , mLastPaintOpaque(false)
   {}
 
+  BasicTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+                        const nsIntRegion& aValidRegion,
+                        const nsIntRegion& aPaintedRegion,
+                        const InfallibleTArray<TileDescriptor>& aTiles,
+                        int aRetainedWidth,
+                        int aRetainedHeight,
+                        float aResolution)
+  {
+    mValidRegion = aValidRegion;
+    mPaintedRegion = aPaintedRegion;
+    mRetainedWidth = aRetainedWidth;
+    mRetainedHeight = aRetainedHeight;
+    mResolution = aResolution;
+
+    for(size_t i = 0; i < aTiles.Length(); i++) {
+      if (aTiles[i].type() == TileDescriptor::TPlaceholderTileDescriptor) {
+        mRetainedTiles.AppendElement(GetPlaceholderTile());
+      } else {
+        mRetainedTiles.AppendElement(BasicTiledLayerTile::OpenDescriptor(aAllocator, aTiles[i]));
+      }
+    }
+  }
+
   void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    LayerManager::DrawThebesLayerCallback aCallback,
                    void* aCallbackData);
 
   void ReadUnlock() {
     for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
       if (mRetainedTiles[i].IsPlaceholderTile()) continue;
@@ -137,24 +170,20 @@ public:
    */
   bool ProgressiveUpdate(nsIntRegion& aValidRegion,
                          nsIntRegion& aInvalidRegion,
                          const nsIntRegion& aOldValidRegion,
                          BasicTiledLayerPaintData* aPaintData,
                          LayerManager::DrawThebesLayerCallback aCallback,
                          void* aCallbackData);
 
-  /**
-   * Copy this buffer duplicating the texture hosts under the tiles
-   * XXX This should go. It is a hack because we need to keep the
-   * surface wrappers alive whilst they are locked by the compositor.
-   * Once we properly implement the texture host/client architecture
-   * for tiled layers we shouldn't need this.
-   */
-  BasicTiledLayerBuffer DeepCopy() const;
+  SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
+
+  static BasicTiledLayerBuffer OpenDescriptor(ISurfaceAllocator* aAllocator,
+                                              const SurfaceDescriptorTiles& aDescriptor);
 
 protected:
   BasicTiledLayerTile ValidateTile(BasicTiledLayerTile aTile,
                                    const nsIntPoint& aTileRect,
                                    const nsIntRegion& dirtyRect);
 
   // If this returns true, we perform the paint operation into a single large
   // buffer and copy it out to the tiles instead of calling PaintThebes() on
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -49,49 +49,39 @@ TiledLayerBufferComposite::ValidateTile(
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   if (PR_IntervalNow() - start > 1) {
     printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start);
   }
 #endif
   return aTile;
 }
 
-TiledContentHost::~TiledContentHost()
-{
-  mMainMemoryTiledBuffer.ReadUnlock();
-  mLowPrecisionMainMemoryTiledBuffer.ReadUnlock();
-}
-
 void
 TiledContentHost::Attach(Layer* aLayer, Compositor* aCompositor)
 {
   CompositableHost::Attach(aLayer, aCompositor);
   static_cast<ThebesLayerComposite*>(aLayer)->EnsureTiled();
 }
 
 void
-TiledContentHost::PaintedTiledLayerBuffer(const BasicTiledLayerBuffer* mTiledBuffer)
+TiledContentHost::PaintedTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+                                          const SurfaceDescriptorTiles& aTiledDescriptor)
 {
-  if (mTiledBuffer->IsLowPrecision()) {
-    mLowPrecisionMainMemoryTiledBuffer.ReadUnlock();
-    mLowPrecisionMainMemoryTiledBuffer = *mTiledBuffer;
+  if (aTiledDescriptor.resolution() < 1) {
+    mLowPrecisionMainMemoryTiledBuffer = BasicTiledLayerBuffer::OpenDescriptor(aAllocator, aTiledDescriptor);
     mLowPrecisionRegionToUpload.Or(mLowPrecisionRegionToUpload,
                                    mLowPrecisionMainMemoryTiledBuffer.GetPaintedRegion());
     mLowPrecisionMainMemoryTiledBuffer.ClearPaintedRegion();
     mPendingLowPrecisionUpload = true;
   } else {
-    mMainMemoryTiledBuffer.ReadUnlock();
-    mMainMemoryTiledBuffer = *mTiledBuffer;
+    mMainMemoryTiledBuffer = BasicTiledLayerBuffer::OpenDescriptor(aAllocator, aTiledDescriptor);
     mRegionToUpload.Or(mRegionToUpload, mMainMemoryTiledBuffer.GetPaintedRegion());
     mMainMemoryTiledBuffer.ClearPaintedRegion();
     mPendingUpload = true;
   }
-
-  // TODO: Remove me once Bug 747811 lands.
-  delete mTiledBuffer;
 }
 
 void
 TiledContentHost::ProcessLowPrecisionUploadQueue()
 {
   if (!mPendingLowPrecisionUpload) {
     return;
   }
@@ -104,18 +94,16 @@ TiledContentHost::ProcessLowPrecisionUpl
   // frame resolution. As it's always updated first when zooming, this
   // should always be true.
   mLowPrecisionVideoMemoryTiledBuffer.Upload(&mLowPrecisionMainMemoryTiledBuffer,
                                  mLowPrecisionMainMemoryTiledBuffer.GetValidRegion(),
                                  mLowPrecisionRegionToUpload,
                                  mVideoMemoryTiledBuffer.GetFrameResolution());
   nsIntRegion validRegion = mLowPrecisionVideoMemoryTiledBuffer.GetValidRegion();
 
-  mLowPrecisionMainMemoryTiledBuffer.ReadUnlock();
-
   mLowPrecisionMainMemoryTiledBuffer = BasicTiledLayerBuffer();
   mLowPrecisionRegionToUpload = nsIntRegion();
   mPendingLowPrecisionUpload = false;
 }
 
 void
 TiledContentHost::ProcessUploadQueue(nsIntRegion* aNewValidRegion,
                                      TiledLayerProperties* aLayerProperties)
@@ -128,22 +116,18 @@ TiledContentHost::ProcessUploadQueue(nsI
   mRegionToUpload.And(mRegionToUpload, mMainMemoryTiledBuffer.GetValidRegion());
 
   mVideoMemoryTiledBuffer.Upload(&mMainMemoryTiledBuffer,
                                  mMainMemoryTiledBuffer.GetValidRegion(),
                                  mRegionToUpload, aLayerProperties->mEffectiveResolution);
 
   *aNewValidRegion = mVideoMemoryTiledBuffer.GetValidRegion();
 
-  mMainMemoryTiledBuffer.ReadUnlock();
   // Release all the tiles by replacing the tile buffer with an empty
-  // tiled buffer. This will prevent us from doing a double unlock when
-  // calling  ~TiledThebesLayerComposite.
-  // XXX: This wont be needed when we do progressive upload and lock
-  // tile by tile.
+  // tiled buffer.
   mMainMemoryTiledBuffer = BasicTiledLayerBuffer();
   mRegionToUpload = nsIntRegion();
   mPendingUpload = false;
 }
 
 void
 TiledContentHost::Composite(EffectChain& aEffectChain,
                             float aOpacity,
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -3,16 +3,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/. */
 
 #ifndef GFX_TILEDCONTENTHOST_H
 #define GFX_TILEDCONTENTHOST_H
 
 #include "ContentHost.h"
 #include "ClientTiledThebesLayer.h" // for BasicTiledLayerBuffer
+#include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/TextureHost.h"
 
 namespace mozilla {
 namespace layers {
 
 class ThebesBuffer;
 class OptionalThebesBuffer;
 struct TexturedEffect;
@@ -132,18 +133,24 @@ class TiledThebesLayerComposite;
 class TiledContentHost : public ContentHost,
                          public TiledLayerComposer
 {
 public:
   TiledContentHost(const TextureInfo& aTextureInfo)
     : ContentHost(aTextureInfo)
     , mPendingUpload(false)
     , mPendingLowPrecisionUpload(false)
-  {}
-  ~TiledContentHost();
+  {
+    MOZ_COUNT_CTOR(TiledContentHost);
+  }
+
+  ~TiledContentHost()
+  {
+    MOZ_COUNT_DTOR(TiledContentHost);
+  }
 
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE
   {
     return LayerRenderState();
   }
 
 
   virtual void UpdateThebes(const ThebesBufferData& aData,
@@ -154,17 +161,18 @@ public:
     MOZ_ASSERT(false, "N/A for tiled layers");
   }
 
   const nsIntRegion& GetValidLowPrecisionRegion() const
   {
     return mLowPrecisionVideoMemoryTiledBuffer.GetValidRegion();
   }
 
-  void PaintedTiledLayerBuffer(const BasicTiledLayerBuffer* mTiledBuffer);
+  void PaintedTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+                               const SurfaceDescriptorTiles& aTiledDescriptor);
 
   // Renders a single given tile.
   void RenderTile(const TiledTexture& aTile,
                   EffectChain& aEffectChain,
                   float aOpacity,
                   const gfx::Matrix4x4& aTransform,
                   const gfx::Point& aOffset,
                   const gfx::Filter& aFilter,
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -13,16 +13,17 @@
 #include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class TextureFactoryIdentifier;
 class SurfaceDescriptor;
+class SurfaceDescriptorTiles;
 class ThebesBufferData;
 class DeprecatedTextureClient;
 class TextureClient;
 class BasicTiledLayerBuffer;
 
 /**
  * A transaction is a set of changes that happenned on the content side, that
  * should be sent to the compositor side.
@@ -80,17 +81,17 @@ public:
 
   /**
    * Tell the compositor that a Compositable is killing its buffer(s),
    * that is TextureClient/Hosts.
    */
   virtual void DestroyThebesBuffer(CompositableClient* aCompositable) = 0;
 
   virtual void PaintedTiledLayerBuffer(CompositableClient* aCompositable,
-                                       BasicTiledLayerBuffer* aTiledLayerBuffer) = 0;
+                                       const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
 
   /**
    * Communicate to the compositor that the texture identified by aCompositable
    * and aTextureId has been updated to aImage.
    */
   virtual void UpdateTexture(CompositableClient* aCompositable,
                              TextureIdentifier aTextureId,
                              SurfaceDescriptor* aDescriptor) = 0;
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -191,18 +191,18 @@ CompositableParentManager::ReceiveCompos
       const OpPaintTiledLayerBuffer& op = aEdit.get_OpPaintTiledLayerBuffer();
       CompositableParent* compositableParent = static_cast<CompositableParent*>(op.compositableParent());
       CompositableHost* compositable =
         compositableParent->GetCompositableHost();
 
       TiledLayerComposer* tileComposer = compositable->AsTiledLayerComposer();
       NS_ASSERTION(tileComposer, "compositable is not a tile composer");
 
-      BasicTiledLayerBuffer* p = reinterpret_cast<BasicTiledLayerBuffer*>(op.tiledLayerBuffer());
-      tileComposer->PaintedTiledLayerBuffer(p);
+      const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
+      tileComposer->PaintedTiledLayerBuffer(this, tileDesc);
       break;
     }
     case CompositableOperation::TOpUseTexture: {
       const OpUseTexture& op = aEdit.get_OpUseTexture();
       if (op.textureID() == 0) {
         NS_WARNING("Invalid texture ID");
         break;
       }
--- a/gfx/layers/ipc/GestureEventListener.cpp
+++ b/gfx/layers/ipc/GestureEventListener.cpp
@@ -105,27 +105,34 @@ nsEventStatus GestureEventListener::Hand
     ScreenIntPoint delta = event.mTouches[0].mScreenPoint - mTouchStartPosition;
     if (mTouches.Length() == 1 &&
         NS_hypot(delta.x, delta.y) >
           mAsyncPanZoomController->GetDPI() * mAsyncPanZoomController->GetTouchStartTolerance())
     {
       HandleTapCancel(event);
     }
 
-    bool foundAlreadyExistingTouch = false;
+    size_t eventTouchesMatched = 0;
     for (size_t i = 0; i < mTouches.Length(); i++) {
+      bool isTouchRemoved = true;
       for (size_t j = 0; j < event.mTouches.Length(); j++) {
         if (mTouches[i].mIdentifier == event.mTouches[j].mIdentifier) {
-          foundAlreadyExistingTouch = true;
+          eventTouchesMatched++;
+          isTouchRemoved = false;
           mTouches[i] = event.mTouches[j];
         }
       }
+      if (isTouchRemoved) {
+        // this touch point was lifted, so remove it from our list
+        mTouches.RemoveElementAt(i);
+        i--;
+      }
     }
 
-    NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch moved, but not in list");
+    NS_WARN_IF_FALSE(eventTouchesMatched == event.mTouches.Length(), "Touch moved, but not in list");
 
     break;
   }
   case MultiTouchInput::MULTITOUCH_END:
   case MultiTouchInput::MULTITOUCH_LEAVE: {
     bool foundAlreadyExistingTouch = false;
     for (size_t i = 0; i < event.mTouches.Length() && !foundAlreadyExistingTouch; i++) {
       for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) {
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -254,17 +254,17 @@ public:
 
   /**
    * See CompositableForwarder::UseTexture
    */
   virtual void UseTexture(CompositableClient* aCompositable,
                           TextureClient* aClient) MOZ_OVERRIDE;
 
   virtual void PaintedTiledLayerBuffer(CompositableClient* aCompositable,
-                                       BasicTiledLayerBuffer* aTiledLayerBuffer) MOZ_OVERRIDE
+                                       const SurfaceDescriptorTiles& aTileLayerDescriptor) MOZ_OVERRIDE
   {
     NS_RUNTIMEABORT("should not be called");
   }
 
   /**
    * Communicate to the compositor that the texture identified by aCompositable
    * and aTextureId has been updated to aDescriptor.
    */
--- a/gfx/layers/ipc/LayerTransaction.ipdlh
+++ b/gfx/layers/ipc/LayerTransaction.ipdlh
@@ -240,19 +240,17 @@ struct OpRemoveChild      { PLayer conta
 struct OpRepositionChild  { PLayer container; PLayer childLayer; PLayer after; };
 struct OpRaiseToTopChild  { PLayer container; PLayer childLayer; };
 
 struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; };
 
 // Paint (buffer update)
 struct OpPaintTiledLayerBuffer {
   PCompositable compositable;
-  // Bug 747811
-  // FIXME: We need to support sharing tile across process.
-  uintptr_t tiledLayerBuffer;
+  SurfaceDescriptorTiles tileLayerDescriptor;
 };
 
 struct OpCreatedTexture {
   PCompositable compositable;
   uint32_t textureId;
   SurfaceDescriptor descriptor;
   TextureInfo textureInfo;
 };
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -48,16 +48,42 @@ struct SurfaceDescriptorD3D10 {
 
 struct SharedTextureDescriptor {
   SharedTextureShareType shareType;
   SharedTextureHandle handle;
   nsIntSize size;
   bool inverted;
 };
 
+struct BasicShmTileDescriptor {
+  Shmem reusableSurface;
+};
+
+struct BasicTileDescriptor {
+  uintptr_t reusableSurface;
+};
+
+struct PlaceholderTileDescriptor {
+};
+
+union TileDescriptor {
+  BasicTileDescriptor;
+  BasicShmTileDescriptor;
+  PlaceholderTileDescriptor;
+};
+
+struct SurfaceDescriptorTiles {
+  nsIntRegion validRegion;
+  nsIntRegion paintedRegion;
+  TileDescriptor[] tiles;
+  int         retainedWidth;
+  int         retainedHeight;
+  float       resolution;
+};
+
 // XXX - soon to be removed
 struct SurfaceDescriptorGralloc {
   PGrallocBuffer buffer;
   /**
    * android::GraphicBuffer has a size information. But there are cases
    * that GraphicBuffer's size and actual video's size are different.
    * Extra size member is necessary. See Bug 850566.
    */
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -290,22 +290,20 @@ ShadowLayerForwarder::RepositionChild(Sh
                    aContainer->AsLayer(), aChild->AsLayer()));
     mTxn->AddEdit(OpRaiseToTopChild(nullptr, Shadow(aContainer),
                                     nullptr, Shadow(aChild)));
   }
 }
 
 void
 ShadowLayerForwarder::PaintedTiledLayerBuffer(CompositableClient* aCompositable,
-                                              BasicTiledLayerBuffer* aTiledLayerBuffer)
+                                              const SurfaceDescriptorTiles& aTileLayerDescriptor)
 {
-  if (XRE_GetProcessType() != GeckoProcessType_Default)
-    NS_RUNTIMEABORT("PaintedTiledLayerBuffer must be made IPC safe (not share pointers)");
   mTxn->AddNoSwapPaint(OpPaintTiledLayerBuffer(nullptr, aCompositable->GetIPDLActor(),
-                                               uintptr_t(aTiledLayerBuffer)));
+                                               aTileLayerDescriptor));
 }
 
 void
 ShadowLayerForwarder::UpdateTexture(CompositableClient* aCompositable,
                                     TextureIdentifier aTextureId,
                                     SurfaceDescriptor* aDescriptor)
 {
   if (aDescriptor->type() != SurfaceDescriptor::T__None &&
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -41,20 +41,20 @@ class PLayerTransactionParent;
 class ShadowableLayer;
 class ThebesLayerComposite;
 class ContainerLayerComposite;
 class ImageLayerComposite;
 class ColorLayerComposite;
 class CanvasLayerComposite;
 class RefLayerComposite;
 class SurfaceDescriptor;
+class SurfaceDescriptorTiles;
 class ThebesBuffer;
 class TiledLayerComposer;
 class Transaction;
-class SurfaceDescriptor;
 class CanvasSurface;
 class DeprecatedTextureClientShmem;
 class ShmemTextureClient;
 class ContentClientRemote;
 class CompositableChild;
 class ImageClient;
 class CanvasClient;
 class ContentClient;
@@ -257,17 +257,17 @@ public:
   /**
    * Notify the compositor that a tiled layer buffer has changed
    * that needs to be synced to the shadow retained copy. The tiled
    * layer buffer will operate directly on the shadow retained buffer
    * and is free to choose it's own internal representation (double buffering,
    * copy on write, tiling).
    */
   virtual void PaintedTiledLayerBuffer(CompositableClient* aCompositable,
-                                       BasicTiledLayerBuffer* aTiledLayerBuffer) MOZ_OVERRIDE;
+                                       const SurfaceDescriptorTiles& aTileLayerDescriptor) MOZ_OVERRIDE;
 
   /**
    * Notify the compositor that a compositable will be updated asynchronously
    * through ImageBridge, using an ID to connect the protocols on the
    * compositor side.
    */
   void AttachAsyncCompositable(PLayerTransactionChild* aLayer, uint64_t aID);
 
--- a/gfx/thebes/gfxBaseSharedMemorySurface.h
+++ b/gfx/thebes/gfxBaseSharedMemorySurface.h
@@ -8,36 +8,39 @@
 #define GFX_SHARED_MEMORYSURFACE_H
 
 #include "mozilla/ipc/Shmem.h"
 #include "mozilla/ipc/SharedMemory.h"
 
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "cairo.h"
+#include "pratom.h"
 
 struct SharedImageInfo {
     int32_t width;
     int32_t height;
     int32_t format;
+    int32_t readCount;
 };
 
 inline SharedImageInfo*
 GetShmInfoPtr(const mozilla::ipc::Shmem& aShmem)
 {
     return reinterpret_cast<SharedImageInfo*>
         (aShmem.get<char>() + aShmem.Size<char>() - sizeof(SharedImageInfo));
 }
 
 extern const cairo_user_data_key_t SHM_KEY;
 
 template <typename Base, typename Sub>
 class gfxBaseSharedMemorySurface : public Base {
     typedef mozilla::ipc::SharedMemory SharedMemory;
     typedef mozilla::ipc::Shmem Shmem;
+    friend class gfxReusableSharedImageSurfaceWrapper;
 
 public:
     virtual ~gfxBaseSharedMemorySurface()
     {
         MOZ_COUNT_DTOR(gfxBaseSharedMemorySurface);
     }
 
     /**
@@ -118,16 +121,38 @@ protected:
 
 private:
     void WriteShmemInfo()
     {
         SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
         shmInfo->width = this->mSize.width;
         shmInfo->height = this->mSize.height;
         shmInfo->format = this->mFormat;
+        shmInfo->readCount = 0;
+    }
+
+    int32_t
+    ReadLock()
+    {
+        SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+        return PR_ATOMIC_INCREMENT(&shmInfo->readCount);
+    }
+
+    int32_t
+    ReadUnlock()
+    {
+        SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+        return PR_ATOMIC_DECREMENT(&shmInfo->readCount);
+    }
+
+    int32_t
+    GetReadCount()
+    {
+        SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+        return shmInfo->readCount;
     }
 
     static size_t GetAlignedSize(const gfxIntSize& aSize, long aStride)
     {
         #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
         return MOZ_ALIGN_WORD(sizeof(SharedImageInfo) + aSize.height * aStride);
     }
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -357,27 +357,28 @@ gfxPlatform::Init()
 #else
     #error "No gfxPlatform implementation available"
 #endif
 
 #ifdef DEBUG
     mozilla::gl::GLContext::StaticInit();
 #endif
 
-    bool useOffMainThreadCompositing = GetPrefLayersOffMainThreadCompositionEnabled() ||
-        Preferences::GetBool("browser.tabs.remote", false);
-    useOffMainThreadCompositing &= GetPlatform()->SupportsOffMainThreadCompositing();
+    bool useOffMainThreadCompositing = OffMainThreadCompositionRequired() ||
+                                       GetPrefLayersOffMainThreadCompositionEnabled();
 
-    if (useOffMainThreadCompositing && (XRE_GetProcessType() ==
-                                        GeckoProcessType_Default)) {
+    if (!OffMainThreadCompositionRequired()) {
+      useOffMainThreadCompositing &= GetPlatform()->SupportsOffMainThreadCompositing();
+    }
+
+    if (useOffMainThreadCompositing && (XRE_GetProcessType() == GeckoProcessType_Default)) {
         CompositorParent::StartUp();
-        if (Preferences::GetBool("layers.async-video.enabled",false)) {
+        if (Preferences::GetBool("layers.async-video.enabled", false)) {
             ImageBridgeChild::StartUp();
         }
-
     }
 
     nsresult rv;
 
 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID) // temporary, until this is implemented on others
     rv = gfxPlatformFontList::Init();
     if (NS_FAILED(rv)) {
         NS_RUNTIMEABORT("Could not initialize gfxPlatformFontList");
@@ -1881,16 +1882,17 @@ static bool sPrefLayersOffMainThreadComp
 static bool sPrefLayersAccelerationForceEnabled = false;
 static bool sPrefLayersAccelerationDisabled = false;
 static bool sPrefLayersPreferOpenGL = false;
 static bool sPrefLayersPreferD3D9 = false;
 static bool sLayersSupportsD3D9 = true;
 static int  sPrefLayoutFrameRate = -1;
 static bool sBufferRotationEnabled = false;
 static bool sComponentAlphaEnabled = true;
+static bool sPrefBrowserTabsRemote = false;
 
 static bool sLayersAccelerationPrefsInitialized = false;
 
 void
 InitLayersAccelerationPrefs()
 {
   if (!sLayersAccelerationPrefsInitialized)
   {
@@ -1899,16 +1901,17 @@ InitLayersAccelerationPrefs()
     sPrefLayersOffMainThreadCompositionForceEnabled = Preferences::GetBool("layers.offmainthreadcomposition.force-enabled", false);
     sPrefLayersAccelerationForceEnabled = Preferences::GetBool("layers.acceleration.force-enabled", false);
     sPrefLayersAccelerationDisabled = Preferences::GetBool("layers.acceleration.disabled", false);
     sPrefLayersPreferOpenGL = Preferences::GetBool("layers.prefer-opengl", false);
     sPrefLayersPreferD3D9 = Preferences::GetBool("layers.prefer-d3d9", false);
     sPrefLayoutFrameRate = Preferences::GetInt("layout.frame_rate", -1);
     sBufferRotationEnabled = Preferences::GetBool("layers.bufferrotation.enabled", true);
     sComponentAlphaEnabled = Preferences::GetBool("layers.componentalpha.enabled", true);
+    sPrefBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false);
 
     nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
     if (gfxInfo) {
       int32_t status;
       if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status))) {
         if (status != nsIGfxInfo::FEATURE_NO_INFO && !sPrefLayersAccelerationForceEnabled) {
           sLayersSupportsD3D9 = false;
         }
@@ -1937,16 +1940,22 @@ gfxPlatform::GetPrefLayersOffMainThreadC
 
 bool
 gfxPlatform::GetPrefLayersAccelerationForceEnabled()
 {
   InitLayersAccelerationPrefs();
   return sPrefLayersAccelerationForceEnabled;
 }
 
+bool gfxPlatform::OffMainThreadCompositionRequired()
+{
+  InitLayersAccelerationPrefs();
+  return sPrefBrowserTabsRemote;
+}
+
 bool
 gfxPlatform::GetPrefLayersAccelerationDisabled()
 {
   InitLayersAccelerationPrefs();
   return sPrefLayersAccelerationDisabled;
 }
 
 bool
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -482,16 +482,18 @@ public:
     static bool GetPrefLayersOffMainThreadCompositionForceEnabled();
     static bool GetPrefLayersAccelerationForceEnabled();
     static bool GetPrefLayersAccelerationDisabled();
     static bool GetPrefLayersPreferOpenGL();
     static bool GetPrefLayersPreferD3D9();
     static bool CanUseDirect3D9();
     static int  GetPrefLayoutFrameRate();
 
+    static bool OffMainThreadCompositionRequired();
+
     /**
      * Is it possible to use buffer rotation
      */
     static bool BufferRotationEnabled();
     static void DisableBufferRotation();
 
     static bool ComponentAlphaEnabled();
 
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -497,17 +497,18 @@ gfxPlatformGtk::GetScreenDepth() const
     }
 
     return sDepth;
 }
 
 bool
 gfxPlatformGtk::SupportsOffMainThreadCompositing()
 {
-#ifdef MOZ_X11
+  // Nightly builds have OMTC support by default for Electrolysis testing.
+#if defined(MOZ_X11) && !defined(NIGHTLY_BUILD)
   return (PR_GetEnv("MOZ_USE_OMTC") != nullptr) ||
          (PR_GetEnv("MOZ_OMTC_ENABLED") != nullptr);
 #else
   return true;
 #endif
 }
 
 qcms_profile *
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxReusableImageSurfaceWrapper.cpp
@@ -0,0 +1,61 @@
+/* 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 "gfxReusableImageSurfaceWrapper.h"
+#include "gfxImageSurface.h"
+
+gfxReusableImageSurfaceWrapper::gfxReusableImageSurfaceWrapper(gfxImageSurface* aSurface)
+  : mSurface(aSurface)
+{
+  MOZ_COUNT_CTOR(gfxReusableImageSurfaceWrapper);
+}
+
+gfxReusableImageSurfaceWrapper::~gfxReusableImageSurfaceWrapper()
+{
+  MOZ_COUNT_DTOR(gfxReusableImageSurfaceWrapper);
+}
+
+void
+gfxReusableImageSurfaceWrapper::ReadLock()
+{
+  NS_CheckThreadSafe(_mOwningThread.GetThread(), "Only the owner thread can call ReadLock");
+  AddRef();
+}
+
+void
+gfxReusableImageSurfaceWrapper::ReadUnlock()
+{
+  Release();
+}
+
+gfxReusableSurfaceWrapper*
+gfxReusableImageSurfaceWrapper::GetWritable(gfxImageSurface** aSurface)
+{
+  NS_CheckThreadSafe(_mOwningThread.GetThread(), "Only the owner thread can call GetWritable");
+
+  if (mRefCnt == 1) {
+    *aSurface = mSurface;
+    return this;
+  }
+
+  // Something else is reading the surface, copy it
+  gfxImageSurface* copySurface = new gfxImageSurface(mSurface->GetSize(), mSurface->Format(), false);
+  copySurface->CopyFrom(mSurface);
+  *aSurface = copySurface;
+
+  return new gfxReusableImageSurfaceWrapper(copySurface);
+}
+
+const unsigned char*
+gfxReusableImageSurfaceWrapper::GetReadOnlyData() const
+{
+  return mSurface->Data();
+}
+
+gfxASurface::gfxImageFormat
+gfxReusableImageSurfaceWrapper::Format()
+{
+  return mSurface->Format();
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxReusableImageSurfaceWrapper.h
@@ -0,0 +1,36 @@
+/* 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 GFXMEMCOWSURFACEWRAPPER
+#define GFXMEMCOWSURFACEWRAPPER
+
+#include "gfxReusableSurfaceWrapper.h"
+
+class gfxImageSurface;
+
+/**
+ * A cross-thread capable implementation of gfxReusableSurfaceWrapper based
+ * on gfxImageSurface.
+ */
+class gfxReusableImageSurfaceWrapper : public gfxReusableSurfaceWrapper {
+public:
+  gfxReusableImageSurfaceWrapper(gfxImageSurface* aSurface);
+  ~gfxReusableImageSurfaceWrapper();
+
+  const unsigned char* GetReadOnlyData() const MOZ_OVERRIDE;
+  gfxASurface::gfxImageFormat Format() MOZ_OVERRIDE;
+  gfxReusableSurfaceWrapper* GetWritable(gfxImageSurface** aSurface) MOZ_OVERRIDE;
+  void ReadLock() MOZ_OVERRIDE;
+  void ReadUnlock() MOZ_OVERRIDE;
+
+  Type GetType()
+  {
+    return TYPE_IMAGE;
+  }
+
+private:
+  nsRefPtr<gfxImageSurface>         mSurface;
+};
+
+#endif // GFXMEMCOWSURFACEWRAPPER
rename from gfx/thebes/gfxReusableSurfaceWrapper.cpp
rename to gfx/thebes/gfxReusableSharedImageSurfaceWrapper.cpp
--- a/gfx/thebes/gfxReusableSurfaceWrapper.cpp
+++ b/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.cpp
@@ -1,73 +1,97 @@
 /* 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 "gfxReusableSurfaceWrapper.h"
-#include "gfxImageSurface.h"
+#include "gfxReusableSharedImageSurfaceWrapper.h"
+#include "gfxSharedImageSurface.h"
+
+using mozilla::ipc::Shmem;
+using mozilla::layers::ISurfaceAllocator;
 
-gfxReusableSurfaceWrapper::gfxReusableSurfaceWrapper(gfxImageSurface* aSurface)
-  : mSurface(aSurface)
-  , mFormat(aSurface->Format())
-  , mSurfaceData(aSurface->Data())
-  , mReadCount(0)
+gfxReusableSharedImageSurfaceWrapper::gfxReusableSharedImageSurfaceWrapper(ISurfaceAllocator* aAllocator,
+                                                                           gfxSharedImageSurface* aSurface)
+  : mAllocator(aAllocator)
+  , mSurface(aSurface)
 {
-  MOZ_COUNT_CTOR(gfxReusableSurfaceWrapper);
+  MOZ_COUNT_CTOR(gfxReusableSharedImageSurfaceWrapper);
+  ReadLock();
 }
 
-class DeleteImageOnMainThread : public nsRunnable {
-public:
-  DeleteImageOnMainThread(gfxImageSurface *aImage)
-  : mImage(aImage)
-  {}
+gfxReusableSharedImageSurfaceWrapper::~gfxReusableSharedImageSurfaceWrapper()
+{
+  MOZ_COUNT_DTOR(gfxReusableSharedImageSurfaceWrapper);
+  ReadUnlock();
+}
 
-  NS_IMETHOD Run()
-  {
-    return NS_OK;
-  }
-private:
-  nsRefPtr<gfxImageSurface> mImage;
-};
+void
+gfxReusableSharedImageSurfaceWrapper::ReadLock()
+{
+  NS_CheckThreadSafe(_mOwningThread.GetThread(), "Only the owner thread can call ReadLock");
+  mSurface->ReadLock();
+}
 
-gfxReusableSurfaceWrapper::~gfxReusableSurfaceWrapper()
+void
+gfxReusableSharedImageSurfaceWrapper::ReadUnlock()
 {
-  NS_ABORT_IF_FALSE(mReadCount == 0, "Should not be locked when released");
-  MOZ_COUNT_DTOR(gfxReusableSurfaceWrapper);
-  if (!NS_IsMainThread()) {
-    NS_DispatchToMainThread(new DeleteImageOnMainThread(mSurface));
+  int32_t readCount = mSurface->ReadUnlock();
+  NS_ABORT_IF_FALSE(readCount >= 0, "Read count should not be negative");
+
+  if (readCount == 0) {
+    mAllocator->DeallocShmem(mSurface->GetShmem());
   }
 }
 
-void
-gfxReusableSurfaceWrapper::ReadLock()
-{
-  NS_CheckThreadSafe(_mOwningThread.GetThread(), "Only the owner thread can call ReadOnlyLock");
-  mReadCount++;
-}
-
-void
-gfxReusableSurfaceWrapper::ReadUnlock()
-{
-  mReadCount--;
-  NS_ABORT_IF_FALSE(mReadCount >= 0, "Should not be negative");
-}
-
 gfxReusableSurfaceWrapper*
-gfxReusableSurfaceWrapper::GetWritable(gfxImageSurface** aSurface)
+gfxReusableSharedImageSurfaceWrapper::GetWritable(gfxImageSurface** aSurface)
 {
   NS_CheckThreadSafe(_mOwningThread.GetThread(), "Only the owner thread can call GetWritable");
 
-  if (mReadCount == 0) {
+  int32_t readCount = mSurface->GetReadCount();
+  NS_ABORT_IF_FALSE(readCount > 0, "A ReadLock must be held when calling GetWritable");
+  if (readCount == 1) {
     *aSurface = mSurface;
     return this;
   }
 
   // Something else is reading the surface, copy it
-  gfxImageSurface* copySurface = new gfxImageSurface(mSurface->GetSize(), mSurface->Format(), false);
+  nsRefPtr<gfxSharedImageSurface> copySurface =
+    gfxSharedImageSurface::CreateUnsafe(mAllocator, mSurface->GetSize(), mSurface->Format());
   copySurface->CopyFrom(mSurface);
   *aSurface = copySurface;
 
-  // We need to create a new wrapper since this wrapper has a read only lock.
-  gfxReusableSurfaceWrapper* wrapper = new gfxReusableSurfaceWrapper(copySurface);
+  // We need to create a new wrapper since this wrapper has an external ReadLock
+  gfxReusableSurfaceWrapper* wrapper = new gfxReusableSharedImageSurfaceWrapper(mAllocator, copySurface);
+
+  // No need to release the ReadLock on the surface, this will happen when
+  // the wrapper is destroyed.
+
   return wrapper;
 }
 
+const unsigned char*
+gfxReusableSharedImageSurfaceWrapper::GetReadOnlyData() const
+{
+  NS_ABORT_IF_FALSE(mSurface->GetReadCount() > 0, "Should have read lock");
+  return mSurface->Data();
+}
+
+gfxASurface::gfxImageFormat
+gfxReusableSharedImageSurfaceWrapper::Format()
+{
+  return mSurface->Format();
+}
+
+Shmem&
+gfxReusableSharedImageSurfaceWrapper::GetShmem()
+{
+  return mSurface->GetShmem();
+}
+
+/* static */ already_AddRefed<gfxReusableSharedImageSurfaceWrapper>
+gfxReusableSharedImageSurfaceWrapper::Open(ISurfaceAllocator* aAllocator, const Shmem& aShmem)
+{
+  nsRefPtr<gfxSharedImageSurface> sharedImage = gfxSharedImageSurface::Open(aShmem);
+  nsRefPtr<gfxReusableSharedImageSurfaceWrapper> wrapper = new gfxReusableSharedImageSurfaceWrapper(aAllocator, sharedImage);
+  wrapper->ReadUnlock();
+  return wrapper.forget();
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxReusableSharedImageSurfaceWrapper.h
@@ -0,0 +1,52 @@
+/* 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 GFXSHMCOWSURFACEWRAPPER
+#define GFXSHMCOWSURFACEWRAPPER
+
+#include "gfxReusableSurfaceWrapper.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+class gfxSharedImageSurface;
+
+/**
+ * A cross-process capable implementation of gfxReusableSurfaceWrapper based
+ * on gfxSharedImageSurface.
+ */
+class gfxReusableSharedImageSurfaceWrapper : public gfxReusableSurfaceWrapper {
+public:
+  gfxReusableSharedImageSurfaceWrapper(mozilla::layers::ISurfaceAllocator* aAllocator,
+                                       gfxSharedImageSurface* aSurface);
+  ~gfxReusableSharedImageSurfaceWrapper();
+
+  const unsigned char* GetReadOnlyData() const MOZ_OVERRIDE;
+  gfxASurface::gfxImageFormat Format() MOZ_OVERRIDE;
+  gfxReusableSurfaceWrapper* GetWritable(gfxImageSurface** aSurface) MOZ_OVERRIDE;
+  void ReadLock() MOZ_OVERRIDE;
+  void ReadUnlock() MOZ_OVERRIDE;
+
+  Type GetType()
+  {
+    return TYPE_SHARED_IMAGE;
+  }
+
+  /**
+   * Returns the shared memory segment that backs the shared image surface.
+   */
+  mozilla::ipc::Shmem& GetShmem();
+
+  /**
+   * Create a gfxReusableSurfaceWrapper from the shared memory segment of a
+   * gfxSharedImageSurface. A ReadLock must be held, which will be adopted by
+   * the returned gfxReusableSurfaceWrapper.
+   */
+  static already_AddRefed<gfxReusableSharedImageSurfaceWrapper>
+  Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem);
+
+private:
+  mozilla::layers::ISurfaceAllocator*     mAllocator;
+  nsRefPtr<gfxSharedImageSurface>         mSurface;
+};
+
+#endif // GFXSHMCOWSURFACEWRAPPER
--- a/gfx/thebes/gfxReusableSurfaceWrapper.h
+++ b/gfx/thebes/gfxReusableSurfaceWrapper.h
@@ -1,73 +1,87 @@
 /* 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 GFXCOWSURFACEWRAPPER
 #define GFXCOWSURFACEWRAPPER
 
-#include "gfxASurface.h"
+#include "gfxImageSurface.h"
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
-#include "mozilla/Atomics.h"
 
-class gfxImageSurface;
 
 /**
- * Provides a cross thread wrapper for a gfxImageSurface
- * that has copy-on-write schemantics.
+ * Provides an interface to implement a cross thread/process wrapper for a
+ * gfxImageSurface that has copy-on-write semantics.
  *
- * Only the owner thread can write to the surface and aquire
- * read locks.
+ * Only the owner thread can write to the surface and acquire
+ * read locks. Destroying a gfxReusableSurfaceWrapper releases
+ * a read lock.
  *
  * OMTC Usage:
  * 1) Content creates a writable copy of this surface
- *    wrapper which be optimized to the same wrapper if there
+ *    wrapper which will be optimized to the same wrapper if there
  *    are no readers.
  * 2) The surface is sent from content to the compositor once
- *    or potentially many time, each increasing a read lock.
- * 3) When the compositor has processed the surface and uploaded
- *    the content it then releases the read lock.
+ *    or potentially many times, each increasing a read lock.
+ * 3) When the compositor receives the surface, it adopts the
+ *    read lock.
+ * 4) Once the compositor has processed the surface and uploaded
+ *    the content, it then releases the read lock by dereferencing
+ *    its wrapper.
  */
 class gfxReusableSurfaceWrapper {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxReusableSurfaceWrapper)
 public:
-  /**
-   * Pass the gfxASurface to the wrapper.
-   * The wrapper should hold the only strong reference
-   * to the surface and its memebers.
-   */
-  gfxReusableSurfaceWrapper(gfxImageSurface* aSurface);
+  virtual ~gfxReusableSurfaceWrapper() {}
 
-  ~gfxReusableSurfaceWrapper();
-
-  const unsigned char* GetReadOnlyData() const {
-    NS_ABORT_IF_FALSE(mReadCount > 0, "Should have read lock");
-    return mSurfaceData;
-  }
-
-  const gfxASurface::gfxImageFormat& Format() { return mFormat; }
+  /**
+   * Returns a read-only pointer to the image data.
+   */
+  virtual const unsigned char* GetReadOnlyData() const = 0;
 
   /**
-   * Get a writable copy of the image.
+   * Returns the image surface format.
+   */
+  virtual gfxASurface::gfxImageFormat Format() = 0;
+
+  /**
+   * Returns a writable copy of the image.
    * If necessary this will copy the wrapper. If there are no contention
-   * the same wrapper will be returned.
+   * the same wrapper will be returned. A ReadLock must be held when
+   * calling this function, and calling it will give up this lock.
    */
-  gfxReusableSurfaceWrapper* GetWritable(gfxImageSurface** aSurface);
+  virtual gfxReusableSurfaceWrapper* GetWritable(gfxImageSurface** aSurface) = 0;
 
   /**
    * A read only lock count is recorded, any attempts to
-   * call GetWritable() while this count is positive will
+   * call GetWritable() while this count is greater than one will
    * create a new surface/wrapper pair.
+   *
+   * When a surface's read count falls to zero, its memory will be
+   * deallocated. It is the responsibility of the user to make sure
+   * that all locks are matched with an equal number of unlocks.
    */
-  void ReadLock();
-  void ReadUnlock();
+  virtual void ReadLock() = 0;
+  virtual void ReadUnlock() = 0;
 
-private:
+  /**
+   * Types for each implementation of gfxReusableSurfaceWrapper.
+   */
+  enum Type {
+    TYPE_SHARED_IMAGE,
+    TYPE_IMAGE,
+
+    TYPE_MAX
+  };
+
+  /**
+   * Returns a unique ID for each implementation of gfxReusableSurfaceWrapper.
+   */
+  virtual Type GetType() = 0;
+
+protected:
   NS_DECL_OWNINGTHREAD
-  nsRefPtr<gfxImageSurface>         mSurface;
-  const gfxASurface::gfxImageFormat mFormat;
-  const unsigned char*              mSurfaceData;
-  mozilla::Atomic<int32_t>                           mReadCount;
 };
 
 #endif // GFXCOWSURFACEWRAPPER
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -31,16 +31,18 @@ EXPORTS += [
     'gfxPattern.h',
     'gfxPlatform.h',
     'gfxPoint.h',
     'gfxPoint3D.h',
     'gfxPointH3D.h',
     'gfxQuad.h',
     'gfxQuaternion.h',
     'gfxRect.h',
+    'gfxReusableImageSurfaceWrapper.h',
+    'gfxReusableSharedImageSurfaceWrapper.h',
     'gfxReusableSurfaceWrapper.h',
     'gfxSVGGlyphs.h',
     'gfxSharedImageSurface.h',
     'gfxSharedQuartzSurface.h',
     'gfxSkipChars.h',
     'gfxTeeSurface.h',
     'gfxTypes.h',
     'gfxUserFontSet.h',
@@ -245,17 +247,18 @@ CPP_SOURCES += [
     'gfxHarfBuzzShaper.cpp',
     'gfxImageSurface.cpp',
     'gfxMatrix.cpp',
     'gfxPath.cpp',
     'gfxPattern.cpp',
     'gfxPlatform.cpp',
     'gfxPlatformFontList.cpp',
     'gfxRect.cpp',
-    'gfxReusableSurfaceWrapper.cpp',
+    'gfxReusableImageSurfaceWrapper.cpp',
+    'gfxReusableSharedImageSurfaceWrapper.cpp',
     'gfxSVGGlyphs.cpp',
     'gfxScriptItemizer.cpp',
     'gfxSkipChars.cpp',
     'gfxTeeSurface.cpp',
     'gfxUserFontSet.cpp',
     'gfxUtils.cpp',
     'nsSurfaceTexture.cpp',
 ]
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -251,17 +251,17 @@ class ObjectPtr
     JSObject *operator->() const { return value; }
     operator JSObject *() const { return value; }
 };
 
 /*
  * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
  * JSTRACE_SHAPE. |thing| should be non-null.
  */
-extern JS_FRIEND_API(void)
+extern JS_FRIEND_API(bool)
 UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind);
 
 /*
  * This should be called when an object that is marked gray is exposed to the JS
  * engine (by handing it to running JS code or writing it into live JS
  * data). During incremental GC, since the gray bits haven't been computed yet,
  * we conservatively mark the object black.
  */
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -123,16 +123,26 @@ static JS_ALWAYS_INLINE Zone *
 GetObjectZone(JSObject *obj)
 {
     return GetGCThingZone(obj);
 }
 
 static JS_ALWAYS_INLINE bool
 GCThingIsMarkedGray(void *thing)
 {
+#ifdef JSGC_GENERATIONAL
+    /*
+     * GC things residing in the nursery cannot be gray: they have no mark bits.
+     * All live objects in the nursery are moved to tenured at the beginning of
+     * each GC slice, so the gray marker never sees nursery things.
+     */
+    JS::shadow::Runtime *rt = js::gc::GetGCThingRuntime(thing);
+    if (uintptr_t(thing) >= rt->gcNurseryStart_ && uintptr_t(thing) < rt->gcNurseryEnd_)
+        return false;
+#endif
     uintptr_t *word, mask;
     js::gc::GetGCThingMarkWordAndMask(thing, js::gc::GRAY, &word, &mask);
     return *word & mask;
 }
 
 static JS_ALWAYS_INLINE bool
 IsIncrementalBarrierNeededOnGCThing(shadow::Runtime *rt, void *thing, JSGCTraceKind kind)
 {
--- a/js/src/TraceLogging.cpp
+++ b/js/src/TraceLogging.cpp
@@ -9,16 +9,17 @@
 #include <cstdarg>
 #include <cstdio>
 #include <cstdlib>
 #include <stdint.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "jsapi.h"
+#include "jsscript.h"
 
 using namespace js;
 
 #ifndef TRACE_LOG_DIR
 # if defined(_WIN32)
 #  define TRACE_LOG_DIR ""
 # else
 #  define TRACE_LOG_DIR "/tmp/"
@@ -146,23 +147,24 @@ TraceLogging::log(Type type, const char*
         copy = strdup(file);
 
     entries[curEntry++] = Entry(now - loggingTime, copy, lineno, type);
 
     // Increase length when not enough place in the array
     if (curEntry >= numEntries)
         grow();
 
-    // Save the time spend logging the information in order to discard this time from the logged time.
-    // Especially needed when increasing the array or flushing the information.
+    // Save the time spend logging the information in order to discard this
+    // time from the logged time. Especially needed when increasing the array
+    // or flushing the information.
     loggingTime += rdtsc()-now;
 }
 
 void
-TraceLogging::log(Type type, const CompileOptions &options)
+TraceLogging::log(Type type, const JS::CompileOptions &options)
 {
     this->log(type, options.filename, options.lineno);
 }
 
 void
 TraceLogging::log(Type type, JSScript* script)
 {
     this->log(type, script->filename(), script->lineno);
--- a/js/src/TraceLogging.h
+++ b/js/src/TraceLogging.h
@@ -7,19 +7,21 @@
 #ifndef TraceLogging_h
 #define TraceLogging_h
 
 #include <stdint.h>
 #include <stdio.h>
 
 class JSScript;
 
-namespace js {
+namespace JS {
+class CompileOptions;
+}
 
-struct CompileOptions;
+namespace js {
 
 class TraceLogging
 {
   public:
     enum Type {
         SCRIPT_START,
         SCRIPT_STOP,
         ION_COMPILE_START,
@@ -67,17 +69,17 @@ class TraceLogging
 
     static const char * const type_name[];
     static TraceLogging* _defaultLogger;
   public:
     TraceLogging();
     ~TraceLogging();
 
     void log(Type type, const char* filename, unsigned int line);
-    void log(Type type, const CompileOptions &options);
+    void log(Type type, const JS::CompileOptions &options);
     void log(Type type, JSScript* script);
     void log(const char* log);
     void log(Type type);
     void flush();
 
     static TraceLogging* defaultLogger();
     static void releaseDefaultLogger();
 
@@ -91,24 +93,26 @@ void TraceLog(TraceLogging* logger, cons
 void TraceLog(TraceLogging* logger, TraceLogging::Type type);
 
 /* Automatic logging at the start and end of function call */
 class AutoTraceLog {
     TraceLogging* logger;
     TraceLogging::Type stop;
 
   public:
-    AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop, const CompileOptions &options)
+    AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop,
+                 const JS::CompileOptions &options)
       : logger(logger),
         stop(stop)
     {
         logger->log(start, options);
     }
 
-    AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop, JSScript* script)
+    AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop,
+                 JSScript* script)
       : logger(logger),
         stop(stop)
     {
         logger->log(start, script);
     }
 
     AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop)
       : logger(logger),
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -646,20 +646,25 @@ gc::MarkObjectSlots(JSTracer *trc, JSObj
 }
 
 static bool
 ShouldMarkCrossCompartment(JSTracer *trc, JSObject *src, Cell *cell)
 {
     if (!IS_GC_MARKING_TRACER(trc))
         return true;
 
+    uint32_t color = AsGCMarker(trc)->getMarkColor();
+    JS_ASSERT(color == BLACK || color == GRAY);
+
+    if (IsInsideNursery(trc->runtime, cell)) {
+        JS_ASSERT(color == BLACK);
+        return false;
+    }
+
     JS::Zone *zone = cell->tenuredZone();
-    uint32_t color = AsGCMarker(trc)->getMarkColor();
-
-    JS_ASSERT(color == BLACK || color == GRAY);
     if (color == BLACK) {
         /*
          * Having black->gray edges violates our promise to the cycle
          * collector. This can happen if we're collecting a compartment and it
          * has an edge to an uncollected compartment: it's possible that the
          * source and destination of the cross-compartment edge should be gray,
          * but the source was marked black by the conservative scanner.
          */
@@ -1573,34 +1578,37 @@ UnmarkGrayChildren(JSTracer *trc, void *
 
 struct UnmarkGrayTracer : public JSTracer
 {
     /*
      * We set eagerlyTraceWeakMaps to false because the cycle collector will fix
      * up any color mismatches involving weakmaps when it runs.
      */
     UnmarkGrayTracer(JSRuntime *rt)
-      : tracingShape(false), previousShape(NULL)
+      : tracingShape(false), previousShape(NULL), unmarkedAny(false)
     {
         JS_TracerInit(this, rt, UnmarkGrayChildren);
         eagerlyTraceWeakMaps = DoNotTraceWeakMaps;
     }
 
     UnmarkGrayTracer(JSTracer *trc, bool tracingShape)
-      : tracingShape(tracingShape), previousShape(NULL)
+      : tracingShape(tracingShape), previousShape(NULL), unmarkedAny(false)
     {
         JS_TracerInit(this, trc->runtime, UnmarkGrayChildren);
         eagerlyTraceWeakMaps = DoNotTraceWeakMaps;
     }
 
     /* True iff we are tracing the immediate children of a shape. */
     bool tracingShape;
 
     /* If tracingShape, shape child or NULL. Otherwise, NULL. */
     void *previousShape;
+
+    /* Whether we unmarked anything. */
+    bool unmarkedAny;
 };
 
 /*
  * The GC and CC are run independently. Consequently, the following sequence of
  * events can occur:
  * 1. GC runs and marks an object gray.
  * 2. Some JS code runs that creates a pointer from a JS root to the gray
  *    object. If we re-ran a GC at this point, the object would now be black.
@@ -1637,57 +1645,69 @@ UnmarkGrayChildren(JSTracer *trc, void *
         /*
          * If we run out of stack, we take a more drastic measure: require that
          * we GC again before the next CC.
          */
         trc->runtime->gcGrayBitsValid = false;
         return;
     }
 
-    if (!JS::GCThingIsMarkedGray(thing))
-        return;
+    UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer *>(trc);
+    if (!IsInsideNursery(trc->runtime, thing)) {
+        if (!JS::GCThingIsMarkedGray(thing))
+            return;
 
-    UnmarkGrayGCThing(thing);
+        UnmarkGrayGCThing(thing);
+        tracer->unmarkedAny = true;
+    }
 
     /*
      * Trace children of |thing|. If |thing| and its parent are both shapes,
      * |thing| will get saved to mPreviousShape without being traced. The parent
      * will later trace |thing|. This is done to avoid increasing the stack
      * depth during shape tracing. It is safe to do because a shape can only
      * have one child that is a shape.
      */
-    UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer *>(trc);
     UnmarkGrayTracer childTracer(tracer, kind == JSTRACE_SHAPE);
 
     if (kind != JSTRACE_SHAPE) {
         JS_TraceChildren(&childTracer, thing, kind);
         JS_ASSERT(!childTracer.previousShape);
+        tracer->unmarkedAny |= childTracer.unmarkedAny;
         return;
     }
 
     if (tracer->tracingShape) {
         JS_ASSERT(!tracer->previousShape);
         tracer->previousShape = thing;
         return;
     }
 
     do {
         JS_ASSERT(!JS::GCThingIsMarkedGray(thing));
         JS_TraceChildren(&childTracer, thing, JSTRACE_SHAPE);
         thing = childTracer.previousShape;
         childTracer.previousShape = NULL;
     } while (thing);
+    tracer->unmarkedAny |= childTracer.unmarkedAny;
 }
 
-JS_FRIEND_API(void)
+JS_FRIEND_API(bool)
 JS::UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind)
 {
     JS_ASSERT(kind != JSTRACE_SHAPE);
 
-    if (!JS::GCThingIsMarkedGray(thing))
-        return;
+    JSRuntime *rt = static_cast<Cell *>(thing)->runtimeFromMainThread();
 
-    UnmarkGrayGCThing(thing);
+    bool unmarkedArg = false;
+    if (!IsInsideNursery(rt, thing)) {
+        if (!JS::GCThingIsMarkedGray(thing))
+            return false;
 
-    JSRuntime *rt = static_cast<Cell *>(thing)->runtimeFromMainThread();
+        UnmarkGrayGCThing(thing);
+        unmarkedArg = true;
+    }
+
     UnmarkGrayTracer trc(rt);
     JS_TraceChildren(&trc, thing, kind);
+
+    return unmarkedArg || trc.unmarkedAny;
 }
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -139,16 +139,17 @@ def main(argv):
 
     # The full test list is ready. Now create copies for each JIT configuration.
     job_list = []
     if options.tbpl:
         # Running all bits would take forever. Instead, we test a few interesting combinations.
         flags = [
             [], # no flags, normal baseline and ion
             ['--ion-eager'], # implies --baseline-eager
+            ['--ion-eager', '--ion-check-range-analysis'],
             ['--baseline-eager'],
             ['--baseline-eager', '--no-ti', '--no-fpu'],
             ['--no-baseline', '--no-ion'],
             ['--no-baseline', '--no-ion', '--no-ti'],
         ]
         for test in test_list:
             for variant in flags:
                 new_test = test.copy()
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1222,23 +1222,22 @@ MacroAssembler::printf(const char *outpu
 void
 MacroAssembler::tracelogStart(JSScript *script)
 {
     void (&TraceLogStart)(TraceLogging*, TraceLogging::Type, JSScript*) = TraceLog;
     RegisterSet regs = RegisterSet::Volatile();
     PushRegsInMask(regs);
 
     Register temp = regs.takeGeneral();
-    Register logger = regs.takeGeneral();
     Register type = regs.takeGeneral();
     Register rscript = regs.takeGeneral();
 
     setupUnalignedABICall(3, temp);
-    movePtr(ImmWord((void *)TraceLogging::defaultLogger()), logger);
-    passABIArg(logger);
+    movePtr(ImmWord((void *)TraceLogging::defaultLogger()), temp);
+    passABIArg(temp);
     move32(Imm32(TraceLogging::SCRIPT_START), type);
     passABIArg(type);
     movePtr(ImmGCPtr(script), rscript);
     passABIArg(rscript);
     callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLogStart));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1932,16 +1932,19 @@ MToDouble::foldsTo(bool useValueNumbers)
         replaceOperand(0, input()->getOperand(0));
 
     return this;
 }
 
 MDefinition *
 MToString::foldsTo(bool useValueNumbers)
 {
+    MDefinition *in = input();
+    if (in->type() == MIRType_String)
+        return in;
     return this;
 }
 
 MDefinition *
 MClampToUint8::foldsTo(bool useValueNumbers)
 {
     if (input()->isConstant()) {
         const Value &v = input()->toConstant()->value();
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -311,44 +311,46 @@ Range::intersect(const Range *lhs, const
     if (!lhs && !rhs)
         return NULL;
 
     if (!lhs)
         return new Range(*rhs);
     if (!rhs)
         return new Range(*lhs);
 
-    Range *r = new Range(
-        Max(lhs->lower_, rhs->lower_),
-        Min(lhs->upper_, rhs->upper_),
-        lhs->decimal_ && rhs->decimal_,
-        Min(lhs->max_exponent_, rhs->max_exponent_));
-
-    r->lower_infinite_ = lhs->lower_infinite_ && rhs->lower_infinite_;
-    r->upper_infinite_ = lhs->upper_infinite_ && rhs->upper_infinite_;
+    int32_t newLower = Max(lhs->lower_, rhs->lower_);
+    int32_t newUpper = Min(lhs->upper_, rhs->upper_);
 
     // :TODO: This information could be used better. If upper < lower, then we
     // have conflicting constraints. Consider:
     //
     // if (x < 0) {
     //   if (x > 0) {
     //     [Some code.]
     //   }
     // }
     //
     // In this case, the block is dead. Right now, we just disregard this fact
     // and make the range infinite, rather than empty.
     //
     // Instead, we should use it to eliminate the dead block.
     // (Bug 765127)
-    if (r->upper_ < r->lower_) {
+    if (newUpper < newLower) {
         *emptyRange = true;
-        r->makeRangeInfinite();
+        return NULL;
     }
 
+    Range *r = new Range(
+        newLower, newUpper,
+        lhs->decimal_ && rhs->decimal_,
+        Min(lhs->max_exponent_, rhs->max_exponent_));
+
+    r->lower_infinite_ = lhs->lower_infinite_ && rhs->lower_infinite_;
+    r->upper_infinite_ = lhs->upper_infinite_ && rhs->upper_infinite_;
+
     return r;
 }
 
 void
 Range::unionWith(const Range *other)
 {
    bool decimal = decimal_ | other->decimal_;
    uint16_t max_exponent = Max(max_exponent_, other->max_exponent_);
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -169,16 +169,19 @@ class Range : public TempObject {
     Range(int64_t l, int64_t h, bool d = false, uint16_t e = MaxInt32Exponent)
         : lower_infinite_(true),
           upper_infinite_(true),
           decimal_(d),
           max_exponent_(e),
           symbolicLower_(NULL),
           symbolicUpper_(NULL)
     {
+        JS_ASSERT(e >= (h == INT64_MIN ? MaxDoubleExponent : mozilla::FloorLog2(mozilla::Abs(h))));
+        JS_ASSERT(e >= (l == INT64_MIN ? MaxDoubleExponent : mozilla::FloorLog2(mozilla::Abs(l))));
+
         setLowerInit(l);
         setUpperInit(h);
         rectifyExponent();
         JS_ASSERT_IF(lower_infinite_, lower_ == JSVAL_INT_MIN);
         JS_ASSERT_IF(upper_infinite_, upper_ == JSVAL_INT_MAX);
     }
 
     Range(const Range &other)
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -429,20 +429,20 @@ class Assembler : public AssemblerX86Sha
             break;
           case Operand::SCALE:
             masm.movq_i32m(imm32.value, dest.disp(), dest.base(), dest.index(), dest.scale());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
-    void movqsd(const Register &src, const FloatRegister &dest) {
+    void movq(const Register &src, const FloatRegister &dest) {
         masm.movq_rr(src.code(), dest.code());
     }
-    void movqsd(const FloatRegister &src, const Register &dest) {
+    void movq(const FloatRegister &src, const Register &dest) {
         masm.movq_rr(src.code(), dest.code());
     }
     void movq(const Register &src, const Register &dest) {
         masm.movq_rr(src.code(), dest.code());
     }
 
     void xchgq(const Register &src, const Register &dest) {
         masm.xchgq_rr(src.code(), dest.code());
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -82,17 +82,17 @@ bool
 CodeGeneratorX64::visitBox(LBox *box)
 {
     const LAllocation *in = box->getOperand(0);
     const LDefinition *result = box->getDef(0);
 
     if (box->type() != MIRType_Double)
         masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
     else
-        masm.movqsd(ToFloatRegister(in), ToRegister(result));
+        masm.movq(ToFloatRegister(in), ToRegister(result));
     return true;
 }
 
 bool
 CodeGeneratorX64::visitUnbox(LUnbox *unbox)
 {
     const ValueOperand value = ToValue(unbox, LUnbox::Input);
     const LDefinition *result = unbox->output();
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -308,12 +308,12 @@ MacroAssemblerX64::handleFailureWithHand
     movq(Operand(esp, offsetof(ResumeFromException, bailoutInfo)), r9);
     movl(Imm32(BAILOUT_RETURN_OK), rax);
     jmp(Operand(rsp, offsetof(ResumeFromException, target)));
 }
 
 Assembler::Condition
 MacroAssemblerX64::testNegativeZero(const FloatRegister &reg, const Register &scratch)
 {
-    movqsd(reg, scratch);
+    movq(reg, scratch);
     subq(Imm32(1), scratch);
     return Overflow;
 }
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -816,17 +816,17 @@ class MacroAssemblerX64 : public MacroAs
     void branchTestValue(Condition cond, const Address &valaddr, const ValueOperand &value,
                          Label *label)
     {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         branchPtr(cond, valaddr, value.valueReg(), label);
     }
 
     void boxDouble(const FloatRegister &src, const ValueOperand &dest) {
-        movqsd(src, dest.valueReg());
+        movq(src, dest.valueReg());
     }
     void boxNonDouble(JSValueType type, const Register &src, const ValueOperand &dest) {
         JS_ASSERT(src != dest.valueReg());
         boxValue(type, src, dest.valueReg());
     }
 
     // Note that the |dest| register here may be ScratchReg, so we shouldn't
     // use it.
@@ -863,17 +863,17 @@ class MacroAssemblerX64 : public MacroAs
         unboxBoolean(Operand(src), dest);
     }
 
     void unboxMagic(const ValueOperand &src, const Register &dest) {
         movl(src.valueReg(), dest);
     }
 
     void unboxDouble(const ValueOperand &src, const FloatRegister &dest) {
-        movqsd(src.valueReg(), dest);
+        movq(src.valueReg(), dest);
     }
     void unboxPrivate(const ValueOperand &src, const Register dest) {
         movq(src.valueReg(), dest);
         shlq(Imm32(1), dest);
     }
 
     void notBoolean(const ValueOperand &val) {
         xorq(Imm32(1), val.valueReg());
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -586,20 +586,21 @@ js::GCThingTraceKind(void *thing)
 {
     JS_ASSERT(thing);
     return gc::GetGCThingTraceKind(thing);
 }
 
 JS_FRIEND_API(void)
 js::VisitGrayWrapperTargets(Zone *zone, GCThingCallback callback, void *closure)
 {
+    JSRuntime *rt = zone->runtimeFromMainThread();
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
         for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
             gc::Cell *thing = e.front().key.wrapped;
-            if (thing->isMarked(gc::GRAY))
+            if (!IsInsideNursery(rt, thing) && thing->isMarked(gc::GRAY))
                 callback(closure, thing);
         }
     }
 }
 
 JS_FRIEND_API(JSObject *)
 js::GetWeakmapKeyDelegate(JSObject *key)
 {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -716,19 +716,21 @@ js_Disassemble1(JSContext *cx, HandleScr
 /************************************************************************/
 
 const size_t Sprinter::DefaultSize = 64;
 
 bool
 Sprinter::realloc_(size_t newSize)
 {
     JS_ASSERT(newSize > (size_t) offset);
-    char *newBuf = (char *) context->realloc_(base, newSize);
-    if (!newBuf)
+    char *newBuf = (char *) js_realloc(base, newSize);
+    if (!newBuf) {
+        reportOutOfMemory();
         return false;
+    }
     base = newBuf;
     size = newSize;
     base[size - 1] = 0;
     return true;
 }
 
 Sprinter::Sprinter(JSContext *cx)
   : context(cx),
@@ -746,19 +748,21 @@ Sprinter::~Sprinter()
 #endif
     js_free(base);
 }
 
 bool
 Sprinter::init()
 {
     JS_ASSERT(!initialized);
-    base = (char *) context->malloc_(DefaultSize);
-    if (!base)
+    base = (char *) js_malloc(DefaultSize);
+    if (!base) {
+        reportOutOfMemory();
         return false;
+    }
 #ifdef DEBUG
     initialized = true;
 #endif
     *base = 0;
     size = DefaultSize;
     base[size - 1] = 0;
     return true;
 }
@@ -895,17 +899,18 @@ Sprinter::getOffset() const
     return offset;
 }
 
 void
 Sprinter::reportOutOfMemory()
 {
     if (reportedOOM)
         return;
-    js_ReportOutOfMemory(context);
+    if (context)
+        js_ReportOutOfMemory(context);
     reportedOOM = true;
 }
 
 bool
 Sprinter::hadOutOfMemory() const
 {
     return reportedOOM;
 }
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -340,20 +340,20 @@ WorkerThreadState::init(JSRuntime *rt)
     for (size_t i = 0; i < numThreads; i++) {
         WorkerThread &helper = threads[i];
         helper.runtime = rt;
         helper.threadData.construct(rt);
         helper.threadData.ref().addToThreadList();
         helper.thread = PR_CreateThread(PR_USER_THREAD,
                                         WorkerThread::ThreadMain, &helper,
                                         PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
-        if (!helper.thread) {
+        if (!helper.thread || !helper.threadData.ref().init()) {
             for (size_t j = 0; j < numThreads; j++)
                 threads[j].destroy();
-            js_delete(threads);
+            js_free(threads);
             threads = NULL;
             numThreads = 0;
             return false;
         }
     }
 
     resetAsmJSFailureState();
     return true;
@@ -363,17 +363,17 @@ WorkerThreadState::~WorkerThreadState()
 {
     /*
      * Join created threads first, which needs locks and condition variables
      * to be intact.
      */
     if (threads) {
         for (size_t i = 0; i < numThreads; i++)
             threads[i].destroy();
-        js_delete(threads);
+        js_free(threads);
     }
 
     if (workerLock)
         PR_DestroyLock(workerLock);
 
     if (mainWakeup)
         PR_DestroyCondVar(mainWakeup);
 
@@ -550,18 +550,20 @@ WorkerThread::destroy()
 
             /* Notify all workers, to ensure that this thread wakes up. */
             state.notifyAll(WorkerThreadState::WORKER);
         }
 
         PR_JoinThread(thread);
     }
 
-    if (!threadData.empty())
+    if (!threadData.empty()) {
         threadData.ref().removeFromThreadList();
+        threadData.destroy();
+    }
 }
 
 /* static */
 void
 WorkerThread::ThreadMain(void *arg)
 {
     PR_SetCurrentThreadName("Analysis Helper");
     static_cast<WorkerThread *>(arg)->threadLoop();
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5374,17 +5374,17 @@ main(int argc, char **argv, char **envp)
                                "  off: disable GVN\n"
                                "  pessimistic: use pessimistic GVN\n"
                                "  optimistic: (default) use optimistic GVN")
         || !op.addStringOption('\0', "ion-licm", "on/off",
                                "Loop invariant code motion (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",
                                "Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-range-analysis", "on/off",
-                               "Range analysis (default: off, on to enable)")
+                               "Range analysis (default: on, off to disable)")
         || !op.addBoolOption('\0', "ion-check-range-analysis",
                                "Range analysis checking")
         || !op.addStringOption('\0', "ion-inlining", "on/off",
                                "Inline methods where possible (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-osr", "on/off",
                                "On-Stack Replacement (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-limit-script-size", "on/off",
                                "Don't compile very large scripts (default: on, off to disable)")
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -10,16 +10,18 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 
 #include "gc/Marking.h"
 #if ENABLE_YARR_JIT
 #include "yarr/YarrJIT.h"
+#else
+#include "yarr/YarrInterpreter.h"
 #endif
 
 /*
  * JavaScript Regular Expressions
  *
  * There are several engine concepts associated with a single logical regexp:
  *
  *   RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp"
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -223,19 +223,22 @@ inline void
 InterpreterStack::purge(JSRuntime *rt)
 {
     rt->freeLifoAlloc.transferUnusedFrom(&allocator_);
 }
 
 uint8_t *
 InterpreterStack::allocateFrame(JSContext *cx, size_t size)
 {
-    size_t maxFrames = cx->compartment()->principals == cx->runtime()->trustedPrincipals()
-                       ? MAX_FRAMES_TRUSTED
-                       : MAX_FRAMES;
+    size_t maxFrames;
+    if (cx->compartment()->principals == cx->runtime()->trustedPrincipals())
+        maxFrames = MAX_FRAMES_TRUSTED;
+    else
+        maxFrames = MAX_FRAMES;
+
     if (JS_UNLIKELY(frameCount_ >= maxFrames)) {
         js_ReportOverRecursed(cx);
         return NULL;
     }
 
     uint8_t *buffer = reinterpret_cast<uint8_t *>(allocator_.alloc(size));
     if (!buffer)
         return NULL;
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -32,16 +32,18 @@
 #include "nsPrincipal.h"
 #include "mozilla/Attributes.h"
 #include "nsIScriptContext.h"
 #include "nsJSEnvironment.h"
 #include "nsXMLHttpRequest.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/XPTInterfaceInfoManager.h"
 #include "nsDOMClassInfoID.h"
+#include "nsGlobalWindow.h"
+
 
 using namespace mozilla;
 using namespace js;
 using namespace xpc;
 
 using mozilla::dom::DestroyProtoAndIfaceCache;
 
 /***************************************************************************/
@@ -2930,16 +2932,363 @@ CreateXMLHttpRequest(JSContext *cx, unsi
 
     rv = nsContentUtils::WrapNative(cx, global, xhr, vp);
     if (NS_FAILED(rv))
         return false;
 
     return true;
 }
 
+bool
+NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable,
+                     bool doclone, MutableHandleValue vp);
+
+/*
+ * Instead of simply wrapping a function into another compartment,
+ * this helper function creates a native function in the target
+ * compartment and forwards the call to the original function.
+ * That call will be different than a regular JS function call in
+ * that, the |this| is left unbound, and all the non-native JS
+ * object arguments will be cloned using the structured clone
+ * algorithm.
+ * The return value is the new forwarder function, wrapped into
+ * the caller's compartment.
+ * The 3rd argument is the name of the property that will
+ * be set on the target scope, with the forwarder function as
+ * the value.
+ * The principal of the caller must subsume that of the target.
+ *
+ * Expected type of the arguments and the return value:
+ * function exportFunction(function funToExport,
+ *                         object targetScope,
+ *                         string name)
+ */
+static bool
+ExportFunction(JSContext *cx, unsigned argc, jsval *vp)
+{
+    MOZ_ASSERT(cx);
+    if (argc < 3) {
+        JS_ReportError(cx, "Function requires at least 3 arguments");
+        return false;
+    }
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args[0].isObject() || !args[1].isObject() || !args[2].isString()) {
+        JS_ReportError(cx, "Invalid argument");
+        return false;
+    }
+
+    RootedObject funObj(cx, &args[0].toObject());
+    RootedObject targetScope(cx, &args[1].toObject());
+    RootedString funName(cx, args[2].toString());
+
+    // We can only export functions to scopes those are transparent for us,
+    // so if there is a security wrapper around targetScope we must throw.
+    targetScope = CheckedUnwrap(targetScope);
+    if (!targetScope) {
+        JS_ReportError(cx, "Permission denied to export function into scope");
+        return false;
+    }
+
+    if (JS_GetStringLength(funName) == 0) {
+        JS_ReportError(cx, "3rd argument should be a non-empty string");
+        return false;
+    }
+
+    {
+        // We need to operate in the target scope from here on, let's enter
+        // its compartment.
+        JSAutoCompartment ac(cx, targetScope);
+
+        // Unwrapping to see if we have a callable.
+        funObj = UncheckedUnwrap(funObj);
+        if (!JS_ObjectIsCallable(cx, funObj)) {
+            JS_ReportError(cx, "First argument must be a function");
+            return false;
+        }
+
+        // The function forwarder will live in the target compartment. Since
+        // this function will be referenced from its private slot, to avoid a
+        // GC hazard, we must wrap it to the same compartment.
+        if (!JS_WrapObject(cx, funObj.address()))
+            return false;
+
+        RootedId id(cx);
+        if (!JS_ValueToId(cx, args[2], id.address()))
+            return false;
+
+        // And now, let's create the forwarder function in the target compartment
+        // for the function the be exported.
+        if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, args.rval())) {
+            JS_ReportError(cx, "Exporting function failed");
+            return false;
+        }
+
+        // We have the forwarder function in the target compartment, now
+        // we have to add it to the target scope as a property.
+        if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(),
+                                   JS_PropertyStub, JS_StrictPropertyStub,
+                                   JSPROP_ENUMERATE))
+            return false;
+    }
+
+    // Finally we have to re-wrap the exported function back to the caller compartment.
+    if (!JS_WrapValue(cx, args.rval().address()))
+        return false;
+
+    return true;
+}
+
+static bool
+GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno)
+{
+    JSScript *script;
+    if (JS_DescribeScriptedCaller(cx, &script, &lineno)) {
+        if (const char *cfilename = JS_GetScriptFilename(cx, script)) {
+            filename.Assign(nsDependentCString(cfilename));
+            return true;
+        }
+    }
+    return false;
+}
+
+namespace xpc {
+bool
+IsReflector(JSObject *obj)
+{
+    return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj);
+}
+} /* namespace xpc */
+
+enum ForwarderCloneTags {
+    SCTAG_BASE = JS_SCTAG_USER_MIN,
+    SCTAG_REFLECTOR
+};
+
+static JSObject *
+CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag,
+                       uint32_t data, void *closure)
+{
+    MOZ_ASSERT(closure, "Null pointer!");
+    AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
+    if (tag == SCTAG_REFLECTOR) {
+        MOZ_ASSERT(!data);
+
+        size_t idx;
+        if (JS_ReadBytes(reader, &idx, sizeof(size_t))) {
+            RootedObject reflector(cx, reflectors->handleAt(idx));
+            MOZ_ASSERT(reflector, "No object pointer?");
+            MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!");
+
+            JS_WrapObject(cx, reflector.address());
+            JS_ASSERT(WrapperFactory::IsXrayWrapper(reflector) ||
+                      IsReflector(reflector));
+
+            return reflector;
+        }
+    }
+
+    JS_ReportError(cx, "CloneNonReflectorsRead error");
+    return nullptr;
+}
+
+static bool
+CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer,
+                        Handle<JSObject *> obj, void *closure)
+{
+    MOZ_ASSERT(closure, "Null pointer!");
+
+    // We need to maintain a list of reflectors to make sure all these objects
+    // are properly rooter. Only their indices will be serialized.
+    AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
+    if (IsReflector(obj)) {
+        if (!reflectors->append(obj))
+            return false;
+
+        size_t idx = reflectors->length()-1;
+        if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) &&
+            JS_WriteBytes(writer, &idx, sizeof(size_t))) {
+            return true;
+        }
+    }
+
+    JS_ReportError(cx, "CloneNonReflectorsWrite error");
+    return false;
+}
+
+JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = {
+    CloneNonReflectorsRead,
+    CloneNonReflectorsWrite,
+    nullptr
+};
+
+/*
+ * This is a special structured cloning, that clones only non-reflectors.
+ * The function assumes the cx is already entered the compartment we want
+ * to clone to, and that if val is an object is from the compartment we
+ * clone from.
+ */
+bool
+CloneNonReflectors(JSContext *cx, MutableHandleValue val)
+{
+    JSAutoStructuredCloneBuffer buffer;
+    AutoObjectVector rootedReflectors(cx);
+    {
+        // For parsing val we have to enter its compartment.
+        // (unless it's a primitive)
+        Maybe<JSAutoCompartment> ac;
+        if (val.isObject()) {
+            ac.construct(cx, &val.toObject());
+        }
+
+        if (!buffer.write(cx, val,
+            &gForwarderStructuredCloneCallbacks,
+            &rootedReflectors))
+        {
+            return false;
+        }
+    }
+
+    // Now recreate the clones in the target compartment.
+    RootedValue rval(cx);
+    if (!buffer.read(cx, val.address(),
+        &gForwarderStructuredCloneCallbacks,
+        &rootedReflectors))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Similar to evalInSandbox except this one is used to eval a script in the
+ * scope of a window. Also note, that the return value and the possible exceptions
+ * in the script are structured cloned, unless they are natives (then they are just
+ * wrapped).
+ * Principal of the caller must subsume the target's.
+ *
+ * Expected type of the arguments:
+ * value evalInWindow(string script,
+ *                    object window)
+ */
+static bool
+EvalInWindow(JSContext *cx, unsigned argc, jsval *vp)
+{
+    MOZ_ASSERT(cx);
+    if (argc < 2) {
+        JS_ReportError(cx, "Function requires two arguments");
+        return false;
+    }
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args[0].isString() || !args[1].isObject()) {
+        JS_ReportError(cx, "Invalid arguments");
+        return false;
+    }
+
+    RootedString srcString(cx, args[0].toString());
+    RootedObject targetScope(cx, &args[1].toObject());
+
+    // If we cannot unwrap we must not eval in it.
+    targetScope = CheckedUnwrap(targetScope);
+    if (!targetScope) {
+        JS_ReportError(cx, "Permission denied to eval in target scope");
+        return false;
+    }
+
+    // Make sure that we have a window object.
+    RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false));
+    nsCOMPtr<nsIGlobalObject> global;
+    nsCOMPtr<nsPIDOMWindow> window;
+    if (!JS_IsGlobalObject(inner) ||
+        !(global = GetNativeForGlobal(inner)) ||
+        !(window = do_QueryInterface(global)))
+    {
+        JS_ReportError(cx, "Second argument must be a window");
+        return false;
+    }
+
+    nsCOMPtr<nsIScriptContext> context =
+        (static_cast<nsGlobalWindow*>(window.get()))->GetScriptContext();
+    if (!context) {
+        JS_ReportError(cx, "Script context needed");
+        return false;
+    }
+
+    if (!context->GetScriptsEnabled()) {
+        JS_ReportError(cx, "Scripts are disabled in this window");
+        return false;
+    }
+
+    nsCString filename;
+    unsigned lineNo;
+    if (!GetFilenameAndLineNumber(cx, filename, lineNo)) {
+        // Default values for non-scripted callers.
+        filename.Assign("Unknown");
+        lineNo = 0;
+    }
+
+    nsDependentJSString srcDepString;
+    srcDepString.init(cx, srcString);
+
+    {
+        // CompileOptions must be created from the context
+        // we will execute this script in.
+        JSContext *wndCx = context->GetNativeContext();
+        AutoCxPusher pusher(wndCx);
+        JS::CompileOptions compileOptions(wndCx);
+        compileOptions.setFileAndLine(filename.get(), lineNo);
+
+        // We don't want the JS engine to automatically report
+        // uncaught exceptions.
+        nsJSUtils::EvaluateOptions evaluateOptions;
+        evaluateOptions.setReportUncaught(false);
+
+        nsresult rv = nsJSUtils::EvaluateString(wndCx,
+                                                srcDepString,
+                                                targetScope,
+                                                compileOptions,
+                                                evaluateOptions,
+                                                args.rval().address());
+
+        if (NS_FAILED(rv)) {
+            // If there was an exception we get it as a return value, if
+            // the evaluation failed for some other reason, then a default
+            // exception is raised.
+            MOZ_ASSERT(!JS_IsExceptionPending(wndCx),
+                       "Exception should be delivered as return value.");
+            if (args.rval().isUndefined()) {
+                MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY);
+                return false;
+            }
+
+            // If there was an exception thrown we should set it
+            // on the calling context.
+            RootedValue exn(wndCx, args.rval());
+            // First we should reset the return value.
+            args.rval().set(UndefinedValue());
+
+            // Then clone the exception.
+            if (CloneNonReflectors(cx, &exn))
+                JS_SetPendingException(cx, exn);
+
+            return false;
+        }
+    }
+
+    // Let's clone the return value back to the callers compartment.
+    if (!CloneNonReflectors(cx, args.rval())) {
+        args.rval().set(UndefinedValue());
+        return false;
+    }
+
+    return true;
+}
+
 static bool
 sandbox_enumerate(JSContext *cx, HandleObject obj)
 {
     return JS_EnumerateStandardClasses(cx, obj);
 }
 
 static bool
 sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
@@ -3345,16 +3694,22 @@ xpc_CreateSandboxObject(JSContext *cx, j
           return NS_ERROR_XPC_UNEXPECTED;
 
         if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
             return NS_ERROR_XPC_UNEXPECTED;
 
         if (options.wantXHRConstructor &&
             !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR))
             return NS_ERROR_XPC_UNEXPECTED;
+
+        if (options.wantExportHelpers &&
+            (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) ||
+             !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0)))
+            return NS_ERROR_XPC_UNEXPECTED;
+
     }
 
     if (vp) {
         // We have this crazy behavior where wantXrays=false also implies that the
         // returned sandbox is implicitly waived. We've stopped advertising it, but
         // keep supporting it for now.
         *vp = OBJECT_TO_JSVAL(sandbox);
         if (options.wantXrays && !JS_WrapValue(cx, vp))
@@ -3615,16 +3970,20 @@ ParseOptionsObject(JSContext *cx, jsval 
     rv = GetBoolPropFromOptions(cx, optionsObject,
                                 "wantComponents", &options.wantComponents);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetBoolPropFromOptions(cx, optionsObject,
                                 "wantXHRConstructor", &options.wantXHRConstructor);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    rv = GetBoolPropFromOptions(cx, optionsObject,
+                                "wantExportHelpers", &options.wantExportHelpers);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     rv = GetStringPropFromOptions(cx, optionsObject,
                                   "sandboxName", options.sandboxName);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetObjPropFromOptions(cx, optionsObject,
                                "sameZoneAs", options.sameZoneAs.address());
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -4200,41 +4559,78 @@ nsXPCComponents_Utils::CreateDateIn(cons
 
     if (!JS_WrapObject(cx, obj.address()))
         return NS_ERROR_FAILURE;
     *rval = ObjectValue(*obj);
     return NS_OK;
 }
 
 bool
-FunctionWrapper(JSContext *cx, unsigned argc, Value *vp)
+NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
     MOZ_ASSERT(v.isObject(), "weird function");
 
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     if (!obj) {
         return false;
     }
     return JS_CallFunctionValue(cx, obj, v, args.length(), args.array(), vp);
 }
 
+/*
+ * Forwards the call to the exported function. Clones all the non reflectors, ignores
+ * the |this| argument.
+ */
 bool
-WrapCallable(JSContext *cx, HandleObject obj, HandleId id, HandleObject propobj,
-             MutableHandleValue vp)
-{
-    JSFunction *fun = js::NewFunctionByIdWithReserved(cx, FunctionWrapper, 0, 0,
-                                                      JS_GetGlobalForObject(cx, obj), id);
+CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
+    NS_ASSERTION(v.isObject(), "weird function");
+    RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject()));
+    {
+        JSAutoCompartment ac(cx, origFunObj);
+        // Note: only the arguments are cloned not the |this| or the |callee|.
+        // Function forwarder does not use those.
+        for (unsigned i = 0; i < args.length(); i++) {
+            if (!CloneNonReflectors(cx, args[i])) {
+                return false;
+            }
+        }
+
+        // JS API does not support any JSObject to JSFunction conversion,
+        // so let's use JS_CallFunctionValue instead.
+        RootedValue functionVal(cx);
+        functionVal.setObject(*origFunObj);
+
+        if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp))
+            return false;
+    }
+
+    // Return value must be wrapped.
+    return JS_WrapValue(cx, vp);
+}
+
+bool
+NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone,
+                     MutableHandleValue vp)
+{
+    JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder :
+                                                                    NonCloningFunctionForwarder,
+                                                                    0,0, JS::CurrentGlobalOrNull(cx), id);
+
     if (!fun)
         return false;
 
     JSObject *funobj = JS_GetFunctionObject(fun);
-    js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*propobj));
+    js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable));
     vp.setObject(*funobj);
     return true;
 }
 
 /* void makeObjectPropsNormal(jsval vobj); */
 NS_IMETHODIMP
 nsXPCComponents_Utils::MakeObjectPropsNormal(const Value &vobj, JSContext *cx)
 {
@@ -4262,17 +4658,17 @@ nsXPCComponents_Utils::MakeObjectPropsNo
         if (v.isPrimitive())
             continue;
 
         RootedObject propobj(cx, &v.toObject());
         // TODO Deal with non-functions.
         if (!js::IsWrapper(propobj) || !JS_ObjectIsCallable(cx, propobj))
             continue;
 
-        if (!WrapCallable(cx, obj, id, propobj, &v) ||
+        if (!NewFunctionForwarder(cx, id, propobj, /* doclone = */ false, &v) ||
             !JS_SetPropertyById(cx, obj, id, v))
             return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -4830,62 +5226,31 @@ nsXPCComponents::SetProperty(nsIXPConnec
             return NS_SUCCESS_I_DID_SOMETHING;
         }
         return NS_ERROR_FAILURE;
     }
 
     return NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN;
 }
 
-static bool
-ContentComponentsGetterOp(JSContext *cx, HandleObject obj, HandleId id,
-                          MutableHandleValue vp)
-{
-    // If chrome is accessing the Components object of content, allow.
-    MOZ_ASSERT(nsContentUtils::GetCurrentJSContext() == cx);
-    if (nsContentUtils::IsCallerChrome())
-        return true;
-
-    // If the caller is XBL, this is ok.
-    if (nsContentUtils::IsCallerXBL())
-        return true;
-
-    // Do Telemetry on how often this happens.
-    Telemetry::Accumulate(Telemetry::COMPONENTS_OBJECT_ACCESSED_BY_CONTENT, true);
-
-    // Warn once.
-    JSAutoCompartment ac(cx, obj);
-    nsCOMPtr<nsPIDOMWindow> win =
-        do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(obj));
-    if (win) {
-        nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
-        if (doc)
-            doc->WarnOnceAbout(nsIDocument::eComponents, /* asError = */ true);
-    }
-
-    return true;
-}
-
 // static
 bool
 nsXPCComponents::AttachComponentsObject(JSContext* aCx,
                                         XPCWrappedNativeScope* aScope)
 {
     RootedObject components(aCx, aScope->GetComponentsJSObject());
     if (!components)
         return false;
 
     RootedObject global(aCx, aScope->GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx));
 
     RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS));
-    JSPropertyOp getter = AccessCheck::isChrome(global) ? nullptr
-                                                        : &ContentComponentsGetterOp;
     return JS_DefinePropertyById(aCx, global, id, js::ObjectValue(*components),
-                                 getter, nullptr, JSPROP_PERMANENT | JSPROP_READONLY);
+                                 nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 /* void lookupMethod (); */
 NS_IMETHODIMP
 nsXPCComponents::LookupMethod(const JS::Value& object,
                               const JS::Value& name,
                               JSContext *cx,
                               JS::Value *retval)
@@ -4923,37 +5288,25 @@ nsXPCComponents::CanCreateWrapper(const 
 }
 
 /* string canCallMethod (in nsIIDPtr iid, in wstring methodName); */
 NS_IMETHODIMP
 nsXPCComponents::CanCallMethod(const nsIID * iid, const PRUnichar *methodName, char **_retval)
 {
     static const char* const allowed[] = { "isSuccessCode", "lookupMethod", nullptr };
     *_retval = xpc_CheckAccessList(methodName, allowed);
-    if (*_retval &&
-        methodName[0] == 'l' &&
-        !nsContentUtils::IsCallerXBL())
-    {
-        Telemetry::Accumulate(Telemetry::COMPONENTS_LOOKUPMETHOD_ACCESSED_BY_CONTENT, true);
-    }
     return NS_OK;
 }
 
 /* string canGetProperty (in nsIIDPtr iid, in wstring propertyName); */
 NS_IMETHODIMP
 nsXPCComponents::CanGetProperty(const nsIID * iid, const PRUnichar *propertyName, char **_retval)
 {
     static const char* const allowed[] = { "interfaces", "interfacesByID", "results", nullptr};
     *_retval = xpc_CheckAccessList(propertyName, allowed);
-    if (*_retval &&
-        propertyName[0] == 'i' &&
-        !nsContentUtils::IsCallerXBL())
-    {
-        Telemetry::Accumulate(Telemetry::COMPONENTS_INTERFACES_ACCESSED_BY_CONTENT, true);
-    }
     return NS_OK;
 }
 
 /* string canSetProperty (in nsIIDPtr iid, in wstring propertyName); */
 NS_IMETHODIMP
 nsXPCComponents::CanSetProperty(const nsIID * iid, const PRUnichar *propertyName, char **_retval)
 {
     // If you have to ask, then the answer is NO
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3695,23 +3695,25 @@ xpc_GetSafeJSContext()
 }
 
 namespace xpc {
 struct SandboxOptions {
     SandboxOptions(JSContext *cx)
         : wantXrays(true)
         , wantComponents(true)
         , wantXHRConstructor(false)
+        , wantExportHelpers(false)
         , proto(xpc_GetSafeJSContext())
         , sameZoneAs(xpc_GetSafeJSContext())
     { }
 
     bool wantXrays;
     bool wantComponents;
     bool wantXHRConstructor;
+    bool wantExportHelpers;
     JS::RootedObject proto;
     nsCString sandboxName;
     JS::RootedObject sameZoneAs;
 };
 
 JSObject *
 CreateGlobalObject(JSContext *cx, JSClass *clasp, nsIPrincipal *principal,
                    JS::CompartmentOptions& aOptions);
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -61,16 +61,18 @@ GetXBLScope(JSContext *cx, JSObject *con
 // Returns whether XBL scopes have been explicitly disabled for code running
 // in this compartment. See the comment around mAllowXBLScope.
 bool
 AllowXBLScope(JSCompartment *c);
 
 bool
 IsSandboxPrototypeProxy(JSObject *obj);
 
+bool
+IsReflector(JSObject *obj);
 } /* namespace xpc */
 
 namespace JS {
 
 struct RuntimeStats;
 
 }
 
--- a/js/xpconnect/tests/chrome/Makefile.in
+++ b/js/xpconnect/tests/chrome/Makefile.in
@@ -56,16 +56,17 @@ MOCHITEST_CHROME_FILES = \
 		outoflinexulscript.js \
 		subscript.js \
 		utf8_subscript.js \
 		test_cows.xul \
 		test_documentdomain.xul \
 		test_doublewrappedcompartments.xul \
 		test_evalInSandbox.xul \
 		file_evalInSandbox.html \
+		test_evalInWindow.xul \
 		test_exnstack.xul \
 		test_expandosharing.xul \
 		file_expandosharing.jsm \
 		test_exposeInDerived.xul \
 		test_getweakmapkeys.xul \
 		test_mozMatchesSelector.xul \
 		test_nodelists.xul \
 		test_precisegc.xul \
--- a/js/xpconnect/tests/chrome/test_bug795275.xul
+++ b/js/xpconnect/tests/chrome/test_bug795275.xul
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
   /** Test for Warning in content scopes about Components. **/
 
   SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv({set: [['dom.omit_components_in_content', false]]}, startLoad);
+  SimpleTest.executeSoon(startLoad);
   function startLoad() {
     for (var i = 1; i <= document.getElementsByTagName('iframe').length; ++i) {
       var frame = document.getElementById('frame' + i);
       frame.contentWindow.location = 'http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_bug795275.html';
       frame.onload = frameLoaded;
     }
   }
 
@@ -55,20 +55,20 @@ https://bugzilla.mozilla.org/show_bug.cg
     if (++gLoadCount == document.getElementsByTagName('iframe').length)
       go();
   }
 
   function getWin(id) { return document.getElementById(id).contentWindow.wrappedJSObject; }
   function go() {
     getWin('frame1').touchComponents();
     getWin('frame2').touchInterfaces();
-    getWin('frame3').touchLookupMethod();
+    ok(getWin('frame3').touchLookupMethod(), "Components.lookupMethod should be undefined");
     getWin('frame4').touchComponents();
     getWin('frame4').touchInterfaces();
-    getWin('frame4').touchLookupMethod();
+    ok(getWin('frame4').touchLookupMethod(), "Components.lookupMethod should be undefined");
     // This shouldn't warn.
     getWin('frame5').touchViaXBL();
 
     // Warnings are dispatched async, so stick ourselves at the end of the event
     // queue.
     setTimeout(done, 0);
   }
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_evalInWindow.xul
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877673
+-->
+<window title="Mozilla Bug 877673"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+      SimpleTest.waitForExplicitFinish();
+      const Cu = Components.utils;
+      var sb = new Cu.Sandbox("http://example.org", {wantExportHelpers: true});
+      sb.ok = ok;
+
+      function executeIn(frame, script, exceptionCb) {
+        sb.frame = frame;
+        sb.exceptionCb = exceptionCb;
+        if (exceptionCb) {
+          return Cu.evalInSandbox("try {evalInWindow('" + script + "',frame); ok(false, 'Exception should have been thrown.')} catch(e) {exceptionCb(e)}", sb);
+        }
+
+        return Cu.evalInSandbox("evalInWindow('" + script + "',frame)", sb);
+      }
+
+      function testSameOrigin(frame) {
+        frame.contentWindow.document.wrappedJSObject.str = "foobar";
+        is(executeIn(frame.contentWindow, "document.str"), "foobar",
+           "Same origin string property access.");
+
+        executeIn(frame.contentWindow, 'document.obj = {prop: "foobar"}');
+        is((executeIn(frame.contentWindow, "document.obj")).prop, "foobar",
+           "Same origin object property access (cloning).");
+        isnot(executeIn(frame.contentWindow, "document.obj"), frame.contentWindow.document.wrappedJSObject.obj,
+              "Ensure cloning for js objects.");
+        is(executeIn(frame.contentWindow, "document"), frame.contentWindow.document,
+           "Xrayables should just pass without cloning.");
+        is( executeIn(frame.contentWindow, "({a:{doc: document}})").a.doc, frame.contentWindow.document,
+           "Deep cloning works.");
+
+        executeIn(frame.contentWindow, "throw 42", function(e){is(e, 42,
+                                                                  "Exception was thrown from script.")});
+
+        executeIn(frame.contentDocument, "var a = 42;", function(e){ok(e.toString().indexOf("Second argument must be a window") > -1,
+                                                                       "Passing non-window to evalInWindow should throw.");});
+        testDone();
+      }
+
+      function testCrossOrigin(frame) {
+        executeIn(frame.contentWindow, "var a = 42;", function(e){ok(e.toString().indexOf("Permission denied") > -1,
+                                                                     "Executing script in a window from cross origin should throw.");});
+        testDone();
+      }
+
+      var testsRun = 0;
+      function testDone() {
+        if (++testsRun == 2)
+          SimpleTest.finish();
+      }
+  ]]></script>
+  <iframe src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"
+          onload="testSameOrigin(this)">
+  </iframe>
+  <iframe src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_empty.html"
+          onload="testCrossOrigin(this)">
+  </iframe>
+</window>
\ No newline at end of file
--- a/js/xpconnect/tests/mochitest/Makefile.in
+++ b/js/xpconnect/tests/mochitest/Makefile.in
@@ -76,17 +76,16 @@ MOCHITEST_FILES =	chrome_wrappers_helper
 		file_bug760131.html \
 		test_bug764389.html \
 		test_bug772288.html \
 		test_bug781476.html \
 		file_bug781476.html \
 		test_bug785096.html \
 		test_bug789713.html \
 		test_bug790732.html \
-		file_bug790732.html \
 		test_bug793969.html \
 		file_bug795275.html \
 		file_bug795275.xml \
 		file_bug799348.html \
 		test_bug800864.html \
 		test_bug802557.html \
 		file_bug802557.html \
 		test_bug803730.html \
deleted file mode 100644
--- a/js/xpconnect/tests/mochitest/file_bug790732.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script>
-function testShim() {
-
-  // Basic stuff
-  ok(Components, "Components shim exists!");
-  var Ci = Components.interfaces;
-  ok(Ci, "interfaces shim exists!");
-  is(typeof Components.classes, 'undefined', "Shouldn't have a Cc");
-
-  // Check each interface that we shim. We start by checking specific
-  // constants for a couple of interfaces, and then once it's pretty clear that
-  // it's working as intended we just check that the objects themselves are the
-  // same.
-  is(Ci.nsIDOMFileReader.DONE, FileReader.DONE);
-  is(Ci.nsIXMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.HEADERS_RECEIVED);
-  is(Ci.nsIDOMDOMException.DATA_CLONE_ERR, DOMException.DATA_CLONE_ERR);
-  is(Ci.nsIDOMNode.DOCUMENT_NODE, Node.DOCUMENT_NODE);
-  is(Ci.nsIDOMUserDataHandler.NODE_CLONED, UserDataHandler.NODE_CLONED);
-  is(Ci.nsIDOMCSSPrimitiveValue.CSS_PX, CSSPrimitiveValue.CSS_PX);
-  is(Ci.nsIDOMCSSRule.NAMESPACE_RULE, CSSRule.NAMESPACE_RULE);
-  is(Ci.nsIDOMCSSValue.CSS_PRIMITIVE_VALUE, CSSValue.CSS_PRIMITIVE_VALUE);
-  is(Ci.nsIDOMEvent.FOCUS, Event.FOCUS);
-  is(Ci.nsIDOMNSEvent.CLICK, Event.CLICK);
-  is(Ci.nsIDOMKeyEvent, KeyEvent);
-  is(Ci.nsIDOMMouseEvent, MouseEvent);
-  is(Ci.nsIDOMMouseScrollEvent, MouseScrollEvent);
-  is(Ci.nsIDOMMutationEvent, MutationEvent);
-  is(Ci.nsIDOMSimpleGestureEvent, SimpleGestureEvent);
-  is(Ci.nsIDOMUIEvent, UIEvent);
-  is(Ci.nsIDOMGeoPositionError, GeoPositionError);
-  is(Ci.nsIDOMHTMLMediaElement, HTMLMediaElement);
-  is(Ci.nsIDOMMediaError, MediaError);
-  is(Ci.nsIDOMLoadStatus, LoadStatus);
-  is(Ci.nsIDOMOfflineResourceList, OfflineResourceList);
-  is(Ci.nsIDOMRange, Range);
-  is(Ci.nsIDOMSVGLength, SVGLength);
-  is(Ci.nsIDOMNodeFilter, NodeFilter);
-  is(Ci.nsIDOMXPathNamespace, XPathNamespace);
-  is(Ci.nsIDOMXPathResult, XPathResult);
-
-  // Test for Bug 895231
-  for (var k of Object.keys(Components.interfaces)) {
-    ok(SpecialPowers.Ci.hasOwnProperty(k),
-       k + " should be removed from the Components shim");
-  }
-}
-</script>
-</head>
-<body>
-</body>
-</html>
--- a/js/xpconnect/tests/mochitest/file_bug795275.html
+++ b/js/xpconnect/tests/mochitest/file_bug795275.html
@@ -8,17 +8,17 @@
 <script type="application/javascript">
   function touchComponents() {
     Components;
   }
   function touchInterfaces() {
     Components.interfaces;
   }
   function touchLookupMethod() {
-    Components.lookupMethod(document, 'getElementById');
+    return !Components.lookupMethod;
   }
 
   function touchViaXBL() {
     // Make sure none of this warns.
     var div = document.getElementById('dummy');
     div.testProp;
     div.testMethod();
   }
--- a/js/xpconnect/tests/mochitest/test_bug790732.html
+++ b/js/xpconnect/tests/mochitest/test_bug790732.html
@@ -5,40 +5,62 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 790732</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
-  /** Test for the Components shim. We split into two files because this stuff
-      is currently pref-controlled. **/
-  SimpleTest.waitForExplicitFinish();
+  // Basic stuff
+  ok(Components, "Components shim exists!");
+  var Ci = Components.interfaces;
+  ok(Ci, "interfaces shim exists!");
+  is(typeof Components.classes, 'undefined', "Shouldn't have a Cc");
 
-  function prepare() {
-    SpecialPowers.pushPrefEnv({set: [['dom.omit_components_in_content', true]]},
-                              function () { $('ifr').onload = go;
-                                            $('ifr').contentWindow.location =
-                                              '/tests/js/xpconnect/tests/mochitest/file_bug790732.html'; }
-                             );
-  }
+  // Check each interface that we shim. We start by checking specific
+  // constants for a couple of interfaces, and then once it's pretty clear that
+  // it's working as intended we just check that the objects themselves are the
+  // same.
+  is(Ci.nsIDOMFileReader.DONE, FileReader.DONE);
+  is(Ci.nsIXMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.HEADERS_RECEIVED);
+  is(Ci.nsIDOMDOMException.DATA_CLONE_ERR, DOMException.DATA_CLONE_ERR);
+  is(Ci.nsIDOMNode.DOCUMENT_NODE, Node.DOCUMENT_NODE);
+  is(Ci.nsIDOMUserDataHandler.NODE_CLONED, UserDataHandler.NODE_CLONED);
+  is(Ci.nsIDOMCSSPrimitiveValue.CSS_PX, CSSPrimitiveValue.CSS_PX);
+  is(Ci.nsIDOMCSSRule.NAMESPACE_RULE, CSSRule.NAMESPACE_RULE);
+  is(Ci.nsIDOMCSSValue.CSS_PRIMITIVE_VALUE, CSSValue.CSS_PRIMITIVE_VALUE);
+  is(Ci.nsIDOMEvent.FOCUS, Event.FOCUS);
+  is(Ci.nsIDOMNSEvent.CLICK, Event.CLICK);
+  is(Ci.nsIDOMKeyEvent, KeyEvent);
+  is(Ci.nsIDOMMouseEvent, MouseEvent);
+  is(Ci.nsIDOMMouseScrollEvent, MouseScrollEvent);
+  is(Ci.nsIDOMMutationEvent, MutationEvent);
+  is(Ci.nsIDOMSimpleGestureEvent, SimpleGestureEvent);
+  is(Ci.nsIDOMUIEvent, UIEvent);
+  is(Ci.nsIDOMGeoPositionError, GeoPositionError);
+  is(Ci.nsIDOMHTMLMediaElement, HTMLMediaElement);
+  is(Ci.nsIDOMMediaError, MediaError);
+  is(Ci.nsIDOMLoadStatus, LoadStatus);
+  is(Ci.nsIDOMOfflineResourceList, OfflineResourceList);
+  is(Ci.nsIDOMRange, Range);
+  is(Ci.nsIDOMSVGLength, SVGLength);
+  is(Ci.nsIDOMNodeFilter, NodeFilter);
+  is(Ci.nsIDOMXPathNamespace, XPathNamespace);
+  is(Ci.nsIDOMXPathResult, XPathResult);
 
-  function go() {
-    ok(true, "Started test");
-    var iwin = $('ifr').contentWindow;
-    iwin.ok = ok;
-    iwin.is = is;
-    iwin.testShim();
-    SimpleTest.finish();
+  // Test for Bug 895231
+  for (var k of Object.keys(Components.interfaces)) {
+    ok(SpecialPowers.Ci.hasOwnProperty(k),
+       k + " should be removed from the Components shim");
   }
 
   </script>
 </head>
-<body onload="prepare()">
+<body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=790732">Mozilla Bug 790732</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <iframe id="ifr"></iframe>
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_exportFunction.js
@@ -0,0 +1,73 @@
+function run_test() {
+  var Cu = Components.utils;
+  var epsb = new Cu.Sandbox(["http://example.com", "http://example.org"], { wantExportHelpers: true });
+  subsb = new Cu.Sandbox("http://example.com", { wantXHRConstructor: true });
+  subsb2 = new Cu.Sandbox("http://example.com", { wantXHRConstructor: true });
+  xorigsb = new Cu.Sandbox("http://test.com");
+
+  epsb.subsb = subsb;
+  epsb.xorigsb = xorigsb;
+  epsb.do_check_true = do_check_true;
+  epsb.do_check_eq = do_check_eq;
+  epsb.do_check_neq = do_check_neq;
+
+  // Exporting should work if prinicipal of the source sandbox
+  // subsumes the principal of the target sandbox.
+  Cu.evalInSandbox("(" + function() {
+    Object.prototype.protoProp = "common";
+    var wasCalled = false;
+    var _this = this;
+    var funToExport = function(a, obj, native, mixed) {
+      do_check_eq(a, 42);
+      do_check_neq(obj, subsb.tobecloned);
+      do_check_eq(obj.cloned, "cloned");
+      do_check_eq(obj.protoProp, "common");
+      do_check_eq(native, subsb.native);
+      do_check_eq(_this, this);
+      do_check_eq(mixed.xrayed, subsb.xrayed);
+      do_check_eq(mixed.xrayed2, subsb.xrayed2);
+      wasCalled = true;
+    };
+    this.checkIfCalled = function() {
+      do_check_true(wasCalled);
+      wasCalled = false;
+    }
+    exportFunction(funToExport, subsb, "imported");
+  }.toSource() + ")()", epsb);
+
+  subsb.xrayed = Cu.evalInSandbox("(" + function () {
+      return new XMLHttpRequest();
+  }.toSource() + ")()", subsb2);
+
+  // Exported function should be able to be call from the
+  // target sandbox. Native arguments should be just wrapped
+  // every other argument should be cloned.
+  Cu.evalInSandbox("(" + function () {
+    native = new XMLHttpRequest();
+    xrayed2 = XPCNativeWrapper(new XMLHttpRequest());
+    mixed = { xrayed: xrayed, xrayed2: xrayed2 };
+    tobecloned = { cloned: "cloned" };
+    imported(42,tobecloned, native, mixed);
+  }.toSource() + ")()", subsb);
+
+  // Apply should work but the |this| argument should not be
+  // possible to be changed.
+  Cu.evalInSandbox("(" + function() {
+    imported.apply("something", [42, tobecloned, native, mixed]);
+  }.toSource() + ")()", subsb);
+
+  Cu.evalInSandbox("(" + function() {
+    checkIfCalled();
+  }.toSource() + ")()", epsb);
+
+  // Exporting should throw if princpal of the source sandbox does
+  // not subsume the principal of the target.
+  Cu.evalInSandbox("(" + function() {
+    try{
+      exportFunction(function(){}, this.xorigsb, "denied");
+      do_check_true(false);
+    } catch (e) {
+      do_check_true(e.toString().indexOf('Permission denied') > -1);
+    }
+  }.toSource() + ")()", epsb);
+}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -42,9 +42,10 @@ fail-if = os == "android"
 [test_attributes.js]
 [test_params.js]
 [test_tearoffs.js]
 [test_want_components.js]
 [test_components.js]
 [test_allowedDomains.js]
 [test_allowedDomainsXHR.js]
 [test_nuke_sandbox.js]
+[test_exportFunction.js]
 [test_watchdog.js]
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -4567,19 +4567,19 @@ nsCSSFrameConstructor::FindMathMLData(El
     SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
     SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
     SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
-    SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmsupFrame),
-    SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmsubFrame),
-    SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmsubsupFrame),
+    SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
+    SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
+    SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
     SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
     SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
     SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
     SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmphantomFrame),
     SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
     SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
     SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame),
     SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame),
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -85,19 +85,16 @@ FRAME_ID(nsMathMLmoFrame)
 FRAME_ID(nsMathMLmoverFrame)
 FRAME_ID(nsMathMLmpaddedFrame)
 FRAME_ID(nsMathMLmphantomFrame)
 FRAME_ID(nsMathMLmrootFrame)
 FRAME_ID(nsMathMLmrowFrame)
 FRAME_ID(nsMathMLmspaceFrame)
 FRAME_ID(nsMathMLmsqrtFrame)
 FRAME_ID(nsMathMLmstyleFrame)
-FRAME_ID(nsMathMLmsubFrame)
-FRAME_ID(nsMathMLmsubsupFrame)
-FRAME_ID(nsMathMLmsupFrame)
 FRAME_ID(nsMathMLmtableFrame)
 FRAME_ID(nsMathMLmtableOuterFrame)
 FRAME_ID(nsMathMLmtdFrame)
 FRAME_ID(nsMathMLmtdInnerFrame)
 FRAME_ID(nsMathMLmtrFrame)
 FRAME_ID(nsMathMLmunderFrame)
 FRAME_ID(nsMathMLmunderoverFrame)
 FRAME_ID(nsMathMLsemanticsFrame)
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -17,16 +17,17 @@
 
 /* nsIFrame is in the process of being deCOMtaminated, i.e., this file is eventually
    going to be eliminated, and all callers will use nsFrame instead.  At the moment
    we're midway through this process, so you will see inlined functions and member
    variables in this file.  -dwh */
 
 #include <stdio.h>
 #include "nsQueryFrame.h"
+#include "nsRegion.h"
 #include "nsStyleContext.h"
 #include "nsStyleStruct.h"
 #include "nsStyleStructFwd.h"
 #include "nsHTMLReflowMetrics.h"
 #include "nsFrameList.h"
 #include "nsIContent.h"
 #include "nsAlgorithm.h"
 #include "mozilla/layout/FrameChildList.h"
--- a/layout/mathml/moz.build
+++ b/layout/mathml/moz.build
@@ -21,16 +21,13 @@ CPP_SOURCES += [
     'nsMathMLmoFrame.cpp',
     'nsMathMLmpaddedFrame.cpp',
     'nsMathMLmphantomFrame.cpp',
     'nsMathMLmrootFrame.cpp',
     'nsMathMLmrowFrame.cpp',
     'nsMathMLmspaceFrame.cpp',
     'nsMathMLmsqrtFrame.cpp',
     'nsMathMLmstyleFrame.cpp',
-    'nsMathMLmsubFrame.cpp',
-    'nsMathMLmsubsupFrame.cpp',
-    'nsMathMLmsupFrame.cpp',
     'nsMathMLmtableFrame.cpp',
     'nsMathMLmunderoverFrame.cpp',
     'nsMathMLsemanticsFrame.cpp',
 ]
 
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -1539,16 +1539,24 @@ nsMathMLContainerFrame::ReportParseError
 
 nsresult
 nsMathMLContainerFrame::ReportChildCountError()
 {
   const PRUnichar* arg = mContent->Tag()->GetUTF16String();
   return ReportErrorToConsole("ChildCountIncorrect", &arg, 1);
 }
 
+nsresult
+nsMathMLContainerFrame::ReportInvalidChildError(nsIAtom* aChildTag)
+{
+  const PRUnichar* argv[] =
+    { aChildTag->GetUTF16String(), mContent->Tag()->GetUTF16String() };
+  return ReportErrorToConsole("InvalidChild", argv, 2);
+}
+
 //==========================
 
 nsIFrame*
 NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
                            uint32_t aFlags)
 {
   nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
   it->SetFlags(aFlags);
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -254,16 +254,24 @@ public:
   /*
    * Helper to call ReportErrorToConsole when certain tags
    * have more than the expected amount of children.
    */
   nsresult
   ReportChildCountError();
 
   /*
+   * Helper to call ReportErrorToConsole when certain tags have
+   * invalid child tags
+   * @param aChildTag The tag which is forbidden in this context
+   */
+  nsresult
+  ReportInvalidChildError(nsIAtom* aChildTag);
+
+  /*
    * Helper to call ReportToConsole when an error occurs.
    * @param aParams see nsContentUtils::ReportToConsole
    */
   nsresult
   ReportErrorToConsole(const char*       aErrorMsgId,
                        const PRUnichar** aParams = nullptr,
                        uint32_t          aParamCount = 0);
 
--- a/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
+++ b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
@@ -11,16 +11,19 @@
 #include "nsStyleConsts.h"
 #include "nsRenderingContext.h"
 
 #include "nsMathMLmmultiscriptsFrame.h"
 #include <algorithm>
 
 //
 // <mmultiscripts> -- attach prescripts and tensor indices to a base - implementation
+// <msub> -- attach a subscript to a base - implementation
+// <msubsup> -- attach a subscript-superscript pair to a base - implementation
+// <msup> -- attach a superscript to a base - implementation
 //
 
 nsIFrame*
 NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsMathMLmmultiscriptsFrame(aContext);
 }
 
@@ -33,431 +36,583 @@ nsMathMLmmultiscriptsFrame::~nsMathMLmmu
 NS_IMETHODIMP
 nsMathMLmmultiscriptsFrame::TransmitAutomaticData()
 {
   // if our base is an embellished operator, let its state bubble to us
   mPresentationData.baseFrame = mFrames.FirstChild();
   GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
 
   // The REC says:
-  // The <mmultiscripts> element increments scriptlevel by 1, and sets
+  // The script element increments scriptlevel by 1, and sets
   // displaystyle to "false", within each of its arguments except base
   UpdatePresentationDataFromChildAt(1, -1,
     ~NS_MATHML_DISPLAYSTYLE, NS_MATHML_DISPLAYSTYLE);
 
   // The TeXbook (Ch 17. p.141) says the superscript inherits the compression
   // while the subscript is compressed. So here we collect subscripts and set
   // the compression flag in them.
+
+  if (mContent->Tag() == nsGkAtoms::msup_)
+    return NS_OK;
+
   int32_t count = 0;
-  bool isSubScript = false;
+  bool isSubScript = true;
+
   nsAutoTArray<nsIFrame*, 8> subScriptFrames;
   nsIFrame* childFrame = mFrames.FirstChild();
   while (childFrame) {
     if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) {
       // mprescripts frame
-    }
-    else if (0 == count) {
+    } else if (0 == count) {
       // base frame
-    }
-    else {
+    } else {
       // super/subscript block
       if (isSubScript) {
         // subscript
         subScriptFrames.AppendElement(childFrame);
-      }
-      else {
+      } else {
         // superscript
       }
       isSubScript = !isSubScript;
     }
     count++;
     childFrame = childFrame->GetNextSibling();
   }
   for (int32_t i = subScriptFrames.Length() - 1; i >= 0; i--) {
     childFrame = subScriptFrames[i];
     PropagatePresentationDataFor(childFrame,
       NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
   }
 
   return NS_OK;
 }
 
-void
-nsMathMLmmultiscriptsFrame::ProcessAttributes()
+/* virtual */ nsresult
+nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext,
+                                  bool                 aPlaceOrigin,
+                                  nsHTMLReflowMetrics& aDesiredSize)
 {
-  mSubScriptShift = 0;
-  mSupScriptShift = 0;
+  nscoord subScriptShift = 0;
+  nscoord supScriptShift = 0;
+  nsIAtom* tag = mContent->Tag();
 
   // subscriptshift
   //
   // "Specifies the minimum amount to shift the baseline of subscript down; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
   // As a minimum, negative values can be ignored.
   //
   nsAutoString value;
-  GetAttribute(mContent, mPresentationData.mstyle,
-               nsGkAtoms::subscriptshift_, value);
-  if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mSubScriptShift, 0, PresContext(),
-                      mStyleContext);
+  if (tag != nsGkAtoms::msup_) {
+    GetAttribute(mContent, mPresentationData.mstyle,
+                 nsGkAtoms::subscriptshift_, value);
+    if (!value.IsEmpty()) {
+      ParseNumericValue(value, &subScriptShift, 0, PresContext(),
+                        mStyleContext);
+    }
   }
   // superscriptshift
   //
   // "Specifies the minimum amount to shift the baseline of superscript up; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
   // As a minimum, negative values can be ignored.
   //
-  GetAttribute(mContent, mPresentationData.mstyle,
-               nsGkAtoms::superscriptshift_, value);
-  if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mSupScriptShift, 0, PresContext(),
-                      mStyleContext);
+  if (tag != nsGkAtoms::msub_) {
+    GetAttribute(mContent, mPresentationData.mstyle,
+                 nsGkAtoms::superscriptshift_, value);
+    if (!value.IsEmpty()) {
+      ParseNumericValue(value, &supScriptShift, 0, PresContext(),
+                        mStyleContext);
+    }
   }
+  // scriptspace from TeX for extra spacing after sup/subscript 
+  // (0.5pt in plain TeX)
+  nscoord scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
+
+  return PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin,
+                          aDesiredSize, this, subScriptShift, supScriptShift,
+                          scriptSpace);
 }
 
-/* virtual */ nsresult
-nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext,
-                                  bool                 aPlaceOrigin,
-                                  nsHTMLReflowMetrics& aDesiredSize)
+// exported routine that both munderover and mmultiscripts share.
+// munderover uses this when movablelimits is set.
+nsresult
+nsMathMLmmultiscriptsFrame::PlaceMultiScript(nsPresContext*      aPresContext,
+                                        nsRenderingContext& aRenderingContext,
+                                        bool                 aPlaceOrigin,
+                                        nsHTMLReflowMetrics& aDesiredSize,
+                                        nsMathMLContainerFrame* aFrame,
+                                        nscoord              aUserSubScriptShift,
+                                        nscoord              aUserSupScriptShift,
+                                        nscoord              aScriptSpace)
 {
-  ////////////////////////////////////
-  // Get the children's desired sizes
+  nsIAtom* tag = aFrame->GetContent()->Tag();
+
+  // This function deals with both munderover etc. as well as msubsup etc.
+  // As the former behaves identically to the later, we treat it as such
+  // to avoid additional checks later.
+  if (tag == nsGkAtoms::mover_)
+    tag = nsGkAtoms::msup_;
+  else if (tag == nsGkAtoms::munder_)
+    tag = nsGkAtoms::msub_;
+  else if (tag  == nsGkAtoms::munderover_)
+    tag = nsGkAtoms::msubsup_;
+
+  nsBoundingMetrics bmFrame;
+  nsHTMLReflowMetrics frameSize;
 
   nscoord minShiftFromXHeight, subDrop, supDrop;
 
   ////////////////////////////////////////
   // Initialize super/sub shifts that
   // depend only on the current font
   ////////////////////////////////////////
 
-  ProcessAttributes();
+  nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild();
+
+  if (!baseFrame) {
+    if (tag == nsGkAtoms::mmultiscripts_)
+      aFrame->ReportErrorToConsole("NoBase");
+    else
+      aFrame->ReportChildCountError();
+    return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+  }
+
 
   // get x-height (an ex)
-  const nsStyleFont* font = StyleFont();
+  const nsStyleFont* font = aFrame->StyleFont();
   nsRefPtr<nsFontMetrics> fm;
-  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
+  nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm));
   aRenderingContext.SetFont(fm);
 
   nscoord xHeight = fm->XHeight();
 
   nscoord ruleSize;
   GetRuleThickness (aRenderingContext, fm, ruleSize);
 
-  // scriptspace from TeX for extra spacing after sup/subscript (0.5pt in plain TeX)
-  // forced to be at least 1 pixel here
+  // force the scriptSpace to be at least 1 pixel
   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
-  nscoord scriptSpace = std::max(nsPresContext::CSSPointsToAppUnits(0.5f), onePixel);
+  aScriptSpace = std::max(onePixel, aScriptSpace);
 
   /////////////////////////////////////
   // first the shift for the subscript
 
   // subScriptShift{1,2}
   // = minimum amount to shift the subscript down
   // = sub{1,2} in TeXbook
   // subScriptShift1 = subscriptshift attribute * x-height
   nscoord subScriptShift1, subScriptShift2;
 
   // Get subScriptShift{1,2} default from font
   GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
-  if (0 < mSubScriptShift) {
+  nscoord subScriptShift;
+  if (tag == nsGkAtoms::msub_) {
+    subScriptShift = subScriptShift1;
+  } else {
+    subScriptShift = std::max(subScriptShift1, subScriptShift2);
+  }
+  if (0 < aUserSubScriptShift) {
     // the user has set the subscriptshift attribute
-    float scaler = ((float) subScriptShift2) / subScriptShift1;
-    subScriptShift1 = std::max(subScriptShift1, mSubScriptShift);
-    subScriptShift2 = NSToCoordRound(scaler * subScriptShift1);
+    subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
   }
-  // the font dependent shift
-  nscoord subScriptShift = std::max(subScriptShift1,subScriptShift2);
 
   /////////////////////////////////////
   // next the shift for the superscript
 
   // supScriptShift{1,2,3}
   // = minimum amount to shift the supscript up
   // = sup{1,2,3} in TeX
   // supScriptShift1 = superscriptshift attribute * x-height
   // Note that there are THREE values for supscript shifts depending
   // on the current style
   nscoord supScriptShift1, supScriptShift2, supScriptShift3;
   // Set supScriptShift{1,2,3} default from font
   GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
-  if (0 < mSupScriptShift) {
-    // the user has set the superscriptshift attribute
-    float scaler2 = ((float) supScriptShift2) / supScriptShift1;
-    float scaler3 = ((float) supScriptShift3) / supScriptShift1;
-    supScriptShift1 = std::max(supScriptShift1, mSupScriptShift);
-    supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1);
-    supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1);
-  }
 
   // get sup script shift depending on current script level and display style
   // Rule 18c, App. G, TeXbook
+  nsPresentationData presentationData;
+  aFrame->GetPresentationData(presentationData);
   nscoord supScriptShift;
   if ( font->mScriptLevel == 0 &&
-       NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags) &&
-      !NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
+       NS_MATHML_IS_DISPLAYSTYLE(presentationData.flags) &&
+      !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
     // Style D in TeXbook
     supScriptShift = supScriptShift1;
-  }
-  else if (NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
+  } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
     // Style C' in TeXbook = D',T',S',SS'
     supScriptShift = supScriptShift3;
-  }
-  else {
+  } else {
     // everything else = T,S,SS
     supScriptShift = supScriptShift2;
   }
 
+  if (0 < aUserSupScriptShift) {
+    // the user has set the supscriptshift attribute
+    supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
+  }
+
   ////////////////////////////////////
   // Get the children's sizes
   ////////////////////////////////////
 
   nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
-  nsIFrame* mprescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
-  bool isSubScript = false;
   nscoord minSubScriptShift = 0, minSupScriptShift = 0;
   nscoord trySubScriptShift = subScriptShift;
   nscoord trySupScriptShift = supScriptShift;
   nscoord maxSubScriptShift = subScriptShift;
   nscoord maxSupScriptShift = supScriptShift;
-  int32_t count = 0;
   nsHTMLReflowMetrics baseSize;
   nsHTMLReflowMetrics subScriptSize;
   nsHTMLReflowMetrics supScriptSize;
-  nsIFrame* baseFrame = nullptr;
+  nsHTMLReflowMetrics multiSubSize, multiSupSize;
+  baseFrame = nullptr;
   nsIFrame* subScriptFrame = nullptr;
   nsIFrame* supScriptFrame = nullptr;
+  nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
 
   bool firstPrescriptsPair = false;
-  nsBoundingMetrics bmBase, bmSubScript, bmSupScript;
+  nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup;
+  multiSubSize.ascent = multiSupSize.ascent = -0x7FFFFFFF;
+  bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF;
+  bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF;
   nscoord italicCorrection = 0;
 
-  mBoundingMetrics.width = 0;
-  mBoundingMetrics.ascent = mBoundingMetrics.descent = -0x7FFFFFFF;
-  nscoord ascent = -0x7FFFFFFF, descent = -0x7FFFFFFF;
+  nsBoundingMetrics boundingMetrics;
+  boundingMetrics.width = 0;
+  boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
   aDesiredSize.width = aDesiredSize.height = 0;
 
-  nsIFrame* childFrame = mFrames.FirstChild();
+  int32_t count = 0;
+  bool foundNoneTag = false;
+
+  // Boolean to determine whether the current child is a subscript.
+  // Note that only msup starts with a superscript.
+  bool isSubScript = (tag != nsGkAtoms::msup_);
+
+  nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
   while (childFrame) {
-    if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) {
-      if (mprescriptsFrame) {
+    nsIAtom* childTag = childFrame->GetContent()->Tag();
+    if (childTag == nsGkAtoms::mprescripts_) {
+      if (tag != nsGkAtoms::mmultiscripts_) {
+        if (aPlaceOrigin) {
+          aFrame->ReportInvalidChildError(childTag);
+        }
+        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+      }
+      if (prescriptsFrame) {
         // duplicate <mprescripts/> found
         // report an error, encourage people to get their markups in order
         if (aPlaceOrigin) {
-          ReportErrorToConsole("DuplicateMprescripts");
+          aFrame->ReportErrorToConsole("DuplicateMprescripts");
         }
-        return ReflowError(aRenderingContext, aDesiredSize);
+        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
       }
-      mprescriptsFrame = childFrame;
+      if (!isSubScript) {
+        if (aPlaceOrigin) {
+          aFrame->ReportErrorToConsole("SubSupMismatch");
+        }
+        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+      }
+
+      prescriptsFrame = childFrame;
       firstPrescriptsPair = true;
-    }
-    else {
-      if (0 == count) {
-        // base
-        baseFrame = childFrame;
-        GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
+    } else if (0 == count) {
+      // base
+
+      if (childTag == nsGkAtoms::none) {
+        if (tag == nsGkAtoms::mmultiscripts_) {
+          if (aPlaceOrigin) {
+            aFrame->ReportErrorToConsole("NoBase");
+          }
+          return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+        } else {
+          //A different error message is triggered later for the other tags
+          foundNoneTag = true;
+        }
+      }
+      baseFrame = childFrame;
+      GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
+
+      if (tag != nsGkAtoms::msub_) {
+        // Apply italics correction if there is the potential for a 
+        // postsupscript.
         GetItalicCorrection(bmBase, italicCorrection);
-        // for the superscript, we always add "a little to spare"
+        // If italics correction is applied, we always add "a little to spare"
+        // (see TeXbook Ch.11, p.64), as we estimate the italic creation
+        // ourselves and it isn't the same as TeX.
         italicCorrection += onePixel;
+      }
 
-        // we update mBoundingMetrics.{ascent,descent} with that
-        // of the baseFrame only after processing all the sup/sub pairs
-        // XXX need italic correction only *if* there are postscripts ?
-        mBoundingMetrics.width = bmBase.width + italicCorrection;
-        mBoundingMetrics.rightBearing = bmBase.rightBearing;
-        mBoundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
+      // we update boundingMetrics.{ascent,descent} with that
+      // of the baseFrame only after processing all the sup/sub pairs
+      // XXX need italic correction only *if* there are postscripts ?
+      boundingMetrics.width = bmBase.width + italicCorrection;
+      boundingMetrics.rightBearing = bmBase.rightBearing;
+      boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
+    } else {
+      // super/subscript block
+      if ( childTag == nsGkAtoms::none) {
+        foundNoneTag = true;
       }
-      else {
-        // super/subscript block