Merge m-c to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 24 Apr 2014 13:41:02 +0200
changeset 198398 370c4a1ef06bae56af5b8068fa492234702f4a87
parent 198397 df150c8d189fb672d1960eb737bf52d96c070478 (current diff)
parent 198375 9d3da41ad0b6ef945bd06c680a9e23fcfc1fa406 (diff)
child 198399 1b6df84cef0eb215274f90daccbaa65a514bf57b
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.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 b2g-inbound
--- a/accessible/src/jsat/TraversalRules.jsm
+++ b/accessible/src/jsat/TraversalRules.jsm
@@ -126,17 +126,16 @@ var gSimpleMatchFunc = function gSimpleM
       if (parent.childCount > 1 && aAccessible.indexInParent == 0 &&
           parent.role == Roles.LISTITEM)
         return Filters.IGNORE;
 
       return Filters.MATCH;
     }
   case Roles.GRAPHIC:
     return TraversalRules._shouldSkipImage(aAccessible);
-  case Roles.LINK:
   case Roles.HEADER:
   case Roles.HEADING:
     if ((aAccessible.childCount > 0 || aAccessible.name) &&
         hasZeroOrSingleChildDescendants()) {
       return Filters.MATCH | Filters.IGNORE_SUBTREE;
     } else {
       return Filters.IGNORE;
     }
--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -34,17 +34,16 @@ let CustomizationHandler = {
       childNode.setAttribute("disabled", true);
 
     let cmd = document.getElementById("cmd_CustomizeToolbars");
     cmd.setAttribute("disabled", "true");
 
     UpdateUrlbarSearchSplitterState();
 
     CombinedStopReload.uninit();
-    CombinedBackForward.uninit();
     PlacesToolbarHelper.customizeStart();
     DownloadsButton.customizeStart();
 
     // The additional padding on the sides of the browser
     // can cause the customize tab to get clipped.
     let tabContainer = gBrowser.tabContainer;
     if (tabContainer.getAttribute("overflow") == "true") {
       let tabstrip = tabContainer.mTabstrip;
@@ -82,17 +81,16 @@ let CustomizationHandler = {
     }
 
     PlacesToolbarHelper.customizeDone();
     DownloadsButton.customizeDone();
 
     // The url bar splitter state is dependent on whether stop/reload
     // and the location bar are combined, so we need this ordering
     CombinedStopReload.init();
-    CombinedBackForward.init();
     UpdateUrlbarSearchSplitterState();
 
     // Update the urlbar
     if (gURLBar) {
       URLBarSetURI();
       XULBrowserWindow.asyncUpdateUI();
     }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -255,25 +255,17 @@ function UpdateBackForwardCommands(aWebN
   var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
   if (backDisabled == aWebNavigation.canGoBack) {
     if (backDisabled)
       backBroadcaster.removeAttribute("disabled");
     else
       backBroadcaster.setAttribute("disabled", true);
   }
 
-  let canGoForward = aWebNavigation.canGoForward;
-  if (forwardDisabled) {
-    // Force the button to either be hidden (if we are already disabled,
-    // and should be), or to show if we're about to un-disable it:
-    // otherwise no transition will occur and it'll never show:
-    CombinedBackForward.setForwardButtonOcclusion(!canGoForward);
-  }
-
-  if (forwardDisabled == canGoForward) {
+  if (forwardDisabled == aWebNavigation.canGoForward) {
     if (forwardDisabled)
       forwardBroadcaster.removeAttribute("disabled");
     else
       forwardBroadcaster.setAttribute("disabled", true);
   }
 }
 
 /**
@@ -915,17 +907,16 @@ var gBrowserInit = {
         gURLBar.setAttribute("readonly", "true");
         gURLBar.setAttribute("enablehistory", "false");
       }
       goSetCommandEnabled("cmd_newNavigatorTab", false);
     }
 
     // Misc. inits.
     CombinedStopReload.init();
-    CombinedBackForward.init();
     gPrivateBrowsingUI.init();
     TabsInTitlebar.init();
 
     // Wait until chrome is painted before executing code not critical to making the window visible
     this._boundDelayedStartup = this._delayedStartup.bind(this, mustLoadSidebar);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
     this._loadHandled = true;
@@ -1262,17 +1253,16 @@ var gBrowserInit = {
     if (desc && !desc.get) {
       DeveloperToolbar.destroy();
     }
 
     // First clean up services initialized in gBrowserInit.onLoad (or those whose
     // uninit methods don't depend on the services having been initialized).
 
     CombinedStopReload.uninit();
-    CombinedBackForward.uninit();
 
     gGestureSupport.init(false);
 
     gHistorySwipeAnimation.uninit();
 
     FullScreen.cleanup();
 
 #ifdef MOZ_SERVICES_SYNC
@@ -3794,17 +3784,16 @@ var XULBrowserWindow = {
     } catch (e) {}
     gIdentityHandler.checkIdentity(this._state, uri);
   },
 
   // simulate all change notifications after switching tabs
   onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
     if (FullZoom.updateBackgroundTabs)
       FullZoom.onLocationChange(gBrowser.currentURI, true);
-    CombinedBackForward.setForwardButtonOcclusion(!gBrowser.webProgress.canGoForward);
     var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
     var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
     // use a pseudo-object instead of a (potentially nonexistent) channel for getting
     // a correct error message - and make sure that the UI is always either in
     // loading (STATE_START) or done (STATE_STOP) mode
     this.onStateChange(
       gBrowser.webProgress,
       { URI: gBrowser.currentURI },
@@ -3869,53 +3858,16 @@ var LinkTargetDisplay = {
 
   _hide: function () {
     clearTimeout(this._timer);
 
     XULBrowserWindow.updateStatusField();
   }
 };
 
-let CombinedBackForward = {
-  init: function() {
-    this.forwardButton = document.getElementById("forward-button");
-    // Add a transition listener to the url bar to hide the forward button
-    // when necessary
-    if (gURLBar)
-      gURLBar.addEventListener("transitionend", this);
-    // On startup, or if the user customizes, our listener isn't attached,
-    // and no transitions fire anyway, so we need to make sure we've hidden the
-    // button if necessary:
-    if (this.forwardButton && this.forwardButton.hasAttribute("disabled")) {
-      this.setForwardButtonOcclusion(true);
-    }
-  },
-  uninit: function() {
-    if (gURLBar)
-      gURLBar.removeEventListener("transitionend", this);
-  },
-  handleEvent: function(aEvent) {
-    if (aEvent.type == "transitionend" &&
-        (aEvent.propertyName == "margin-left" || aEvent.propertyName == "margin-right") &&
-        this.forwardButton.hasAttribute("disabled")) {
-      this.setForwardButtonOcclusion(true);
-    }
-  },
-  setForwardButtonOcclusion: function(shouldBeOccluded) {
-    if (!this.forwardButton)
-      return;
-
-    let hasAttribute = this.forwardButton.hasAttribute("occluded-by-urlbar");
-    if (shouldBeOccluded && !hasAttribute)
-      this.forwardButton.setAttribute("occluded-by-urlbar", "true");
-    else if (!shouldBeOccluded && hasAttribute)
-      this.forwardButton.removeAttribute("occluded-by-urlbar");
-  }
-}
-
 var CombinedStopReload = {
   init: function () {
     if (this._initialized)
       return;
 
     let reload = document.getElementById("urlbar-reload-button");
     let stop = document.getElementById("urlbar-stop-button");
     if (!stop || !reload || reload.nextSibling != stop)
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -651,31 +651,24 @@
                      class="chromeclass-location" overflows="false">
           <toolbarbutton id="back-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                          label="&backCmd.label;"
                          command="Browser:BackOrBackDuplicate"
                          cui-areatype="toolbar"
                          onclick="checkForMiddleClick(this, event);"
                          tooltip="back-button-tooltip"
                          context="backForwardMenu"/>
-          <toolbarbutton id="forward-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                         label="&forwardCmd.label;"
-                         command="Browser:ForwardOrForwardDuplicate"
-                         cui-areatype="toolbar"
-                         onclick="checkForMiddleClick(this, event);"
-                         tooltip="forward-button-tooltip"
-                         context="backForwardMenu"/>
-          <dummyobservertarget hidden="true"
-                               onbroadcast="if (this.getAttribute('disabled') == 'true')
-                                              this.parentNode.setAttribute('forwarddisabled', 'true');
-                                            else
-                                              this.parentNode.removeAttribute('forwarddisabled');">
-            <observes element="Browser:ForwardOrForwardDuplicate" attribute="disabled"/>
-          </dummyobservertarget>
-          <hbox id="urlbar-wrapper" flex="1" align="center">
+          <hbox id="urlbar-wrapper" flex="1">
+            <toolbarbutton id="forward-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                           label="&forwardCmd.label;"
+                           command="Browser:ForwardOrForwardDuplicate"
+                           cui-areatype="toolbar"
+                           onclick="checkForMiddleClick(this, event);"
+                           tooltip="forward-button-tooltip"
+                           context="backForwardMenu"/>
             <textbox id="urlbar" flex="1"
                      placeholder="&urlbar.placeholder2;"
                      type="autocomplete"
                      autocompletesearch="urlinline history"
                      autocompletesearchparam="enable-actions"
                      autocompletepopup="PopupAutoCompleteRichResult"
                      completeselectedindex="true"
                      tabscrolling="true"
@@ -1183,32 +1176,23 @@
                          oncommand="DeveloperToolbar.hide();"
                          tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
 #endif
    </toolbar>
   </vbox>
 
   <svg:svg height="0">
 #include tab-shape.inc.svg
-
+    <svg:clipPath id="urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
 #ifndef XP_MACOSX
-    <svg:clipPath id="keyhole-forward-clip-path" clipPathUnits="objectBoundingBox">
-      <svg:path d="m 0,0 c .3,.25 .3,.75, 0,1 l 1,0 0,-1 z"/>
-    </svg:clipPath>
-    <svg:clipPath id="urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
-      <svg:path d="m 0,-5 l 0,7.8 c 2.5,3.2 4,6.2 4,10.2 c 0,4 -1.5,7 -4,10 l 0,22l10000,0 l 0,-50 l -10000,0 z"/>
+      <svg:path d="m 1,-5 l 0,7.8 c 2.5,3.2 4,6.2 4,10.2 c 0,4 -1.5,7 -4,10 l 0,22l10000,0 l 0,-50 l -10000,0 z"/>
+#else
+      <svg:path d="M -11,-5 a 16 16 0 0 1 0,34 l 10000,0 l 0,-34 l -10000,0 z"/>
+#endif
     </svg:clipPath>
-#else
-    <svg:clipPath id="osx-keyhole-forward-clip-path" clipPathUnits="userSpaceOnUse">
-      <svg:path d="M 0,0 a 16 16 0 0 1 0,24 l 10000,0 l 0,-24 l -10000,0 z"/>
-    </svg:clipPath>
-    <svg:clipPath id="osx-urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
-      <svg:path d="M -12,-5 a 16 16 0 0 1 0,34 l 10000,0 l 0,-34 l -10000,0 z"/>
-    </svg:clipPath>
-#endif
   </svg:svg>
 
 </vbox>
 # <iframe id="tab-view"> is dynamically appended as the 2nd child of #tab-view-deck.
 #     Introducing the iframe dynamically, as needed, was found to be better than
 #     starting with an empty iframe here in browser.xul from a Ts standpoint.
 </deck>
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1007,22 +1007,22 @@
 
             newBrowser.setAttribute("type", "content-primary");
             newBrowser.docShellIsActive =
               (window.windowState != window.STATE_MINIMIZED);
             this.mCurrentBrowser = newBrowser;
             this.mCurrentTab = this.tabContainer.selectedItem;
             this.showTab(this.mCurrentTab);
 
-            var backForwardContainer = document.getElementById("urlbar-container");
-            if (backForwardContainer) {
-              backForwardContainer.setAttribute("switchingtabs", "true");
+            var forwardButtonContainer = document.getElementById("urlbar-wrapper");
+            if (forwardButtonContainer) {
+              forwardButtonContainer.setAttribute("switchingtabs", "true");
               window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
                 window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
-                backForwardContainer.removeAttribute("switchingtabs");
+                forwardButtonContainer.removeAttribute("switchingtabs");
               });
             }
 
             this._appendStatusPanel();
 
             if (updateBlockedPopups)
               this.mCurrentBrowser.updateBlockedPopups(false);
 
--- a/browser/base/content/test/general/browser_aboutHome.js
+++ b/browser/base/content/test/general/browser_aboutHome.js
@@ -85,67 +85,68 @@ let gTests = [
        "Search engine logo's alt text is a nonempty string");
 
     isnot(altText, "undefined",
           "Search engine logo's alt text shouldn't be the string 'undefined'");
   }
 },
 
 // Disabled on Linux for intermittent issues with FHR, see Bug 945667.
-// Disabled always due to bug 992485
 {
   desc: "Check that performing a search fires a search event and records to " +
         "Firefox Health Report.",
   setup: function () { },
   run: function () {
-    // Skip this test always for now since it loads google.com and that causes bug 992485
-    return;
-
     // Skip this test on Linux.
     if (navigator.platform.indexOf("Linux") == 0) { return; }
 
     try {
       let cm = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
       cm.getCategoryEntry("healthreport-js-provider-default", "SearchesProvider");
     } catch (ex) {
       // Health Report disabled, or no SearchesProvider.
       return Promise.resolve();
     }
 
     let numSearchesBefore = 0;
-    let deferred = Promise.defer();
+    let searchEventDeferred = Promise.defer();
     let doc = gBrowser.contentDocument;
     let engineName = doc.documentElement.getAttribute("searchEngineName");
 
     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
       let data = JSON.parse(e.detail);
       is(data.engineName, engineName, "Detail is search engine name");
 
       // We use executeSoon() to ensure that this code runs after the
       // count has been updated in browser.js, since it uses the same
       // event.
       executeSoon(function () {
         getNumberOfSearches(engineName).then(num => {
           is(num, numSearchesBefore + 1, "One more search recorded.");
-          deferred.resolve();
+          searchEventDeferred.resolve();
         });
       });
     }, true, true);
 
     // Get the current number of recorded searches.
+    let searchStr = "a search";
     getNumberOfSearches(engineName).then(num => {
       numSearchesBefore = num;
 
       info("Perform a search.");
-      doc.getElementById("searchText").value = "a search";
+      doc.getElementById("searchText").value = searchStr;
       doc.getElementById("searchSubmit").click();
-      gBrowser.stop();
     });
 
-    return deferred.promise;
+    let expectedURL = Services.search.currentEngine.
+                      getSubmission(searchStr, null, "homepage").
+                      uri.spec;
+    let loadPromise = waitForDocLoadAndStopIt(expectedURL);
+
+    return Promise.all([searchEventDeferred.promise, loadPromise]);
   }
 },
 
 {
   desc: "Check snippets map is cleared if cached version is old",
   setup: function (aSnippetsMap)
   {
     aSnippetsMap.set("snippets", "test");
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -287,16 +287,48 @@ function promiseHistoryClearedState(aURI
          "history visit " + aURI.spec + " should " + niceStr + " exist");
       callbackDone();
     });
   });
 
   return deferred.promise;
 }
 
+/**
+ * Waits for the next top-level document load in the current browser.  The URI
+ * of the document is compared against aExpectedURL.  The load is then stopped
+ * before it actually starts.
+ *
+ * @param aExpectedURL
+ *        The URL of the document that is expected to load.
+ * @return promise
+ */
+function waitForDocLoadAndStopIt(aExpectedURL) {
+  let deferred = Promise.defer();
+  let progressListener = {
+    onStateChange: function (webProgress, req, flags, status) {
+      info("waitForDocLoadAndStopIt: onStateChange: " + req.name);
+      let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
+                     Ci.nsIWebProgressListener.STATE_START;
+      if ((flags & docStart) && webProgress.isTopLevel) {
+        info("waitForDocLoadAndStopIt: Document start: " +
+             req.QueryInterface(Ci.nsIChannel).URI.spec);
+        is(req.originalURI.spec, aExpectedURL,
+           "waitForDocLoadAndStopIt: The expected URL was loaded");
+        req.cancel(Components.results.NS_ERROR_FAILURE);
+        gBrowser.removeProgressListener(progressListener);
+        deferred.resolve();
+      }
+    },
+  };
+  gBrowser.addProgressListener(progressListener);
+  info("waitForDocLoadAndStopIt: Waiting for URL: " + aExpectedURL);
+  return deferred.promise;
+}
+
 let FullZoomHelper = {
 
   selectTabAndWaitForLocationChange: function selectTabAndWaitForLocationChange(tab) {
     if (!tab)
       throw new Error("tab must be given.");
     if (gBrowser.selectedTab == tab)
       return Promise.resolve();
     gBrowser.selectedTab = tab;
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -99,16 +99,20 @@
       <richlistbox id="downloadsListBox"
                    class="plain"
                    flex="1"
                    context="downloadsContextMenu"
                    onmouseover="DownloadsView.onDownloadMouseOver(event);"
                    onmouseout="DownloadsView.onDownloadMouseOut(event);"
                    oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
                    ondragstart="DownloadsView.onDownloadDragStart(event);"/>
+      <description id="emptyDownloads"
+                   mousethrough="always">
+         &downloadsPanelEmpty.label;
+      </description>
 
       <vbox id="downloadsFooter">
         <hbox id="downloadsSummary"
               align="center"
               orient="horizontal"
               onkeydown="DownloadsSummary.onKeyDown(event);"
               onclick="DownloadsSummary.onClick(event);">
           <image class="downloadTypeIcon" />
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
@@ -79,13 +79,18 @@
 <!ENTITY clearDownloadsButton.tooltip     "Clears completed, canceled and failed downloads">
 
 <!-- LOCALIZATION NOTE (downloadsListEmpty.label):
      This string is shown when there are no items in the Downloads view, when it
      is displayed inside a browser tab.
      -->
 <!ENTITY downloadsListEmpty.label         "There are no downloads.">
 
+<!-- LOCALIZATION NOTE (downloadsPanelEmpty.label):
+     This string is shown when there are no items in the Downloads Panel.
+     -->
+<!ENTITY downloadsPanelEmpty.label        "No downloads for this session.">
+
 <!-- LOCALIZATION NOTE (downloadsListNoMatch.label):
      This string is shown when some search terms are specified, but there are no
      results in the Downloads view.
      -->
 <!ENTITY downloadsListNoMatch.label       "Could not find any matching downloads.">
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -10,17 +10,17 @@
 @namespace html url("http://www.w3.org/1999/xhtml");
 @namespace svg url("http://www.w3.org/2000/svg");
 
 %include ../shared/browser.inc
 %include linuxShared.inc
 %filter substitution
 
 %define forwardTransitionLength 150ms
-%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-container
+%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-wrapper
 %define conditionalForwardWithUrlbarWidth 30
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
 #main-menubar {
   -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
@@ -730,45 +730,49 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 #main-window:not([customizing]) #back-button[disabled] > .toolbarbutton-icon {
   box-shadow: 0 0 0 1px hsla(210,54%,20%,.55),
               0 1px 0 hsla(210,54%,20%,.65) !important;
   transition: none;
 }
 
-#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
-#forward-button:-moz-locale-dir(rtl) {
+#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
-@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
-  opacity: 0;
-}
-
-@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
-  transition: opacity @forwardTransitionLength@ ease-out;
-}
-
-@conditionalForwardWithUrlbar@ > #forward-button[occluded-by-urlbar] {
-  visibility: hidden;
-}
-
 #forward-button {
   padding: 0;
 }
 
 #forward-button > .toolbarbutton-icon {
   background-clip: padding-box;
-  clip-path: url("chrome://browser/content/browser.xul#keyhole-forward-clip-path");
-  margin-left: -6px;
+  padding-left: 9px;
+  padding-right: 3px;
+  border: 1px solid #9a9a9a;
   border-left-style: none;
   border-radius: 0;
-  padding: 2px 3px 2px 9px;
-  border: 1px solid #9a9a9a;
+}
+
+@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
+  transition: margin-left @forwardTransitionLength@ ease-out;
+}
+
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] {
+  margin-left: -@conditionalForwardWithUrlbarWidth@px;
+}
+
+@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] {
+  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
+  transition-delay: 100s;
+}
+
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
+  /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
+  margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
 }
 
 /* tabview menu item */
 
 #menu_tabview {
   list-style-image: url(chrome://browser/skin/tabview/tabview.png);
   -moz-image-region: rect(0, 80px, 16px, 64px);
 }
@@ -898,67 +902,44 @@ toolbarbutton[sdk-button="true"][cui-are
 .urlbar-history-dropmarker {
   -moz-appearance: toolbarbutton-dropdown;
 }
 
 #urlbar-container {
   -moz-box-align: center;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper {
-  padding-left: @conditionalForwardWithUrlbarWidth@px;
-  -moz-margin-start: -@conditionalForwardWithUrlbarWidth@px;
-  position: relative;
-  pointer-events: none;
+@conditionalForwardWithUrlbar@ > #urlbar {
+  -moz-border-start: none;
+  margin-left: 0;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar {
-  -moz-border-start: none;
-  margin-left: 0;
-  pointer-events: all;
-}
-
-@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  transition: margin-left @forwardTransitionLength@ ease-out;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) {
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(ltr) {
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(rtl) {
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper {
+@conditionalForwardWithUrlbar@ {
   clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path");
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar {
-  margin-left: -@conditionalForwardWithUrlbarWidth@px;
+  -moz-margin-start: -5px;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-  transition-delay: 100s;
+@conditionalForwardWithUrlbar@:-moz-locale-dir(rtl),
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(rtl) {
+  /* let urlbar-back-button-clip-path clip the urlbar's right side for RTL */
+  transform: scaleX(-1);
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar,
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar {
-  /* when switching tabs, or when not hovered anymore, trigger a new transition
-   * to hide the forward button immediately */
-  margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
-  /* let windows-urlbar-back-button-mask clip the urlbar's right side for RTL */
-  transform: scaleX(-1);
+@conditionalForwardWithUrlbar@:-moz-locale-dir(rtl) {
+  -moz-box-direction: reverse;
 }
 
 #urlbar-icons {
   -moz-box-align: center;
 }
 
 .urlbar-icon {
   cursor: pointer;
@@ -1010,43 +991,43 @@ toolbarbutton[sdk-button="true"][cui-are
   border-bottom-right-radius: 1.5px;
 }
 
 #notification-popup-box:not([hidden]) + #identity-box {
   -moz-padding-start: 10px;
   border-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box {
+@conditionalForwardWithUrlbar@ > #urlbar > #identity-box {
   border-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
-  padding-left: 5px;
-  transition: padding-left;
+@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar > #identity-box {
+  transition: padding-left, padding-right;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-  padding-right: 5px;
-  transition: padding-right;
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
+  padding-left: 5px;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box {
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
+  padding-right: 5px;
+}
+
+@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
   /* forward button hiding is delayed when hovered */
   transition-delay: 100s;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-left: 5.01px;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-right: 5.01px;
 }
 
 #urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
 #urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
   -moz-margin-end: 4px;
 }
@@ -1066,24 +1047,20 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 %include ../shared/identity-block.inc.css
 
 #page-proxy-favicon {
   margin-top: 1px;
   margin-bottom: 1px;
   -moz-margin-start: 3px;
-  -moz-margin-end: 2px;
+  -moz-margin-end: 1px;
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box > #page-proxy-favicon {
-  -moz-margin-end: 1px;
-}
-
 #identity-box:hover > #page-proxy-favicon {
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 #identity-box:hover:active > #page-proxy-favicon,
 #identity-box[open=true] > #page-proxy-favicon {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
--- a/browser/themes/linux/downloads/downloads.css
+++ b/browser/themes/linux/downloads/downloads.css
@@ -13,23 +13,32 @@
   padding: 4px;
   color: inherit;
 }
 
 #downloadsPanel:not([hasdownloads]) > #downloadsListBox {
   display: none;
 }
 
+#downloadsPanel[hasdownloads] > #emptyDownloads {
+  display: none;
+}
+
+#emptyDownloads {
+  padding: 10px 20px;
+  max-width: 40ch;
+}
+
 #downloadsHistory {
   background: transparent;
   color: -moz-nativehyperlinktext;
   cursor: pointer;
 }
 
-#downloadsPanel[hasdownloads] > #downloadsFooter {
+#downloadsFooter {
   border-top: 1px solid ThreeDShadow;
   background-image: linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
 }
 
 #downloadsHistory > .button-box {
   margin: 1em;
 }
 
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2,18 +2,18 @@
  * 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/. */
 
 @import url("chrome://global/skin/");
 
 %include shared.inc
 %filter substitution
 %define forwardTransitionLength 150ms
-%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-container
-%define conditionalForwardWithUrlbarWidth 30
+%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-wrapper
+%define conditionalForwardWithUrlbarWidth 32
 %define spaceAboveTabbar 9px
 %define toolbarButtonPressed :hover:active:not([disabled="true"]):not([cui-areatype="menu-panel"])
 %define windowButtonMarginTop 11px
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 @namespace svg url("http://www.w3.org/2000/svg");
 
@@ -1415,30 +1415,27 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 #back-button:-moz-window-inactive,
 #forward-button:-moz-window-inactive {
   background-color: rgba(0,0,0,0.04);
   border-color: rgba(0,0,0,0.2);
 }
 
-#back-button:-moz-locale-dir(rtl),
-#forward-button:-moz-locale-dir(rtl) {
+#back-button:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
 /* Back button styles */
 
 #back-button {
-  -moz-margin-end: -7px;
-  position: relative;
-  z-index: 1;
   width: 32px;
   height: 32px;
   padding: 4px 5px 4px 3px;
+  -moz-margin-end: 0;
   border-radius: 10000px;
 }
 
 #back-button:not(:-moz-lwtheme) {
   height: 33px;
   padding: 4px 5px 5px 3px;
   margin-bottom: -1px;
   background: url(chrome://browser/skin/keyhole-circle.png) 0 0 no-repeat;
@@ -1458,68 +1455,50 @@ toolbarbutton[sdk-button="true"][cui-are
 #back-button:not([disabled="true"]):active:hover:not(:-moz-lwtheme),
 #back-button[open="true"]:not(:-moz-lwtheme) {
   background-position: -32px 0;
 }
 
 /* Forward button styles */
 
 #forward-button {
-  -moz-margin-start: 0;
-  -moz-margin-end: 0;
+  margin-left: -2px;
+  margin-right: 0;
+  padding-left: 2px;
   width: 32px;
-  clip-path: url(chrome://browser/content/browser.xul#osx-keyhole-forward-clip-path);
 }
 
 #forward-button > .toolbarbutton-icon {
   /* shift the icon away from the back button */
   margin-left: 3px;
   margin-right: -1px;
 }
 
-#forward-button:-moz-lwtheme {
-  -moz-padding-start: 2px;
-  -moz-padding-end: 0;
-}
-
 #forward-button:not(:-moz-lwtheme) {
-  -moz-padding-start: 2px;
   background: linear-gradient(hsl(0,0%,99%), hsl(0,0%,67%)) padding-box;
   border: 1px solid;
   border-color: hsl(0,0%,31%) hsla(0,0%,29%,.6) hsl(0,0%,27%);
   box-shadow: inset 0 1px 0 hsla(0,0%,100%,.35),
               0 1px 0 hsla(0,0%,100%,.2);
 }
 
-#urlbar-container:not([switchingtabs]) > #forward-button {
-  transition: opacity @forwardTransitionLength@ ease-out;
-}
-
 #forward-button:hover:active:not(:-moz-lwtheme) {
   background-image: linear-gradient(hsl(0,0%,74%), hsl(0,0%,61%));
   box-shadow: inset rgba(0,0,0,.3) 0 -6px 10px,
               inset #000 0 1px 3px,
               inset rgba(0,0,0,.2) 0 1px 3px,
               0 1px 0 hsla(0,0%,100%,.2);
 }
 
 #forward-button:-moz-window-inactive:not(:-moz-lwtheme) {
   border-color: hsl(0,0%,64%) hsl(0,0%,65%) hsl(0,0%,66%);
   background-image: linear-gradient(hsl(0,0%,99%), hsl(0,0%,82%));
   box-shadow: inset 0 1px 0 hsla(0,0%,100%,.35);
 }
 
-#urlbar-container:not(:hover) > #forward-button[disabled] {
-  opacity: 0;
-}
-
-@conditionalForwardWithUrlbar@ > #forward-button[occluded-by-urlbar] {
-  visibility: hidden;
-}
-
 @media (-moz-mac-lion-theme) {
   #forward-button:not(:-moz-lwtheme) {
     background-image: linear-gradient(hsla(0,0%,100%,.73), hsla(0,0%,100%,.05) 85%);
     border-color: hsla(0,0%,0%,.35) hsla(0,0%,0%,.25) hsla(0,0%,0%,.2);
     box-shadow: inset 0 1px 0 hsla(0,0%,100%,.2),
                 inset 0 0 1px hsla(0,0%,100%,.1),
                 0 1px 0 hsla(0,0%,100%,.2);
   }
@@ -1533,16 +1512,34 @@ toolbarbutton[sdk-button="true"][cui-are
   }
 
   #forward-button:-moz-window-inactive:not(:-moz-lwtheme) {
     background-image: none;
     border-color: hsla(0,0%,0%,.2);
   }
 }
 
+@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
+  transition: margin-left @forwardTransitionLength@ ease-out;
+}
+
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] {
+  margin-left: -@conditionalForwardWithUrlbarWidth@px;
+}
+
+@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] {
+  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
+  transition-delay: 100s;
+}
+
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
+  /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
+  margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
+}
+
 .unified-nav-back[_moz-menuactive]:-moz-locale-dir(ltr),
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/menu-back.png") !important;
 }
 
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(ltr),
 .unified-nav-back[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/menu-forward.png") !important;
@@ -1716,69 +1713,46 @@ toolbarbutton[sdk-button="true"][cui-are
   -moz-box-align: center;
 }
 
 #urlbar {
   -moz-padding-end: 4px;
   border-radius: @toolbarbuttonCornerRadius@;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper {
-  padding-left: @conditionalForwardWithUrlbarWidth@px;
-  -moz-margin-start: -@conditionalForwardWithUrlbarWidth@px;
-  position: relative;
-  pointer-events: none;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar {
+@conditionalForwardWithUrlbar@ > #urlbar {
   -moz-border-start: none;
   margin-left: 0;
-  pointer-events: all;
-}
-
-@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  transition: margin-left @forwardTransitionLength@ ease-out;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) {
+}
+
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(ltr) {
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(rtl) {
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper {
-  clip-path: url("chrome://browser/content/browser.xul#osx-urlbar-back-button-clip-path");
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar {
-  margin-left: -@conditionalForwardWithUrlbarWidth@px;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-  transition-delay: 100s;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar,
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar {
-  /* when switching tabs, or when not hovered anymore, trigger a new transition
-   * to hide the forward button immediately */
-  margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
-  /* let osx-urlbar-back-button-clip-path clip the urlbar's right side for RTL */
+@conditionalForwardWithUrlbar@ {
+  clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path");
+  -moz-margin-start: -6px;
+}
+
+@conditionalForwardWithUrlbar@:-moz-locale-dir(rtl),
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(rtl) {
+  /* let urlbar-back-button-clip-path clip the urlbar's right side for RTL */
   transform: scaleX(-1);
 }
 
+@conditionalForwardWithUrlbar@:-moz-locale-dir(rtl) {
+  -moz-box-direction: reverse;
+}
+
 #identity-box {
   -moz-margin-end: 3px;
   padding-top: 1px;
   padding-bottom: 1px;
   -moz-padding-start: 4px;
   -moz-padding-end: 0;
   font-size: .9em;
 }
@@ -1793,42 +1767,42 @@ toolbarbutton[sdk-button="true"][cui-are
   border-bottom-right-radius: 2px;
 }
 
 #notification-popup-box:not([hidden]) + #identity-box {
   -moz-padding-start: 10px;
   border-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box {
+@conditionalForwardWithUrlbar@ > #urlbar > #identity-box {
   border-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
-  transition: 0s padding-left;
+@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar > #identity-box {
+  transition: padding-left, padding-right;
+}
+
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   padding-left: 10px;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-  transition: 0s padding-right;
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
   padding-right: 10px;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box {
-  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
+@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
+  /* forward button hiding is delayed when hovered */
   transition-delay: 100s;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   padding-left: 10.01px;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
   padding-right: 10.01px;
 }
 
 #urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
 #urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
   -moz-padding-end: 4px;
 }
 
@@ -3395,17 +3369,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 @media (min-resolution: 2dppx) {
   #notification-popup-box {
     border-image: url("chrome://browser/skin/urlbar-arrow@2x.png") 0 16 0 0 fill;
   }
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box {
+@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar > #notification-popup-box {
   padding-left: 7px;
 }
 
 #notification-popup-box:-moz-locale-dir(rtl),
 .notification-anchor-icon:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
--- a/browser/themes/osx/downloads/downloads.css
+++ b/browser/themes/osx/downloads/downloads.css
@@ -17,28 +17,37 @@
   padding: 4px;
   color: inherit;
 }
 
 #downloadsPanel:not([hasdownloads]) > #downloadsListBox {
   display: none;
 }
 
+#downloadsPanel[hasdownloads] > #emptyDownloads {
+  display: none;
+}
+
+#emptyDownloads {
+  padding: 10px 20px;
+  max-width: 40ch;
+}
+
 #downloadsFooter {
   border-bottom-left-radius: 4px;
   border-bottom-right-radius: 4px;
 }
 
 #downloadsHistory {
   background: transparent;
   color: hsl(210,100%,75%);
   cursor: pointer;
 }
 
-#downloadsPanel[hasdownloads] > #downloadsFooter {
+#downloadsFooter {
   background: #e5e5e5;
   border-top: 1px solid hsla(0,0%,0%,.1);
   box-shadow: 0 -1px hsla(0,0%,100%,.5) inset, 0 1px 1px hsla(0,0%,0%,.03) inset;
 }
 
 #downloadsHistory > .button-box {
   color: #808080;
   margin: 1em;
@@ -47,21 +56,16 @@
 #downloadsPanel[keyfocus] > #downloadsFooter > #downloadsSummary:focus,
 #downloadsPanel[keyfocus] > #downloadsFooter > #downloadsHistory:focus {
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
   -moz-outline-radius-bottomleft: 4px;
   -moz-outline-radius-bottomright: 4px;
 }
 
-#downloadsPanel:not([hasdownloads]) > #downloadsFooter > #downloadsHistory:focus {
-  -moz-outline-radius-topleft: 4px;
-  -moz-outline-radius-topright: 4px;
-}
-
 /*** Downloads Summary and List items ***/
 
 #downloadsSummary,
 richlistitem[type="download"] {
   height: 7em;
   -moz-padding-end: 0;
   color: inherit;
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -9,17 +9,17 @@
 @namespace svg url("http://www.w3.org/2000/svg");
 
 %include ../shared/browser.inc
 %include windowsShared.inc
 %filter substitution
 %define toolbarShadowColor hsla(209,67%,12%,0.35)
 %define navbarTextboxCustomBorder border-color: rgba(0,0,0,.32);
 %define forwardTransitionLength 150ms
-%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-container
+%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-wrapper
 %define conditionalForwardWithUrlbarWidth 30
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
 #main-menubar {
   -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
@@ -881,46 +881,38 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 #forward-button > menupopup {
   margin-top: 1px !important;
 }
 
 #forward-button > .toolbarbutton-icon {
   background-clip: padding-box !important;
-  /*mask: url(keyhole-forward-mask.svg#mask); XXX: this regresses twinopen */
-  clip-path: url(chrome://browser/content/browser.xul#keyhole-forward-clip-path) !important;
-  margin-left: -6px !important;
   border-left-style: none !important;
   border-radius: 0 !important;
   padding-left: 9px !important;
   padding-right: 3px !important;
 }
 
-%ifdef WINDOWS_AERO
-@media (-moz-os-version: windows-vista),
-       (-moz-os-version: windows-win7) {
-%endif
-  #forward-button > .toolbarbutton-icon {
-    margin-left: -6px !important;
-  }
-%ifdef WINDOWS_AERO
-}
-%endif
-
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
-  transition: opacity @forwardTransitionLength@ ease-out;
+  transition: margin-left @forwardTransitionLength@ ease-out;
+}
+
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] {
+  margin-left: -@conditionalForwardWithUrlbarWidth@px;
+}
+
+@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] {
+  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
+  transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
-  opacity: 0;
-}
-
-@conditionalForwardWithUrlbar@ > #forward-button[occluded-by-urlbar] {
-  visibility: hidden;
+  /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
+  margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
 }
 
 #back-button {
   padding-top: 3px !important;
   padding-bottom: 3px !important;
   -moz-padding-start: 5px !important;
   -moz-padding-end: 0 !important;
   position: relative !important;
@@ -994,18 +986,17 @@ toolbarbutton[sdk-button="true"][cui-are
     box-shadow: 0 0 0 1px hsla(210,54%,20%,.55),
                 0 1px 0 hsla(210,54%,20%,.65) !important;
     transition: none;
   }
 %ifdef WINDOWS_AERO
 }
 %endif
 
-#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
-#forward-button:-moz-locale-dir(rtl) {
+#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 .unified-nav-back[_moz-menuactive]:-moz-locale-dir(ltr),
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/menu-back.png") !important;
 }
 
@@ -1167,69 +1158,46 @@ toolbarbutton[sdk-button="true"][cui-are
   background-color: rgba(255,255,255,.9);
 }
 
 #urlbar:-moz-lwtheme[focused]:not([readonly]),
 .searchbar-textbox:-moz-lwtheme[focused] {
   background-color: white;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper {
-  padding-left: @conditionalForwardWithUrlbarWidth@px;
-  -moz-margin-start: -@conditionalForwardWithUrlbarWidth@px;
-  position: relative;
-  pointer-events: none;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar {
+@conditionalForwardWithUrlbar@ > #urlbar {
   -moz-border-start: none;
   margin-left: 0;
-  pointer-events: all;
-}
-
-@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  transition: margin-left @forwardTransitionLength@ ease-out;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) {
+}
+
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(ltr) {
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(rtl) {
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper {
+@conditionalForwardWithUrlbar@ {
   clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path");
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar {
-  margin-left: -@conditionalForwardWithUrlbarWidth@px;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-  transition-delay: 100s;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar,
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar {
-  /* when switching tabs, or when not hovered anymore, trigger a new transition
-   * to hide the forward button immediately */
-  margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
-  /* let windows-urlbar-back-button-mask clip the urlbar's right side for RTL */
+  -moz-margin-start: -5px;
+}
+
+@conditionalForwardWithUrlbar@:-moz-locale-dir(rtl),
+@conditionalForwardWithUrlbar@ > #urlbar:-moz-locale-dir(rtl) {
+  /* let urlbar-back-button-clip-path clip the urlbar's right side for RTL */
   transform: scaleX(-1);
 }
 
+@conditionalForwardWithUrlbar@:-moz-locale-dir(rtl) {
+  -moz-box-direction: reverse;
+}
+
 html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
 .searchbar-textbox:-moz-lwtheme > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input::-moz-placeholder {
   opacity: 1.0;
   color: #777;
 }
 
 #urlbar-container {
   -moz-box-align: center;
@@ -1309,43 +1277,43 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
   border-bottom-right-radius: 1.5px;
 }
 
 #notification-popup-box:not([hidden]) + #identity-box {
   -moz-padding-start: 10px;
   border-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box {
+@conditionalForwardWithUrlbar@ > #urlbar > #identity-box {
   border-radius: 0;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
+@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar > #identity-box {
+  transition: padding-left, padding-right;
+}
+
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   padding-left: 5px;
-  transition: padding-left;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
+}
+
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
   padding-right: 5px;
-  transition: padding-right;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box {
+}
+
+@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
   /* forward button hiding is delayed when hovered */
   transition-delay: 100s;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-left: 5.01px;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-right: 5.01px;
 }
 
 #urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
 #urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
   -moz-margin-end: 4px;
 }
@@ -1390,24 +1358,20 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 /* page proxy icon */
 
 %include ../shared/identity-block.inc.css
 
 #page-proxy-favicon {
   margin-top: 1px;
   margin-bottom: 1px;
   -moz-margin-start: 3px;
-  -moz-margin-end: 2px;
+  -moz-margin-end: 1px;
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box > #page-proxy-favicon {
-  -moz-margin-end: 1px;
-}
-
 #identity-box:hover > #page-proxy-favicon {
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 #identity-box:hover:active > #page-proxy-favicon,
 #identity-box[open=true] > #page-proxy-favicon {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
--- a/browser/themes/windows/downloads/downloads.css
+++ b/browser/themes/windows/downloads/downloads.css
@@ -13,16 +13,25 @@
   padding: 4px;
   color: inherit;
 }
 
 #downloadsPanel:not([hasdownloads]) > #downloadsListBox {
   display: none;
 }
 
+#downloadsPanel[hasdownloads] > #emptyDownloads {
+  display: none;
+}
+
+#emptyDownloads {
+  padding: 10px 20px;
+  max-width: 40ch;
+}
+
 #downloadsHistory {
   background: transparent;
   cursor: pointer;
 }
 
 %ifdef WINDOWS_AERO
 @media (-moz-os-version: windows-vista),
        (-moz-os-version: windows-win7) {
@@ -39,46 +48,46 @@
   outline-offset: -1px;
 }
 
 #downloadsHistory > .button-box {
   border: none;
   margin: 1em;
 }
 
-#downloadsPanel[hasdownloads] > #downloadsFooter {
+#downloadsFooter {
   background-color: hsla(210,4%,10%,.04);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.08) inset;
   transition-duration: 150ms;
   transition-property: background-color;
 }
 
-#downloadsPanel[hasdownloads] > #downloadsFooter:hover {
+#downloadsFooter:hover {
   background-color: hsla(210,4%,10%,.05);
 }
 
-#downloadsPanel[hasdownloads] > #downloadsFooter:hover:active {
+#downloadsFooter:hover:active {
   background-color: hsla(210,4%,10%,.1);
   box-shadow: 0 2px 0 0 hsla(210,4%,10%,.1) inset;
 }
 
 %ifdef WINDOWS_AERO
 @media (-moz-os-version: windows-vista),
        (-moz-os-version: windows-win7) {
 %endif
 @media (-moz-windows-default-theme) {
-  #downloadsPanel[hasdownloads] > #downloadsFooter {
+  #downloadsFooter {
     border-bottom-left-radius: 3px;
     border-bottom-right-radius: 3px;
     transition-duration: 0s;
   }
 
-  #downloadsPanel[hasdownloads] > #downloadsFooter,
-  #downloadsPanel[hasdownloads] > #downloadsFooter:hover,
-  #downloadsPanel[hasdownloads] > #downloadsFooter:hover:active {
+  #downloadsFooter,
+  #downloadsFooter:hover,
+  #downloadsFooter:hover:active {
 %ifdef WINDOWS_AERO
     background-color: #f1f5fb;
 %else
     background-color: hsla(216,45%,88%,.98);
 %endif
     box-shadow: 0px 1px 2px rgb(204,214,234) inset;
   }
 }
--- a/configure.in
+++ b/configure.in
@@ -5045,20 +5045,16 @@ AC_SUBST(MOZ_IPDL_TESTS)
 dnl ========================================================
 dnl = Disable building dbm
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(dbm,
 [  --disable-dbm           Disable building dbm],
     NSS_DISABLE_DBM=1,
     NSS_DISABLE_DBM=)
 
-dnl bi-directional support always on
-IBMBIDI=1
-AC_DEFINE(IBMBIDI)
-
 dnl ========================================================
 dnl accessibility support on by default on all platforms
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(accessibility,
 [  --disable-accessibility Disable accessibility support],
     ACCESSIBILITY=,
     ACCESSIBILITY=1 )
 if test "$ACCESSIBILITY"; then
@@ -8482,17 +8478,16 @@ AC_SUBST(JAVAC)
 AC_SUBST(JAVAH)
 AC_SUBST(JAR)
 AC_SUBST(JARSIGNER)
 AC_SUBST(KEYTOOL)
 
 AC_SUBST(MOZ_PROFILELOCKING)
 
 AC_SUBST(ENABLE_TESTS)
-AC_SUBST(IBMBIDI)
 AC_SUBST(MOZ_UNIVERSALCHARDET)
 AC_SUBST(ACCESSIBILITY)
 AC_SUBST(MOZ_SPELLCHECK)
 AC_SUBST(MOZ_ANDROID_OMTC)
 AC_SUBST(MOZ_ANDROID_ANR_REPORTER)
 AC_SUBST(MOZ_CRASHREPORTER)
 AC_SUBST(MOZ_CRASHREPORTER_INJECTOR)
 AC_SUBST(MOZ_CRASHREPORTER_UPLOAD_FULL_SYMBOLS)
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -128,19 +128,17 @@ typedef void* (*DeferredFinalizeAppendFu
 
 // Called to finalize a number of objects. Slice is the number of objects
 // to finalize, or if it's UINT32_MAX, all objects should be finalized.
 // Return value indicates whether it finalized all objects in the buffer.
 typedef bool (*DeferredFinalizeFunction)(uint32_t slice, void* data);
 
 } // namespace mozilla
 
-#ifdef IBMBIDI
 class nsIBidiKeyboard;
-#endif
 
 extern const char kLoadAsData[];
 
 // Stolen from nsReadableUtils, but that's OK, since we can declare the same
 // name multiple times.
 const nsAFlatString& EmptyString();
 const nsAFlatCString& EmptyCString();
 
@@ -456,20 +454,18 @@ public:
     return sNameSpaceManager;
   }
 
   static nsIIOService* GetIOService()
   {
     return sIOService;
   }
 
-#ifdef IBMBIDI
   static nsIBidiKeyboard* GetBidiKeyboard();
-#endif
-  
+
   /**
    * Get the cache security manager service. Can return null if the layout
    * module has been shut down.
    */
   static nsIScriptSecurityManager* GetSecurityManager()
   {
     return sSecurityManager;
   }
@@ -2200,19 +2196,17 @@ private:
   static nsIStringBundle* sStringBundles[PropertiesFile_COUNT];
 
   static nsIContentPolicy* sContentPolicyService;
   static bool sTriedToGetContentPolicy;
 
   static nsILineBreaker* sLineBreaker;
   static nsIWordBreaker* sWordBreaker;
 
-#ifdef IBMBIDI
   static nsIBidiKeyboard* sBidiKeyboard;
-#endif
 
   static bool sInitialized;
   static uint32_t sScriptBlockerCount;
 #ifdef DEBUG
   static uint32_t sDOMNodeRemovedSuppressCount;
 #endif
   static uint32_t sMicroTaskLevel;
   // Not an nsCOMArray because removing elements from those is slower
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -172,19 +172,17 @@
 #include "nsViewManager.h"
 #include "nsViewportInfo.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsXULPopupManager.h"
 #include "xpcprivate.h" // nsXPConnect
 #include "HTMLSplitOnSpacesTokenizer.h"
 #include "nsContentTypeParser.h"
 
-#ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
-#endif
 
 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
                                       const char** next, char16_t* result);
 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
                                  int ns_aware, const char** colon);
 
 class imgLoader;
 
@@ -205,19 +203,17 @@ nsDataHashtable<nsISupportsHashKey, Even
 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
 nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
 nsIStringBundleService *nsContentUtils::sStringBundleService;
 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
 nsIContentPolicy *nsContentUtils::sContentPolicyService;
 bool nsContentUtils::sTriedToGetContentPolicy = false;
 nsILineBreaker *nsContentUtils::sLineBreaker;
 nsIWordBreaker *nsContentUtils::sWordBreaker;
-#ifdef IBMBIDI
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
-#endif
 uint32_t nsContentUtils::sScriptBlockerCount = 0;
 #ifdef DEBUG
 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
 #endif
 uint32_t nsContentUtils::sMicroTaskLevel = 0;
 nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nullptr;
 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
@@ -942,29 +938,27 @@ nsContentUtils::ParseSandboxAttributeToF
   IF_KEYWORD(allowtopnavigation, SANDBOXED_TOPLEVEL_NAVIGATION)
   IF_KEYWORD(allowpointerlock, SANDBOXED_POINTER_LOCK)
   IF_KEYWORD(allowpopups, SANDBOXED_AUXILIARY_NAVIGATION)
 
   return out;
 #undef IF_KEYWORD
 }
 
-#ifdef IBMBIDI
 nsIBidiKeyboard*
 nsContentUtils::GetBidiKeyboard()
 {
   if (!sBidiKeyboard) {
     nsresult rv = CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
     if (NS_FAILED(rv)) {
       sBidiKeyboard = nullptr;
     }
   }
   return sBidiKeyboard;
 }
-#endif
 
 template <class OutputIterator>
 struct NormalizeNewlinesCharTraits {
   public:
     typedef typename OutputIterator::value_type value_type;
 
   public:
     NormalizeNewlinesCharTraits(OutputIterator& aIterator) : mIterator(aIterator) { }
@@ -1439,19 +1433,17 @@ nsContentUtils::Shutdown()
   NS_IF_RELEASE(sStringBundleService);
   NS_IF_RELEASE(sConsoleService);
   sXPConnect = nullptr;
   NS_IF_RELEASE(sSecurityManager);
   NS_IF_RELEASE(sParserService);
   NS_IF_RELEASE(sIOService);
   NS_IF_RELEASE(sLineBreaker);
   NS_IF_RELEASE(sWordBreaker);
-#ifdef IBMBIDI
   NS_IF_RELEASE(sBidiKeyboard);
-#endif
 
   delete sAtomEventTable;
   sAtomEventTable = nullptr;
   delete sStringEventTable;
   sStringEventTable = nullptr;
   delete sUserDefinedEvents;
   sUserDefinedEvents = nullptr;
 
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -27,16 +27,18 @@
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIDOMClassInfo.h"
 #include "nsIDOMFile.h"
 #include "xpcpublic.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
+#include "mozilla/dom/PBlobChild.h"
+#include "mozilla/dom/PBlobParent.h"
 #include "JavaScriptChild.h"
 #include "JavaScriptParent.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "nsPrintfCString.h"
 #include <algorithm>
 
 #ifdef ANDROID
 #include <android/log.h>
@@ -123,16 +125,41 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
                                                    mChrome && mIsBroadcaster)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender,
                                                    mChrome && !mIsBroadcaster)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
 
+enum ActorFlavorEnum {
+  Parent = 0,
+  Child
+};
+
+template <ActorFlavorEnum>
+struct BlobTraits
+{ };
+
+template <>
+struct BlobTraits<Parent>
+{
+  typedef mozilla::dom::BlobParent BlobType;
+  typedef mozilla::dom::PBlobParent ProtocolType;
+  typedef mozilla::dom::ContentParent ConcreteContentManagerType;
+};
+
+template <>
+struct BlobTraits<Child>
+{
+  typedef mozilla::dom::BlobChild BlobType;
+  typedef mozilla::dom::PBlobChild ProtocolType;
+  typedef mozilla::dom::ContentChild ConcreteContentManagerType;
+};
+
 template<ActorFlavorEnum>
 struct DataBlobs
 { };
 
 template<>
 struct DataBlobs<Parent>
 {
   typedef BlobTraits<Parent>::ProtocolType ProtocolType;
@@ -175,17 +202,18 @@ BuildClonedMessageData(typename BlobTrai
   buffer.dataLength = aData.mDataLength;
   const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
   if (!blobs.IsEmpty()) {
     typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
     InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
     uint32_t length = blobs.Length();
     blobList.SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
-      Blob<Flavor>* protocolActor = aManager->GetOrCreateActorForBlob(blobs[i]);
+      typename BlobTraits<Flavor>::BlobType* protocolActor =
+        aManager->GetOrCreateActorForBlob(blobs[i]);
       if (!protocolActor) {
         return false;
       }
       blobList.AppendElement(protocolActor);
     }
   }
   return true;
 }
@@ -215,17 +243,18 @@ UnpackClonedMessageData(const ClonedMess
   const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aData);
   StructuredCloneData cloneData;
   cloneData.mData = buffer.data;
   cloneData.mDataLength = buffer.dataLength;
   if (!blobs.IsEmpty()) {
     uint32_t length = blobs.Length();
     cloneData.mClosure.mBlobs.SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
-      Blob<Flavor>* blob = static_cast<Blob<Flavor>*>(blobs[i]);
+      auto* blob =
+        static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
       MOZ_ASSERT(blob);
       nsCOMPtr<nsIDOMBlob> domBlob = blob->GetBlob();
       MOZ_ASSERT(domBlob);
       cloneData.mClosure.mBlobs.AppendElement(domBlob);
     }
   }
   return cloneData;
 }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8425,16 +8425,23 @@ nsGlobalWindow::EnterModalState()
 
       if (activeShell) {
         nsRefPtr<nsFrameSelection> frameSelection = activeShell->FrameSelection();
         frameSelection->SetMouseDownState(false);
       }
     }
   }
 
+  // If there are any drag and drop operations in flight, try to end them.
+  nsCOMPtr<nsIDragService> ds =
+    do_GetService("@mozilla.org/widget/dragservice;1");
+  if (ds) {
+    ds->EndDragSession(true);
+  }
+
   // Clear the capturing content if it is under topDoc.
   // Usually the activeESM check above does that, but there are cases when
   // we don't have activeESM, or it is for different document.
   nsIDocument* topDoc = topWin->GetExtantDoc();
   nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
   if (capturingContent && topDoc &&
       nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) {
     nsIPresShell::SetCapturingContent(nullptr, 0);
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/UIEvent.h"
 
 #include "ContentEventHandler.h"
+#include "IMEContentObserver.h"
 #include "WheelHandlingHelper.h"
 
 #include "nsCOMPtr.h"
 #include "nsFocusManager.h"
 #include "nsIContent.h"
 #include "nsINodeInfo.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
@@ -332,16 +333,18 @@ EventStateManager::Init()
     Prefs::Init();
   }
 
   return NS_OK;
 }
 
 EventStateManager::~EventStateManager()
 {
+  ReleaseCurrentIMEContentObserver();
+
   if (sActiveESM == this) {
     sActiveESM = nullptr;
   }
   if (Prefs::ClickHoldContextMenu())
     KillClickHoldTimer();
 
   if (mDocument == sMouseOverDocument)
     sMouseOverDocument = nullptr;
@@ -405,34 +408,61 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
    NS_INTERFACE_MAP_ENTRY(nsIObserver)
    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventStateManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventStateManager)
 
-NS_IMPL_CYCLE_COLLECTION_16(EventStateManager,
+NS_IMPL_CYCLE_COLLECTION_17(EventStateManager,
                             mCurrentTargetContent,
                             mGestureDownContent,
                             mGestureDownFrameOwner,
                             mLastLeftMouseDownContent,
                             mLastLeftMouseDownContentParent,
                             mLastMiddleMouseDownContent,
                             mLastMiddleMouseDownContentParent,
                             mLastRightMouseDownContent,
                             mLastRightMouseDownContentParent,
                             mActiveContent,
                             mHoverContent,
                             mURLTargetContent,
                             mMouseEnterLeaveHelper,
                             mPointersEnterLeaveHelper,
                             mDocument,
+                            mIMEContentObserver,
                             mAccessKeys)
 
+void
+EventStateManager::ReleaseCurrentIMEContentObserver()
+{
+  if (mIMEContentObserver) {
+    mIMEContentObserver->DisconnectFromEventStateManager();
+  }
+  mIMEContentObserver = nullptr;
+}
+
+void
+EventStateManager::OnStartToObserveContent(
+                     IMEContentObserver* aIMEContentObserver)
+{
+  ReleaseCurrentIMEContentObserver();
+  mIMEContentObserver = aIMEContentObserver;
+}
+
+void
+EventStateManager::OnStopObservingContent(
+                     IMEContentObserver* aIMEContentObserver)
+{
+  aIMEContentObserver->DisconnectFromEventStateManager();
+  NS_ENSURE_TRUE_VOID(mIMEContentObserver == aIMEContentObserver);
+  mIMEContentObserver = nullptr;
+}
+
 nsresult
 EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
                                   WidgetEvent* aEvent,
                                   nsIFrame* aTargetFrame,
                                   nsEventStatus* aStatus)
 {
   NS_ENSURE_ARG_POINTER(aStatus);
   NS_ENSURE_ARG(aPresContext);
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -29,16 +29,17 @@ class nsIMarkupDocumentViewer;
 class nsIScrollableFrame;
 class nsITimer;
 class nsPresContext;
 
 namespace mozilla {
 
 class EnterLeaveDispatcher;
 class EventStates;
+class IMEContentObserver;
 class ScrollbarsForWheel;
 class WheelTransaction;
 
 namespace dom {
 class DataTransfer;
 class TabParent;
 } // namespace dom
 
@@ -129,16 +130,24 @@ public:
    *                  frame reconstructions that may occur, but this does not
    *                  affect the return value.
    */
   bool SetContentState(nsIContent* aContent, EventStates aState);
   void ContentRemoved(nsIDocument* aDocument, nsIContent* aContent);
   bool EventStatusOK(WidgetGUIEvent* aEvent);
 
   /**
+   * EventStateManager stores IMEContentObserver while it's observing contents.
+   * Following mehtods are called by IMEContentObserver when it starts to
+   * observe or stops observing the content.
+   */
+  void OnStartToObserveContent(IMEContentObserver* aIMEContentObserver);
+  void OnStopObservingContent(IMEContentObserver* aIMEContentObserver);
+
+  /**
    * Register accesskey on the given element. When accesskey is activated then
    * the element will be notified via nsIContent::PerformAccesskey() method.
    *
    * @param  aContent  the given element
    * @param  aKey      accesskey
    */
   void RegisterAccessKey(nsIContent* aContent, uint32_t aKey);
 
@@ -772,16 +781,18 @@ protected:
 
   bool DispatchCrossProcessEvent(WidgetEvent* aEvent,
                                  nsFrameLoader* aRemote,
                                  nsEventStatus *aStatus);
   bool HandleCrossProcessEvent(WidgetEvent* aEvent,
                                nsIFrame* aTargetFrame,
                                nsEventStatus* aStatus);
 
+  void ReleaseCurrentIMEContentObserver();
+
 private:
   static inline void DoStateChange(dom::Element* aElement,
                                    EventStates aState, bool aAddState);
   static inline void DoStateChange(nsIContent* aContent, EventStates aState,
                                    bool aAddState);
   static void UpdateAncestorState(nsIContent* aStartNode,
                                   nsIContent* aStopBefore,
                                   EventStates aState,
@@ -834,16 +845,18 @@ private:
   nsCOMPtr<nsIContent> mActiveContent;
   nsCOMPtr<nsIContent> mHoverContent;
   static nsCOMPtr<nsIContent> sDragOverContent;
   nsCOMPtr<nsIContent> mURLTargetContent;
 
   nsPresContext* mPresContext;      // Not refcnted
   nsCOMPtr<nsIDocument> mDocument;   // Doesn't necessarily need to be owner
 
+  nsRefPtr<IMEContentObserver> mIMEContentObserver;
+
   uint32_t mLClickCount;
   uint32_t mMClickCount;
   uint32_t mRClickCount;
 
   bool m_haveShutdown;
 
   // Time at which we began handling user input.
   static TimeStamp sHandlingInputStart;
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentEventHandler.h"
 #include "IMEContentObserver.h"
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/EventStateManager.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/dom/Element.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
@@ -28,32 +29,45 @@
 #include "nsPresContext.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 
 namespace mozilla {
 
 using namespace widget;
 
-NS_IMPL_ISUPPORTS5(IMEContentObserver,
-                   nsIMutationObserver,
-                   nsISelectionListener,
-                   nsIReflowObserver,
-                   nsIScrollObserver,
-                   nsISupportsWeakReference)
+NS_IMPL_CYCLE_COLLECTION_5(IMEContentObserver,
+                           mWidget, mSelection,
+                           mRootContent, mEditableNode, mDocShell)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
+ NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
 
 IMEContentObserver::IMEContentObserver()
+  : mESM(nullptr)
 {
 }
 
 void
 IMEContentObserver::Init(nsIWidget* aWidget,
                          nsPresContext* aPresContext,
                          nsIContent* aContent)
 {
+  mESM = aPresContext->EventStateManager();
+  mESM->OnStartToObserveContent(this);
+
   mWidget = aWidget;
   mEditableNode = IMEStateManager::GetRootEditableNode(aPresContext, aContent);
   if (!mEditableNode) {
     return;
   }
 
   nsIPresShell* presShell = aPresContext->PresShell();
 
@@ -169,16 +183,27 @@ IMEContentObserver::Destroy()
   if (mUpdatePreference.WantPositionChanged() && mDocShell) {
     mDocShell->RemoveWeakScrollObserver(this);
     mDocShell->RemoveWeakReflowObserver(this);
   }
   mRootContent = nullptr;
   mEditableNode = nullptr;
   mDocShell = nullptr;
   mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
+
+  if (mESM) {
+    mESM->OnStopObservingContent(this);
+    mESM = nullptr;
+  }
+}
+
+void
+IMEContentObserver::DisconnectFromEventStateManager()
+{
+  mESM = nullptr;
 }
 
 bool
 IMEContentObserver::IsManaging(nsPresContext* aPresContext,
                                nsIContent* aContent)
 {
   if (!mSelection || !mRootContent || !mEditableNode) {
     return false; // failed to initialize.
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -4,58 +4,69 @@
  * 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_IMEContentObserver_h_
 #define mozilla_IMEContentObserver_h_
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsIDocShell.h" // XXX Why does only this need to be included here?
 #include "nsIReflowObserver.h"
 #include "nsISelectionListener.h"
 #include "nsIScrollObserver.h"
 #include "nsIWidget.h" // for nsIMEUpdatePreference
 #include "nsStubMutationObserver.h"
 #include "nsWeakReference.h"
 
 class nsIContent;
 class nsINode;
 class nsISelection;
 class nsPresContext;
 
 namespace mozilla {
 
+class EventStateManager;
+
 // IMEContentObserver notifies widget of any text and selection changes
 // in the currently focused editor
 class IMEContentObserver MOZ_FINAL : public nsISelectionListener,
                                      public nsStubMutationObserver,
                                      public nsIReflowObserver,
                                      public nsIScrollObserver,
                                      public nsSupportsWeakReference
 {
 public:
   IMEContentObserver();
 
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IMEContentObserver,
+                                           nsISelectionListener)
   NS_DECL_NSISELECTIONLISTENER
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIREFLOWOBSERVER
 
   // nsIScrollObserver
   virtual void ScrollPositionChanged() MOZ_OVERRIDE;
 
   void Init(nsIWidget* aWidget, nsPresContext* aPresContext,
             nsIContent* aContent);
   void Destroy();
+  /**
+   * IMEContentObserver is stored by EventStateManager during observing.
+   * DisconnectFromEventStateManager() is called when EventStateManager stops
+   * storing the instance.
+   */
+  void DisconnectFromEventStateManager();
   bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent);
   bool IsEditorHandlingEventForComposition() const;
   bool KeepAliveDuringDeactive() const
   {
     return mUpdatePreference.WantDuringDeactive();
   }
   nsIWidget* GetWidget() const { return mWidget; }
   nsresult GetSelectionAndRoot(nsISelection** aSelection,
@@ -65,15 +76,18 @@ private:
   void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
   void ObserveEditableNode();
 
   nsCOMPtr<nsIWidget> mWidget;
   nsCOMPtr<nsISelection> mSelection;
   nsCOMPtr<nsIContent> mRootContent;
   nsCOMPtr<nsINode> mEditableNode;
   nsCOMPtr<nsIDocShell> mDocShell;
+
+  EventStateManager* mESM;
+
   nsIMEUpdatePreference mUpdatePreference;
   uint32_t mPreAttrChangeLength;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_IMEContentObserver_h_
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1,69 +1,68 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "Blob.h"
+
+#include "ContentChild.h"
+#include "ContentParent.h"
+#include "FileDescriptorSetChild.h"
+#include "jsapi.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
-
-#include "base/basictypes.h"
-
-#include "Blob.h"
-
+#include "mozilla/Monitor.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/PBlobStreamChild.h"
+#include "mozilla/dom/PBlobStreamParent.h"
+#include "mozilla/dom/PFileDescriptorSetParent.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDOMFile.h"
 #include "nsIDOMFile.h"
 #include "nsIInputStream.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsIRemoteBlob.h"
 #include "nsISeekableStream.h"
-
-#include "mozilla/Monitor.h"
-#include "mozilla/unused.h"
-#include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/dom/PFileDescriptorSetParent.h"
-#include "nsDOMFile.h"
+#include "nsNetCID.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
-#include "jsapi.h"
-
-#include "ContentChild.h"
-#include "ContentParent.h"
-#include "nsNetCID.h"
-#include "FileDescriptorSetChild.h"
 
 #define PRIVATE_REMOTE_INPUT_STREAM_IID \
   {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}}
 
+using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::dom::ipc;
 using namespace mozilla::ipc;
 
 namespace {
 
-/**
- * Ensure that a nsCOMPtr/nsRefPtr is released on the main thread.
- */
+enum ActorType
+{
+  ChildActor,
+  ParentActor
+};
+
+// Ensure that a nsCOMPtr/nsRefPtr is released on the main thread.
 template <template <class> class SmartPtr, class T>
 void
 ProxyReleaseToMainThread(SmartPtr<T>& aDoomed)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   NS_ENSURE_TRUE_VOID(mainThread);
 
   if (NS_FAILED(NS_ProxyRelease(mainThread, aDoomed, true))) {
     NS_WARNING("Failed to proxy release to main thread!");
   }
 }
 
-
 class NS_NO_VTABLE IPrivateRemoteInputStream : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
 
   // This will return the underlying stream.
   virtual nsIInputStream*
   BlockAndGetInternalStream() = 0;
@@ -150,40 +149,41 @@ class RemoteInputStream : public nsIInpu
                           public nsISeekableStream,
                           public nsIIPCSerializableInputStream,
                           public IPrivateRemoteInputStream
 {
   mozilla::Monitor mMonitor;
   nsCOMPtr<nsIInputStream> mStream;
   nsCOMPtr<nsIDOMBlob> mSourceBlob;
   nsISeekableStream* mWeakSeekableStream;
-  ActorFlavorEnum mOrigin;
+  ActorType mOrigin;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  RemoteInputStream(nsIDOMBlob* aSourceBlob, ActorFlavorEnum aOrigin)
+  RemoteInputStream(nsIDOMBlob* aSourceBlob, ActorType aOrigin)
   : mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob),
     mWeakSeekableStream(nullptr), mOrigin(aOrigin)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aSourceBlob);
   }
 
   void
   Serialize(InputStreamParams& aParams,
             FileDescriptorArray& /* aFileDescriptors */)
   {
     nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mSourceBlob);
     MOZ_ASSERT(remote);
 
-    if (mOrigin == Parent) {
+    if (mOrigin == ParentActor) {
       aParams = RemoteInputStreamParams(
         static_cast<PBlobParent*>(remote->GetPBlob()), nullptr);
     } else {
+      MOZ_ASSERT(mOrigin == ChildActor);
       aParams = RemoteInputStreamParams(
         nullptr, static_cast<PBlobChild*>(remote->GetPBlob()));
     }
   }
 
   bool
   Deserialize(const InputStreamParams& aParams,
               const FileDescriptorArray& /* aFileDescriptors */)
@@ -426,182 +426,102 @@ NS_IMPL_RELEASE(RemoteInputStream)
 NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream)
 NS_INTERFACE_MAP_END
 
-template <ActorFlavorEnum ActorFlavor>
-class InputStreamActor : public BlobTraits<ActorFlavor>::StreamType
+class InputStreamChild MOZ_FINAL
+  : public PBlobStreamChild
 {
   nsRefPtr<RemoteInputStream> mRemoteStream;
 
 public:
-  InputStreamActor(RemoteInputStream* aRemoteStream)
+  InputStreamChild(RemoteInputStream* aRemoteStream)
   : mRemoteStream(aRemoteStream)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aRemoteStream);
   }
 
-  InputStreamActor()
+  InputStreamChild()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
 private:
   // This method is only called by the IPDL message machinery.
   virtual bool
   Recv__delete__(const InputStreamParams& aParams,
                  const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
 };
 
-template <>
-bool
-InputStreamActor<Parent>::Recv__delete__(const InputStreamParams& aParams,
-                                         const OptionalFileDescriptorSet& aFDs)
+class InputStreamParent MOZ_FINAL
+  : public PBlobStreamParent
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mRemoteStream);
-
-  if (aFDs.type() != OptionalFileDescriptorSet::Tvoid_t) {
-    NS_WARNING("Child cannot send FileDescriptors to the parent!");
-    return false;
-  }
-
-  nsTArray<FileDescriptor> fds;
-  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
-  if (!stream) {
-    return false;
-  }
-
-  MOZ_ASSERT(fds.IsEmpty());
+  nsRefPtr<RemoteInputStream> mRemoteStream;
 
-  mRemoteStream->SetStream(stream);
-  return true;
-}
-
-template <>
-bool
-InputStreamActor<Child>::Recv__delete__(const InputStreamParams& aParams,
-                                        const OptionalFileDescriptorSet& aFDs)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mRemoteStream);
-
-  nsTArray<FileDescriptor> fds;
-  if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
-    FileDescriptorSetChild* fdSetActor =
-      static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
-    MOZ_ASSERT(fdSetActor);
-
-    fdSetActor->ForgetFileDescriptors(fds);
-    MOZ_ASSERT(!fds.IsEmpty());
-
-    fdSetActor->Send__delete__(fdSetActor);
+public:
+  InputStreamParent(RemoteInputStream* aRemoteStream)
+  : mRemoteStream(aRemoteStream)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aRemoteStream);
   }
 
-  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
-  if (!stream) {
-    return false;
+  InputStreamParent()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
   }
 
-  mRemoteStream->SetStream(stream);
-  return true;
-}
-
-template <ActorFlavorEnum ActorFlavor>
-inline
-already_AddRefed<nsIDOMBlob>
-GetBlobFromParams(const SlicedBlobConstructorParams& aParams)
-{
-  static_assert(ActorFlavor == Parent,
-                "No other flavor is supported here!");
-
-  BlobParent* actor =
-    const_cast<BlobParent*>(
-      static_cast<const BlobParent*>(aParams.sourceParent()));
-  MOZ_ASSERT(actor);
-
-  return actor->GetBlob();
-}
+private:
+  // This method is only called by the IPDL message machinery.
+  virtual bool
+  Recv__delete__(const InputStreamParams& aParams,
+                 const OptionalFileDescriptorSet& aFDs) MOZ_OVERRIDE;
+};
 
-template <>
-inline
-already_AddRefed<nsIDOMBlob>
-GetBlobFromParams<Child>(const SlicedBlobConstructorParams& aParams)
-{
-  BlobChild* actor =
-    const_cast<BlobChild*>(
-      static_cast<const BlobChild*>(aParams.sourceChild()));
-  MOZ_ASSERT(actor);
-
-  return actor->GetBlob();
-}
-
-inline
-void
-SetBlobOnParams(BlobChild* aActor, SlicedBlobConstructorParams& aParams)
-{
-  aParams.sourceChild() = aActor;
-}
-
-inline
-void
-SetBlobOnParams(BlobParent* aActor, SlicedBlobConstructorParams& aParams)
-{
-  aParams.sourceParent() = aActor;
-}
-
-inline
 nsDOMFileBase*
 ToConcreteBlob(nsIDOMBlob* aBlob)
 {
   // XXX This is only safe so long as all blob implementations in our tree
   //     inherit nsDOMFileBase. If that ever changes then this will need to grow
   //     a real interface or something.
   return static_cast<nsDOMFileBase*>(aBlob);
 }
 
 } // anonymous namespace
 
-namespace mozilla {
-namespace dom {
-namespace ipc {
-
 // Each instance of this class will be dispatched to the network stream thread
 // pool to run the first time where it will open the file input stream. It will
 // then dispatch itself back to the main thread to send the child process its
 // response (assuming that the child has not crashed). The runnable will then
 // dispatch itself to the thread pool again in order to close the file input
 // stream.
-class BlobTraits<Parent>::BaseType::OpenStreamRunnable : public nsRunnable
+class BlobParent::OpenStreamRunnable MOZ_FINAL
+  : public nsRunnable
 {
   friend class nsRevocableEventPtr<OpenStreamRunnable>;
 
-  typedef BlobTraits<Parent> TraitsType;
-  typedef TraitsType::BaseType BlobActorType;
-  typedef TraitsType::StreamType BlobStreamProtocolType;
-
   // Only safe to access these pointers if mRevoked is false!
-  BlobActorType* mBlobActor;
-  BlobStreamProtocolType* mStreamActor;
+  BlobParent* mBlobActor;
+  PBlobStreamParent* mStreamActor;
 
   nsCOMPtr<nsIInputStream> mStream;
   nsCOMPtr<nsIIPCSerializableInputStream> mSerializable;
   nsCOMPtr<nsIEventTarget> mTarget;
 
   bool mRevoked;
   bool mClosing;
 
 public:
-  OpenStreamRunnable(BlobActorType* aBlobActor,
-                     BlobStreamProtocolType* aStreamActor,
+  OpenStreamRunnable(BlobParent* aBlobActor,
+                     PBlobStreamParent* aStreamActor,
                      nsIInputStream* aStream,
                      nsIIPCSerializableInputStream* aSerializable,
                      nsIEventTarget* aTarget)
   : mBlobActor(aBlobActor), mStreamActor(aStreamActor), mStream(aStream),
     mSerializable(aSerializable), mTarget(aTarget), mRevoked(false),
     mClosing(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -728,36 +648,36 @@ private:
       nsAutoTArray<FileDescriptor, 10> fds;
       serializable->Serialize(params, fds);
 
       MOZ_ASSERT(params.type() != InputStreamParams::T__None);
 
       PFileDescriptorSetParent* fdSet = nullptr;
 
       if (!fds.IsEmpty()) {
-        Blob<Parent>* blob = static_cast<Blob<Parent>*>(mBlobActor);
+        auto* manager = static_cast<ContentParent*>(mBlobActor->Manager());
+        MOZ_ASSERT(manager);
 
-        MOZ_ASSERT(blob->Manager());
-
-        fdSet = blob->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
+        fdSet = manager->SendPFileDescriptorSetConstructor(fds[0]);
         if (fdSet) {
           for (uint32_t index = 1; index < fds.Length(); index++) {
             unused << fdSet->SendAddFileDescriptor(fds[index]);
           }
         }
       }
 
       OptionalFileDescriptorSet optionalFDs;
       if (fdSet) {
         optionalFDs = fdSet;
       } else {
         optionalFDs = mozilla::void_t();
       }
 
-      unused << mStreamActor->Send__delete__(mStreamActor, params, optionalFDs);
+      unused <<
+        PBlobStreamParent::Send__delete__(mStreamActor, params, optionalFDs);
 
       mBlobActor->NoteRunnableCompleted(this);
 
 #ifdef DEBUG
       mBlobActor = nullptr;
       mStreamActor = nullptr;
 #endif
     }
@@ -769,513 +689,465 @@ private:
 
     nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 };
 
-BlobTraits<Parent>::BaseType::BaseType()
-{
-}
+/*******************************************************************************
+ * BlobChild::RemoteBlob Declaration
+ ******************************************************************************/
 
-BlobTraits<Parent>::BaseType::~BaseType()
-{
-}
-
-void
-BlobTraits<Parent>::BaseType::NoteRunnableCompleted(
-                    BlobTraits<Parent>::BaseType::OpenStreamRunnable* aRunnable)
+class BlobChild::RemoteBlob MOZ_FINAL
+  : public nsDOMFile
+  , public nsIRemoteBlob
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  class StreamHelper;
+  class SliceHelper;
 
-  for (uint32_t index = 0; index < mOpenStreamRunnables.Length(); index++) {
-    nsRevocableEventPtr<BaseType::OpenStreamRunnable>& runnable =
-      mOpenStreamRunnables[index];
+  BlobChild* mActor;
 
-    if (runnable.get() == aRunnable) {
-      runnable.Forget();
-      mOpenStreamRunnables.RemoveElementAt(index);
-      return;
-    }
+public:
+  RemoteBlob(const nsAString& aName,
+             const nsAString& aContentType,
+             uint64_t aLength,
+             uint64_t aModDate)
+    : nsDOMFile(aName, aContentType, aLength, aModDate)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob(const nsAString& aContentType, uint64_t aLength)
+    : nsDOMFile(aContentType, aLength)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
   }
 
-  MOZ_CRASH("Runnable not in our array!");
-}
-
-template <ActorFlavorEnum ActorFlavor>
-struct RemoteBlobBase;
+  RemoteBlob()
+    : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
 
-template <>
-struct RemoteBlobBase<Parent>
-{
-  InputStreamParams mInputStreamParams;
-};
+  void
+  SetActor(BlobChild* aActor)
+  {
+    MOZ_ASSERT(!aActor || !mActor);
+    mActor = aActor;
+  }
 
-template <>
-struct RemoteBlobBase<Child>
-{ };
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
+              MOZ_OVERRIDE;
 
-template <ActorFlavorEnum ActorFlavor>
-class RemoteBlob : public RemoteBlobBase<ActorFlavor>,
-                   public nsDOMFile,
-                   public nsIRemoteBlob
-{
-public:
-  typedef RemoteBlob<ActorFlavor> SelfType;
-  typedef Blob<ActorFlavor> ActorType;
-  typedef InputStreamActor<ActorFlavor> StreamActorType;
-  typedef typename ActorType::ConstructorParamsType ConstructorParamsType;
+  NS_IMETHOD
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
+
+  NS_IMETHOD
+  GetLastModifiedDate(JSContext* cx,
+                      JS::MutableHandle<JS::Value> aLastModifiedDate)
+                      MOZ_OVERRIDE;
+
+  virtual void*
+  GetPBlob() MOZ_OVERRIDE;
 
 private:
-  ActorType* mActor;
-
-  virtual ~RemoteBlob()
+  ~RemoteBlob()
   {
     if (mActor) {
       mActor->NoteDyingRemoteBlob();
     }
   }
+};
+
+class BlobChild::RemoteBlob::StreamHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobChild* mActor;
+  nsCOMPtr<nsIDOMBlob> mSourceBlob;
+  nsRefPtr<RemoteInputStream> mInputStream;
+  bool mDone;
+
+public:
+  StreamHelper(BlobChild* aActor, nsIDOMBlob* aSourceBlob)
+    : mMonitor("BlobChild::RemoteBlob::StreamHelper::mMonitor")
+    , mActor(aActor)
+    , mSourceBlob(aSourceBlob)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+    MOZ_ASSERT(aSourceBlob);
+  }
 
   nsresult
-  GetInternalStreamViaHelper(nsIInputStream** aStream)
+  GetStream(nsIInputStream** aInputStream)
   {
-    if (!mActor) {
+    // This may be called on any thread.
+    MOZ_ASSERT(aInputStream);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
+        }
+      }
+    }
+
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
+
+    if (!mInputStream) {
       return NS_ERROR_UNEXPECTED;
     }
 
-    nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
-    return helper->GetStream(aStream);
+    mInputStream.forget(aInputStream);
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
   }
 
-  class StreamHelper : public nsRunnable
+private:
+  void
+  RunInternal(bool aNotify)
   {
-    typedef Blob<ActorFlavor> ActorType;
-    typedef InputStreamActor<ActorFlavor> StreamActorType;
-
-    mozilla::Monitor mMonitor;
-    ActorType* mActor;
-    nsCOMPtr<nsIDOMBlob> mSourceBlob;
-    nsRefPtr<RemoteInputStream> mInputStream;
-    bool mDone;
-
-  public:
-    StreamHelper(ActorType* aActor, nsIDOMBlob* aSourceBlob)
-    : mMonitor("RemoteBlob::StreamHelper::mMonitor"), mActor(aActor),
-      mSourceBlob(aSourceBlob), mDone(false)
-    {
-      // This may be created on any thread.
-      MOZ_ASSERT(aActor);
-      MOZ_ASSERT(aSourceBlob);
-    }
-
-    nsresult
-    GetStream(nsIInputStream** aInputStream)
-    {
-      // This may be called on any thread.
-      MOZ_ASSERT(aInputStream);
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mInputStream);
-      MOZ_ASSERT(!mDone);
-
-      if (NS_IsMainThread()) {
-        RunInternal(false);
-      }
-      else {
-        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-        NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
-
-        nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        {
-          MonitorAutoLock lock(mMonitor);
-          while (!mDone) {
-            lock.Wait();
-          }
-        }
-      }
-
-      MOZ_ASSERT(!mActor);
-      MOZ_ASSERT(mDone);
-
-      if (!mInputStream) {
-        return NS_ERROR_UNEXPECTED;
-      }
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
 
-      mInputStream.forget(aInputStream);
-      return NS_OK;
-    }
-
-    NS_IMETHOD
-    Run()
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      RunInternal(true);
-      return NS_OK;
-    }
-
-  private:
-    void
-    RunInternal(bool aNotify)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mInputStream);
-      MOZ_ASSERT(!mDone);
-
-      nsRefPtr<RemoteInputStream> stream = new RemoteInputStream(mSourceBlob,
-                                                                 ActorFlavor);
-
-      StreamActorType* streamActor = new StreamActorType(stream);
-      if (mActor->SendPBlobStreamConstructor(streamActor)) {
-        stream.swap(mInputStream);
-      }
-
-      mActor = nullptr;
+    nsRefPtr<RemoteInputStream> stream =
+      new RemoteInputStream(mSourceBlob, ChildActor);
 
-      if (aNotify) {
-        MonitorAutoLock lock(mMonitor);
-        mDone = true;
-        lock.Notify();
-      }
-      else {
-        mDone = true;
-      }
-    }
-  };
-
-  class SliceHelper : public nsRunnable
-  {
-    typedef Blob<ActorFlavor> ActorType;
-
-    mozilla::Monitor mMonitor;
-    ActorType* mActor;
-    nsCOMPtr<nsIDOMBlob> mSlice;
-    uint64_t mStart;
-    uint64_t mLength;
-    nsString mContentType;
-    bool mDone;
-
-  public:
-    SliceHelper(ActorType* aActor)
-    : mMonitor("RemoteBlob::SliceHelper::mMonitor"), mActor(aActor), mStart(0),
-      mLength(0), mDone(false)
-    {
-      // This may be created on any thread.
-      MOZ_ASSERT(aActor);
+    InputStreamChild* streamActor = new InputStreamChild(stream);
+    if (mActor->SendPBlobStreamConstructor(streamActor)) {
+      stream.swap(mInputStream);
     }
 
-    nsresult
-    GetSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
-             nsIDOMBlob** aSlice)
-    {
-      // This may be called on any thread.
-      MOZ_ASSERT(aSlice);
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mSlice);
-      MOZ_ASSERT(!mDone);
+    mActor = nullptr;
 
-      mStart = aStart;
-      mLength = aLength;
-      mContentType = aContentType;
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
+  }
+};
+
+class BlobChild::RemoteBlob::SliceHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobChild* mActor;
+  nsCOMPtr<nsIDOMBlob> mSlice;
+  uint64_t mStart;
+  uint64_t mLength;
+  nsString mContentType;
+  bool mDone;
 
-      if (NS_IsMainThread()) {
-        RunInternal(false);
-      }
-      else {
-        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-        NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+public:
+  SliceHelper(BlobChild* aActor)
+    : mMonitor("BlobChild::RemoteBlob::SliceHelper::mMonitor")
+    , mActor(aActor)
+    , mStart(0)
+    , mLength(0)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+  }
 
-        nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
-        NS_ENSURE_SUCCESS(rv, rv);
+  nsresult
+  GetSlice(uint64_t aStart,
+           uint64_t aLength,
+           const nsAString& aContentType,
+           nsIDOMBlob** aSlice)
+  {
+    // This may be called on any thread.
+    MOZ_ASSERT(aSlice);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
 
-        {
-          MonitorAutoLock lock(mMonitor);
-          while (!mDone) {
-            lock.Wait();
-          }
+    mStart = aStart;
+    mLength = aLength;
+    mContentType = aContentType;
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
         }
       }
-
-      MOZ_ASSERT(!mActor);
-      MOZ_ASSERT(mDone);
-
-      if (!mSlice) {
-        return NS_ERROR_UNEXPECTED;
-      }
-
-      mSlice.forget(aSlice);
-      return NS_OK;
-    }
-
-    NS_IMETHOD
-    Run()
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      RunInternal(true);
-      return NS_OK;
     }
 
-  private:
-    void
-    RunInternal(bool aNotify)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-      MOZ_ASSERT(mActor);
-      MOZ_ASSERT(!mSlice);
-      MOZ_ASSERT(!mDone);
-
-      NS_ENSURE_TRUE_VOID(mActor->Manager());
-
-      NormalBlobConstructorParams normalParams;
-      normalParams.contentType() = mContentType;
-      normalParams.length() = mLength;
-
-      typename ActorType::ConstructorParamsType params;
-      ActorType::BaseType::SetBlobConstructorParams(params, normalParams);
-
-      ActorType* newActor = ActorType::Create(mActor->Manager(), params);
-      MOZ_ASSERT(newActor);
-
-      SlicedBlobConstructorParams slicedParams;
-      slicedParams.contentType() = mContentType;
-      slicedParams.begin() = mStart;
-      slicedParams.end() = mStart + mLength;
-      SetBlobOnParams(mActor, slicedParams);
-
-      typename ActorType::OtherSideConstructorParamsType otherSideParams;
-      ActorType::BaseType::SetBlobConstructorParams(otherSideParams,
-                                                    slicedParams);
-
-      if (mActor->Manager()->SendPBlobConstructor(newActor, otherSideParams)) {
-        mSlice = newActor->GetBlob();
-      }
-
-      mActor = nullptr;
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
 
-      if (aNotify) {
-        MonitorAutoLock lock(mMonitor);
-        mDone = true;
-        lock.Notify();
-      }
-      else {
-        mDone = true;
-      }
-    }
-  };
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  RemoteBlob(const nsAString& aName, const nsAString& aContentType,
-             uint64_t aLength, uint64_t aModDate)
-  : nsDOMFile(aName, aContentType, aLength, aModDate), mActor(nullptr)
-  {
-    mImmutable = true;
-  }
-
-  RemoteBlob(const nsAString& aContentType, uint64_t aLength)
-  : nsDOMFile(aContentType, aLength), mActor(nullptr)
-  {
-    mImmutable = true;
-  }
-
-  RemoteBlob()
-  : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
-  , mActor(nullptr)
-  {
-    mImmutable = true;
-  }
-
-  void
-  SetActor(ActorType* aActor)
-  {
-    MOZ_ASSERT(!aActor || !mActor);
-    mActor = aActor;
-  }
-
-  void
-  MaybeSetInputStream(const ConstructorParamsType& aParams);
-
-  virtual already_AddRefed<nsIDOMBlob>
-  CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
-              MOZ_OVERRIDE
-  {
-    if (!mActor) {
-      return nullptr;
+    if (!mSlice) {
+      return NS_ERROR_UNEXPECTED;
     }
 
-    nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
-
-    nsCOMPtr<nsIDOMBlob> slice;
-    nsresult rv =
-      helper->GetSlice(aStart, aLength, aContentType, getter_AddRefs(slice));
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    return slice.forget();
+    mSlice.forget(aSlice);
+    return NS_OK;
   }
 
   NS_IMETHOD
-  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
-
-  virtual void*
-  GetPBlob() MOZ_OVERRIDE
+  Run()
   {
-    return static_cast<typename ActorType::ProtocolType*>(mActor);
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
   }
 
-  NS_IMETHOD
-  GetLastModifiedDate(JSContext* cx,
-                      JS::MutableHandle<JS::Value> aLastModifiedDate) MOZ_OVERRIDE
+private:
+  void
+  RunInternal(bool aNotify)
   {
-    if (IsDateUnknown()) {
-      aLastModifiedDate.setNull();
-    } else {
-      JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
-      if (!date) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-      aLastModifiedDate.setObject(*date);
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
+
+    NS_ENSURE_TRUE_VOID(mActor->Manager());
+
+    NormalBlobConstructorParams normalParams;
+    normalParams.contentType() = mContentType;
+    normalParams.length() = mLength;
+
+    auto* manager = static_cast<ContentChild*>(mActor->Manager());
+    MOZ_ASSERT(manager);
+
+    BlobChild* newActor = BlobChild::Create(manager, normalParams);
+    MOZ_ASSERT(newActor);
+
+    SlicedBlobConstructorParams slicedParams;
+    slicedParams.contentType() = mContentType;
+    slicedParams.begin() = mStart;
+    slicedParams.end() = mStart + mLength;
+    slicedParams.sourceChild() = mActor;
+
+    ParentBlobConstructorParams otherSideParams;
+    otherSideParams.blobParams() = slicedParams;
+    otherSideParams.optionalInputStreamParams() = mozilla::void_t();
+
+    if (mActor->Manager()->SendPBlobConstructor(newActor, otherSideParams)) {
+      mSlice = newActor->GetBlob();
     }
-    return NS_OK;
+
+    mActor = nullptr;
+
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
   }
 };
 
-template <>
-void
-RemoteBlob<Parent>::MaybeSetInputStream(const ConstructorParamsType& aParams)
+/*******************************************************************************
+ * BlobChild::RemoteBlob Implementation
+ ******************************************************************************/
+
+NS_IMPL_ISUPPORTS_INHERITED1(BlobChild::RemoteBlob, nsDOMFile, nsIRemoteBlob)
+
+already_AddRefed<nsIDOMBlob>
+BlobChild::
+RemoteBlob::CreateSlice(uint64_t aStart,
+                        uint64_t aLength,
+                        const nsAString& aContentType)
 {
-  if (aParams.optionalInputStreamParams().type() ==
-      OptionalInputStreamParams::TInputStreamParams) {
-    mInputStreamParams =
-      aParams.optionalInputStreamParams().get_InputStreamParams();
+  if (!mActor) {
+    return nullptr;
   }
+
+  nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
+
+  nsCOMPtr<nsIDOMBlob> slice;
+  nsresult rv =
+    helper->GetSlice(aStart, aLength, aContentType, getter_AddRefs(slice));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return slice.forget();
 }
 
-template <>
-void
-RemoteBlob<Child>::MaybeSetInputStream(const ConstructorParamsType& aParams)
+NS_IMETHODIMP
+BlobChild::
+RemoteBlob::GetInternalStream(nsIInputStream** aStream)
 {
-  // Nothing needed on the child side!
+  if (!mActor) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
+  return helper->GetStream(aStream);
 }
 
-template <>
 NS_IMETHODIMP
-RemoteBlob<Parent>::GetInternalStream(nsIInputStream** aStream)
+BlobChild::
+RemoteBlob::GetLastModifiedDate(JSContext* cx,
+                                JS::MutableHandle<JS::Value> aLastModifiedDate)
 {
-  if (mInputStreamParams.type() != InputStreamParams::T__None) {
-    nsTArray<FileDescriptor> fds;
-    nsCOMPtr<nsIInputStream> realStream =
-      DeserializeInputStream(mInputStreamParams, fds);
-    if (!realStream) {
-      NS_WARNING("Failed to deserialize stream!");
-      return NS_ERROR_UNEXPECTED;
+  if (IsDateUnknown()) {
+    aLastModifiedDate.setNull();
+  } else {
+    JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
+    if (!date) {
+      return NS_ERROR_OUT_OF_MEMORY;
     }
-
-    nsCOMPtr<nsIInputStream> stream =
-      new BlobInputStreamTether(realStream, this);
-    stream.forget(aStream);
-    return NS_OK;
+    aLastModifiedDate.setObject(*date);
   }
-
-  return GetInternalStreamViaHelper(aStream);
+  return NS_OK;
 }
 
-template <>
-NS_IMETHODIMP
-RemoteBlob<Child>::GetInternalStream(nsIInputStream** aStream)
+void*
+BlobChild::
+RemoteBlob::GetPBlob()
 {
-  return GetInternalStreamViaHelper(aStream);
+  return static_cast<PBlobChild*>(mActor);
 }
 
-template <ActorFlavorEnum ActorFlavor>
-Blob<ActorFlavor>::Blob(ContentManager* aManager, nsIDOMBlob* aBlob)
-: mBlob(aBlob), mRemoteBlob(nullptr), mOwnsBlob(true)
-, mBlobIsFile(false), mManager(aManager)
+/*******************************************************************************
+ * BlobChild
+ ******************************************************************************/
+
+BlobChild::BlobChild(ContentChild* aManager, nsIDOMBlob* aBlob)
+  : mBlob(aBlob)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(true)
+  , mBlobIsFile(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
   MOZ_ASSERT(aBlob);
-  MOZ_ASSERT(aManager);
+
   aBlob->AddRef();
 
   nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
   mBlobIsFile = !!file;
 }
 
-template <ActorFlavorEnum ActorFlavor>
-Blob<ActorFlavor>::Blob(ContentManager* aManager,
-                        const ConstructorParamsType& aParams)
-: mBlob(nullptr), mRemoteBlob(nullptr), mOwnsBlob(false)
-, mBlobIsFile(false), mManager(aManager)
+BlobChild::BlobChild(ContentChild* aManager,
+                     const ChildBlobConstructorParams& aParams)
+  : mBlob(nullptr)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(false)
+  , mBlobIsFile(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aManager);
 
-  ChildBlobConstructorParams::Type paramType =
-    BaseType::GetBlobConstructorParams(aParams).type();
+  ChildBlobConstructorParams::Type paramsType = aParams.type();
 
   mBlobIsFile =
-    paramType == ChildBlobConstructorParams::TFileBlobConstructorParams ||
-    paramType == ChildBlobConstructorParams::TMysteryBlobConstructorParams;
+    paramsType == ChildBlobConstructorParams::TFileBlobConstructorParams ||
+    paramsType == ChildBlobConstructorParams::TMysteryBlobConstructorParams;
 
-  nsRefPtr<RemoteBlobType> remoteBlob = CreateRemoteBlob(aParams);
+  nsRefPtr<RemoteBlob> remoteBlob = CreateRemoteBlob(aParams);
   MOZ_ASSERT(remoteBlob);
 
   remoteBlob->SetActor(this);
-  remoteBlob->MaybeSetInputStream(aParams);
   remoteBlob.forget(&mRemoteBlob);
 
   mBlob = mRemoteBlob;
   mOwnsBlob = true;
 }
 
-template <ActorFlavorEnum ActorFlavor>
-Blob<ActorFlavor>*
-Blob<ActorFlavor>::Create(ContentManager* aManager,
-                          const ConstructorParamsType& aParams)
+BlobChild::~BlobChild()
+{
+}
+
+BlobChild*
+BlobChild::Create(ContentChild* aManager,
+                  const ChildBlobConstructorParams& aParams)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
 
-  const ChildBlobConstructorParams& blobParams =
-    BaseType::GetBlobConstructorParams(aParams);
-
-  switch (blobParams.type()) {
+  switch (aParams.type()) {
     case ChildBlobConstructorParams::TNormalBlobConstructorParams:
     case ChildBlobConstructorParams::TFileBlobConstructorParams:
     case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
-      return new Blob<ActorFlavor>(aManager, aParams);
+      return new BlobChild(aManager, aParams);
 
     case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {
       const SlicedBlobConstructorParams& params =
-        blobParams.get_SlicedBlobConstructorParams();
+        aParams.get_SlicedBlobConstructorParams();
 
-      nsCOMPtr<nsIDOMBlob> source = GetBlobFromParams<ActorFlavor>(params);
+      auto* actor =
+        const_cast<BlobChild*>(
+          static_cast<const BlobChild*>(params.sourceChild()));
+      MOZ_ASSERT(actor);
+
+      nsCOMPtr<nsIDOMBlob> source = actor->GetBlob();
       MOZ_ASSERT(source);
 
       nsCOMPtr<nsIDOMBlob> slice;
       nsresult rv =
         source->Slice(params.begin(), params.end(), params.contentType(), 3,
                       getter_AddRefs(slice));
       NS_ENSURE_SUCCESS(rv, nullptr);
 
-      return new Blob<ActorFlavor>(aManager, slice);
+      return new BlobChild(aManager, slice);
     }
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   return nullptr;
 }
 
-template <ActorFlavorEnum ActorFlavor>
 already_AddRefed<nsIDOMBlob>
-Blob<ActorFlavor>::GetBlob()
+BlobChild::GetBlob()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
 
   nsCOMPtr<nsIDOMBlob> blob;
 
   // Remote blobs are held alive until the first call to GetBlob. Thereafter we
   // only hold a weak reference. Normal blobs are held alive until the actor is
@@ -1288,151 +1160,944 @@ Blob<ActorFlavor>::GetBlob()
     blob = mBlob;
   }
 
   MOZ_ASSERT(blob);
 
   return blob.forget();
 }
 
-template <ActorFlavorEnum ActorFlavor>
 bool
-Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aName,
-                                      const nsString& aContentType,
-                                      uint64_t aLength,
-                                      uint64_t aLastModifiedDate)
+BlobChild::SetMysteryBlobInfo(const nsString& aName,
+                              const nsString& aContentType,
+                              uint64_t aLength,
+                              uint64_t aLastModifiedDate)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(mRemoteBlob);
   MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
 
-  ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType,
-                                     aLength, aLastModifiedDate);
+  ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType, aLength,
+                                     aLastModifiedDate);
 
-  FileBlobConstructorParams params(aName, aContentType,
-                                   aLength, aLastModifiedDate);
-  return ProtocolType::SendResolveMystery(params);
+  FileBlobConstructorParams params(aName, aContentType, aLength,
+                                   aLastModifiedDate);
+  return SendResolveMystery(params);
 }
 
-template <ActorFlavorEnum ActorFlavor>
 bool
-Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aContentType,
-                                      uint64_t aLength)
+BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(mRemoteBlob);
 
   nsString voidString;
   voidString.SetIsVoid(true);
 
-  ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType,
-                                     aLength, UINT64_MAX);
+  ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType, aLength,
+                                     UINT64_MAX);
 
   NormalBlobConstructorParams params(aContentType, aLength);
-  return ProtocolType::SendResolveMystery(params);
+  return SendResolveMystery(params);
 }
 
-template <ActorFlavorEnum ActorFlavor>
-already_AddRefed<typename Blob<ActorFlavor>::RemoteBlobType>
-Blob<ActorFlavor>::CreateRemoteBlob(const ConstructorParamsType& aParams)
+already_AddRefed<BlobChild::RemoteBlob>
+BlobChild::CreateRemoteBlob(const ChildBlobConstructorParams& aParams)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  const ChildBlobConstructorParams& blobParams =
-    BaseType::GetBlobConstructorParams(aParams);
+  nsRefPtr<RemoteBlob> remoteBlob;
 
-  nsRefPtr<RemoteBlobType> remoteBlob;
-
-  switch (blobParams.type()) {
+  switch (aParams.type()) {
     case ChildBlobConstructorParams::TNormalBlobConstructorParams: {
       const NormalBlobConstructorParams& params =
-        blobParams.get_NormalBlobConstructorParams();
-      remoteBlob = new RemoteBlobType(params.contentType(), params.length());
+        aParams.get_NormalBlobConstructorParams();
+      remoteBlob = new RemoteBlob(params.contentType(), params.length());
       break;
     }
 
     case ChildBlobConstructorParams::TFileBlobConstructorParams: {
       const FileBlobConstructorParams& params =
-        blobParams.get_FileBlobConstructorParams();
+        aParams.get_FileBlobConstructorParams();
       remoteBlob =
-        new RemoteBlobType(params.name(), params.contentType(),
-                           params.length(), params.modDate());
+        new RemoteBlob(params.name(), params.contentType(), params.length(),
+                       params.modDate());
       break;
     }
 
     case ChildBlobConstructorParams::TMysteryBlobConstructorParams: {
-      remoteBlob = new RemoteBlobType();
+      remoteBlob = new RemoteBlob();
       break;
     }
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   MOZ_ASSERT(remoteBlob);
 
   if (NS_FAILED(remoteBlob->SetMutable(false))) {
     MOZ_CRASH("Failed to make remote blob immutable!");
   }
 
   return remoteBlob.forget();
 }
 
-template <ActorFlavorEnum ActorFlavor>
 void
-Blob<ActorFlavor>::NoteDyingRemoteBlob()
+BlobChild::NoteDyingRemoteBlob()
 {
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(mRemoteBlob);
   MOZ_ASSERT(!mOwnsBlob);
 
   // This may be called on any thread due to the fact that RemoteBlob is
   // designed to be passed between threads. We must start the shutdown process
   // on the main thread, so we proxy here if necessary.
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIRunnable> runnable =
-      NS_NewNonOwningRunnableMethod(this,
-                                    &Blob<ActorFlavor>::NoteDyingRemoteBlob);
+      NS_NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlob);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       MOZ_ASSERT(false, "Should never fail!");
     }
 
     return;
   }
 
   // Must do this before calling Send__delete__ or we'll crash there trying to
   // access a dangling pointer.
   mBlob = nullptr;
   mRemoteBlob = nullptr;
 
-  mozilla::unused << ProtocolType::Send__delete__(this);
+  PBlobChild::Send__delete__(this);
 }
 
-template <ActorFlavorEnum ActorFlavor>
 void
-Blob<ActorFlavor>::ActorDestroy(ActorDestroyReason aWhy)
+BlobChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mRemoteBlob) {
     mRemoteBlob->SetActor(nullptr);
   }
 
   if (mBlob && mOwnsBlob) {
     mBlob->Release();
   }
 
-  mManager = nullptr;
+  mStrongManager = nullptr;
+}
+
+PBlobStreamChild*
+BlobChild::AllocPBlobStreamChild()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return new InputStreamChild();
+}
+
+bool
+BlobChild::RecvPBlobStreamConstructor(PBlobStreamChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+    do_QueryInterface(stream);
+  if (!serializable) {
+    MOZ_ASSERT(false, "Must be serializable!");
+    return false;
+  }
+
+  InputStreamParams params;
+  nsTArray<FileDescriptor> fds;
+  serializable->Serialize(params, fds);
+
+  MOZ_ASSERT(params.type() != InputStreamParams::T__None);
+  MOZ_ASSERT(fds.IsEmpty());
+
+  return aActor->Send__delete__(aActor, params, mozilla::void_t());
+}
+
+bool
+BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  delete static_cast<InputStreamChild*>(aActor);
+  return true;
+}
+
+bool
+BlobChild::RecvResolveMystery(const ResolveMysteryParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+  MOZ_ASSERT(mOwnsBlob);
+
+  if (!mBlobIsFile) {
+    MOZ_ASSERT(false, "Must always be a file!");
+    return false;
+  }
+
+  nsDOMFileBase* blob = ToConcreteBlob(mBlob);
+
+  switch (aParams.type()) {
+    case ResolveMysteryParams::TNormalBlobConstructorParams: {
+      const NormalBlobConstructorParams& params =
+        aParams.get_NormalBlobConstructorParams();
+      nsString voidString;
+      voidString.SetIsVoid(true);
+      blob->SetLazyData(voidString, params.contentType(), params.length(),
+                        UINT64_MAX);
+      break;
+    }
+
+    case ResolveMysteryParams::TFileBlobConstructorParams: {
+      const FileBlobConstructorParams& params =
+        aParams.get_FileBlobConstructorParams();
+      blob->SetLazyData(params.name(), params.contentType(), params.length(),
+                        params.modDate());
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Unknown params!");
+  }
+
+  return true;
 }
 
-template <ActorFlavorEnum ActorFlavor>
+/*******************************************************************************
+ * BlobParent::RemoteBlob Declaration
+ ******************************************************************************/
+
+class BlobParent::RemoteBlob MOZ_FINAL
+  : public nsDOMFile
+  , public nsIRemoteBlob
+{
+  class StreamHelper;
+  class SliceHelper;
+
+  BlobParent* mActor;
+  InputStreamParams mInputStreamParams;
+
+public:
+  RemoteBlob(const nsAString& aName,
+             const nsAString& aContentType,
+             uint64_t aLength,
+             uint64_t aModDate)
+    : nsDOMFile(aName, aContentType, aLength, aModDate)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob(const nsAString& aContentType, uint64_t aLength)
+    : nsDOMFile(aContentType, aLength)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob()
+    : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
+    , mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  void
+  SetActor(BlobParent* aActor)
+  {
+    MOZ_ASSERT(!aActor || !mActor);
+    mActor = aActor;
+  }
+
+  void
+  MaybeSetInputStream(const ParentBlobConstructorParams& aParams)
+  {
+    if (aParams.optionalInputStreamParams().type() ==
+        OptionalInputStreamParams::TInputStreamParams) {
+      mInputStreamParams =
+        aParams.optionalInputStreamParams().get_InputStreamParams();
+    }
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
+              MOZ_OVERRIDE;
+
+  NS_IMETHOD
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
+
+  NS_IMETHOD
+  GetLastModifiedDate(JSContext* cx,
+                      JS::MutableHandle<JS::Value> aLastModifiedDate)
+                      MOZ_OVERRIDE;
+
+  virtual void*
+  GetPBlob() MOZ_OVERRIDE;
+
+private:
+  ~RemoteBlob()
+  {
+    if (mActor) {
+      mActor->NoteDyingRemoteBlob();
+    }
+  }
+};
+
+class BlobParent::RemoteBlob::StreamHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobParent* mActor;
+  nsCOMPtr<nsIDOMBlob> mSourceBlob;
+  nsRefPtr<RemoteInputStream> mInputStream;
+  bool mDone;
+
+public:
+  StreamHelper(BlobParent* aActor, nsIDOMBlob* aSourceBlob)
+    : mMonitor("BlobParent::RemoteBlob::StreamHelper::mMonitor")
+    , mActor(aActor)
+    , mSourceBlob(aSourceBlob)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+    MOZ_ASSERT(aSourceBlob);
+  }
+
+  nsresult
+  GetStream(nsIInputStream** aInputStream)
+  {
+    // This may be called on any thread.
+    MOZ_ASSERT(aInputStream);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
+        }
+      }
+    }
+
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
+
+    if (!mInputStream) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    mInputStream.forget(aInputStream);
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
+  }
+
+private:
+  void
+  RunInternal(bool aNotify)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(!mDone);
+
+    nsRefPtr<RemoteInputStream> stream =
+      new RemoteInputStream(mSourceBlob, ParentActor);
+
+    InputStreamParent* streamActor = new InputStreamParent(stream);
+    if (mActor->SendPBlobStreamConstructor(streamActor)) {
+      stream.swap(mInputStream);
+    }
+
+    mActor = nullptr;
+
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
+  }
+};
+
+class BlobParent::RemoteBlob::SliceHelper MOZ_FINAL
+  : public nsRunnable
+{
+  mozilla::Monitor mMonitor;
+  BlobParent* mActor;
+  nsCOMPtr<nsIDOMBlob> mSlice;
+  uint64_t mStart;
+  uint64_t mLength;
+  nsString mContentType;
+  bool mDone;
+
+public:
+  SliceHelper(BlobParent* aActor)
+    : mMonitor("BlobParent::RemoteBlob::SliceHelper::mMonitor")
+    , mActor(aActor)
+    , mStart(0)
+    , mLength(0)
+    , mDone(false)
+  {
+    // This may be created on any thread.
+    MOZ_ASSERT(aActor);
+  }
+
+  nsresult
+  GetSlice(uint64_t aStart,
+           uint64_t aLength,
+           const nsAString& aContentType,
+           nsIDOMBlob** aSlice)
+  {
+    // This may be called on any thread.
+    MOZ_ASSERT(aSlice);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
+
+    mStart = aStart;
+    mLength = aLength;
+    mContentType = aContentType;
+
+    if (NS_IsMainThread()) {
+      RunInternal(false);
+    }
+    else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+      nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      {
+        MonitorAutoLock lock(mMonitor);
+        while (!mDone) {
+          lock.Wait();
+        }
+      }
+    }
+
+    MOZ_ASSERT(!mActor);
+    MOZ_ASSERT(mDone);
+
+    if (!mSlice) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    mSlice.forget(aSlice);
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal(true);
+    return NS_OK;
+  }
+
+private:
+  void
+  RunInternal(bool aNotify)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(!mSlice);
+    MOZ_ASSERT(!mDone);
+
+    NS_ENSURE_TRUE_VOID(mActor->Manager());
+
+    NormalBlobConstructorParams normalParams;
+    normalParams.contentType() = mContentType;
+    normalParams.length() = mLength;
+
+    ParentBlobConstructorParams params;
+    params.blobParams() = normalParams;
+    params.optionalInputStreamParams() = void_t();
+
+    auto* manager = static_cast<ContentParent*>(mActor->Manager());
+    MOZ_ASSERT(manager);
+
+    BlobParent* newActor = BlobParent::Create(manager, params);
+    MOZ_ASSERT(newActor);
+
+    SlicedBlobConstructorParams slicedParams;
+    slicedParams.contentType() = mContentType;
+    slicedParams.begin() = mStart;
+    slicedParams.end() = mStart + mLength;
+    slicedParams.sourceParent() = mActor;
+
+    ChildBlobConstructorParams otherSideParams = slicedParams;
+
+    if (mActor->Manager()->SendPBlobConstructor(newActor, otherSideParams)) {
+      mSlice = newActor->GetBlob();
+    }
+
+    mActor = nullptr;
+
+    if (aNotify) {
+      MonitorAutoLock lock(mMonitor);
+      mDone = true;
+      lock.Notify();
+    }
+    else {
+      mDone = true;
+    }
+  }
+};
+
+/*******************************************************************************
+ * BlobChild::RemoteBlob Implementation
+ ******************************************************************************/
+
+NS_IMPL_ISUPPORTS_INHERITED1(BlobParent::RemoteBlob, nsDOMFile, nsIRemoteBlob)
+
+already_AddRefed<nsIDOMBlob>
+BlobParent::
+RemoteBlob::CreateSlice(uint64_t aStart,
+                        uint64_t aLength,
+                        const nsAString& aContentType)
+{
+  if (!mActor) {
+    return nullptr;
+  }
+
+  nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
+
+  nsCOMPtr<nsIDOMBlob> slice;
+  nsresult rv =
+    helper->GetSlice(aStart, aLength, aContentType, getter_AddRefs(slice));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return slice.forget();
+}
+
+NS_IMETHODIMP
+BlobParent::
+RemoteBlob::GetInternalStream(nsIInputStream** aStream)
+{
+  if (mInputStreamParams.type() != InputStreamParams::T__None) {
+    nsTArray<FileDescriptor> fds;
+    nsCOMPtr<nsIInputStream> realStream =
+      DeserializeInputStream(mInputStreamParams, fds);
+    if (!realStream) {
+      NS_WARNING("Failed to deserialize stream!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    nsCOMPtr<nsIInputStream> stream =
+      new BlobInputStreamTether(realStream, this);
+    stream.forget(aStream);
+    return NS_OK;
+  }
+
+  if (!mActor) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
+  return helper->GetStream(aStream);
+}
+
+NS_IMETHODIMP
+BlobParent::
+RemoteBlob::GetLastModifiedDate(JSContext* cx,
+                                JS::MutableHandle<JS::Value> aLastModifiedDate)
+{
+  if (IsDateUnknown()) {
+    aLastModifiedDate.setNull();
+  } else {
+    JSObject* date = JS_NewDateObjectMsec(cx, mLastModificationDate);
+    if (!date) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    aLastModifiedDate.setObject(*date);
+  }
+  return NS_OK;
+}
+
+void*
+BlobParent::
+RemoteBlob::GetPBlob()
+{
+  return static_cast<PBlobParent*>(mActor);
+}
+
+/*******************************************************************************
+ * BlobParent
+ ******************************************************************************/
+
+BlobParent::BlobParent(ContentParent* aManager, nsIDOMBlob* aBlob)
+  : mBlob(aBlob)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(true)
+  , mBlobIsFile(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+  MOZ_ASSERT(aBlob);
+
+  aBlob->AddRef();
+
+  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+  mBlobIsFile = !!file;
+}
+
+BlobParent::BlobParent(ContentParent* aManager,
+                       const ParentBlobConstructorParams& aParams)
+  : mBlob(nullptr)
+  , mRemoteBlob(nullptr)
+  , mStrongManager(aManager)
+  , mOwnsBlob(false)
+  , mBlobIsFile(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+
+  ChildBlobConstructorParams::Type paramsType = aParams.blobParams().type();
+
+  mBlobIsFile =
+    paramsType == ChildBlobConstructorParams::TFileBlobConstructorParams ||
+    paramsType == ChildBlobConstructorParams::TMysteryBlobConstructorParams;
+
+  nsRefPtr<RemoteBlob> remoteBlob = CreateRemoteBlob(aParams);
+  MOZ_ASSERT(remoteBlob);
+
+  remoteBlob->SetActor(this);
+  remoteBlob->MaybeSetInputStream(aParams);
+  remoteBlob.forget(&mRemoteBlob);
+
+  mBlob = mRemoteBlob;
+  mOwnsBlob = true;
+}
+
+BlobParent::~BlobParent()
+{
+}
+
+BlobParent*
+BlobParent::Create(ContentParent* aManager,
+                   const ParentBlobConstructorParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+
+  const ChildBlobConstructorParams& blobParams = aParams.blobParams();
+
+  switch (blobParams.type()) {
+    case ChildBlobConstructorParams::TNormalBlobConstructorParams:
+    case ChildBlobConstructorParams::TFileBlobConstructorParams:
+    case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
+      return new BlobParent(aManager, aParams);
+
+    case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {
+      const SlicedBlobConstructorParams& params =
+        blobParams.get_SlicedBlobConstructorParams();
+
+      auto* actor =
+        const_cast<BlobParent*>(
+          static_cast<const BlobParent*>(params.sourceParent()));
+      MOZ_ASSERT(actor);
+
+      nsCOMPtr<nsIDOMBlob> source = actor->GetBlob();
+      MOZ_ASSERT(source);
+
+      nsCOMPtr<nsIDOMBlob> slice;
+      nsresult rv =
+        source->Slice(params.begin(), params.end(), params.contentType(), 3,
+                      getter_AddRefs(slice));
+      NS_ENSURE_SUCCESS(rv, nullptr);
+
+      return new BlobParent(aManager, slice);
+    }
+
+    default:
+      MOZ_CRASH("Unknown params!");
+  }
+
+  return nullptr;
+}
+
+already_AddRefed<nsIDOMBlob>
+BlobParent::GetBlob()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+
+  nsCOMPtr<nsIDOMBlob> blob;
+
+  // Remote blobs are held alive until the first call to GetBlob. Thereafter we
+  // only hold a weak reference. Normal blobs are held alive until the actor is
+  // destroyed.
+  if (mRemoteBlob && mOwnsBlob) {
+    blob = dont_AddRef(mBlob);
+    mOwnsBlob = false;
+  }
+  else {
+    blob = mBlob;
+  }
+
+  MOZ_ASSERT(blob);
+
+  return blob.forget();
+}
+
 bool
-Blob<ActorFlavor>::RecvResolveMystery(const ResolveMysteryParams& aParams)
+BlobParent::SetMysteryBlobInfo(const nsString& aName,
+                               const nsString& aContentType,
+                               uint64_t aLength,
+                               uint64_t aLastModifiedDate)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
+
+  ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType, aLength,
+                                     aLastModifiedDate);
+
+  FileBlobConstructorParams params(aName, aContentType, aLength,
+                                   aLastModifiedDate);
+  return SendResolveMystery(params);
+}
+
+bool
+BlobParent::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+
+  nsString voidString;
+  voidString.SetIsVoid(true);
+
+  ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType, aLength,
+                                     UINT64_MAX);
+
+  NormalBlobConstructorParams params(aContentType, aLength);
+  return SendResolveMystery(params);
+}
+
+already_AddRefed<BlobParent::RemoteBlob>
+BlobParent::CreateRemoteBlob(const ParentBlobConstructorParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  const ChildBlobConstructorParams& blobParams = aParams.blobParams();
+
+  nsRefPtr<RemoteBlob> remoteBlob;
+
+  switch (blobParams.type()) {
+    case ChildBlobConstructorParams::TNormalBlobConstructorParams: {
+      const NormalBlobConstructorParams& params =
+        blobParams.get_NormalBlobConstructorParams();
+      remoteBlob = new RemoteBlob(params.contentType(), params.length());
+      break;
+    }
+
+    case ChildBlobConstructorParams::TFileBlobConstructorParams: {
+      const FileBlobConstructorParams& params =
+        blobParams.get_FileBlobConstructorParams();
+      remoteBlob =
+        new RemoteBlob(params.name(), params.contentType(), params.length(),
+                       params.modDate());
+      break;
+    }
+
+    case ChildBlobConstructorParams::TMysteryBlobConstructorParams: {
+      remoteBlob = new RemoteBlob();
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Unknown params!");
+  }
+
+  MOZ_ASSERT(remoteBlob);
+
+  if (NS_FAILED(remoteBlob->SetMutable(false))) {
+    MOZ_CRASH("Failed to make remote blob immutable!");
+  }
+
+  return remoteBlob.forget();
+}
+
+void
+BlobParent::NoteDyingRemoteBlob()
+{
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(!mOwnsBlob);
+
+  // This may be called on any thread due to the fact that RemoteBlob is
+  // designed to be passed between threads. We must start the shutdown process
+  // on the main thread, so we proxy here if necessary.
+  if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlob);
+    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+      MOZ_ASSERT(false, "Should never fail!");
+    }
+
+    return;
+  }
+
+  // Must do this before calling Send__delete__ or we'll crash there trying to
+  // access a dangling pointer.
+  mBlob = nullptr;
+  mRemoteBlob = nullptr;
+
+  mozilla::unused << PBlobParent::Send__delete__(this);
+}
+
+void
+BlobParent::NoteRunnableCompleted(OpenStreamRunnable* aRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  for (uint32_t index = 0; index < mOpenStreamRunnables.Length(); index++) {
+    nsRevocableEventPtr<OpenStreamRunnable>& runnable =
+      mOpenStreamRunnables[index];
+
+    if (runnable.get() == aRunnable) {
+      runnable.Forget();
+      mOpenStreamRunnables.RemoveElementAt(index);
+      return;
+    }
+  }
+
+  MOZ_CRASH("Runnable not in our array!");
+}
+
+void
+BlobParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mRemoteBlob) {
+    mRemoteBlob->SetActor(nullptr);
+  }
+
+  if (mBlob && mOwnsBlob) {
+    mBlob->Release();
+  }
+
+  mStrongManager = nullptr;
+}
+
+PBlobStreamParent*
+BlobParent::AllocPBlobStreamParent()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return new InputStreamParent();
+}
+
+bool
+BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlob);
+
+  nsCOMPtr<IPrivateRemoteInputStream> remoteStream;
+  if (remoteBlob) {
+    remoteStream = do_QueryInterface(stream);
+  }
+
+  // There are three cases in which we can use the stream obtained from the blob
+  // directly as our serialized stream:
+  //
+  //   1. The blob is not a remote blob.
+  //   2. The blob is a remote blob that represents this actor.
+  //   3. The blob is a remote blob representing a different actor but we
+  //      already have a non-remote, i.e. serialized, serialized stream.
+  //
+  // In all other cases we need to be on a background thread before we can get
+  // to the real stream.
+  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream;
+  if (!remoteBlob ||
+      static_cast<PBlobParent*>(remoteBlob->GetPBlob()) == this ||
+      !remoteStream) {
+    serializableStream = do_QueryInterface(stream);
+    if (!serializableStream) {
+      MOZ_ASSERT(false, "Must be serializable!");
+      return false;
+    }
+  }
+
+  nsCOMPtr<nsIEventTarget> target =
+    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(target, false);
+
+  nsRefPtr<OpenStreamRunnable> runnable =
+    new OpenStreamRunnable(this, aActor, stream, serializableStream, target);
+
+  rv = runnable->Dispatch();
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // nsRevocableEventPtr lacks some of the operators needed for anything nicer.
+  *mOpenStreamRunnables.AppendElement() = runnable;
+  return true;
+}
+
+bool
+BlobParent::DeallocPBlobStreamParent(PBlobStreamParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  delete static_cast<InputStreamParent*>(aActor);
+  return true;
+}
+
+bool
+BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mBlob);
   MOZ_ASSERT(!mRemoteBlob);
   MOZ_ASSERT(mOwnsBlob);
 
   if (!mBlobIsFile) {
     MOZ_ASSERT(false, "Must always be a file!");
@@ -1462,141 +2127,59 @@ Blob<ActorFlavor>::RecvResolveMystery(co
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   return true;
 }
 
-template <>
 bool
-Blob<Parent>::RecvPBlobStreamConstructor(StreamType* aActor)
+InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
+                                 const OptionalFileDescriptorSet& aFDs)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mBlob);
-  MOZ_ASSERT(!mRemoteBlob);
+  MOZ_ASSERT(mRemoteStream);
 
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, false);
+  nsTArray<FileDescriptor> fds;
+  if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
+    FileDescriptorSetChild* fdSetActor =
+      static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
+    MOZ_ASSERT(fdSetActor);
 
-  nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlob);
+    fdSetActor->ForgetFileDescriptors(fds);
+    MOZ_ASSERT(!fds.IsEmpty());
 
-  nsCOMPtr<IPrivateRemoteInputStream> remoteStream;
-  if (remoteBlob) {
-    remoteStream = do_QueryInterface(stream);
+    fdSetActor->Send__delete__(fdSetActor);
   }
 
-  // There are three cases in which we can use the stream obtained from the blob
-  // directly as our serialized stream:
-  //
-  //   1. The blob is not a remote blob.
-  //   2. The blob is a remote blob that represents this actor.
-  //   3. The blob is a remote blob representing a different actor but we
-  //      already have a non-remote, i.e. serialized, serialized stream.
-  //
-  // In all other cases we need to be on a background thread before we can get
-  // to the real stream.
-  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream;
-  if (!remoteBlob ||
-      static_cast<ProtocolType*>(remoteBlob->GetPBlob()) == this ||
-      !remoteStream) {
-    serializableStream = do_QueryInterface(stream);
-    if (!serializableStream) {
-      MOZ_ASSERT(false, "Must be serializable!");
-      return false;
-    }
-  }
-
-  nsCOMPtr<nsIEventTarget> target =
-    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(target, false);
-
-  nsRefPtr<BaseType::OpenStreamRunnable> runnable =
-    new BaseType::OpenStreamRunnable(this, aActor, stream, serializableStream,
-                                     target);
-
-  rv = runnable->Dispatch();
-  NS_ENSURE_SUCCESS(rv, false);
-
-  nsRevocableEventPtr<BaseType::OpenStreamRunnable>* arrayMember =
-    mOpenStreamRunnables.AppendElement();
-  *arrayMember = runnable;
-  return true;
-}
-
-template <>
-bool
-Blob<Child>::RecvPBlobStreamConstructor(StreamType* aActor)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mBlob);
-  MOZ_ASSERT(!mRemoteBlob);
-
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  nsCOMPtr<nsIIPCSerializableInputStream> serializable =
-    do_QueryInterface(stream);
-  if (!serializable) {
-    MOZ_ASSERT(false, "Must be serializable!");
+  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+  if (!stream) {
     return false;
   }
 
-  InputStreamParams params;
-  nsTArray<FileDescriptor> fds;
-  serializable->Serialize(params, fds);
-
-  MOZ_ASSERT(params.type() != InputStreamParams::T__None);
-  MOZ_ASSERT(fds.IsEmpty());
-
-  return aActor->Send__delete__(aActor, params, mozilla::void_t());
-}
-
-BlobTraits<Parent>::StreamType*
-BlobTraits<Parent>::BaseType::AllocPBlobStreamParent()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return new InputStreamActor<Parent>();
-}
-
-BlobTraits<Child>::StreamType*
-BlobTraits<Child>::BaseType::AllocPBlobStreamChild()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return new InputStreamActor<Child>();
-}
-
-bool
-BlobTraits<Parent>::BaseType::DeallocPBlobStreamParent(BlobTraits<Parent>::StreamType* aActor)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  delete aActor;
+  mRemoteStream->SetStream(stream);
   return true;
 }
 
 bool
-BlobTraits<Child>::BaseType::DeallocPBlobStreamChild(BlobTraits<Child>::StreamType* aActor)
+InputStreamParent::Recv__delete__(const InputStreamParams& aParams,
+                                  const OptionalFileDescriptorSet& aFDs)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  delete aActor;
+  MOZ_ASSERT(mRemoteStream);
+
+  if (aFDs.type() != OptionalFileDescriptorSet::Tvoid_t) {
+    NS_WARNING("Child cannot send FileDescriptors to the parent!");
+    return false;
+  }
+
+  nsTArray<FileDescriptor> fds;
+  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+  if (!stream) {
+    return false;
+  }
+
+  MOZ_ASSERT(fds.IsEmpty());
+
+  mRemoteStream->SetStream(stream);
   return true;
 }
-
-template <ActorFlavorEnum ActorFlavor>
-NS_IMPL_ADDREF_INHERITED(RemoteBlob<ActorFlavor>, nsDOMFile)
-
-template <ActorFlavorEnum ActorFlavor>
-NS_IMPL_RELEASE_INHERITED(RemoteBlob<ActorFlavor>, nsDOMFile)
-
-template <ActorFlavorEnum ActorFlavor>
-NS_IMPL_QUERY_INTERFACE_INHERITED1(RemoteBlob<ActorFlavor>, nsDOMFile,
-                                                            nsIRemoteBlob)
-
-// Explicit instantiation of both classes.
-template class Blob<Parent>;
-template class Blob<Child>;
-
-} // namespace ipc
-} // namespace dom
-} // namespace mozilla
--- a/dom/ipc/Blob.h
+++ b/dom/ipc/Blob.h
@@ -1,248 +1,196 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ipc_Blob_h
 #define mozilla_dom_ipc_Blob_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/PBlobParent.h"
-#include "mozilla/dom/PBlobStreamChild.h"
-#include "mozilla/dom/PBlobStreamParent.h"
-
 #include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
 #include "nsTArray.h"
 
 class nsIDOMBlob;
-template<class T> class nsRevocableEventPtr;
+class nsString;
+template <class> class nsRevocableEventPtr;
 
 namespace mozilla {
 namespace dom {
 
+class ContentChild;
 class ContentParent;
-class ContentChild;
-
-namespace ipc {
-
-enum ActorFlavorEnum
-{
-  Parent = 0,
-  Child
-};
-
-template <ActorFlavorEnum>
-struct BlobTraits;
-
-template <>
-struct BlobTraits<Parent>
-{
-  typedef mozilla::dom::PBlobParent ProtocolType;
-  typedef mozilla::dom::PBlobStreamParent StreamType;
-  typedef mozilla::dom::ParentBlobConstructorParams ConstructorParamsType;
-  typedef mozilla::dom::ChildBlobConstructorParams
-          OtherSideConstructorParamsType;
-  typedef mozilla::dom::ContentParent ConcreteContentManagerType;
-
-  // BaseType on the parent side is a bit more complicated than for the child
-  // side. In the case of nsIInputStreams backed by files we need to ensure that
-  // the files are actually opened and closed on a background thread before we
-  // can send their file handles across to the child. The child process could
-  // crash during this process so we need to make sure we cancel the intended
-  // response in such a case. We do that by holding an array of
-  // nsRevocableEventPtr. If the child crashes then this actor will be destroyed
-  // and the nsRevocableEventPtr destructor will cancel any stream events that
-  // are currently in flight.
-  class BaseType : public ProtocolType
-  {
-  public:
-    static const ChildBlobConstructorParams&
-    GetBlobConstructorParams(const ConstructorParamsType& aParams)
-    {
-      return aParams.blobParams();
-    }
-
-    static void
-    SetBlobConstructorParams(ConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams.blobParams() = aBlobParams;
-      aParams.optionalInputStreamParams() = mozilla::void_t();
-    }
-
-    static void
-    SetBlobConstructorParams(OtherSideConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams = aBlobParams;
-    }
-
-  protected:
-    virtual StreamType*
-    AllocPBlobStreamParent() MOZ_OVERRIDE;
-
-    virtual bool
-    DeallocPBlobStreamParent(StreamType* aActor) MOZ_OVERRIDE;
-
-    BaseType();
-    virtual ~BaseType();
-
-    class OpenStreamRunnable;
-    friend class OpenStreamRunnable;
-
-    void
-    NoteRunnableCompleted(OpenStreamRunnable* aRunnable);
-
-    nsTArray<nsRevocableEventPtr<OpenStreamRunnable> > mOpenStreamRunnables;
-  };
-};
+class PBlobStreamChild;
+class PBlobStreamParent;
 
-template <>
-struct BlobTraits<Child>
+class BlobChild MOZ_FINAL
+  : public PBlobChild
 {
-  typedef mozilla::dom::PBlobChild ProtocolType;
-  typedef mozilla::dom::PBlobStreamChild StreamType;
-  typedef mozilla::dom::ChildBlobConstructorParams ConstructorParamsType;
-  typedef mozilla::dom::ParentBlobConstructorParams
-          OtherSideConstructorParamsType;
-  typedef mozilla::dom::ContentChild ConcreteContentManagerType;
-
-
-  class BaseType : public ProtocolType
-  {
-  public:
-    static const ChildBlobConstructorParams&
-    GetBlobConstructorParams(const ConstructorParamsType& aParams)
-    {
-      return aParams;
-    }
-
-    static void
-    SetBlobConstructorParams(ConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams = aBlobParams;
-    }
-
-    static void
-    SetBlobConstructorParams(OtherSideConstructorParamsType& aParams,
-                             const ChildBlobConstructorParams& aBlobParams)
-    {
-      aParams.blobParams() = aBlobParams;
-      aParams.optionalInputStreamParams() = mozilla::void_t();
-    }
+  friend class ContentChild;
 
-  protected:
-    virtual StreamType*
-    AllocPBlobStreamChild() MOZ_OVERRIDE;
-
-    virtual bool
-    DeallocPBlobStreamChild(StreamType* aActor) MOZ_OVERRIDE;
-
-    BaseType()
-    { }
-
-    virtual ~BaseType()
-    { }
-  };
-};
-
-template <ActorFlavorEnum>
-class RemoteBlob;
+  class RemoteBlob;
+  friend class RemoteBlob;
 
-template <ActorFlavorEnum ActorFlavor>
-class Blob : public BlobTraits<ActorFlavor>::BaseType
-{
-  friend class RemoteBlob<ActorFlavor>;
-  friend class BlobTraits<ActorFlavor>::BaseType;
+  nsIDOMBlob* mBlob;
+  RemoteBlob* mRemoteBlob;
+  nsRefPtr<ContentChild> mStrongManager;
 
-public:
-  typedef typename BlobTraits<ActorFlavor>::ConcreteContentManagerType ContentManager;
-  typedef typename BlobTraits<ActorFlavor>::ProtocolType ProtocolType;
-  typedef typename BlobTraits<ActorFlavor>::StreamType StreamType;
-  typedef typename BlobTraits<ActorFlavor>::ConstructorParamsType
-          ConstructorParamsType;
-  typedef typename BlobTraits<ActorFlavor>::OtherSideConstructorParamsType
-          OtherSideConstructorParamsType;
-  typedef typename BlobTraits<ActorFlavor>::BaseType BaseType;
-  typedef RemoteBlob<ActorFlavor> RemoteBlobType;
-  typedef mozilla::ipc::IProtocolManager<
-                      mozilla::ipc::IProtocol>::ActorDestroyReason
-          ActorDestroyReason;
-
-protected:
-  nsIDOMBlob* mBlob;
-  RemoteBlobType* mRemoteBlob;
   bool mOwnsBlob;
   bool mBlobIsFile;
 
 public:
   // This create function is called on the sending side.
-  static Blob*
-  Create(ContentManager* aManager, nsIDOMBlob* aBlob)
+  static BlobChild*
+  Create(ContentChild* aManager, nsIDOMBlob* aBlob)
   {
-    return new Blob(aManager, aBlob);
+    return new BlobChild(aManager, aBlob);
   }
 
-  // This create function is called on the receiving side.
-  static Blob*
-  Create(ContentManager* aManager, const ConstructorParamsType& aParams);
+  // Get the blob associated with this actor. This may always be called on the
+  // sending side. It may also be called on the receiving side unless this is a
+  // "mystery" blob that has not yet received a SetMysteryBlobInfo() call.
+  already_AddRefed<nsIDOMBlob>
+  GetBlob();
+
+  // Use this for files.
+  bool
+  SetMysteryBlobInfo(const nsString& aName,
+                     const nsString& aContentType,
+                     uint64_t aLength,
+                     uint64_t aLastModifiedDate);
+
+  // Use this for non-file blobs.
+  bool
+  SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength);
+
+private:
+  // This constructor is called on the sending side.
+  BlobChild(ContentChild* aManager, nsIDOMBlob* aBlob);
+
+  // This constructor is called on the receiving side.
+  BlobChild(ContentChild* aManager, const ChildBlobConstructorParams& aParams);
+
+  // Only destroyed by ContentChild.
+  ~BlobChild();
+
+  // This create function is called on the receiving side by ContentChild.
+  static BlobChild*
+  Create(ContentChild* aManager, const ChildBlobConstructorParams& aParams);
+
+  static already_AddRefed<RemoteBlob>
+  CreateRemoteBlob(const ChildBlobConstructorParams& aParams);
+
+  void
+  NoteDyingRemoteBlob();
+
+  // These methods are only called by the IPDL message machinery.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
+
+  virtual PBlobStreamChild*
+  AllocPBlobStreamChild() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBlobStreamConstructor(PBlobStreamChild* aActor) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBlobStreamChild(PBlobStreamChild* aActor) MOZ_OVERRIDE;
+};
+
+class BlobParent MOZ_FINAL
+  : public PBlobParent
+{
+  friend class ContentParent;
+
+  class OpenStreamRunnable;
+  friend class OpenStreamRunnable;
+
+  class RemoteBlob;
+  friend class RemoteBlob;
+
+  nsIDOMBlob* mBlob;
+  RemoteBlob* mRemoteBlob;
+  nsRefPtr<ContentParent> mStrongManager;
+
+  // nsIInputStreams backed by files must ensure that the files are actually
+  // opened and closed on a background thread before we can send their file
+  // handles across to the child. The child process could crash during this
+  // process so we need to make sure we cancel the intended response in such a
+  // case. We do that by holding an array of nsRevocableEventPtr. If the child
+  // crashes then this actor will be destroyed and the nsRevocableEventPtr
+  // destructor will cancel any stream events that are currently in flight.
+  nsTArray<nsRevocableEventPtr<OpenStreamRunnable>> mOpenStreamRunnables;
+
+  bool mOwnsBlob;
+  bool mBlobIsFile;
+
+public:
+  // This create function is called on the sending side.
+  static BlobParent*
+  Create(ContentParent* aManager, nsIDOMBlob* aBlob)
+  {
+    return new BlobParent(aManager, aBlob);
+  }
 
   // Get the blob associated with this actor. This may always be called on the
   // sending side. It may also be called on the receiving side unless this is a
   // "mystery" blob that has not yet received a SetMysteryBlobInfo() call.
   already_AddRefed<nsIDOMBlob>
   GetBlob();
 
   // Use this for files.
   bool
   SetMysteryBlobInfo(const nsString& aName, const nsString& aContentType,
                      uint64_t aLength, uint64_t aLastModifiedDate);
 
   // Use this for non-file blobs.
   bool
   SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength);
 
-  ContentManager* Manager()
-  {
-    return mManager;
-  }
-
 private:
   // This constructor is called on the sending side.
-  Blob(ContentManager* aManager, nsIDOMBlob* aBlob);
+  BlobParent(ContentParent* aManager, nsIDOMBlob* aBlob);
 
   // This constructor is called on the receiving side.
-  Blob(ContentManager* aManager, const ConstructorParamsType& aParams);
+  BlobParent(ContentParent* aManager,
+             const ParentBlobConstructorParams& aParams);
+
+  ~BlobParent();
 
-  static already_AddRefed<RemoteBlobType>
-  CreateRemoteBlob(const ConstructorParamsType& aParams);
+  // This create function is called on the receiving side by ContentParent.
+  static BlobParent*
+  Create(ContentParent* aManager, const ParentBlobConstructorParams& aParams);
+
+  static already_AddRefed<RemoteBlob>
+  CreateRemoteBlob(const ParentBlobConstructorParams& aParams);
 
   void
   NoteDyingRemoteBlob();
 
+  void
+  NoteRunnableCompleted(OpenStreamRunnable* aRunnable);
+
   // These methods are only called by the IPDL message machinery.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
-  virtual bool
-  RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
+  virtual PBlobStreamParent*
+  AllocPBlobStreamParent() MOZ_OVERRIDE;
 
   virtual bool
-  RecvPBlobStreamConstructor(StreamType* aActor) MOZ_OVERRIDE;
-
-  nsRefPtr<ContentManager> mManager;
-};
+  RecvPBlobStreamConstructor(PBlobStreamParent* aActor) MOZ_OVERRIDE;
 
-} // namespace ipc
+  virtual bool
+  DeallocPBlobStreamParent(PBlobStreamParent* aActor) MOZ_OVERRIDE;
 
-typedef mozilla::dom::ipc::Blob<mozilla::dom::ipc::Child> BlobChild;
-typedef mozilla::dom::ipc::Blob<mozilla::dom::ipc::Parent> BlobParent;
+  virtual bool
+  RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
+};
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ipc_Blob_h
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1624,68 +1624,58 @@ int64_t GetPluginLastModifiedTime(const 
   localfile->GetLastModifiedTime(&fileModTime);
 #endif
 
   return fileModTime;
 }
 
 bool
 GetPluginIsFromExtension(const nsCOMPtr<nsIFile>& pluginFile,
-                         const nsCOMPtr<nsISimpleEnumerator>& extensionDirs)
+                         const nsCOMArray<nsIFile>& extensionDirs)
 {
-  if (!extensionDirs) {
-    return false;
-  }
-
-  bool hasMore;
-  while (NS_SUCCEEDED(extensionDirs->HasMoreElements(&hasMore)) && hasMore) {
-    nsCOMPtr<nsISupports> supports;
-    nsresult rv = extensionDirs->GetNext(getter_AddRefs(supports));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-
-    nsCOMPtr<nsIFile> extDir(do_QueryInterface(supports, &rv));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-
-    nsCOMPtr<nsIFile> dir;
-    if (NS_FAILED(extDir->Clone(getter_AddRefs(dir)))) {
-      continue;
-    }
-
+  for (uint32_t i = 0; i < extensionDirs.Length(); ++i) {
     bool contains;
-    if (NS_FAILED(dir->Contains(pluginFile, true, &contains)) || !contains) {
+    if (NS_FAILED(extensionDirs[i]->Contains(pluginFile, true, &contains)) || !contains) {
       continue;
     }
 
     return true;
   }
 
   return false;
 }
 
-nsCOMPtr<nsISimpleEnumerator>
-GetExtensionDirectories()
+void
+GetExtensionDirectories(nsCOMArray<nsIFile>& dirs)
 {
   nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   if (!dirService) {
-    return nullptr;
+    return;
   }
 
   nsCOMPtr<nsISimpleEnumerator> list;
   nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST,
                                 NS_GET_IID(nsISimpleEnumerator),
                                 getter_AddRefs(list));
   if (NS_FAILED(rv)) {
-    return nullptr;
+    return;
   }
 
-  return list;
+  bool more;
+  while (NS_SUCCEEDED(list->HasMoreElements(&more)) && more) {
+    nsCOMPtr<nsISupports> next;
+    if (NS_FAILED(list->GetNext(getter_AddRefs(next)))) {
+      break;
+    }
+    nsCOMPtr<nsIFile> file = do_QueryInterface(next);
+    if (file) {
+      file->Normalize();
+      dirs.AppendElement(file);
+    }
+  }
 }
 
 struct CompareFilesByTime
 {
   bool
   LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
   {
     return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
@@ -1741,20 +1731,18 @@ nsresult nsPluginHost::ScanPluginsDirect
 
     if (nsPluginsDir::IsPluginFile(dirEntry)) {
       pluginFiles.AppendElement(dirEntry);
     }
   }
 
   pluginFiles.Sort(CompareFilesByTime());
 
-  nsCOMPtr<nsISimpleEnumerator> extensionDirs = GetExtensionDirectories();
-  if (!extensionDirs) {
-    PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("Could not get extension directories."));
-  }
+  nsCOMArray<nsIFile> extensionDirs;
+  GetExtensionDirectories(extensionDirs);
 
   bool warnOutdated = false;
 
   for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
     nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
 
     nsString utf16FilePath;
     rv = localfile->GetPath(utf16FilePath);
--- a/dom/plugins/test/testaddon/Makefile.in
+++ b/dom/plugins/test/testaddon/Makefile.in
@@ -1,23 +1,23 @@
 # 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 $(topsrcdir)/config/rules.mk
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-plugin_file_name = Test.plugin
+plugin_file_names = Test.plugin SecondTest.plugin
 addon_file_name = testaddon_$(TARGET_XPCOM_ABI).xpi
 else
-plugin_file_name = $(DLL_PREFIX)nptest$(DLL_SUFFIX)
+plugin_file_names = $(DLL_PREFIX)nptest$(DLL_SUFFIX) $(DLL_PREFIX)npsecondtest$(DLL_SUFFIX)
 addon_file_name = testaddon.xpi
 endif
 
 # This is so hacky. Waiting on bug 988938.
 testdir = $(abspath $(DEPTH)/_tests/xpcshell/dom/plugins/test/unit/)
 addonpath = $(testdir)/$(addon_file_name)
 
 libs::
 	$(NSINSTALL) -D $(testdir)
 	rm -f $(addonpath)
 	cd $(srcdir) && zip -rD $(addonpath) install.rdf
-	cd $(DIST) && zip -rD $(addonpath) plugins/$(plugin_file_name)
+	cd $(DIST) && zip -rD $(addonpath) $(foreach name,$(plugin_file_names),plugins/$(name))
--- a/dom/plugins/test/unit/head_plugins.js
+++ b/dom/plugins/test/unit/head_plugins.js
@@ -9,37 +9,23 @@ Cu.import("resource://gre/modules/Promis
 
 const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 const gIsOSX = ("nsILocalFileMac" in Ci);
 const gIsLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc) ||
   ("@mozilla.org/gio-service;1" in Cc);
 const gDirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
 
 // Finds the test plugin library
-function get_test_plugin() {
+function get_test_plugin(secondplugin=false) {
   var pluginEnum = gDirSvc.get("APluginsDL", Ci.nsISimpleEnumerator);
   while (pluginEnum.hasMoreElements()) {
     let dir = pluginEnum.getNext().QueryInterface(Ci.nsILocalFile);
+    let name = get_platform_specific_plugin_name(secondplugin);
     let plugin = dir.clone();
-    // OSX plugin
-    plugin.append("Test.plugin");
-    if (plugin.exists()) {
-      plugin.normalize();
-      return plugin;
-    }
-    plugin = dir.clone();
-    // *nix plugin
-    plugin.append("libnptest.so");
-    if (plugin.exists()) {
-      plugin.normalize();
-      return plugin;
-    }
-    // Windows plugin
-    plugin = dir.clone();
-    plugin.append("nptest.dll");
+    plugin.append(name);
     if (plugin.exists()) {
       plugin.normalize();
       return plugin;
     }
   }
   return null;
 }
 
@@ -88,21 +74,27 @@ function do_get_profile_startup() {
       throw Components.results.NS_ERROR_NO_INTERFACE;
     }
   };
   dirSvc.QueryInterface(Components.interfaces.nsIDirectoryService)
         .registerProvider(provider);
   return file.clone();
 }
 
-function get_platform_specific_plugin_name() {
-  if (gIsWindows) return "nptest.dll";
-  else if (gIsOSX) return "Test.plugin";
-  else if (gIsLinux) return "libnptest.so";
-  else return null;
+function get_platform_specific_plugin_name(secondplugin=false) {
+  if (secondplugin) {
+    if (gIsWindows) return "npsecondtest.dll";
+    if (gIsOSX) return "SecondTest.plugin";
+    if (gIsLinux) return "libnpsecondtest.so";
+  } else {
+    if (gIsWindows) return "nptest.dll";
+    if (gIsOSX) return "Test.plugin";
+    if (gIsLinux) return "libnptest.so";
+  }
+  return null;
 }
 
 function get_platform_specific_plugin_suffix() {
   if (gIsWindows) return ".dll";
   else if (gIsOSX) return ".plugin";
   else if (gIsLinux) return ".so";
   else return null;
 }
--- a/dom/plugins/test/unit/test_plugin_default_state_xpi.js
+++ b/dom/plugins/test/unit/test_plugin_default_state_xpi.js
@@ -36,16 +36,21 @@ function run_test() {
   run_next_test();
 }
 
 add_task(function* test_state() {
   // Remove test so we will have only one "Test Plug-in" registered.
   // xpcshell tests have plugins in per-test profiles, so that's fine.
   let file = get_test_plugin();
   file.remove(true);
+  file = get_test_plugin(true);
+  file.remove(true);
+
+  Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+  Services.prefs.setIntPref("plugin.defaultXpi.state", Ci.nsIPluginTag.STATE_ENABLED);
 
   let success = yield installAddon(getTestaddonFilename());
   Assert.ok(success, "Should have installed addon.");
   let addonDir = getAddonRoot(gProfileDir, ADDON_ID);
 
   let provider = {
     classID: Components.ID("{0af6b2d7-a06c-49b7-babc-636d292b0dbb}"),
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider,
@@ -100,9 +105,13 @@ add_task(function* test_state() {
   Assert.ok(!addon.userDisabled, "Addon should not be user disabled");
 
   let testPlugin = get_test_plugintag();
   Assert.notEqual(testPlugin, null, "Test plugin should have been found");
   Assert.equal(testPlugin.enabledState, Ci.nsIPluginTag.STATE_ENABLED, "Test plugin from addon should have state enabled");
 
   pluginDir.append(testPlugin.filename);
   Assert.ok(pluginDir.exists(), "Plugin file should exist in addon directory: " + pluginDir.path);
+
+  testPlugin = get_test_plugintag("Second Test Plug-in");
+  Assert.notEqual(testPlugin, null, "Second test plugin should have been found");
+  Assert.equal(testPlugin.enabledState, Ci.nsIPluginTag.STATE_ENABLED, "Second test plugin from addon should have state enabled");
 });
--- a/gfx/layers/client/ClientTiledThebesLayer.h
+++ b/gfx/layers/client/ClientTiledThebesLayer.h
@@ -39,16 +39,19 @@ class ClientTiledThebesLayer : public Th
                                public ClientLayer
 {
   typedef ThebesLayer Base;
 
 public:
   ClientTiledThebesLayer(ClientLayerManager* const aManager);
   ~ClientTiledThebesLayer();
 
+  // Override name to distinguish it from ClientThebesLayer in layer dumps
+  virtual const char* Name() const { return "TiledThebesLayer"; }
+
   // Thebes Layer
   virtual Layer* AsLayer() { return this; }
   virtual void InvalidateRegion(const nsIntRegion& aRegion) {
     mInvalidRegion.Or(mInvalidRegion, aRegion);
     mValidRegion.Sub(mValidRegion, aRegion);
     mLowPrecisionValidRegion.Sub(mLowPrecisionValidRegion, aRegion);
   }
 
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -112,17 +112,17 @@ CompositableHost::RemoveMaskEffect()
 TemporaryRef<CompositableBackendSpecificData> CreateCompositableBackendSpecificDataOGL();
 
 /* static */ TemporaryRef<CompositableHost>
 CompositableHost::Create(const TextureInfo& aTextureInfo)
 {
   RefPtr<CompositableHost> result;
   switch (aTextureInfo.mCompositableType) {
   case BUFFER_BRIDGE:
-    MOZ_CRASH("Cannot create an image bridge compositable this way");
+    NS_ERROR("Cannot create an image bridge compositable this way");
     break;
   case BUFFER_CONTENT_INC:
     result = new ContentHostIncremental(aTextureInfo);
     break;
   case BUFFER_TILED:
   case BUFFER_SIMPLE_TILED:
     result = new TiledContentHost(aTextureInfo);
     break;
@@ -131,17 +131,17 @@ CompositableHost::Create(const TextureIn
     break;
   case COMPOSITABLE_CONTENT_SINGLE:
     result = new ContentHostSingleBuffered(aTextureInfo);
     break;
   case COMPOSITABLE_CONTENT_DOUBLE:
     result = new ContentHostDoubleBuffered(aTextureInfo);
     break;
   default:
-    MOZ_CRASH("Unknown CompositableType");
+    NS_ERROR("Unknown CompositableType");
   }
   // We know that Tiled buffers don't use the compositable backend-specific
   // data, so don't bother creating it.
   if (result && aTextureInfo.mCompositableType != BUFFER_TILED) {
     RefPtr<CompositableBackendSpecificData> data = CreateCompositableBackendSpecificDataOGL();
     result->SetCompositableBackendSpecificData(data);
   }
   return result;
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -199,21 +199,22 @@ public:
    * Ensure that a suitable texture host exists in this compsitable.
    *
    * Only used with ContentHostIncremental.
    *
    * No SurfaceDescriptor or TextureIdentifier is provider as we
    * don't have a single surface for the texture contents, and we
    * need to allocate our own one to be updated later.
    */
-  virtual void CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
+  virtual bool CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
                                          const TextureInfo& aTextureInfo,
                                          const nsIntRect& aBufferRect)
   {
-    MOZ_ASSERT(false, "should be implemented or not used");
+    NS_ERROR("should be implemented or not used");
+    return false;
   }
 
   /**
    * Returns the front buffer.
    */
   virtual TextureHost* GetAsTextureHost() { return nullptr; }
 
   virtual LayerRenderState GetRenderState() = 0;
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -405,25 +405,26 @@ ContentHostIncremental::~ContentHostIncr
 void
 ContentHostIncremental::DestroyTextures()
 {
   mSource = nullptr;
   mSourceOnWhite = nullptr;
   mUpdateList.Clear();
 }
 
-void
+bool
 ContentHostIncremental::CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
                                                   const TextureInfo& aTextureInfo,
                                                   const nsIntRect& aBufferRect)
 {
   mUpdateList.AppendElement(new TextureCreationRequest(aTextureInfo,
                                                        aBufferRect));
   mDeAllocator = aAllocator;
   FlushUpdateQueue();
+  return true;
 }
 
 void
 ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId,
                                           SurfaceDescriptor& aSurface,
                                           const nsIntRegion& aUpdated,
                                           const nsIntRect& aBufferRect,
                                           const nsIntPoint& aBufferRotation)
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -255,17 +255,17 @@ class ContentHostIncremental : public Co
 public:
   ContentHostIncremental(const TextureInfo& aTextureInfo);
   ~ContentHostIncremental();
 
   virtual CompositableType GetType() { return BUFFER_CONTENT_INC; }
 
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE { return LayerRenderState(); }
 
-  virtual void CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
+  virtual bool CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
                                          const TextureInfo& aTextureInfo,
                                          const nsIntRect& aBufferRect) MOZ_OVERRIDE;
 
   virtual void UpdateIncremental(TextureIdentifier aTextureId,
                                  SurfaceDescriptor& aSurface,
                                  const nsIntRegion& aUpdated,
                                  const nsIntRect& aBufferRect,
                                  const nsIntPoint& aBufferRotation) MOZ_OVERRIDE;
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -42,17 +42,16 @@ class Matrix4x4;
 }
 
 namespace layers {
 
 class Compositor;
 class ISurfaceAllocator;
 class Layer;
 class ThebesBufferData;
-class TiledThebesLayerComposite;
 struct EffectChain;
 
 
 class TileHost {
 public:
   // Constructs a placeholder TileHost. See the comments above
   // TiledLayerBuffer for more information on what this is used for;
   // essentially, this is a sentinel used to represent an invalid or blank
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -73,19 +73,23 @@ CompositableParentManager::ReceiveCompos
     case CompositableOperation::TOpCreatedIncrementalTexture: {
       MOZ_LAYERS_LOG(("[ParentSide] Created texture"));
       const OpCreatedIncrementalTexture& op = aEdit.get_OpCreatedIncrementalTexture();
 
       CompositableParent* compositableParent =
         static_cast<CompositableParent*>(op.compositableParent());
       CompositableHost* compositable = compositableParent->GetCompositableHost();
 
-      compositable->CreatedIncrementalTexture(compositableParent->GetCompositableManager(),
-                                              op.textureInfo(),
-                                              op.bufferRect());
+      bool success =
+        compositable->CreatedIncrementalTexture(compositableParent->GetCompositableManager(),
+                                                op.textureInfo(),
+                                                op.bufferRect());
+      if (!success) {
+        return false;
+      }
       break;
     }
     case CompositableOperation::TOpPaintTextureRegion: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint ThebesLayer"));
 
       const OpPaintTextureRegion& op = aEdit.get_OpPaintTextureRegion();
       CompositableParent* compositableParent = static_cast<CompositableParent*>(op.compositableParent());
       CompositableHost* compositable =
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -10,17 +10,16 @@
 #include "CompositableHost.h"           // for CompositableParent, Get, etc
 #include "ImageLayers.h"                // for ImageLayer
 #include "Layers.h"                     // for Layer, ContainerLayer, etc
 #include "ShadowLayerParent.h"          // for ShadowLayerParent
 #include "gfx3DMatrix.h"                // for gfx3DMatrix
 #include "gfxPoint3D.h"                 // for gfxPoint3D
 #include "CompositableTransactionParent.h"  // for EditReplyVector
 #include "ShadowLayersManager.h"        // for ShadowLayersManager
-#include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/BasePoint3D.h"    // for BasePoint3D
 #include "mozilla/layers/CanvasLayerComposite.h"
 #include "mozilla/layers/ColorLayerComposite.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ContainerLayerComposite.h"
 #include "mozilla/layers/ImageLayerComposite.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
@@ -514,17 +513,20 @@ LayerTransactionParent::RecvUpdate(const
       }
       cast(op.compositableParent())->SetCompositorID(
         mLayerManager->GetCompositor()->GetCompositorID());
       break;
     }
     case Edit::TOpAttachAsyncCompositable: {
       const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable();
       CompositableParent* compositableParent = CompositableMap::Get(op.containerID());
-      MOZ_ASSERT(compositableParent, "CompositableParent not found in the map");
+      if (!compositableParent) {
+        NS_ERROR("CompositableParent not found in the map");
+        return false;
+      }
       if (!Attach(cast(op.layerParent()), compositableParent, true)) {
         return false;
       }
       compositableParent->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
       break;
     }
     default:
       NS_RUNTIMEABORT("not reached");
@@ -669,23 +671,27 @@ LayerTransactionParent::RecvGetAnimation
 bool
 LayerTransactionParent::RecvSetAsyncScrollOffset(PLayerParent* aLayer,
                                                  const int32_t& aX, const int32_t& aY)
 {
   if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
     return false;
   }
 
-  ContainerLayer* layer = cast(aLayer)->AsLayer()->AsContainerLayer();
+  Layer* layer = cast(aLayer)->AsLayer();
   if (!layer) {
-    return true;
+    return false;
   }
-  AsyncPanZoomController* controller = layer->GetAsyncPanZoomController();
+  ContainerLayer* containerLayer = layer->AsContainerLayer();
+  if (!containerLayer) {
+    return false;
+  }
+  AsyncPanZoomController* controller = containerLayer->GetAsyncPanZoomController();
   if (!controller) {
-    return true;
+    return false;
   }
   controller->SetTestAsyncScrollOffset(CSSPoint(aX, aY));
   return true;
 }
 
 bool
 LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent,
                                CompositableParent* aCompositable,
@@ -699,17 +705,19 @@ LayerTransactionParent::Attach(ShadowLay
   if (!layer) {
     return false;
   }
 
   Compositor* compositor
     = static_cast<LayerManagerComposite*>(aLayerParent->AsLayer()->Manager())->GetCompositor();
 
   CompositableHost* compositable = aCompositable->GetCompositableHost();
-  MOZ_ASSERT(compositable);
+  if (!compositable) {
+    return false;
+  }
   if (!layer->SetCompositableHost(compositable)) {
     // not all layer types accept a compositable, see bug 967824
     return false;
   }
   compositable->Attach(aLayerParent->AsLayer(),
                        compositor,
                        aIsAsyncVideo
                          ? CompositableHost::ALLOW_REATTACH
--- a/gfx/thebes/gfxFT2Utils.cpp
+++ b/gfx/thebes/gfxFT2Utils.cpp
@@ -119,24 +119,36 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Me
     gfxFloat lineHeight;
     if (os2 && os2->sTypoAscender && yScale > 0.0) {
         aMetrics->emAscent = os2->sTypoAscender * yScale;
         aMetrics->emDescent = -os2->sTypoDescender * yScale;
         FT_Short typoHeight =
             os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
         lineHeight = typoHeight * yScale;
 
-        // maxAscent/maxDescent get used for frame heights, and some fonts
-        // don't have the HHEA table ascent/descent set (bug 279032).
-        // We use NS_round here to parallel the pixel-rounded values that
-        // freetype gives us for ftMetrics.ascender/descender.
-        aMetrics->maxAscent =
-            std::max(aMetrics->maxAscent, NS_round(aMetrics->emAscent));
-        aMetrics->maxDescent =
-            std::max(aMetrics->maxDescent, NS_round(aMetrics->emDescent));
+        // If the OS/2 fsSelection USE_TYPO_METRICS bit is set,
+        // or if this is an OpenType Math font,
+        // set maxAscent/Descent from the sTypo* fields instead of hhea.
+        const uint16_t kUseTypoMetricsMask = 1 << 7;
+        FT_ULong length = 0;
+        if ((os2->fsSelection & kUseTypoMetricsMask) ||
+            0 == FT_Load_Sfnt_Table(mFace, FT_MAKE_TAG('M','A','T','H'),
+                                    0, nullptr, &length)) {
+            aMetrics->maxAscent = NS_round(aMetrics->emAscent);
+            aMetrics->maxDescent = NS_round(aMetrics->emDescent);
+        } else {
+            // maxAscent/maxDescent get used for frame heights, and some fonts
+            // don't have the HHEA table ascent/descent set (bug 279032).
+            // We use NS_round here to parallel the pixel-rounded values that
+            // freetype gives us for ftMetrics.ascender/descender.
+            aMetrics->maxAscent =
+                std::max(aMetrics->maxAscent, NS_round(aMetrics->emAscent));
+            aMetrics->maxDescent =
+                std::max(aMetrics->maxDescent, NS_round(aMetrics->emDescent));
+        }
     } else {
         aMetrics->emAscent = aMetrics->maxAscent;
         aMetrics->emDescent = aMetrics->maxDescent;
         lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
     }
 
     cairo_text_extents_t extents;
     *aSpaceGlyph = GetCharExtents(' ', &extents);
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2026,30 +2026,33 @@ gfxFont::~gfxFont()
     if (mGlyphChangeObservers) {
         mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr);
     }
 }
 
 gfxFloat
 gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
 {
+    if (!SetupCairoFont(aCtx)) {
+        return 0;
+    }
     if (ProvidesGlyphWidths()) {
         return GetGlyphWidth(aCtx, aGID) / 65536.0;
     }
     if (mFUnitsConvFactor == 0.0f) {
         GetMetrics();
     }
     NS_ASSERTION(mFUnitsConvFactor > 0.0f,
                  "missing font unit conversion factor");
     if (!mHarfBuzzShaper) {
         mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
     }
     gfxHarfBuzzShaper* shaper =
         static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
-    if (!shaper->Initialize() || !SetupCairoFont(aCtx)) {
+    if (!shaper->Initialize()) {
         return 0;
     }
     return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
 }
 
 /*static*/
 PLDHashOperator
 gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
--- a/intl/unicharutil/util/nsBidiUtils.h
+++ b/intl/unicharutil/util/nsBidiUtils.h
@@ -100,20 +100,16 @@ typedef enum nsCharType nsCharType;
    }
 
   /**
    * Give an nsString.
    * @return true if the string contains right-to-left characters
    */
    bool HasRTLChars(const nsAString& aString);
 
-// --------------------------------------------------
-// IBMBIDI 
-// --------------------------------------------------
-//
 // These values are shared with Preferences dialog
 //  ------------------
 //  If Pref values are to be changed
 //  in the XUL file of Prefs. the values
 //  Must be changed here too..
 //  ------------------
 //
 #define IBMBIDI_TEXTDIRECTION_STR       "bidi.direction"
--- a/ipc/dbus/DBusUtils.h
+++ b/ipc/dbus/DBusUtils.h
@@ -53,22 +53,20 @@ private:
 };
 
 /**
  * DBusReplyHandler represents a handler for DBus reply messages. Inherit
  * from this class and implement the Handle method. The method Callback
  * should be passed to the DBus send function, with the class instance as
  * user-data argument.
  */
-class DBusReplyHandler : public mozilla::RefCounted<DBusReplyHandler>
+class DBusReplyHandler
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(DBusReplyHandler)
-  virtual ~DBusReplyHandler() {
-  }
+  NS_INLINE_DECL_REFCOUNTING(DBusReplyHandler)
 
   /**
    * Implements a call-back function for DBus. The supplied value for
    * aData must be a pointer to an instance of DBusReplyHandler.
    */
   static void Callback(DBusMessage* aReply, void* aData);
 
   /**
@@ -84,16 +82,20 @@ protected:
   DBusReplyHandler(const DBusReplyHandler& aHandler)
   {
   }
 
   DBusReplyHandler& operator = (const DBusReplyHandler& aRhs)
   {
     return *this;
   }
+
+  virtual ~DBusReplyHandler()
+  {
+  }
 };
 
 void log_and_free_dbus_error(DBusError* err,
                              const char* function,
                              DBusMessage* msg = nullptr);
 
 int dbus_returns_int32(DBusMessage *reply);
 
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -228,19 +228,19 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
       case AsmJSImm_aeabi_idivmod:
         return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2);
       case AsmJSImm_aeabi_uidivmod:
         return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2);
 #endif
       case AsmJSImm_ModD:
         return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble);
       case AsmJSImm_SinD:
-        return RedirectCall(FuncCast<double (double)>(math_sin_impl), Args_Double_Double);
+        return RedirectCall(FuncCast<double (double)>(sin), Args_Double_Double);
       case AsmJSImm_CosD:
-        return RedirectCall(FuncCast<double (double)>(math_cos_impl), Args_Double_Double);
+        return RedirectCall(FuncCast<double (double)>(cos), Args_Double_Double);
       case AsmJSImm_TanD:
         return RedirectCall(FuncCast<double (double)>(tan), Args_Double_Double);
       case AsmJSImm_ASinD:
         return RedirectCall(FuncCast<double (double)>(asin), Args_Double_Double);
       case AsmJSImm_ACosD:
         return RedirectCall(FuncCast<double (double)>(acos), Args_Double_Double);
       case AsmJSImm_ATanD:
         return RedirectCall(FuncCast<double (double)>(atan), Args_Double_Double);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4343,30 +4343,35 @@ bool
 CodeGenerator::visitMathFunctionD(LMathFunctionD *ins)
 {
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
 
     const MathCache *mathCache = ins->mir()->cache();
 
+    masm.setupUnalignedABICall(mathCache ? 2 : 1, temp);
+    if (mathCache) {
+        masm.movePtr(ImmPtr(mathCache), temp);
+        masm.passABIArg(temp);
+    }
+    masm.passABIArg(input, MoveOp::DOUBLE);
+
 #   define MAYBE_CACHED(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached)
 
     void *funptr = nullptr;
     switch (ins->mir()->function()) {
       case MMathFunction::Log:
         funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log));
         break;
       case MMathFunction::Sin:
-        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_sin_impl);
-        mathCache = nullptr;
+        funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_sin));
         break;
       case MMathFunction::Cos:
-        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_cos_impl);
-        mathCache = nullptr;
+        funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cos));
         break;
       case MMathFunction::Exp:
         funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_exp));
         break;
       case MMathFunction::Tan:
         funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_tan));
         break;
       case MMathFunction::ATan:
@@ -4414,39 +4419,29 @@ CodeGenerator::visitMathFunctionD(LMathF
       case MMathFunction::Trunc:
         funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_trunc));
         break;
       case MMathFunction::Cbrt:
         funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cbrt));
         break;
       case MMathFunction::Floor:
         funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_floor_impl);
-        mathCache = nullptr;
         break;
       case MMathFunction::Ceil:
         funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_ceil_impl);
-        mathCache = nullptr;
         break;
       case MMathFunction::Round:
         funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_round_impl);
-        mathCache = nullptr;
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown math function");
     }
 
 #   undef MAYBE_CACHED
 
-    masm.setupUnalignedABICall(mathCache ? 2 : 1, temp);
-    if (mathCache) {
-        masm.movePtr(ImmPtr(mathCache), temp);
-        masm.passABIArg(temp);
-    }
-    masm.passABIArg(input, MoveOp::DOUBLE);
-
     masm.callWithABI(funptr, MoveOp::DOUBLE);
     return true;
 }
 
 bool
 CodeGenerator::visitMathFunctionF(LMathFunctionF *ins)
 {
     Register temp = ToRegister(ins->temp());
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -327,165 +327,47 @@ js::math_clz32(JSContext *cx, unsigned a
         args.rval().setInt32(32);
         return true;
     }
 
     args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
     return true;
 }
 
-/*
- * Fast sine and cosine approximation code, based on the sin [0] and cos [1]
- * implementations [2] in the cephes library [3].
- * Some of the optimization ideas are inspired by the fast_sincos in VDT [4].
- *
- * This implementation satisfies the requirements for sin and cos in JS [5].
- * However, it does not take the standard's recommendation to use fdlibm [6],
- * nor does it take advantage of the standard's intent to permit JS to use the
- * system C math library.
- *
- * The code carefully avoids branching, to avoid the cost of mispredictions
- * either on random input sets or on input sets straddling a boundary condition
- * in the algorithm. It contains only one branch, which is for testing for
- * unusual inputs (infinities, NaNs, and extremely large values), and it
- * should be very predictable.
- *
- * This implementation computes both a sin and cos value even when only one
- * of the two is needed. While creating specialized routines for computing just
- * sin or just cost would allow them to do less work, the speed benefits would
- * be expected to be marginal, and not worth the extra code it would take, given
- * that we'll still want the ability to compute sin and cos together anyway.
- *
- * [0] http://netlib.org/cephes/doubldoc.html#sin
- * [1] http://netlib.org/cephes/doubldoc.html#cos
- * [2] http://netlib.org/cephes/cmath.tgz
- * [3] http://netlib.org/cephes/
- * [4] https://svnweb.cern.ch/trac/vdt
- * [5] http://www.ecma-international.org/ecma-262/5.1/#sec-15.8.2
- * [6] http://netlib.org/fdlibm
- */
-
-static double polevl_sin(double z, double zz)
-{
-    // Constants generated using Mathematica's GeneralMiniMaxApproximation
-    double ans = 1.59046813973877163292e-10; // 6152825598094877 / exp2(85)
-    ans *= zz;
-    ans += -2.50509001624159785668e-08; // -7571170002733246 / exp2(78)
-    ans *= zz;
-    ans +=  2.75573146431678644161e-06; //  6506786951439440 / exp2(71)
-    ans *= zz;
-    ans += -1.98412698327005105692e-04; // -7320136534024805 / exp2(65)
-    ans *= zz;
-    ans +=  8.33333333332626768897e-03; //  4803839602524456 / exp2(59)
-    ans *= zz;
-    ans += -1.66666666666666490881e-01; // -6004799503160655 / exp2(55)
-    ans *= zz * z;
-    ans += z;
-    return ans;
-}
-
-static double polevl_cos(double zz)
+double
+js::math_cos_impl(MathCache *cache, double x)
 {
-    // Constants generated using Mathematica's GeneralMiniMaxApproximation.
-    // This set uses one less coefficient than usual implementations to
-    // increase performance, raising the maximum approximation error to 2 bits.
-    double ans = 2.06467337476762997948e-9;
-    ans *= zz;
-    ans += -2.75555495413759160741e-7;
-    ans *= zz;
-    ans +=  2.48015808595638122085e-5;
-    ans *= zz;
-    ans += -1.38888888779622760722e-3;
-    ans *= zz;
-    ans +=  4.16666666665987187046e-2;
-    ans *= zz;
-    ans += -4.99999999999999888978e-1;
-    ans *= zz;
-    ans += 1.0;
-    return ans;
-}
-
-namespace {
-struct sincos_result { double s, c; };
-}
-
-static sincos_result fast_sincos(double x)
-{
-    // Make argument non-negative but save the sign.
-    double orig_sign = js_copysign(1.0, x);
-    double absx = fabs(x);
-
-    // The optimized algorithm below doesn't currently support values of x beyond
-    // pow(2, 32) - 2. If x is beyond the range we support, fall back to the libm
-    // implementation. This check also handles the Infinity and NaN input cases.
-    // abs(x) < (221069929647945 / pow(2,16))
-    if (MOZ_UNLIKELY(!(absx < 3.37325942455970764160e9))) {
-        sincos_result result = {
-            sin(x),
-            cos(x)
-        };
-        return result;
-    }
-
-    static const double m_4_pi = 1.27323954473516276487; // 4.0 / M_PI
-    uint32_t i = static_cast<uint32_t>(absx * m_4_pi);
-
-    // Integer and fractional part modulo one octant.
-    uint32_t quad_index = ((i + 1) >> 1) & 3;
-    double y = static_cast<double>(i + (i & 1));
-
-    // Extended precision modular arithmetic
-    double e0 = y * -7.85398006439208984375e-1;  // 1647099 / pow(2,21)
-    double e1 = y * -1.56958208208379801363e-7;  // 1380619 / pow(2,43)
-    double e2 = y * -3.11168608594830669189e-14; // 4930663418217751 / pow(2,97)
-    double z = absx + e0 + e1 + e2;
-
-    // Compute the sin/cos in quadrant 0.
-    double zz = z * z;
-    double q0_sin = polevl_sin(z, zz);
-    double q0_cos = polevl_cos(zz);
-
-    // Reflect the result into the correct quadrant.
-    const double reflect[4] = {
-      q0_sin, q0_cos, -q0_sin, -q0_cos
-    };
-
-    // Adjust the sine value by the sign of the input.
-    // Missed optimization: C++ doesn't provide convenient access to
-    // floating-point xor; hand-written assembler could change the copysign
-    // above to use 0.0 instead of 1.0, and then just xor the sign with p[0]
-    // here instead of multiplying.
-    sincos_result result = {
-        reflect[quad_index] * orig_sign,
-        reflect[(quad_index + 1) & 3]
-    };
-    return result;
+    return cache->lookup(cos, x);
 }
 
 double
-js::math_cos_impl(double x)
+js::math_cos_uncached(double x)
 {
-    return fast_sincos(x).c;
+    return cos(x);
 }
 
 bool
 js::math_cos(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    double z = math_cos_impl(x);
+    MathCache *mathCache = cx->runtime()->getMathCache(cx);
+    if (!mathCache)
+        return false;
+
+    double z = math_cos_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
 #ifdef _WIN32
 #define EXP_IF_OUT_OF_RANGE(x)                  \
     if (!IsNaN(x)) {                            \
         if (x == PositiveInfinity<double>())    \
@@ -926,36 +808,46 @@ js::math_round(JSContext *cx, unsigned a
         return false;
 
     double z = math_round_impl(x);
     args.rval().setNumber(z);
     return true;
 }
 
 double
-js::math_sin_impl(double x)
+js::math_sin_impl(MathCache *cache, double x)
 {
-    return fast_sincos(x).s;
+    return cache->lookup(sin, x);
+}
+
+double
+js::math_sin_uncached(double x)
+{
+    return sin(x);
 }
 
 bool
 js::math_sin(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         args.rval().setNaN();
         return true;
     }
 
     double x;
     if (!ToNumber(cx, args[0], &x))
         return false;
 
-    double z = math_sin_impl(x);
+    MathCache *mathCache = cx->runtime()->getMathCache(cx);
+    if (!mathCache)
+        return false;
+
+    double z = math_sin_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
 bool
 js_math_sqrt(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -123,23 +123,29 @@ math_log_impl(MathCache *cache, double x
 
 extern double
 math_log_uncached(double x);
 
 extern bool
 math_sin(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern double
-math_sin_impl(double x);
+math_sin_impl(MathCache *cache, double x);
+
+extern double
+math_sin_uncached(double x);
 
 extern bool
 math_cos(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern double
-math_cos_impl(double x);
+math_cos_impl(MathCache *cache, double x);
+
+extern double
+math_cos_uncached(double x);
 
 extern bool
 math_exp(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern double
 math_exp_impl(MathCache *cache, double x);
 
 extern double
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -369,16 +369,22 @@ def check_output(out, err, rc, timed_out
         if line.startswith('Trace stats check failed'):
             return False
 
     for line in err.split('\n'):
         if 'Assertion failed:' in line:
             return False
 
     if rc != test.expect_status:
+        # Tests which expect a timeout check for exit code 6.
+        # Sometimes 0 is returned on Windows for unknown reasons.
+        # See bug 899697.
+        if sys.platform in ['win32', 'cygwin'] and rc == 0:
+            return True
+
         # Allow a non-zero exit code if we want to allow OOM, but only if we
         # actually got OOM.
         return test.allow_oom and 'out of memory' in err and 'Assertion failure' not in err
 
     return True
 
 def print_tinderbox(ok, res):
     # Output test failures in a TBPL parsable format, eg:
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -2681,16 +2681,17 @@ ContainerState::InvalidateForLayerChange
   ThebesDisplayItemLayerUserData* data =
     static_cast<ThebesDisplayItemLayerUserData*>(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData));
   // If the frame is marked as invalidated, and didn't specify a rect to invalidate  then we want to
   // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas.
   // If we do get an invalid rect, then we want to add this on top of the change areas.
   nsRect invalid;
   nsRegion combined;
   nsPoint shift = aTopLeft - data->mLastAnimatedGeometryRootOrigin;
+  bool notifyRenderingChanged = true;
   if (!oldLayer) {
     // This item is being added for the first time, invalidate its entire area.
     //TODO: We call GetGeometry again in AddThebesDisplayItem, we should reuse this.
     combined = aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion());
 #ifdef MOZ_DUMP_PAINTING
     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
       printf_stderr("Display item type %s(%p) added to layer %p!\n", aItem->Name(), aItem->Frame(), aNewLayer);
     }
@@ -2703,16 +2704,31 @@ ContainerState::InvalidateForLayerChange
 #ifdef MOZ_DUMP_PAINTING
     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
       printf_stderr("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), aItem->Frame(), aNewLayer);
     }
 #endif
   } else {
     // Let the display item check for geometry changes and decide what needs to be
     // repainted.
+
+    // We have an optimization to cache the drawing background-attachment: fixed canvas
+    // background images so we can scroll and just blit them when they are flattened into
+    // the same layer as scrolling content. NotifyRenderingChanged is only used to tell
+    // the canvas bg image item to purge this cache. We want to be careful not to accidentally
+    // purge the cache if we are just invalidating due to scrolling (ie the background image
+    // moves on the scrolling layer but it's rendering stays the same) so if
+    // AddOffsetAndComputeDifference is the only thing that will invalidate we skip the
+    // NotifyRenderingChanged call (ComputeInvalidationRegion for background images also calls
+    // NotifyRenderingChanged if anything changes).
+    if (oldGeometry->ComputeInvalidationRegion() == aGeometry->ComputeInvalidationRegion() &&
+        *oldClip == aClip && invalid.IsEmpty() && changedFrames.Length() == 0) {
+      notifyRenderingChanged = false;
+    }
+
     oldGeometry->MoveBy(shift);
     aItem->ComputeInvalidationRegion(mBuilder, oldGeometry, &combined);
     oldClip->AddOffsetAndComputeDifference(shift, oldGeometry->ComputeInvalidationRegion(),
                                            aClip, aGeometry->ComputeInvalidationRegion(),
                                            &combined);
 
     // Add in any rect that the frame specified
     combined.Or(combined, invalid);
@@ -2730,17 +2746,19 @@ ContainerState::InvalidateForLayerChange
     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
       if (!combined.IsEmpty()) {
         printf_stderr("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), aItem->Frame(), aNewLayer);
       }
     }
 #endif
   }
   if (!combined.IsEmpty()) {
-    aItem->NotifyRenderingChanged();
+    if (notifyRenderingChanged) {
+      aItem->NotifyRenderingChanged();
+    }
     InvalidatePostTransformRegion(newThebesLayer,
         combined.ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
         GetTranslationForThebesLayer(newThebesLayer));
   }
 }
 
 void
 FrameLayerBuilder::AddThebesDisplayItem(ThebesLayerData* aLayerData,
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -3,22 +3,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'nsIStyleSheetService.idl',
 ]
 
-if CONFIG['IBMBIDI']:
-    UNIFIED_SOURCES += [
-        'nsBidi.cpp',
-        'nsBidiPresUtils.cpp',
-    ]
-
 if CONFIG['MOZ_DEBUG']:
     UNIFIED_SOURCES += [
         'nsAutoLayoutPhase.cpp',
     ]
 
 XPIDL_MODULE = 'layout_base'
 
 EXPORTS += [
@@ -67,16 +61,18 @@ EXPORTS.mozilla += [
 UNIFIED_SOURCES += [
     'ActiveLayerTracker.cpp',
     'DisplayItemClip.cpp',
     'DisplayListClipState.cpp',
     'FrameLayerBuilder.cpp',
     'FramePropertyTable.cpp',
     'GeometryUtils.cpp',
     'MaskLayerImageCache.cpp',
+    'nsBidi.cpp',
+    'nsBidiPresUtils.cpp',
     'nsCaret.cpp',
     'nsCounterManager.cpp',
     'nsCSSColorUtils.cpp',
     'nsCSSFrameConstructor.cpp',
     'nsCSSRendering.cpp',
     'nsCSSRenderingBorders.cpp',
     'nsDisplayList.cpp',
     'nsDisplayListInvalidation.cpp',
--- a/layout/base/nsBidi.cpp
+++ b/layout/base/nsBidi.cpp
@@ -1,14 +1,13 @@
 /* -*- Mode: C; 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/. */
-#ifdef IBMBIDI
 
 #include "nsBidi.h"
 #include "nsUnicodeProperties.h"
 #include "nsCRTGlue.h"
 
 using namespace mozilla::unicode;
 
 // These are #defined in <sys/regset.h> under Solaris 10 x86
@@ -2213,9 +2212,8 @@ nsresult nsBidi::WriteReverse(const char
   }
 
   if(aSrcLength>0) {
     *aDestSize = doWriteReverse(aSrc, aSrcLength, aDest, aOptions);
   }
   return NS_OK;
 }
 #endif // FULL_BIDI_ENGINE
-#endif // IBMBIDI
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1,15 +1,13 @@
 /* -*- Mode: C++; 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/. */
 
-#ifdef IBMBIDI
-
 #include "nsBidiPresUtils.h"
 #include "nsGkAtoms.h"
 #include "nsPresContext.h"
 #include "nsRenderingContext.h"
 #include "nsBidiUtils.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsContainerFrame.h"
 #include "nsInlineFrame.h"
@@ -2240,9 +2238,8 @@ nsBidiPresUtils::BidiLevelFromStyle(nsSt
   }
 
   if (aStyleContext->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
     return NSBIDI_RTL;
   }
 
   return NSBIDI_LTR;
 }
-#endif // IBMBIDI
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -2,18 +2,16 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsBidiPresUtils_h___
 #define nsBidiPresUtils_h___
 
-#ifdef IBMBIDI
-
 #include "nsBidi.h"
 #include "nsBidiUtils.h"
 #include "nsHashKeys.h"
 #include "nsCoord.h"
 
 #ifdef DrawText
 #undef DrawText
 #endif
@@ -507,11 +505,9 @@ private:
                                      nsBidiLevel aBaseDirection,
                                      nsBidi* aBidiEngine);
 
   static void WriteReverse(const char16_t* aSrc,
                            uint32_t aSrcLength,
                            char16_t* aDest);
 };
 
-#endif // IBMBIDI
-
 #endif /* nsBidiPresUtils_h___ */
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -32,20 +32,18 @@
 #include "mozilla/Selection.h"
 #include <algorithm>
 
 // The bidi indicator hangs off the caret to one side, to show which
 // direction the typing is in. It needs to be at least 2x2 to avoid looking like 
 // an insignificant dot
 static const int32_t kMinBidiIndicatorPixels = 2;
 
-#ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #include "nsContentUtils.h"
-#endif //IBMBIDI
 
 using namespace mozilla;
 
 /**
  * Find the first frame in an in-order traversal of the frame subtree rooted
  * at aFrame which is either a text frame logically at the end of a line,
  * or which is aStopAtFrame. Return null if no such frame is found. We don't
  * descend into the children of non-eLineParticipant frames.
@@ -115,20 +113,18 @@ nsCaret::nsCaret()
 : mPresShell(nullptr)
 , mBlinkRate(500)
 , mVisible(false)
 , mDrawn(false)
 , mPendingDraw(false)
 , mReadOnly(false)
 , mShowDuringSelection(false)
 , mIgnoreUserModify(true)
-#ifdef IBMBIDI
 , mKeyboardRTL(false)
 , mLastBidiLevel(0)
-#endif
 , mLastContentOffset(0)
 , mLastHint(nsFrameSelection::HINTLEFT)
 {
 }
 
 //-----------------------------------------------------------------------------
 nsCaret::~nsCaret()
 {
@@ -175,19 +171,17 @@ nsresult nsCaret::Init(nsIPresShell *inP
     privateSelection->AddSelectionListener(this);
   mDomSelectionWeak = do_GetWeakReference(domSelection);
   
   // set up the blink timer
   if (mVisible)
   {
     StartBlinking();
   }
-#ifdef IBMBIDI
   mBidiUI = Preferences::GetBool("bidi.browser.ui");
-#endif
 
   return NS_OK;
 }
 
 static bool
 DrawCJKCaret(nsIFrame* aFrame, int32_t aOffset)
 {
   nsIContent* content = aFrame->GetContent();
@@ -1045,17 +1039,16 @@ nsCaret::UpdateCaretRects(nsIFrame* aFra
     return false;
   }
 
   // on RTL frames the right edge of mCaretRect must be equal to framePos
   const nsStyleVisibility* vis = aFrame->StyleVisibility();
   if (NS_STYLE_DIRECTION_RTL == vis->mDirection)
     mCaretRect.x -= mCaretRect.width;
 
-#ifdef IBMBIDI
   mHookRect.SetEmpty();
 
   // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
   bool isCaretRTL = false;
   nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
   // if bidiKeyboard->IsLangRTL() fails, there is no way to tell the
   // keyboard direction, or the user has no right-to-left keyboard
   // installed, so we never draw the hook.
@@ -1081,17 +1074,16 @@ nsCaret::UpdateCaretRects(nsIFrame* aFra
     // rectangle.
     mHookRect.SetRect(mCaretRect.x + ((isCaretRTL) ?
                       bidiIndicatorSize * -1 :
                       mCaretRect.width),
                       mCaretRect.y + bidiIndicatorSize,
                       bidiIndicatorSize,
                       mCaretRect.width);
   }
-#endif //IBMBIDI
   return true;
 }
 
 //-----------------------------------------------------------------------------
 /* static */
 void nsCaret::CaretBlinkCallback(nsITimer *aTimer, void *aClosure)
 {
   nsCaret   *theCaret = reinterpret_cast<nsCaret*>(aClosure);
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -196,21 +196,17 @@ protected:
     // it shouldn't be.
     bool          MustDrawCaret(bool aIgnoreDrawnState);
 
     void          DrawCaret(bool aInvalidate);
     void          DrawCaretAfterBriefDelay();
     bool          UpdateCaretRects(nsIFrame* aFrame, int32_t aFrameOffset);
     nsRect        GetHookRect()
     {
-#ifdef IBMBIDI
       return mHookRect;
-#else
-      return nsRect();
-#endif
     }
     void          ToggleDrawnStatus() { mDrawn = !mDrawn; }
 
     nsFrameSelection* GetFrameSelection();
 
     // Returns true if we should not draw the caret because of XUL menu popups.
     // The caret should be hidden if:
     // 1. An open popup contains the caret, but a menu popup exists before the
@@ -239,22 +235,20 @@ protected:
     bool                  mDrawn;             // Denotes when the caret is physically drawn on the screen.
     bool                  mPendingDraw;       // True when the last on-state draw was suppressed.
 
     bool                  mReadOnly;          // it the caret in readonly state (draws differently)      
     bool                  mShowDuringSelection; // show when text is selected
 
     bool                  mIgnoreUserModify;
 
-#ifdef IBMBIDI
     bool                  mKeyboardRTL;       // is the keyboard language right-to-left
     bool                  mBidiUI;            // is bidi UI turned on
     nsRect                mHookRect;          // directional hook on the caret
     uint8_t               mLastBidiLevel;     // saved bidi level of the last draw request, to use when we erase
-#endif
     nsRect                mCaretRect;         // the last caret rect, in the coodinates of the last frame.
 
     nsCOMPtr<nsIContent>  mLastContent;       // store the content the caret was last requested to be drawn
                                               // in (by DrawAtPosition()/DrawCaret()),
                                               // note that this can be different than where it was
                                               // actually drawn (anon <BR> in text control)
     int32_t               mLastContentOffset; // the offset for the last request
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2320,28 +2320,36 @@ void nsDisplayBackgroundImage::ComputeIn
   nsRect bounds = GetBounds(aBuilder, &snap);
   nsRect positioningArea = GetPositioningArea();
   if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() ||
       (positioningArea.Size() != geometry->mPositioningArea.Size() &&
        RenderingMightDependOnPositioningAreaSizeChange())) {
     // Positioning area changed in a way that could cause everything to change,
     // so invalidate everything (both old and new painting areas).
     aInvalidRegion->Or(bounds, geometry->mBounds);
+
+    if (positioningArea.Size() != geometry->mPositioningArea.Size()) {
+      NotifyRenderingChanged();
+    }
     return;
   }
   if (aBuilder->ShouldSyncDecodeImages()) {
     if (mBackgroundStyle &&
         !nsCSSRendering::IsBackgroundImageDecodedForStyleContextAndLayer(mBackgroundStyle, mLayer)) {
       aInvalidRegion->Or(*aInvalidRegion, bounds);
+
+      NotifyRenderingChanged();
     }
   }
   if (!bounds.IsEqualInterior(geometry->mBounds)) {
     // Positioning area is unchanged, so invalidate just the change in the
     // painting area.
     aInvalidRegion->Xor(bounds, geometry->mBounds);
+
+    NotifyRenderingChanged();
   }
 }
 
 nsRect
 nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
   *aSnap = true;
   return mBounds;
 }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1022,33 +1022,35 @@ nsLayoutUtils::GetChildListNameFor(nsIFr
 // static
 nsIFrame*
 nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "NULL frame pointer");
   NS_ASSERTION(!aFrame->GetPrevContinuation(),
                "aFrame must be first continuation");
 
-  nsIFrame* firstFrame = GetFirstChildFrame(aFrame, aFrame->GetContent());
+  nsIFrame* cif = aFrame->GetContentInsertionFrame();
+  nsIFrame* firstFrame = GetFirstChildFrame(cif, aFrame->GetContent());
 
   if (firstFrame && IsGeneratedContentFor(nullptr, firstFrame,
                                           nsCSSPseudoElements::before)) {
     return firstFrame;
   }
 
   return nullptr;
 }
 
 // static
 nsIFrame*
 nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "NULL frame pointer");
 
-  nsIFrame* lastFrame = GetLastChildFrame(aFrame, aFrame->GetContent());
+  nsIFrame* cif = aFrame->GetContentInsertionFrame();
+  nsIFrame* lastFrame = GetLastChildFrame(cif, aFrame->GetContent());
 
   if (lastFrame && IsGeneratedContentFor(nullptr, lastFrame,
                                          nsCSSPseudoElements::after)) {
     return lastFrame;
   }
 
   return nullptr;
 }
@@ -4282,50 +4284,46 @@ nsLayoutUtils::GetSnappedBaselineY(nsIFr
 void
 nsLayoutUtils::DrawString(const nsIFrame*       aFrame,
                           nsRenderingContext*   aContext,
                           const char16_t*      aString,
                           int32_t               aLength,
                           nsPoint               aPoint,
                           nsStyleContext*       aStyleContext)
 {
-#ifdef IBMBIDI
   nsresult rv = NS_ERROR_FAILURE;
   nsPresContext* presContext = aFrame->PresContext();
   if (presContext->BidiEnabled()) {
     nsBidiLevel level =
       nsBidiPresUtils::BidiLevelFromStyle(aStyleContext ?
                                           aStyleContext : aFrame->StyleContext());
     rv = nsBidiPresUtils::RenderText(aString, aLength, level,
                                      presContext, *aContext, *aContext,
                                      aPoint.x, aPoint.y);
   }
   if (NS_FAILED(rv))
-#endif // IBMBIDI
   {
     aContext->SetTextRunRTL(false);
     aContext->DrawString(aString, aLength, aPoint.x, aPoint.y);
   }
 }
 
 nscoord
 nsLayoutUtils::GetStringWidth(const nsIFrame*      aFrame,
                               nsRenderingContext* aContext,
                               const char16_t*     aString,
                               int32_t              aLength)
 {
-#ifdef IBMBIDI
   nsPresContext* presContext = aFrame->PresContext();
   if (presContext->BidiEnabled()) {
     nsBidiLevel level =
       nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());
     return nsBidiPresUtils::MeasureTextWidth(aString, aLength,
                                              level, presContext, *aContext);
   }
-#endif // IBMBIDI
   aContext->SetTextRunRTL(false);
   return aContext->GetWidth(aString, aLength);
 }
 
 /* static */ void
 nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
                                nsRenderingContext* aContext,
                                const nsRect& aTextRect,
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -160,29 +160,27 @@ void
 nsPresContext::PrefChangedUpdateTimerCallback(nsITimer *aTimer, void *aClosure)
 {
   nsPresContext*  presContext = (nsPresContext*)aClosure;
   NS_ASSERTION(presContext != nullptr, "bad instance data");
   if (presContext)
     presContext->UpdateAfterPreferencesChanged();
 }
 
-#ifdef IBMBIDI
 static bool
 IsVisualCharset(const nsCString& aCharset)
 {
   if (aCharset.LowerCaseEqualsLiteral("ibm862")             // Hebrew
       || aCharset.LowerCaseEqualsLiteral("iso-8859-8") ) {  // Hebrew
     return true; // visual text type
   }
   else {
     return false; // logical text type
   }
 }
-#endif // IBMBIDI
 
   // NOTE! nsPresContext::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
 nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
   : mType(aType), mDocument(aDocument), mBaseMinFontSize(0),
     mTextZoom(1.0), mFullZoom(1.0), mLastFontInflationScreenWidth(-1.0),
     mPageSize(-1, -1), mPPScale(1.0f),
@@ -289,21 +287,19 @@ nsPresContext::~nsPresContext()
                                   "browser.active_color",
                                   this);
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "browser.visited_color",
                                   this);
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "image.animation_mode",
                                   this);
-#ifdef IBMBIDI
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "bidi.",
                                   this);
-#endif // IBMBIDI
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "dom.send_after_paint_to_content",
                                   this);
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "gfx.font_rendering.",
                                   this);
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "layout.css.dpi",
@@ -1023,21 +1019,19 @@ nsPresContext::Init(nsDeviceContext* aDe
                                 "browser.active_color",
                                 this);
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "browser.visited_color",
                                 this);
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "image.animation_mode",
                                 this);
-#ifdef IBMBIDI
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "bidi.",
                                 this);
-#endif
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "dom.send_after_paint_to_content",
                                 this);
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "gfx.font_rendering.",
                                 this);
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "layout.css.dpi",
@@ -1159,34 +1153,31 @@ nsPresContext::UpdateCharSet(const nsCSt
     // this will be a language group (or script) code rather than a true language code
 
     // bug 39570: moved from nsLanguageAtomService::LookupCharSet()
     if (mLanguage == nsGkAtoms::Unicode) {
       mLanguage = mLangService->GetLocaleLanguage();
     }
     ResetCachedFontPrefs();
   }
-#ifdef IBMBIDI
-  //ahmed
 
   switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
 
     case IBMBIDI_TEXTTYPE_LOGICAL:
       SetVisualMode(false);
       break;
 
     case IBMBIDI_TEXTTYPE_VISUAL:
       SetVisualMode(true);
       break;
 
     case IBMBIDI_TEXTTYPE_CHARSET:
     default:
       SetVisualMode(IsVisualCharset(aCharSet));
   }
-#endif // IBMBIDI
 }
 
 NS_IMETHODIMP
 nsPresContext::Observe(nsISupports* aSubject,
                         const char* aTopic,
                         const char16_t* aData)
 {
   if (!nsCRT::strcmp(aTopic, "charset")) {
@@ -1568,17 +1559,16 @@ nsPresContext::StyleUpdateForAllAnimatio
 }
 
 void
 nsPresContext::TickLastStyleUpdateForAllAnimations()
 {
   mLastStyleUpdateForAllAnimations = mRefreshDriver->MostRecentRefresh();
 }
 
-#ifdef IBMBIDI
 bool
 nsPresContext::BidiEnabledExternal() const
 {
   return BidiEnabledInternal();
 }
 
 bool
 nsPresContext::BidiEnabledInternal() const
@@ -1634,18 +1624,16 @@ nsPresContext::SetBidi(uint32_t aSource,
 }
 
 uint32_t
 nsPresContext::GetBidi() const
 {
   return Document()->GetBidiOptions();
 }
 
-#endif //IBMBIDI
-
 bool
 nsPresContext::IsTopLevelWindowInactive()
 {
   nsCOMPtr<nsIDocShellTreeItem> treeItem(mContainer);
   if (!treeItem)
     return false;
 
   nsCOMPtr<nsIDocShellTreeItem> rootItem;
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -32,20 +32,17 @@
 #include "nsAutoPtr.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/AppUnits.h"
 #include "prclist.h"
 #include "nsThreadUtils.h"
 #include "ScrollbarStyles.h"
 
-#ifdef IBMBIDI
 class nsBidiPresUtils;
-#endif // IBMBIDI
-
 class nsAString;
 class nsIPrintSettings;
 class nsDocShell;
 class nsIDocShell;
 class nsIDocument;
 class nsILanguageAtomService;
 class nsITheme;
 class nsIContent;
@@ -687,17 +684,16 @@ public:
    */
   bool ThrottledTransitionStyleIsUpToDate() const;
   void TickLastUpdateThrottledTransitionStyle();
   bool ThrottledAnimationStyleIsUpToDate() const;
   void TickLastUpdateThrottledAnimationStyle();
   bool StyleUpdateForAllAnimationsIsUpToDate();
   void TickLastStyleUpdateForAllAnimations();
 
-#ifdef IBMBIDI
   /**
    *  Check if bidi enabled (set depending on the presence of RTL
    *  characters or when default directionality is RTL).
    *  If enabled, we should apply the Unicode Bidi Algorithm
    *
    *  @lina 07/12/2000
    */
 #ifdef MOZILLA_INTERNAL_API
@@ -750,17 +746,16 @@ public:
                            bool aForceRestyle = false);
 
   /**
    * Get the Bidi options for the presentation context
    * Not inline so consumers of nsPresContext are not forced to
    * include nsIDocument.
    */
   NS_HIDDEN_(uint32_t) GetBidi() const;
-#endif // IBMBIDI
 
   /**
    * Render only Selection
    */
   void SetIsRenderingOnlySelection(bool aResult)
   {
     mIsRenderingOnlySelection = aResult;
   }
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -464,8 +464,9 @@ skip-if = true || (toolkit == 'android')
 [test_getBoxQuads_convertPointRectQuad.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 948948
 [test_bug687297.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 948948
 support-files =
   bug687297_a.html
   bug687297_b.html
   bug687297_c.html
+[test_bug990340.html]
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_bug990340.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=990340
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 990340</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 Bug 990340 **/
+
+  function testbug990340() {
+    ok(document.querySelector('#bug990340 span').clientHeight < 100,
+       "'height' is in transition")
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=990340">Mozilla Bug 990340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<style type="text/css">
+
+#bug990340::before {
+    content: ":before";
+}
+
+#bug990340 span {
+    display: inline-block;
+    border:1px solid blue;
+    height: 20px;
+    transition: height 30s;
+}
+
+#bug990340.s span {
+    height: 100px;
+}
+</style>
+<div id="bug990340" style="overflow:scroll">
+    <span>Transition height</span>
+</div>
+</pre>
+
+<script>
+document.body.offsetHeight;
+document.querySelector('#bug990340').className='s';
+testbug990340()
+</script>
+
+</body>
+</html>
--- a/layout/forms/nsLegendFrame.cpp
+++ b/layout/forms/nsLegendFrame.cpp
@@ -61,21 +61,19 @@ nsLegendFrame::Reflow(nsPresContext*    
   return nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
 }
 
 // REVIEW: We don't need to override BuildDisplayList, nsBlockFrame will honour
 // our visibility setting
 int32_t nsLegendFrame::GetAlign()
 {
   int32_t intValue = NS_STYLE_TEXT_ALIGN_LEFT;
-#ifdef IBMBIDI
   if (mParent && NS_STYLE_DIRECTION_RTL == mParent->StyleVisibility()->mDirection) {
     intValue = NS_STYLE_TEXT_ALIGN_RIGHT;
   }
-#endif // IBMBIDI
 
   nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
 
   if (content) {
     const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::align);
     if (attr && attr->Type() == nsAttrValue::eEnum) {
       intValue = attr->GetEnumValue();
     }
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -44,19 +44,17 @@
 #include "nsLayoutUtils.h"
 #include "nsDisplayList.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsRenderingContext.h"
 #include "TextOverflow.h"
 #include "nsIFrameInlines.h"
 
-#ifdef IBMBIDI
 #include "nsBidiPresUtils.h"
-#endif // IBMBIDI
 
 static const int MIN_LINES_NEEDING_CURSOR = 20;
 
 static const char16_t kDiscCharacter = 0x2022;
 static const char16_t kCircleCharacter = 0x25e6;
 static const char16_t kSquareCharacter = 0x25aa;
 
 #define DISABLE_FLOAT_BREAKING_IN_COLUMNS
@@ -1018,20 +1016,18 @@ nsBlockFrame::Reflow(nsPresContext*     
   IsMarginRoot(&topMarginRoot, &bottomMarginRoot);
 
   // Cache the consumed height in the block reflow state so that we don't have
   // to continually recompute it.
   nsBlockReflowState state(*reflowState, aPresContext, this,
                            topMarginRoot, bottomMarginRoot, needFloatManager,
                            consumedHeight);
 
-#ifdef IBMBIDI
   if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
     static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
-#endif // IBMBIDI
 
   if (RenumberLists(aPresContext)) {
     AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   }
 
   nsresult rv = NS_OK;
 
   // ALWAYS drain overflow. We never want to leave the previnflow's
@@ -4759,20 +4755,17 @@ nsBlockFrame::InsertFrames(ChildListID a
   if (aListID != kPrincipalList) {
     if (kAbsoluteList == aListID) {
       return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
     }
     else if (kFloatList == aListID) {
       mFloats.InsertFrames(this, aPrevFrame, aFrameList);
       return NS_OK;
     }
-#ifdef IBMBIDI
-    else if (kNoReflowPrincipalList == aListID) {}
-#endif // IBMBIDI
-    else {
+    else if (kNoReflowPrincipalList != aListID) {
       NS_ERROR("unexpected child list");
       return NS_ERROR_INVALID_ARG;
     }
   }
 
 #ifdef NOISY_REFLOW_REASON
   ListTag(stdout);
   printf(": insert ");
@@ -4781,19 +4774,17 @@ nsBlockFrame::InsertFrames(ChildListID a
     printf(" after ");
     nsFrame::ListTag(stdout, aPrevFrame);
   }
   printf("\n");
 #endif
 
   AddFrames(aFrameList, aPrevFrame);
 
-#ifdef IBMBIDI
   if (aListID != kNoReflowPrincipalList)
-#endif // IBMBIDI
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
   return NS_OK;
 }
 
 static bool
 ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame)
@@ -5066,22 +5057,20 @@ nsBlockFrame::RemoveFrame(ChildListID aL
                  "RemoveFrame should not be called on pushed floats.");
     for (nsIFrame* f = aOldFrame;
          f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
          f = f->GetNextContinuation()) {
       MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame*>(f->GetParent()));
     }
     DoRemoveOutOfFlowFrame(aOldFrame);
   }
-#ifdef IBMBIDI
   else if (kNoReflowPrincipalList == aListID) {
     // Skip the call to |FrameNeedsReflow| below by returning now.
     return DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
   }
-#endif // IBMBIDI
   else {
     NS_ERROR("unexpected child list");
     rv = NS_ERROR_INVALID_ARG;
   }
 
   if (NS_SUCCEEDED(rv)) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
@@ -7145,31 +7134,29 @@ nsBlockFrame::ComputeFinalHeight(const n
                                  aContentHeight);
       NS_FRAME_SET_INCOMPLETE(*aStatus);
       if (!GetNextInFlow())
         *aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
     }
   }
 }
 
-#ifdef IBMBIDI
 nsresult
 nsBlockFrame::ResolveBidi()
 {
   NS_ASSERTION(!GetPrevInFlow(),
                "ResolveBidi called on non-first continuation");
 
   nsPresContext* presContext = PresContext();
   if (!presContext->BidiEnabled()) {
     return NS_OK;
   }
 
   return nsBidiPresUtils::Resolve(this);
 }
-#endif
 
 #ifdef DEBUG
 void
 nsBlockFrame::VerifyLines(bool aFinalCheckOK)
 {
   if (!gVerifyLines) {
     return;
   }
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -429,32 +429,30 @@ protected:
    * This block thinks in terms of lines, but the frame construction code
    * knows nothing about lines at all so we need to find the line that
    * contains aPrevSibling and add aFrameList after aPrevSibling on that line.
    * New lines are created as necessary to handle block data in aFrameList.
    * This function will clear aFrameList.
    */
   void AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling);
 
-#ifdef IBMBIDI
   /**
    * Perform Bidi resolution on this frame
    */
   nsresult ResolveBidi();
 
   /**
    * Test whether the frame is a form control in a visual Bidi page.
    * This is necessary for backwards-compatibility, because most visual
    * pages use logical order for form controls so that they will
    * display correctly on native widgets in OSs with Bidi support
    * @param aPresContext the pres context
    * @return whether the frame is a BIDI form control
    */
   bool IsVisualFormControl(nsPresContext* aPresContext);
-#endif
 
 public:
   /**
    * Does all the real work for removing aDeletedFrame
    * -- finds the line containing aDeletedFrame
    * -- removes all aDeletedFrame next-in-flows (or all continuations,
    * if REMOVE_FIXED_CONTINUATIONS is given)
    * -- marks lines dirty as needed
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -165,22 +165,16 @@ public:
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) MOZ_OVERRIDE
   {
     const nsDisplayItemBoundsGeometry* geometry = static_cast<const nsDisplayItemBoundsGeometry*>(aGeometry);
     ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion);
   }
 
-  virtual void NotifyRenderingChanged() MOZ_OVERRIDE
-  {
-    mFrame->Properties().Delete(nsIFrame::CachedBackgroundImage());
-    mFrame->Properties().Delete(nsIFrame::CachedBackgroundImageDT());
-  }
-
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
 
   void SetExtraBackgroundColor(nscolor aColor)
   {
     mColor = aColor;
   }
 
@@ -194,16 +188,22 @@ class nsDisplayCanvasBackgroundImage : p
 public:
   nsDisplayCanvasBackgroundImage(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                  uint32_t aLayer, const nsStyleBackground* aBg)
     : nsDisplayBackgroundImage(aBuilder, aFrame, aLayer, aBg)
   {}
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE;
 
+  virtual void NotifyRenderingChanged() MOZ_OVERRIDE
+  {
+    mFrame->Properties().Delete(nsIFrame::CachedBackgroundImage());
+    mFrame->Properties().Delete(nsIFrame::CachedBackgroundImageDT());
+  }
+
   virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
   {
     // Put background-attachment:fixed canvas background images in their own
     // compositing layer. Since we know their background painting area can't
     // change (unless the viewport size itself changes), async scrolling
     // will work well.
     return mBackgroundStyle->mLayers[mLayer].mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED &&
            !mBackgroundStyle->mLayers[mLayer].mImage.IsEmpty();
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -91,31 +91,27 @@ nsContainerFrame::SetInitialChildList(Ch
   return result;
 }
 
 nsresult
 nsContainerFrame::AppendFrames(ChildListID  aListID,
                                nsFrameList& aFrameList)
 {
   if (aListID != kPrincipalList) {
-#ifdef IBMBIDI
     if (aListID != kNoReflowPrincipalList)
-#endif
     {
       NS_ERROR("unexpected child list");
       return NS_ERROR_INVALID_ARG;
     }
   }
   if (aFrameList.NotEmpty()) {
     mFrames.AppendFrames(this, aFrameList);
 
     // Ask the parent frame to reflow me.
-#ifdef IBMBIDI
     if (aListID == kPrincipalList)
-#endif
     {
       PresContext()->PresShell()->
         FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                          NS_FRAME_HAS_DIRTY_CHILDREN);
     }
   }
   return NS_OK;
 }
@@ -124,63 +120,55 @@ nsresult
 nsContainerFrame::InsertFrames(ChildListID aListID,
                                nsIFrame* aPrevFrame,
                                nsFrameList& aFrameList)
 {
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
                "inserting after sibling frame with different parent");
 
   if (aListID != kPrincipalList) {
-#ifdef IBMBIDI
     if (aListID != kNoReflowPrincipalList)
-#endif
     {
       NS_ERROR("unexpected child list");
       return NS_ERROR_INVALID_ARG;
     }
   }
   if (aFrameList.NotEmpty()) {
     // Insert frames after aPrevFrame
     mFrames.InsertFrames(this, aPrevFrame, aFrameList);
 
-#ifdef IBMBIDI
     if (aListID == kPrincipalList)
-#endif
     {
       PresContext()->PresShell()->
         FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                          NS_FRAME_HAS_DIRTY_CHILDREN);
     }
   }
   return NS_OK;
 }
 
 nsresult
 nsContainerFrame::RemoveFrame(ChildListID aListID,
                               nsIFrame* aOldFrame)
 {
   if (aListID != kPrincipalList) {
-#ifdef IBMBIDI
     if (kNoReflowPrincipalList != aListID)
-#endif
     {
       NS_ERROR("unexpected child list");
       return NS_ERROR_INVALID_ARG;
     }
   }
 
   // Loop and destroy aOldFrame and all of its continuations.
   // Request a reflow on the parent frames involved unless we were explicitly
   // told not to (kNoReflowPrincipalList).
   bool generateReflowCommand = true;
-#ifdef IBMBIDI
   if (kNoReflowPrincipalList == aListID) {
     generateReflowCommand = false;
   }
-#endif
   nsIPresShell* shell = PresContext()->PresShell();
   nsContainerFrame* lastParent = nullptr;
   while (aOldFrame) {
     //XXXfr probably should use StealFrame here. I'm not sure if we need to
     //      check the overflow lists atm, but we'll need a prescontext lookup
     //      for overflow containers once we can split abspos elements with
     //      inline containing blocks.
     nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -43,19 +43,17 @@
 #include "nsFrameTraversal.h"
 #include "nsRange.h"
 #include "nsITextControlFrame.h"
 #include "nsNameSpaceManager.h"
 #include "nsIPercentHeightObserver.h"
 #include "nsStyleStructInlines.h"
 #include <algorithm>
 
-#ifdef IBMBIDI
 #include "nsBidiPresUtils.h"
-#endif
 
 // For triple-click pref
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsError.h"
 #include "nsContainerFrame.h"
 #include "nsBoxLayoutState.h"
 #include "nsBlockFrame.h"
@@ -6546,34 +6544,31 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct*
       NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
 
       int32_t lineFrameCount;
       nsIFrame *firstFrame;
       nsRect usedRect;
       uint32_t lineFlags;
       nsIFrame* baseFrame = nullptr;
       bool endOfLine = (eSelectEndLine == aPos->mAmount);
-      
-#ifdef IBMBIDI
+
       if (aPos->mVisual && PresContext()->BidiEnabled()) {
         bool lineIsRTL = it->GetDirection();
         bool isReordered;
         nsIFrame *lastFrame;
         result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
         baseFrame = endOfLine ? lastFrame : firstFrame;
         if (baseFrame) {
           nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame);
           // If the direction of the frame on the edge is opposite to that of the line,
           // we'll need to drill down to its opposite end, so reverse endOfLine.
           if ((embeddingLevel & 1) == !lineIsRTL)
             endOfLine = !endOfLine;
         }
-      } else
-#endif
-      {
+      } else {
         it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags);
 
         nsIFrame* frame = firstFrame;
         for (int32_t count = lineFrameCount; count;
              --count, frame = frame->GetNextSibling()) {
           if (!frame->IsGeneratedContentFrame()) {
             baseFrame = frame;
             if (!endOfLine)
@@ -6775,17 +6770,16 @@ nsIFrame::GetFrameFromDirection(nsDirect
       return NS_ERROR_FAILURE;
 
     nsAutoLineIterator it = blockFrame->GetLineIterator();
     NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
 
     bool atLineEdge;
     nsIFrame *firstFrame;
     nsIFrame *lastFrame;
-#ifdef IBMBIDI
     if (aVisual && presContext->BidiEnabled()) {
       bool lineIsRTL = it->GetDirection();
       bool isReordered;
       result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
       nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
       if (*framePtr) {
         nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr);
         if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) ==
@@ -6793,19 +6787,17 @@ nsIFrame::GetFrameFromDirection(nsDirect
           nsFrame::GetFirstLeaf(presContext, framePtr);
         } else {
           nsFrame::GetLastLeaf(presContext, framePtr);
         }
         atLineEdge = *framePtr == traversedFrame;
       } else {
         atLineEdge = true;
       }
-    } else
-#endif
-    {
+    } else {
       nsRect  nonUsedRect;
       int32_t lineFrameCount;
       uint32_t lineFlags;
       result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
                            &lineFlags);
       if (NS_FAILED(result))
         return result;
 
@@ -6851,24 +6843,22 @@ nsIFrame::GetFrameFromDirection(nsDirect
     traversedFrame = frameTraversal->CurrentItem();
     if (!traversedFrame)
       return NS_ERROR_FAILURE;
     traversedFrame->IsSelectable(&selectable, nullptr);
   } // while (!selectable)
 
   *aOutOffset = (aDirection == eDirNext) ? 0 : -1;
 
-#ifdef IBMBIDI
   if (aVisual) {
     uint8_t newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame);
     uint8_t newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame);
     if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end
       *aOutOffset = -1 - *aOutOffset;
   }
-#endif
   *aOutFrame = traversedFrame;
   return NS_OK;
 }
 
 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const
 {
   nsPoint offset(0,0);
   for (const nsIFrame *f = this; f; f = f->GetParent()) {
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -4,21 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsFrameList.h"
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 
-#ifdef IBMBIDI
 #include "nsGkAtoms.h"
 #include "nsILineIterator.h"
 #include "nsBidiPresUtils.h"
-#endif // IBMBIDI
 
 namespace mozilla {
 namespace layout {
 namespace detail {
 const AlignedFrameListBytes gEmptyFrameListBytes = { 0 };
 }
 }
 }
@@ -339,17 +337,16 @@ nsFrameList::List(FILE* out) const
   for (nsIFrame* frame = mFirstChild; frame;
        frame = frame->GetNextSibling()) {
     frame->List(out, "  ");
   }
   fprintf_stderr(out, ">\n");
 }
 #endif
 
-#ifdef IBMBIDI
 nsIFrame*
 nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const
 {
   if (!mFirstChild)
     return nullptr;
   
   nsIFrame* parent = mFirstChild->GetParent();
   if (!parent)
@@ -488,17 +485,16 @@ nsFrameList::GetNextVisualFor(nsIFrame* 
     if (baseLevel == NSBIDI_LTR) {
       frame = nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine, numFramesOnLine);
     } else { // RTL
       frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine, numFramesOnLine);
     }
   }
   return frame;
 }
-#endif
 
 #ifdef DEBUG_FRAME_LIST
 void
 nsFrameList::VerifyList() const
 {
   NS_ASSERTION((mFirstChild == nullptr) == (mLastChild == nullptr),
                "bad list state");
 
--- a/layout/generic/nsFrameList.h
+++ b/layout/generic/nsFrameList.h
@@ -264,29 +264,27 @@ public:
   /**
    * If this frame list is non-empty then append it to aLists as the
    * aListID child list.
    * (this method is implemented in FrameChildList.h for dependency reasons)
    */
   inline void AppendIfNonempty(nsTArray<mozilla::layout::FrameChildList>* aLists,
                                mozilla::layout::FrameChildListID aListID) const;
 
-#ifdef IBMBIDI
   /**
    * Return the frame before this frame in visual order (after Bidi reordering).
    * If aFrame is null, return the last frame in visual order.
    */
   nsIFrame* GetPrevVisualFor(nsIFrame* aFrame) const;
 
   /**
    * Return the frame after this frame in visual order (after Bidi reordering).
    * If aFrame is null, return the first frame in visual order.
    */
   nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const;
-#endif // IBMBIDI
 
 #ifdef DEBUG_FRAME_DUMP
   void List(FILE* out) const;
 #endif
 
   static inline const nsFrameList& EmptyList();
 
   class Enumerator;
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -19,19 +19,17 @@
 class nsTableOuterFrame;
 
 // IID for the nsFrameSelection interface
 // 3c6ae2d0-4cf1-44a1-9e9d-2411867f19c6
 #define NS_FRAME_SELECTION_IID      \
 { 0x3c6ae2d0, 0x4cf1, 0x44a1, \
   { 0x9e, 0x9d, 0x24, 0x11, 0x86, 0x7f, 0x19, 0xc6 } }
 
-#ifdef IBMBIDI // Constant for Set/Get CaretBidiLevel
 #define BIDI_LEVEL_UNDEFINED 0x80
-#endif
 
 //----------------------------------------------------------------------
 
 // Selection interface
 
 struct SelectionDetails
 {
 #ifdef NS_BUILD_REFCNT_LOGGING
@@ -406,32 +404,30 @@ public:
    */
   /*unsafe*/
   void CommonPageMove(bool aForward,
                       bool aExtend,
                       nsIScrollableFrame* aScrollableFrame);
 
   void SetHint(HINT aHintRight) { mHint = aHintRight; }
   HINT GetHint() const { return mHint; }
-  
-#ifdef IBMBIDI
+
   /** SetCaretBidiLevel sets the caret bidi level
    *  @param aLevel the caret bidi level
    *  This method is virtual since it gets called from outside of layout.
    */
   virtual void SetCaretBidiLevel (uint8_t aLevel);
   /** GetCaretBidiLevel gets the caret bidi level
    *  This method is virtual since it gets called from outside of layout.
    */
   virtual uint8_t GetCaretBidiLevel() const;
   /** UndefineCaretBidiLevel sets the caret bidi level to "undefined"
    *  This method is virtual since it gets called from outside of layout.
    */
   virtual void UndefineCaretBidiLevel();
-#endif
 
   /** CharacterMove will generally be called from the nsiselectioncontroller implementations.
    *  the effect being the selection will move one character left or right.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
   /*unsafe*/
   nsresult CharacterMove(bool aForward, bool aExtend);
@@ -700,19 +696,17 @@ private:
   nsCOMPtr<nsIContent> mAncestorLimiter;
 
   nsIPresShell *mShell;
 
   int16_t mSelectionChangeReason; // reason for notifications of selection changing
   int16_t mDisplaySelection; //for visual display purposes.
 
   HINT  mHint;   //hint to tell if the selection is at the end of this line or beginning of next
-#ifdef IBMBIDI
   uint8_t mCaretBidiLevel;
-#endif
 
   int32_t mDesiredX;
   uint32_t mDelayedMouseEventClickCount;
   bool mDelayedMouseEventIsShift;
   bool mDelayedMouseEventValid;
 
   bool mChangesDuringBatching;
   bool mNotifyFrames;
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -143,21 +143,19 @@ FRAME_STATE_BIT(Generic, 15, NS_FRAME_PA
 
 // If this bit is set, then transforms (e.g. CSS or SVG transforms) are allowed
 // to affect the frame, and a transform may currently be in affect. If this bit
 // is not set, then any transforms on the frame will be ignored.
 // This is used primarily in GetTransformMatrix to optimize for the
 // common case.
 FRAME_STATE_BIT(Generic, 16, NS_FRAME_MAY_BE_TRANSFORMED)
 
-#ifdef IBMBIDI
 // If this bit is set, the frame itself is a bidi continuation,
 // or is incomplete (its next sibling is a bidi continuation)
 FRAME_STATE_BIT(Generic, 17, NS_FRAME_IS_BIDI)
-#endif
 
 // If this bit is set the frame has descendant with a view
 FRAME_STATE_BIT(Generic, 18, NS_FRAME_HAS_CHILD_WITH_VIEW)
 
 // If this bit is set, then reflow may be dispatched from the current
 // frame instead of the root frame.
 FRAME_STATE_BIT(Generic, 19, NS_FRAME_REFLOW_ROOT)
 
--- a/layout/generic/nsILineIterator.h
+++ b/layout/generic/nsILineIterator.h
@@ -88,24 +88,22 @@ public:
                          nsIFrame** aFrameFound,
                          bool* aXIsBeforeFirstFrame,
                          bool* aXIsAfterLastFrame) = 0;
 
   // Give the line iterator implementor a chance todo something more complicated than
   // nsIFrame::GetNextSibling()
   NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) = 0;
 
-#ifdef IBMBIDI
   // Check whether visual and logical order of frames within a line are identical.
   //  If not, return the first and last visual frames
   NS_IMETHOD CheckLineOrder(int32_t                  aLine,
                             bool                     *aIsReordered,
                             nsIFrame                 **aFirstVisual,
                             nsIFrame                 **aLastVisual) = 0;
-#endif
 };
 
 class nsAutoLineIterator
 {
 public:
   nsAutoLineIterator() : mRawPtr(nullptr) { }
   nsAutoLineIterator(nsILineIterator *i) : mRawPtr(i) { }
 
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -5,19 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* representation of one line within a block frame, a CSS line box */
 
 #include "nsLineBox.h"
 #include "prprf.h"
 #include "nsFrame.h"
 #include "nsPresArena.h"
-#ifdef IBMBIDI
 #include "nsBidiPresUtils.h"
-#endif
 #include "nsIFrameInlines.h"
 #include "WritingModes.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Likely.h"
 #include "nsPrintfCString.h"
 
 #ifdef DEBUG
 static int32_t ctorCount;
@@ -679,17 +677,16 @@ nsLineIterator::FindLineContaining(nsIFr
     if (line->Contains(aFrame)) {
       return lineNumber;
     }
     ++lineNumber;
   }
   return -1;
 }
 
-#ifdef IBMBIDI
 NS_IMETHODIMP
 nsLineIterator::CheckLineOrder(int32_t                  aLine,
                                bool                     *aIsReordered,
                                nsIFrame                 **aFirstVisual,
                                nsIFrame                 **aLastVisual)
 {
   NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
   nsLineBox* line = mLines[aLine];
@@ -706,17 +703,16 @@ nsLineIterator::CheckLineOrder(int32_t  
   *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
 
   // map leftmost/rightmost to first/last according to paragraph direction
   *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
   *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
 
   return NS_OK;
 }
-#endif // IBMBIDI
 
 NS_IMETHODIMP
 nsLineIterator::FindFrameAt(int32_t aLineNumber,
                             nscoord aX,
                             nsIFrame** aFrameFound,
                             bool* aXIsBeforeFirstFrame,
                             bool* aXIsAfterLastFrame)
 {
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -1694,22 +1694,20 @@ public:
   virtual int32_t FindLineContaining(nsIFrame* aFrame, int32_t aStartLine = 0) MOZ_OVERRIDE;
   NS_IMETHOD FindFrameAt(int32_t aLineNumber,
                          nscoord aX,
                          nsIFrame** aFrameFound,
                          bool* aXIsBeforeFirstFrame,
                          bool* aXIsAfterLastFrame) MOZ_OVERRIDE;
 
   NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) MOZ_OVERRIDE;
-#ifdef IBMBIDI
   NS_IMETHOD CheckLineOrder(int32_t                  aLine,
                             bool                     *aIsReordered,
                             nsIFrame                 **aFirstVisual,
                             nsIFrame                 **aLastVisual) MOZ_OVERRIDE;
-#endif
   nsresult Init(nsLineList& aLines, bool aRightToLeft);
 
 private:
   nsLineBox* PrevLine() {
     if (0 == mIndex) {
       return nullptr;
     }
     return mLines[--mIndex];
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -8,19 +8,17 @@
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsPageContentFrame.h"
 #include "nsDisplayList.h"
 #include "nsLayoutUtils.h" // for function BinarySearchForPosition
 #include "nsSimplePageSequenceFrame.h" // for nsSharedPageData
 #include "nsTextFormatter.h" // for page number localization formatting
-#ifdef IBMBIDI
 #include "nsBidiUtils.h"
-#endif
 #include "nsIPrintSettings.h"
 
 #include "prlog.h"
 #ifdef PR_LOGGING 
 extern PRLogModuleInfo *GetLayoutPrintingLog();
 #define PR_PL(_p1)  PR_LOG(GetLayoutPrintingLog(), PR_LOG_DEBUG, _p1)
 #else
 #define PR_PL(_p1)
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -29,17 +29,16 @@
 #include "nsTableCellFrame.h"
 #include "nsIScrollableFrame.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsIContentIterator.h"
 #include "nsIDocumentEncoder.h"
 #include "nsTextFragment.h"
 #include <algorithm>
 
-// for IBMBIDI
 #include "nsGkAtoms.h"
 #include "nsIFrameTraversal.h"
 #include "nsLayoutUtils.h"
 #include "nsLayoutCID.h"
 #include "nsBidiPresUtils.h"
 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
 #include "nsTextFrame.h"
 
@@ -65,19 +64,17 @@ static NS_DEFINE_CID(kFrameTraversalCID,
 #include "nsIDocument.h"
 
 #include "nsISelectionController.h"//for the enums
 #include "nsAutoCopyListener.h"
 #include "nsCopySupport.h"
 #include "nsIClipboard.h"
 #include "nsIFrameInlines.h"
 
-#ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
-#endif // IBMBIDI
 
 #include "nsError.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/SelectionBinding.h"
 
 using namespace mozilla;
@@ -326,19 +323,17 @@ nsFrameSelection::nsFrameSelection()
   }
   mBatching = 0;
   mChangesDuringBatching = false;
   mNotifyFrames = true;
   
   mMouseDoubleDownState = false;
   
   mHint = HINTLEFT;
-#ifdef IBMBIDI
   mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
-#endif
   mDragSelectingCells = false;
   mSelectingTableCellMode = 0;
   mSelectedCellIndex = 0;
 
   // Check to see if the autocopy pref is enabled
   //   and add the autocopy listener if it is
   if (Preferences::GetBool("clipboard.autocopy")) {
     nsAutoCopyListener *autoCopy = nsAutoCopyListener::GetInstance();
@@ -593,17 +588,16 @@ nsFrameSelection::ConstrainFrameAndPoint
   // system used by aRetFrame.
   //
 
   aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
 
   return NS_OK;
 }
 
-#ifdef IBMBIDI
 void
 nsFrameSelection::SetCaretBidiLevel(uint8_t aLevel)
 {
   // If the current level is undefined, we have just inserted new text.
   // In this case, we don't want to reset the keyboard language
   mCaretBidiLevel = aLevel;
   return;
 }
@@ -614,18 +608,16 @@ nsFrameSelection::GetCaretBidiLevel() co
   return mCaretBidiLevel;
 }
 
 void
 nsFrameSelection::UndefineCaretBidiLevel()
 {
   mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
 }
-#endif
-
 
 #ifdef PRINT_RANGE
 void printRange(nsRange *aDomRange)
 {
   if (!aDomRange)
   {
     printf("NULL nsIDOMRange\n");
   }
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -380,17 +380,17 @@ nsSubDocumentFrame::BuildDisplayList(nsD
     dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
     // and convert into the appunits of the subdoc
     dirty = dirty.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
 
     if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
       // for root content documents we want the base to be the composition bounds
       nsRect displayportBase = presContext->IsRootContentDocument() ?
           nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
-          dirty;
+          dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
       nsRect displayPort;
       if (nsLayoutUtils::GetOrMaybeCreateDisplayPort(
             *aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
         haveDisplayPort = true;
         dirty = displayPort;
       }
 
       ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -3967,20 +3967,18 @@ void
 nsContinuingTextFrame::Init(nsIContent* aContent,
                             nsIFrame*   aParent,
                             nsIFrame*   aPrevInFlow)
 {
   NS_ASSERTION(aPrevInFlow, "Must be a continuation!");
   // NOTE: bypassing nsTextFrame::Init!!!
   nsFrame::Init(aContent, aParent, aPrevInFlow);
 
-#ifdef IBMBIDI
   nsTextFrame* nextContinuation =
     static_cast<nsTextFrame*>(aPrevInFlow->GetNextContinuation());
-#endif // IBMBIDI
   // Hook the frame into the flow
   SetPrevInFlow(aPrevInFlow);
   aPrevInFlow->SetNextInFlow(this);
   nsTextFrame* prev = static_cast<nsTextFrame*>(aPrevInFlow);
   mContentOffset = prev->GetContentOffset() + prev->GetContentLengthHint();
   NS_ASSERTION(mContentOffset < int32_t(aContent->GetText()->GetLength()),
                "Creating ContinuingTextFrame, but there is no more content");
   if (prev->StyleContext() != StyleContext()) {
@@ -3994,17 +3992,16 @@ nsContinuingTextFrame::Init(nsIContent* 
     if (inflation != 1.0f) {
       gfxTextRun *uninflatedTextRun =
         prev->GetTextRun(nsTextFrame::eNotInflated);
       if (uninflatedTextRun) {
         SetTextRun(uninflatedTextRun, nsTextFrame::eNotInflated, 1.0f);
       }
     }
   }
-#ifdef IBMBIDI
   if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
     FramePropertyTable *propTable = PresContext()->PropertyTable();
     // Get all the properties from the prev-in-flow first to take
     // advantage of the propTable's cache and simplify the assertion below
     void* embeddingLevel = propTable->Get(aPrevInFlow, EmbeddingLevelProperty());
     void* baseLevel = propTable->Get(aPrevInFlow, BaseLevelProperty());
     void* paragraphDepth = propTable->Get(aPrevInFlow, ParagraphDepthProperty());
     propTable->Set(this, EmbeddingLevelProperty(), embeddingLevel);
@@ -4023,17 +4020,16 @@ nsContinuingTextFrame::Init(nsIContent* 
           paragraphDepth == propTable->Get(nextContinuation, ParagraphDepthProperty()),
           "stealing text from different type of BIDI continuation");
         nextContinuation->mContentOffset = mContentOffset;
         nextContinuation = static_cast<nsTextFrame*>(nextContinuation->GetNextContinuation());
       }
     }
     mState |= NS_FRAME_IS_BIDI;
   } // prev frame is bidi
-#endif // IBMBIDI
 }
 
 void
 nsContinuingTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   ClearFrameOffsetCache();
 
   // The text associated with this frame will become associated with our
--- a/layout/reftests/border-image/reftest.list
+++ b/layout/reftests/border-image/reftest.list
@@ -74,9 +74,9 @@ fuzzy(1,437) fails-if(OSX) == border-ima
 fuzzy(1,1357) fails-if(OSX) == border-image-repeating-radial-gradient-slice-fill-1.html border-image-repeating-radial-gradient-slice-fill-1-ref.html
 fuzzy(1,1058) fails-if(OSX) == border-image-repeating-radial-gradient-slice-fill-2.html border-image-repeating-radial-gradient-slice-fill-2-ref.html
 fuzzy(1,602) fails-if(OSX) == border-image-repeating-radial-gradient-width.html border-image-repeating-radial-gradient-width-ref.html
 fuzzy(3,18000) fails-if(OSX) == border-image-repeating-radial-gradient-slice-width.html border-image-repeating-radial-gradient-slice-width-ref.html
 fails-if(OSX) == border-image-repeating-radial-gradient-repeat-repeat-2.html border-image-repeating-radial-gradient-repeat-repeat-2-ref.html
 fuzzy(1,1054) fails-if(OSX) == border-image-repeating-radial-gradient-repeat-round-2.html border-image-repeating-radial-gradient-repeat-round-2-ref.html
 
 # border-image-source (-moz-)element
-fuzzy(125,5808) == border-image-element.html border-image-element-ref.html
+fuzzy(125,5808) fuzzy-if(B2G,151,5809) == border-image-element.html border-image-element-ref.html
--- a/layout/reftests/xul/reftest.list
+++ b/layout/reftests/xul/reftest.list
@@ -1,9 +1,9 @@
 == menuitem-key.xul menuitem-key-ref.xul
 # these random-if(Android) are due to differences between Android Native & Xul, see bug 732569
 random-if(Android||B2G) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul
 random-if(Android||B2G) fails-if(winWidget) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul
 == textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658
 # accesskeys are not normally displayed on Mac, so skip this test
 skip-if(cocoaWidget) == accesskey.xul accesskey-ref.xul
-fails-if(cocoaWidget) fails-if(B2G) == tree-row-outline-1.xul tree-row-outline-1-ref.xul
+fails-if(cocoaWidget) == tree-row-outline-1.xul tree-row-outline-1-ref.xul
 != tree-row-outline-1.xul tree-row-outline-1-notref.xul
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -3726,19 +3726,19 @@ nsStyleAnimation::Value::SetAndAdoptCSSR
 }
 
 void
 nsStyleAnimation::Value::SetAndAdoptCSSValueListValue(
                            nsCSSValueList *aValueList, Unit aUnit)
 {
   FreeValue();
   NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit");
-  NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aUnit != eUnit_Filter ||
+  NS_ABORT_IF_FALSE(aUnit == eUnit_Shadow || aUnit == eUnit_Filter ||
                     aValueList != nullptr,
-                    "dasharrays and filters may not be null");
+                    "value lists other than shadows and filters may not be null");
   mUnit = aUnit;
   mValue.mCSSValueList = aValueList; // take ownership
 }
 
 void
 nsStyleAnimation::Value::SetTransformValue(nsCSSValueSharedList* aList)
 {
   FreeValue();
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -1706,30 +1706,28 @@ nsTableRowGroupFrame::FindLineContaining
   nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
   NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row");
 
   int32_t rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex();
 
   return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1;
 }
 
-#ifdef IBMBIDI
 NS_IMETHODIMP
 nsTableRowGroupFrame::CheckLineOrder(int32_t                  aLine,
                                      bool                     *aIsReordered,
                                      nsIFrame                 **aFirstVisual,
                                      nsIFrame                 **aLastVisual)
 {
   *aIsReordered = false;
   *aFirstVisual = nullptr;
   *aLastVisual = nullptr;
   return NS_OK;
 }
-#endif // IBMBIDI
-  
+
 NS_IMETHODIMP
 nsTableRowGroupFrame::FindFrameAt(int32_t    aLineNumber, 
                                   nscoord    aX, 
                                   nsIFrame** aFrameFound,
                                   bool*    aXIsBeforeFirstFrame, 
                                   bool*    aXIsAfterLastFrame)
 {
    nsTableFrame* table = nsTableFrame::GetTableFrame(this);
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -240,30 +240,28 @@ public:
     *                               cellframe
     */
   NS_IMETHOD FindFrameAt(int32_t aLineNumber,
                          nscoord aX,
                          nsIFrame** aFrameFound,
                          bool* aXIsBeforeFirstFrame,
                          bool* aXIsAfterLastFrame) MOZ_OVERRIDE;
 
-#ifdef IBMBIDI
    /** Check whether visual and logical order of cell frames within a line are
      * identical. As the layout will reorder them this is always the case
      * @param aLine        - the index of the row relative to the table
      * @param aIsReordered - returns false
      * @param aFirstVisual - if the table is rtl first originating cell frame
      * @param aLastVisual  - if the table is rtl last originating cell frame
      */
 
   NS_IMETHOD CheckLineOrder(int32_t                  aLine,
                             bool                     *aIsReordered,
                             nsIFrame                 **aFirstVisual,
                             nsIFrame                 **aLastVisual) MOZ_OVERRIDE;
-#endif
 
   /** Find the next originating cell frame that originates in the row.    
     * @param aFrame      - cell frame to start with, will return the next cell
     *                      originating in a row
     * @param aLineNumber - the index of the row relative to the table
     */  
   NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) MOZ_OVERRIDE;
 
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -31,20 +31,18 @@
 #include "mozilla/Preferences.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/Attributes.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
-#ifdef IBMBIDI
 #include "nsBidiUtils.h"
 #include "nsBidiPresUtils.h"
-#endif // IBMBIDI
 
 using namespace mozilla;
 
 class nsAccessKeyInfo
 {
 public:
     int32_t mAccesskeyIndex;
     nscoord mBeforeWidth, mAccessWidth, mAccessUnderlineSize, mAccessOffset;
@@ -503,17 +501,16 @@ nsTextBoxFrame::DrawText(nsRenderingCont
 
     aRenderingContext.SetFont(fontMet);
     refContext->SetFont(fontMet);
 
     CalculateUnderline(*refContext);
 
     aRenderingContext.SetColor(aOverrideColor ? *aOverrideColor : StyleColor()->mColor);
 
-#ifdef IBMBIDI
     nsresult rv = NS_ERROR_FAILURE;
 
     if (mState & NS_FRAME_IS_BIDI) {
       presContext->SetBidiEnabled();
       nsBidiLevel level = nsBidiPresUtils::BidiLevelFromStyle(StyleContext());
       if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
           // We let the RenderText function calculate the mnemonic's
           // underline position for us.
@@ -531,19 +528,17 @@ nsTextBoxFrame::DrawText(nsRenderingCont
       else
       {
           rv = nsBidiPresUtils::RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), level,
                                            presContext, aRenderingContext,
                                            *refContext,
                                            aTextRect.x, baseline);
       }
     }
-    if (NS_FAILED(rv) )
-#endif // IBMBIDI
-    {
+    if (NS_FAILED(rv)) {
        aRenderingContext.SetTextRunRTL(false);
 
        if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
            // In the simple (non-BiDi) case, we calculate the mnemonic's
            // underline position by getting the text metric.
            // XXX are attribute values always two byte?
            if (mAccessKeyInfo->mAccesskeyIndex > 0)
                mAccessKeyInfo->mBeforeWidth =
@@ -614,21 +609,19 @@ nsTextBoxFrame::CalculateTitleForWidth(n
     aRenderingContext.SetFont(fm);
 
     // see if the text will completely fit in the width given
     nscoord titleWidth = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
                                                        mTitle.get(), mTitle.Length());
 
     if (titleWidth <= aWidth) {
         mCroppedTitle = mTitle;
-#ifdef IBMBIDI
         if (HasRTLChars(mTitle)) {
             mState |= NS_FRAME_IS_BIDI;
         }
-#endif // IBMBIDI
         return titleWidth;  // fits, done.
     }
 
     const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
     // start with an ellipsis
     mCroppedTitle.Assign(kEllipsis);
 
     // see if the width is even smaller than the ellipsis
@@ -662,21 +655,19 @@ nsTextBoxFrame::CalculateTitleForWidth(n
             for (i = 0; i < length; ++i) {
                 char16_t ch = mTitle.CharAt(i);
                 // still in LTR mode
                 cwidth = aRenderingContext.GetWidth(ch);
                 if (twidth + cwidth > aWidth)
                     break;
 
                 twidth += cwidth;
-#ifdef IBMBIDI
                 if (UCS2_CHAR_IS_BIDI(ch) ) {
                   mState |= NS_FRAME_IS_BIDI;
                 }
-#endif // IBMBIDI
             }
 
             if (i == 0)
                 return titleWidth;
 
             // insert what character we can in.
             nsAutoString title( mTitle );
             title.Truncate(i);
@@ -692,21 +683,19 @@ nsTextBoxFrame::CalculateTitleForWidth(n
             int i;
             for (i=length-1; i >= 0; --i) {
                 char16_t ch = mTitle.CharAt(i);
                 cwidth = aRenderingContext.GetWidth(ch);
                 if (twidth + cwidth > aWidth)
                     break;
 
                 twidth += cwidth;
-#ifdef IBMBIDI
                 if (UCS2_CHAR_IS_BIDI(ch) ) {
                   mState |= NS_FRAME_IS_BIDI;
                 }
-#endif // IBMBIDI
             }
 
             if (i == length-1)
                 return titleWidth;
 
             nsAutoString copy;
             mTitle.Right(copy, length-1-i);
             mCroppedTitle += copy;
@@ -738,36 +727,32 @@ nsTextBoxFrame::CalculateTitleForWidth(n
                 ch = mTitle.CharAt(leftPos);
                 charWidth = aRenderingContext.GetWidth(ch);
                 totalWidth += charWidth;
                 if (totalWidth > aWidth)
                     // greater than the allowable width
                     break;
                 leftString.Insert(ch, leftString.Length());
 
-#ifdef IBMBIDI
                 if (UCS2_CHAR_IS_BIDI(ch))
                     mState |= NS_FRAME_IS_BIDI;
-#endif
 
                 // look at the next character on the right end
                 if (rightPos > leftPos) {
                     // haven't looked at this character yet
                     ch = mTitle.CharAt(rightPos);
                     charWidth = aRenderingContext.GetWidth(ch);
                     totalWidth += charWidth;
                     if (totalWidth > aWidth)
                         // greater than the allowable width
                         break;
                     rightString.Insert(ch, 0);
 
-#ifdef IBMBIDI
                     if (UCS2_CHAR_IS_BIDI(ch))
                         mState |= NS_FRAME_IS_BIDI;
-#endif
                 }
 
                 // look at the next two characters
                 leftPos++;
                 rightPos--;
             }
 
             mCroppedTitle = leftString + kEllipsis + rightString;
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -61,19 +61,17 @@
 #include "nsIScriptableRegion.h"
 #include <algorithm>
 #include "ScrollbarActivity.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #include "nsIWritablePropertyBag2.h"
 #endif
-#ifdef IBMBIDI
 #include "nsBidiUtils.h"
-#endif
 
 using namespace mozilla;
 using namespace mozilla::layout;
 
 // Enumeration function that cancels all the image requests in our cache
 static PLDHashOperator
 CancelImageRequest(const nsAString& aKey,
                    nsTreeImageCacheEntry aEntry, void* aData)
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1915,17 +1915,17 @@ abstract public class BrowserApp extends
     }
 
     /**
      * Add the provided item to the provided menu, which should be
      * the root (mMenu).
      */
     private void addAddonMenuItemToMenu(final Menu menu, final MenuItemInfo info) {
         info.added = true;
-        
+
         final Menu destination;
         if (info.parent == 0) {
             destination = menu;
         } else if (info.parent == GECKO_TOOLS_MENU) {
             MenuItem tools = menu.findItem(R.id.tools);
             destination = tools != null ? tools.getSubMenu() : menu;
         } else {
             MenuItem parent = menu.findItem(info.parent);
@@ -2051,17 +2051,17 @@ abstract public class BrowserApp extends
         }
     }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         // Sets mMenu = menu.
         super.onCreateOptionsMenu(menu);
 
-        // Inform the menu about the action-items bar. 
+        // Inform the menu about the action-items bar.
         if (menu instanceof GeckoMenu &&
             HardwareUtils.isTablet()) {
             ((GeckoMenu) menu).setActionItemBarPresenter(mBrowserToolbar);
         }
 
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.browser_app_menu, mMenu);
 
@@ -2465,17 +2465,17 @@ abstract public class BrowserApp extends
             }
         }
         return super.onKeyLongPress(keyCode, event);
     }
 
     /*
      * If the app has been launched a certain number of times, and we haven't asked for feedback before,
      * open a new tab with about:feedback when launching the app from the icon shortcut.
-     */ 
+     */
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
 
         String action = intent.getAction();
 
         if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 10 && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
             String uri = intent.getDataString();
@@ -2567,17 +2567,17 @@ abstract public class BrowserApp extends
             }
         }).execute();
     }
 
     // HomePager.OnNewTabsListener
     @Override
     public void onNewTabs(String[] urls) {
         final EnumSet<OnUrlOpenListener.Flags> flags = EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB);
- 
+
         for (String url : urls) {
             if (!maybeSwitchToTab(url, flags)) {
                 openUrlAndStopEditing(url, true);
             }
         }
     }
 
     // HomePager.OnUrlOpenListener
--- a/mobile/android/base/gfx/TouchEventHandler.java
+++ b/mobile/android/base/gfx/TouchEventHandler.java
@@ -249,16 +249,18 @@ final class TouchEventHandler implements
 
         MotionEvent event = mEventQueue.poll();
         while (true) {
             // event being null here is valid and represents a block of events
             // that has already been dispatched.
 
             if (event != null) {
                 dispatchEvent(event, allowDefaultAction);
+                event.recycle();
+                event = null;
             }
             if (mEventQueue.isEmpty()) {
                 // we have processed the backlog of events, and are all caught up.
                 // now we can set clear the hold flag and set the dispatch flag so
                 // that the handleEvent() function can do the right thing for all
                 // remaining events in this block (which is still ongoing) without
                 // having to put them in the queue.
                 mHoldInQueue = false;
--- a/mobile/android/base/home/PanelItemView.java
+++ b/mobile/android/base/home/PanelItemView.java
@@ -73,16 +73,19 @@ class PanelItemView extends LinearLayout
                    .into(image);
         }
     }
 
     private static class ArticleItemView extends PanelItemView {
         private ArticleItemView(Context context) {
             super(context, R.layout.panel_article_item);
             setOrientation(LinearLayout.HORIZONTAL);
+
+            final int padding = getResources().getDimensionPixelSize(R.dimen.article_item_view_padding);
+            setPadding(0, padding, 0, padding);
         }
     }
 
     private static class ImageItemView extends PanelItemView {
         private ImageItemView(Context context) {
             super(context, R.layout.panel_image_item);
             setOrientation(LinearLayout.VERTICAL);
         }
--- a/mobile/android/base/home/TabMenuStrip.java
+++ b/mobile/android/base/home/TabMenuStrip.java
@@ -11,191 +11,90 @@ import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-public class TabMenuStrip extends LinearLayout
-                          implements HomePager.Decor,
-                                     View.OnFocusChangeListener {
-    private static final String LOGTAG = "GeckoTabMenuStrip";
+/**
+ * {@code TabMenuStrip} is the view used to display {@code HomePager} tabs
+ * on tablets. See {@code TabMenuStripLayout} for details about how the
+ * tabs are created and updated.
+ */
+public class TabMenuStrip extends HorizontalScrollView
+                          implements HomePager.Decor {
 
-    private HomePager.OnTitleClickListener mOnTitleClickListener;
-    private Drawable mStrip;
-    private View mSelectedView;
+    // Offset between the selected tab title and the edge of the screen,
+    // except for the first and last tab in the tab strip.
+    private static final int TITLE_OFFSET_DIPS = 24;
 
-    // Data associated with the scrolling of the strip drawable.
-    private View toTab;
-    private View fromTab;
-    private float progress;
-
-    // This variable is used to predict the direction of scroll.
-    private float mPrevProgress;
+    private final int titleOffset;
+    private final TabMenuStripLayout layout;
 
     public TabMenuStrip(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabMenuStrip);
-        final int stripResId = a.getResourceId(R.styleable.TabMenuStrip_strip, -1);
-        a.recycle();
+        // Disable the scroll bar.
+        setHorizontalScrollBarEnabled(false);
 
-        if (stripResId != -1) {
-            mStrip = getResources().getDrawable(stripResId);
-        }
+        titleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
 
-        setWillNotDraw(false);
+        layout = new TabMenuStripLayout(context, attrs);
+        addView(layout, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
     }
 
     @Override
     public void onAddPagerView(String title) {
-        final TextView button = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.tab_menu_strip, this, false);
-        button.setText(title.toUpperCase());
-
-        addView(button);
-        button.setOnClickListener(new ViewClickListener(getChildCount() - 1));
-        button.setOnFocusChangeListener(this);
+        layout.onAddPagerView(title);
     }
 
     @Override
     public void removeAllPagerViews() {
-        removeAllViews();
+        layout.removeAllViews();
     }
 
     @Override
     public void onPageSelected(final int position) {
-        mSelectedView = getChildAt(position);
-
-        // Callback to measure and draw the strip after the view is visible.
-        ViewTreeObserver vto = mSelectedView.getViewTreeObserver();
-        if (vto.isAlive()) {
-            vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-                @Override
-                public void onGlobalLayout() {
-                    mSelectedView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-
-                    if (mStrip != null) {
-                        mStrip.setBounds(mSelectedView.getLeft(),
-                                         mSelectedView.getTop(),
-                                         mSelectedView.getRight(),
-                                         mSelectedView.getBottom());
-                    }
-
-                    mPrevProgress = position;
-                }
-            });
-        }
+        layout.onPageSelected(position);
     }
 
-    // Page scroll animates the drawable and its bounds from the previous to next child view.
     @Override
     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-        if (mStrip == null) {
-            return;
-        }
+        layout.onPageScrolled(position, positionOffset, positionOffsetPixels);
 
-        setScrollingData(position, positionOffset);
-
-        if (fromTab == null || toTab == null) {
+        final View selectedTitle = layout.getChildAt(position);
+        if (selectedTitle == null) {
             return;
         }
 
-        final int fromTabLeft =  fromTab.getLeft();
-        final int fromTabRight = fromTab.getRight();
-
-        final int toTabLeft =  toTab.getLeft();
-        final int toTabRight = toTab.getRight();
-
-        mStrip.setBounds((int) (fromTabLeft + ((toTabLeft - fromTabLeft) * progress)),
-                         0,
-                         (int) (fromTabRight + ((toTabRight - fromTabRight) * progress)),
-                         getHeight());
-        invalidate();
-    }
+        final int selectedTitleOffset = (int) (positionOffset * selectedTitle.getWidth());
 
-    /*
-     * position + positionOffset goes from 0 to 2 as we scroll from page 1 to 3.
-     * Normalized progress is relative to the the direction the page is being scrolled towards.
-     * For this, we maintain direction of scroll with a state, and the child view we are moving towards and away from.
-     */
-    private void setScrollingData(int position, float positionOffset) {
-        if (position >= getChildCount() - 1) {
-            return;
-        }
-
-        final float currProgress = position + positionOffset;
-
-        if (mPrevProgress > currProgress) {
-            toTab = getChildAt(position);
-            fromTab = getChildAt(position + 1);
-            progress = 1 - positionOffset;
-        } else {
-            toTab = getChildAt(position + 1);
-            fromTab = getChildAt(position);
-            progress = positionOffset;
+        int titleLeft = selectedTitle.getLeft() + selectedTitleOffset;
+        if (position > 0) {
+            titleLeft -= titleOffset;
         }
 
-        mPrevProgress = currProgress;
-    }
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (mStrip != null) {
-            mStrip.draw(canvas);
-        }
-    }
-
-    @Override
-    public void onFocusChange(View v, boolean hasFocus) {
-        if (v == this && hasFocus && getChildCount() > 0) {
-            mSelectedView.requestFocus();
-            return;
+        int titleRight = selectedTitle.getRight() + selectedTitleOffset;
+        if (position < layout.getChildCount() - 1) {
+            titleRight += titleOffset;
         }
 
-        if (!hasFocus) {
-            return;
-        }
-
-        int i = 0;
-        final int numTabs = getChildCount();
-
-        while (i < numTabs) {
-            View view = getChildAt(i);
-            if (view == v) {
-                view.requestFocus();
-                if (isShown()) {
-                    // A view is focused so send an event to announce the menu strip state.
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-                }
-                break;
-            }
-
-            i++;
+        final int scrollX = getScrollX();
+        if (titleLeft < scrollX) {
+            // Tab strip overflows to the left.
+            scrollTo(titleLeft, 0);
+        } else if (titleRight > scrollX + getWidth()) {
+            // Tab strip overflows to the right.
+            scrollTo(titleRight - getWidth(), 0);
         }
     }
 
     @Override
     public void setOnTitleClickListener(HomePager.OnTitleClickListener onTitleClickListener) {
-        mOnTitleClickListener = onTitleClickListener;
-    }
-
-    private class ViewClickListener implements OnClickListener {
-        private final int mIndex;
-
-        public ViewClickListener(int index) {
-            mIndex = index;
-        }
-
-        @Override
-        public void onClick(View view) {
-            if (mOnTitleClickListener != null) {
-                mOnTitleClickListener.onTitleClicked(mIndex);
-            }
-        }
+        layout.setOnTitleClickListener(onTitleClickListener);
     }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/home/TabMenuStripLayout.java
@@ -0,0 +1,194 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.home;
+
+import org.mozilla.gecko.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * {@code TabMenuStripLayout} is the view that draws the {@code HomePager}
+ * tabs that are displayed in {@code TabMenuStrip}.
+ */
+class TabMenuStripLayout extends LinearLayout
+                         implements View.OnFocusChangeListener {
+
+    private HomePager.OnTitleClickListener onTitleClickListener;
+    private Drawable strip;
+    private View selectedView;
+
+    // Data associated with the scrolling of the strip drawable.
+    private View toTab;
+    private View fromTab;
+    private float progress;
+
+    // This variable is used to predict the direction of scroll.
+    private float prevProgress;
+
+    TabMenuStripLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabMenuStrip);
+        final int stripResId = a.getResourceId(R.styleable.TabMenuStrip_strip, -1);
+        a.recycle();
+
+        if (stripResId != -1) {
+            strip = getResources().getDrawable(stripResId);
+        }
+
+        setWillNotDraw(false);
+    }
+
+    void onAddPagerView(String title) {
+        final TextView button = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.tab_menu_strip, this, false);
+        button.setText(title.toUpperCase());
+
+        addView(button);
+        button.setOnClickListener(new ViewClickListener(getChildCount() - 1));
+        button.setOnFocusChangeListener(this);
+    }
+
+    void onPageSelected(final int position) {
+        selectedView = getChildAt(position);
+
+        // Callback to measure and draw the strip after the view is visible.
+        ViewTreeObserver vto = selectedView.getViewTreeObserver();
+        if (vto.isAlive()) {
+            vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+                @Override
+                public void onGlobalLayout() {
+                    selectedView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+
+                    if (strip != null) {
+                        strip.setBounds(selectedView.getLeft(),
+                                        selectedView.getTop(),
+                                        selectedView.getRight(),
+                                        selectedView.getBottom());
+                    }
+
+                    prevProgress = position;
+                }
+            });
+        }
+    }
+
+    // Page scroll animates the drawable and its bounds from the previous to next child view.
+    void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+        if (strip == null) {
+            return;
+        }
+
+        setScrollingData(position, positionOffset);
+
+        if (fromTab == null || toTab == null) {
+            return;
+        }
+
+        final int fromTabLeft =  fromTab.getLeft();
+        final int fromTabRight = fromTab.getRight();
+
+        final int toTabLeft =  toTab.getLeft();
+        final int toTabRight = toTab.getRight();
+
+        strip.setBounds((int) (fromTabLeft + ((toTabLeft - fromTabLeft) * progress)),
+                         0,
+                         (int) (fromTabRight + ((toTabRight - fromTabRight) * progress)),
+                         getHeight());
+        invalidate();
+    }
+
+    /*
+     * position + positionOffset goes from 0 to 2 as we scroll from page 1 to 3.
+     * Normalized progress is relative to the the direction the page is being scrolled towards.
+     * For this, we maintain direction of scroll with a state, and the child view we are moving towards and away from.
+     */
+    void setScrollingData(int position, float positionOffset) {
+        if (position >= getChildCount() - 1) {
+            return;
+        }
+
+        final float currProgress = position + positionOffset;
+
+        if (prevProgress > currProgress) {
+            toTab = getChildAt(position);
+            fromTab = getChildAt(position + 1);
+            progress = 1 - positionOffset;
+        } else {
+            toTab = getChildAt(position + 1);
+            fromTab = getChildAt(position);
+            progress = positionOffset;
+        }
+
+        prevProgress = currProgress;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (strip != null) {
+            strip.draw(canvas);
+        }
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        if (v == this && hasFocus && getChildCount() > 0) {
+            selectedView.requestFocus();
+            return;
+        }
+
+        if (!hasFocus) {
+            return;
+        }
+
+        int i = 0;
+        final int numTabs = getChildCount();
+
+        while (i < numTabs) {
+            View view = getChildAt(i);
+            if (view == v) {
+                view.requestFocus();
+                if (isShown()) {
+                    // A view is focused so send an event to announce the menu strip state.
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                }
+                break;
+            }
+
+            i++;
+        }
+    }
+
+    void setOnTitleClickListener(HomePager.OnTitleClickListener onTitleClickListener) {
+        this.onTitleClickListener = onTitleClickListener;
+    }
+
+    private class ViewClickListener implements OnClickListener {
+        private final int mIndex;
+
+        public ViewClickListener(int index) {
+            mIndex = index;
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (onTitleClickListener != null) {
+                onTitleClickListener.onTitleClicked(mIndex);
+            }
+        }
+    }
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -274,16 +274,17 @@ gbjar.sources += [
     'home/ReadingListPanel.java',
     'home/ReadingListRow.java',
     'home/SearchEngine.java',
     'home/SearchEngineRow.java',
     'home/SearchLoader.java',
     'home/SimpleCursorLoader.java',
     'home/SuggestClient.java',
     'home/TabMenuStrip.java',
+    'home/TabMenuStripLayout.java',
     'home/TopSitesGridItemView.java',
     'home/TopSitesGridView.java',
     'home/TopSitesPanel.java',
     'home/TopSitesThumbnailView.java',
     'home/TwoLinePageRow.java',
     'InputMethods.java',
     'JavaAddonManager.java',
     'LightweightTheme.java',
--- a/mobile/android/base/resources/layout/panel_article_item.xml
+++ b/mobile/android/base/resources/layout/panel_article_item.xml
@@ -3,29 +3,24 @@
    - 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/. -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <ImageView android:id="@+id/image"
                android:layout_width="54dp"
                android:layout_height="44dp"
-               android:layout_marginTop="10dip"
-               android:layout_marginBottom="10dip"
                android:layout_marginLeft="10dip"
                android:scaleType="centerCrop"/>
 
     <LinearLayout android:id="@+id/title_desc_container"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
-                  android:paddingTop="15dip"
-                  android:paddingBottom="15dip"
                   android:paddingLeft="10dip"
                   android:paddingRight="10dip"
-                  android:minHeight="@dimen/page_row_height"
                   android:gravity="center_vertical"
                   android:orientation="vertical">
 
         <TextView android:id="@+id/title"
                   style="@style/Widget.PanelItemView.Title"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"/>
 
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -102,9 +102,12 @@
     <dimen name="home_banner_height">72dp</dimen>
 
     <!-- Icon Grid -->
     <dimen name="icongrid_columnwidth">128dp</dimen>
     <dimen name="icongrid_padding">16dp</dimen>
 
     <!-- PanelGridView dimensions -->
     <dimen name="panel_grid_view_column_width">150dp</dimen>
+
+    <!-- ArticleItemView dimensions -->
+    <dimen name="article_item_view_padding">15dp</dimen>
 </resources>
--- a/mobile/android/base/tests/BaseTest.java
+++ b/mobile/android/base/tests/BaseTest.java
@@ -433,19 +433,18 @@ abstract class BaseTest extends BaseRobo
     public void selectMenuItemByPath(String[] listItems) {
         int listLength = listItems.length;
         if (listLength > 0) {
             selectMenuItem(listItems[0]);
         }
         if (listLength > 1) {
             for (int i = 1; i < listLength; i++) {
                 String itemName = "^" + listItems[i] + "$";
-                if (!waitForPreferencesText(itemName)) {
-                    mSolo.scrollDown();
-                }
+                mAsserter.ok(waitForPreferencesText(itemName), "Waiting for and scrolling once to find item " + itemName, itemName + " found");
+                mAsserter.ok(waitForEnabledText(itemName), "Waiting for enabled text " + itemName, itemName + " option is present and enabled");
                 mSolo.clickOnText(itemName);
             }
         }
     }
 
     public final void selectMenuItem(String menuItemName) {
         // build the item name ready to be used
         String itemName = "^" + menuItemName + "$";
--- a/mobile/android/base/tests/MotionEventHelper.java
+++ b/mobile/android/base/tests/MotionEventHelper.java
@@ -21,39 +21,54 @@ class MotionEventHelper {
         mSurfaceOffsetY = surfaceOffsetY;
         Log.i(LOGTAG, "Initialized using offset (" + mSurfaceOffsetX + "," + mSurfaceOffsetY + ")");
     }
 
     public long down(float x, float y) {
         Log.d(LOGTAG, "Triggering down at (" + x + "," + y + ")");
         long downTime = SystemClock.uptimeMillis();
         MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, mSurfaceOffsetX + x, mSurfaceOffsetY + y, 0);
-        mInstrumentation.sendPointerSync(event);
+        try {
+            mInstrumentation.sendPointerSync(event);
+        } finally {
+            event.recycle();
+            event = null;
+        }
         return downTime;
     }
 
     public long move(long downTime, float x, float y) {
         return move(downTime, SystemClock.uptimeMillis(), x, y);
     }
 
     public long move(long downTime, long moveTime, float x, float y) {
         Log.d(LOGTAG, "Triggering move to (" + x + "," + y + ")");
         MotionEvent event = MotionEvent.obtain(downTime, moveTime, MotionEvent.ACTION_MOVE, mSurfaceOffsetX + x, mSurfaceOffsetY + y, 0);
-        mInstrumentation.sendPointerSync(event);
+        try {
+            mInstrumentation.sendPointerSync(event);
+        } finally {
+            event.recycle();
+            event = null;
+        }
         return downTime;
     }
 
     public long up(long downTime, float x, float y) {
         return up(downTime, SystemClock.uptimeMillis(), x, y);
     }
 
     public long up(long downTime, long upTime, float x, float y) {
         Log.d(LOGTAG, "Triggering up at (" + x + "," + y + ")");
         MotionEvent event = MotionEvent.obtain(downTime, upTime, MotionEvent.ACTION_UP, mSurfaceOffsetX + x, mSurfaceOffsetY + y, 0);
-        mInstrumentation.sendPointerSync(event);
+        try {
+            mInstrumentation.sendPointerSync(event);
+        } finally {
+            event.recycle();
+            event = null;
+        }
         return -1L;
     }
 
     public Thread dragAsync(final float startX, final float startY, final float endX, final float endY, final long durationMillis) {
         Thread t = new Thread() {
             @Override
             public void run() {
                 int numEvents = (int)(durationMillis * DRAG_EVENTS_PER_SECOND / 1000);
--- a/mobile/android/base/tests/MotionEventReplayer.java
+++ b/mobile/android/base/tests/MotionEventReplayer.java
@@ -198,18 +198,23 @@ class MotionEventReplayer {
                             long.class, long.class, int.class, int.class, pointerIds.getClass(),
                             pointerData.getClass(), int.class, float.class, float.class,
                             int.class, int.class);
                     }
                     event = (MotionEvent)mObtainNanoMethod.invoke(null, downTime, eventTime,
                             eventTime * 1000000, action, pointerCount, pointerIds, (float[])pointerData,
                             metaState, xPrecision, yPrecision, deviceId, edgeFlags);
                 }
-                Log.v(LOGTAG, "Injecting " + event.toString());
-                mInstrumentation.sendPointerSync(event);
+                try {
+                    Log.v(LOGTAG, "Injecting " + event.toString());
+                    mInstrumentation.sendPointerSync(event);
+                } finally {
+                    event.recycle();
+                    event = null;
+                }
 
                 eventProperties.clear();
             }
         } finally {
             br.close();
         }
     }
 }
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -45,16 +45,17 @@ import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MenuInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
@@ -185,17 +186,20 @@ public class BrowserToolbar extends Them
         final Resources res = getResources();
         urlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left);
         defaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset);
         urlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout);
         urlBarEntry = findViewById(R.id.url_bar_entry);
         urlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout);
 
         urlBarEntryDefaultLayoutParams = (RelativeLayout.LayoutParams) urlBarEntry.getLayoutParams();
-        urlBarEntryShrunkenLayoutParams = new RelativeLayout.LayoutParams(urlBarEntryDefaultLayoutParams);
+        // API level 19 adds a RelativeLayout.LayoutParams copy constructor, so we explicitly cast
+        // to ViewGroup.MarginLayoutParams to ensure consistency across platforms.
+        urlBarEntryShrunkenLayoutParams = new RelativeLayout.LayoutParams(
+                (ViewGroup.MarginLayoutParams) urlBarEntryDefaultLayoutParams);
         urlBarEntryShrunkenLayoutParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.edit_layout);
         urlBarEntryShrunkenLayoutParams.rightMargin = 0;
 
         // This will clip the translating edge's image at 60% of its width
         urlBarTranslatingEdge = (ImageView) findViewById(R.id.url_bar_translating_edge);
         if (urlBarTranslatingEdge != null) {
             urlBarTranslatingEdge.getDrawable().setLevel(6000);
         }
@@ -392,17 +396,17 @@ public class BrowserToolbar extends Them
     }
 
     public void refresh() {
         urlDisplayLayout.dismissSiteIdentityPopup();
     }
 
     public boolean onBackPressed() {
         if (isEditing()) {
-            stopEditing();
+            cancelEdit();
             return true;
         }
 
         return urlDisplayLayout.dismissSiteIdentityPopup();
     }
 
     public boolean onKey(int keyCode, KeyEvent event) {
         if (event.getAction() != KeyEvent.ACTION_DOWN) {
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1712,20 +1712,16 @@ pref("profile.migration_directory", "");
 // if -1, we never think your profile is defunct
 // and users will never see the remigrate UI.
 pref("profile.seconds_until_defunct", -1);
 // We can show it anytime from menus
 pref("profile.manage_only_at_launch", false);
 
 pref("prefs.converted-to-utf8",false);
 
-// --------------------------------------------------
-// IBMBIDI
-// --------------------------------------------------
-//
 // ------------------
 //  Text Direction
 // ------------------
 // 1 = directionLTRBidi *
 // 2 = directionRTLBidi
 pref("bidi.direction", 1);
 // ------------------
 //  Text Type
--- a/testing/mozbase/mozversion/setup.py
+++ b/testing/mozbase/mozversion/setup.py
@@ -1,15 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from setuptools import setup
 
-PACKAGE_VERSION = '0.3'
+PACKAGE_VERSION = '0.4'
 
 dependencies = ['mozdevice >= 0.29',
                 'mozfile >= 1.0',
                 'mozlog >= 1.5']
 
 setup(name='mozversion',
       version=PACKAGE_VERSION,
       description='Library to get version information for applications',
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -24,16 +24,17 @@
 #include "nsIX509Cert.h"
 #include "nsIX509CertDB.h"
 #include "nsIX509CertList.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/LoadContext.h"
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsNetCID.h"
 #include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
@@ -762,16 +763,23 @@ PendingLookup::SendRemoteQueryInternal()
   nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = uploadChannel->ExplicitSetUploadStream(sstream,
     NS_LITERAL_CSTRING("application/octet-stream"), serialized.size(),
     NS_LITERAL_CSTRING("POST"), false);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Set the Safebrowsing cookie jar, so that the regular Google cookie is not
+  // sent with this request. See bug 897516.
+  nsCOMPtr<nsIInterfaceRequestor> loadContext =
+    new mozilla::LoadContext(NECKO_SAFEBROWSING_APP_ID);
+  rv = channel->SetNotificationCallbacks(loadContext);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   rv = channel->AsyncOpen(this, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIStreamListener
--- a/toolkit/components/downloads/moz.build
+++ b/toolkit/components/downloads/moz.build
@@ -32,17 +32,19 @@ SOURCES += [
 
 if CONFIG['MOZ_URL_CLASSIFIER']:
     UNIFIED_SOURCES += [
         'ApplicationReputation.cpp',
         'csd.pb.cc'
     ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
-    UNIFIED_SOURCES += [
+    # Can't build unified because we need CreateEvent which some IPC code
+    # included in LoadContext ends up undefining.
+    SOURCES += [
         'nsDownloadScanner.cpp',
     ]
 
 # XXX - Until Suite builds off XULRunner we can't guarantee our implementation
 # of nsIDownloadManagerUI overrides toolkit's.
 if not CONFIG['MOZ_SUITE']:
     EXTRA_COMPONENTS += [
         'nsDownloadManagerUI.js',
@@ -50,11 +52,12 @@ if not CONFIG['MOZ_SUITE']:
     ]
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'toolkitcomps'
 
 LOCAL_INCLUDES += [
     '../protobuf',
+    '/ipc/chromium/src'
 ]
 
 DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
--- a/widget/windows/nsDragService.cpp
+++ b/widget/windows/nsDragService.cpp
@@ -586,13 +586,20 @@ nsDragService::IsCollectionObject(IDataO
 //
 // Override the default to make sure that we release the data object
 // when the drag ends. It seems that OLE doesn't like to let apps quit
 // w/out crashing when we're still holding onto their data
 //
 NS_IMETHODIMP
 nsDragService::EndDragSession(bool aDoneDrag)
 {
+  // Bug 100180: If we've got mouse events captured, make sure we release it -
+  // that way, if we happen to call EndDragSession before diving into a nested
+  // event loop, we can still respond to mouse events.
+  if (::GetCapture()) {
+    ::ReleaseCapture();
+  }
+
   nsBaseDragService::EndDragSession(aDoneDrag);
   NS_IF_RELEASE(mDataObject);
 
   return NS_OK;
 }