Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Thu, 30 Jan 2014 20:28:12 -0800
changeset 166187 5e0a22097bff081b5e5520d8e75cf0579e626329
parent 166186 1c6a696b65a77a1553e0aa5ef6c9afb0a70b77e2 (current diff)
parent 166109 6f544aa66c1ac8fb80ccef6c4b4f0d25086e3b61 (diff)
child 166188 93d3ecdfad07dfb320f1a209ed3b9e7caa9b0a48
push id39146
push usercbook@mozilla.com
push dateFri, 31 Jan 2014 12:04:07 +0000
treeherdermozilla-inbound@fe5449bbf9bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
5e0a22097bff / 29.0a1 / 20140131030203 / files
nightly linux64
5e0a22097bff / 29.0a1 / 20140131030203 / files
nightly mac
5e0a22097bff / 29.0a1 / 20140131030203 / files
nightly win32
5e0a22097bff / 29.0a1 / 20140131030203 / files
nightly win64
5e0a22097bff / 29.0a1 / 20140131030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
dom/interfaces/base/nsIDOMPkcs11.idl
mobile/android/app/mobile.js
mobile/android/base/BrowserApp.java
mobile/android/base/moz.build
toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mk
toolkit/crashreporter/google-breakpad/src/client/windows/handler/objs.mk
toolkit/crashreporter/google-breakpad/src/client/windows/sender/objs.mk
toolkit/crashreporter/google-breakpad/src/common/windows/objs.mk
new file mode 100644
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,1 @@
+BasedOnStyle: Mozilla
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -1,2 +1,3 @@
 \mfbt/
 \js/
+\media/
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -1912,18 +1912,20 @@ DocAccessible::UpdateTreeInternal(Access
     if (parent)
       parent->RemoveChild(aChild);
 
     UncacheChildrenInSubtree(aChild);
   }
 
   // XXX: do we really want to send focus to focused DOM node not taking into
   // account active item?
-  if (focusedAcc)
+  if (focusedAcc) {
     FocusMgr()->DispatchFocusEvent(this, focusedAcc);
+    SelectionMgr()->SetControlSelectionListener(focusedAcc->GetNode()->AsElement());
+  }
 
   return updateFlags;
 }
 
 void
 DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
                                       Accessible** aFocusedAcc)
 {
--- a/accessible/src/jsat/EventManager.jsm
+++ b/accessible/src/jsat/EventManager.jsm
@@ -52,16 +52,17 @@ this.EventManager.prototype = {
 
         this._started = true;
 
         AccessibilityEventObserver.addListener(this);
 
         this.webProgress.addProgressListener(this,
           (Ci.nsIWebProgress.NOTIFY_STATE_ALL |
            Ci.nsIWebProgress.NOTIFY_LOCATION));
+        this.addEventListener('wheel', this, true);
         this.addEventListener('scroll', this, true);
         this.addEventListener('resize', this, true);
       }
       this.present(Presentation.tabStateChanged(null, 'newtab'));
 
     } catch (x) {
       Logger.logException(x, 'Failed to start EventManager');
     }
@@ -72,28 +73,46 @@ this.EventManager.prototype = {
   stop: function stop() {
     if (!this._started) {
       return;
     }
     Logger.debug('EventManager.stop');
     AccessibilityEventObserver.removeListener(this);
     try {
       this.webProgress.removeProgressListener(this);
+      this.removeEventListener('wheel', this, true);
       this.removeEventListener('scroll', this, true);
       this.removeEventListener('resize', this, true);
     } catch (x) {
       // contentScope is dead.
     } finally {
       this._started = false;
     }
   },
 
   handleEvent: function handleEvent(aEvent) {
     try {
       switch (aEvent.type) {
+      case 'wheel':
+      {
+        let attempts = 0;
+        let vc = Utils.getVirtualCursor(this.contentScope.content.document);
+        let intervalId = this.contentScope.content.setInterval(() => {
+          if (!Utils.isAliveAndVisible(vc.position, true)) {
+            this.contentScope.content.clearInterval(intervalId);
+            let delta = aEvent.deltaX || aEvent.deltaY;
+            this.contentScope.content.setTimeout(() => {
+              vc[delta > 0 ? 'moveNext' : 'movePrevious'](TraversalRules.SimpleOnScreen);
+            }, 100);
+          } else if (++attempts > 5) {
+            this.contentScope.content.clearInterval(intervalId);
+          }
+        }, 150);
+        break;
+      }
       case 'scroll':
       case 'resize':
       {
         // the target could be an element, document or window
         let window = null;
         if (aEvent.target instanceof Ci.nsIDOMWindow)
           window = aEvent.target;
         else if (aEvent.target instanceof Ci.nsIDOMDocument)
@@ -250,21 +269,30 @@ this.EventManager.prototype = {
         break;
       }
       case Events.FOCUS:
       {
         // Put vc where the focus is at
         let acc = aEvent.accessible;
         let doc = aEvent.accessibleDocument;
         if (acc.role != Roles.DOCUMENT && doc.role != Roles.CHROME_WINDOW) {
+          this.contentScope.content.clearTimeout(this._autoMove);
           let vc = Utils.getVirtualCursor(doc);
           vc.moveNext(TraversalRules.Simple, acc, true);
         }
         break;
       }
+      case Events.DOCUMENT_LOAD_COMPLETE:
+      {
+        this._autoMove = this.contentScope.content.setTimeout(() => {
+          Utils.getVirtualCursor(aEvent.accessibleDocument)
+            .moveNext(TraversalRules.Simple, aEvent.accessible, true);
+        }, 500);
+        break;
+      }
     }
   },
 
   _handleText: function _handleText(aEvent, aLiveRegion, aIsPolite) {
     let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
     let isInserted = event.isInserted;
     let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
 
--- a/accessible/src/jsat/OutputGenerator.jsm
+++ b/accessible/src/jsat/OutputGenerator.jsm
@@ -313,17 +313,18 @@ this.OutputGenerator = {
     'gridcell': NAME_FROM_SUBTREE_RULE,
     'check rich option': NAME_FROM_SUBTREE_RULE,
     'term': NAME_FROM_SUBTREE_RULE,
     'definition': NAME_FROM_SUBTREE_RULE,
     'key': NAME_FROM_SUBTREE_RULE,
     'image map': INCLUDE_DESC,
     'option': INCLUDE_DESC,
     'listbox': INCLUDE_DESC,
-    'definitionlist': INCLUDE_DESC | INCLUDE_NAME},
+    'definitionlist': INCLUDE_DESC | INCLUDE_NAME,
+    'dialog': INCLUDE_DESC | INCLUDE_NAME },
 
   objectOutputFunctions: {
     _generateBaseOutput: function _generateBaseOutput(aAccessible, aRoleStr, aState, aFlags) {
       let output = [];
 
       if (aFlags & INCLUDE_DESC) {
         let desc = this._getLocalizedState(aState);
         let roleStr = this._getLocalizedRole(aRoleStr);
--- a/accessible/src/jsat/TraversalRules.jsm
+++ b/accessible/src/jsat/TraversalRules.jsm
@@ -17,36 +17,32 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Filters',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'States',
   'resource://gre/modules/accessibility/Constants.jsm');
 
 let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images');
 
-function BaseTraversalRule(aRoles, aMatchFunc) {
+function BaseTraversalRule(aRoles, aMatchFunc, aPreFilter) {
   this._explicitMatchRoles = new Set(aRoles);
   this._matchRoles = aRoles;
   if (aRoles.indexOf(Roles.LABEL) < 0) {
     this._matchRoles.push(Roles.LABEL);
   }
   this._matchFunc = aMatchFunc || function (acc) { return Filters.MATCH; };
+  this.preFilter = aPreFilter || gSimplePreFilter;
 }
 
 BaseTraversalRule.prototype = {
     getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
       aRules.value = this._matchRoles;
       return aRules.value.length;
     },
 
-    preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
-    Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE |
-    Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN |
-    Ci.nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT,
-
     match: function BaseTraversalRule_match(aAccessible)
     {
       let role = aAccessible.role;
       if (role == Roles.INTERNAL_FRAME) {
         return (Utils.getMessageManager(aAccessible.DOMNode)) ?
           Filters.MATCH  | Filters.IGNORE_SUBTREE : Filters.IGNORE;
       }
 
@@ -91,69 +87,81 @@ var gSimpleTraversalRoles =
    Roles.HEADER,
    Roles.HEADING,
    Roles.SLIDER,
    Roles.SPINBUTTON,
    Roles.OPTION,
    // Used for traversing in to child OOP frames.
    Roles.INTERNAL_FRAME];
 
-this.TraversalRules = {
-  Simple: new BaseTraversalRule(
-    gSimpleTraversalRoles,
-    function Simple_match(aAccessible) {
-      function hasZeroOrSingleChildDescendants () {
-        for (let acc = aAccessible; acc.childCount > 0; acc = acc.firstChild) {
-          if (acc.childCount > 1) {
-            return false;
-          }
-        }
-
-        return true;
-      }
-
-      switch (aAccessible.role) {
-      case Roles.COMBOBOX:
-        // We don't want to ignore the subtree because this is often
-        // where the list box hangs out.
-        return Filters.MATCH;
-      case Roles.TEXT_LEAF:
-        {
-          // Nameless text leaves are boring, skip them.
-          let name = aAccessible.name;
-          if (name && name.trim())
-            return Filters.MATCH;
-          else
-            return Filters.IGNORE;
-        }
-      case Roles.STATICTEXT:
-        {
-          let parent = aAccessible.parent;
-          // Ignore prefix static text in list items. They are typically bullets or numbers.
-          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:
-        return hasZeroOrSingleChildDescendants() ?
-          (Filters.MATCH | Filters.IGNORE_SUBTREE) : (Filters.IGNORE);
-      default:
-        // Ignore the subtree, if there is one. So that we don't land on
-        // the same content that was already presented by its parent.
-        return Filters.MATCH |
-          Filters.IGNORE_SUBTREE;
+var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
+  function hasZeroOrSingleChildDescendants () {
+    for (let acc = aAccessible; acc.childCount > 0; acc = acc.firstChild) {
+      if (acc.childCount > 1) {
+        return false;
       }
     }
-  ),
+
+    return true;
+  }
+
+  switch (aAccessible.role) {
+  case Roles.COMBOBOX:
+    // We don't want to ignore the subtree because this is often
+    // where the list box hangs out.
+    return Filters.MATCH;
+  case Roles.TEXT_LEAF:
+    {
+      // Nameless text leaves are boring, skip them.
+      let name = aAccessible.name;
+      if (name && name.trim())
+        return Filters.MATCH;
+      else
+        return Filters.IGNORE;
+    }
+  case Roles.STATICTEXT:
+    {
+      let parent = aAccessible.parent;
+      // Ignore prefix static text in list items. They are typically bullets or numbers.
+      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:
+    return hasZeroOrSingleChildDescendants() ?
+      (Filters.MATCH | Filters.IGNORE_SUBTREE) : (Filters.IGNORE);
+  default:
+    // Ignore the subtree, if there is one. So that we don't land on
+    // the same content that was already presented by its parent.
+    return Filters.MATCH |
+      Filters.IGNORE_SUBTREE;
+  }
+};
+
+var gSimplePreFilter = Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
+  Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE |
+  Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN |
+  Ci.nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT;
+
+this.TraversalRules = {
+  Simple: new BaseTraversalRule(gSimpleTraversalRoles, gSimpleMatchFunc),
+
+  SimpleOnScreen: new BaseTraversalRule(
+    gSimpleTraversalRoles, gSimpleMatchFunc,
+    Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
+      Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE |
+      Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN |
+      Ci.nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT |
+      Ci.nsIAccessibleTraversalRule.PREFILTER_OFFSCREEN),
 
   Anchor: new BaseTraversalRule(
     [Roles.LINK],
     function Anchor_match(aAccessible)
     {
       // We want to ignore links, only focus named anchors.
       if (Utils.getState(aAccessible).contains(States.LINKED)) {
         return Filters.IGNORE;
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -264,24 +264,25 @@ this.Utils = {
       let hidden = Utils.getAttributes(acc).hidden;
       if (hidden && JSON.parse(hidden)) {
         return true;
       }
     }
     return false;
   },
 
-  isAliveAndVisible: function isAliveAndVisible(aAccessible) {
+  isAliveAndVisible: function isAliveAndVisible(aAccessible, aIsOnScreen) {
     if (!aAccessible) {
       return false;
     }
 
     try {
       let state = this.getState(aAccessible);
       if (state.contains(States.DEFUNCT) || state.contains(States.INVISIBLE) ||
+          (aIsOnScreen && state.contains(States.OFFSCREEN)) ||
           Utils.inHiddenSubtree(aAccessible)) {
         return false;
       }
     } catch (x) {
       return false;
     }
 
     return true;
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -573,16 +573,17 @@ pref("device.storage.enabled", true);
 // Enable pre-installed applications
 pref("dom.webapps.useCurrentProfile", true);
 
 // Enable system message
 pref("dom.sysmsg.enabled", true);
 pref("media.plugins.enabled", false);
 pref("media.omx.enabled", true);
 pref("media.rtsp.enabled", true);
+pref("media.rtsp.video.enabled", false);
 
 // Disable printing (particularly, window.print())
 pref("dom.disable_window_print", true);
 
 // Disable window.showModalDialog
 pref("dom.disable_window_showModalDialog", true);
 
 // Enable new experimental html forms
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -7,23 +7,23 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -6,20 +6,20 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="317f25e0a4cb3e8e86e2b76c37a14081372f0307">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -7,23 +7,23 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "621040ee199cdde3bdd2aeaf2a016ff7806875bb", 
+    "revision": "7240a5ab28eff83c26891c7a9141613149f226e9", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -6,22 +6,22 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -5,17 +5,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -7,22 +7,22 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -6,22 +6,22 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="575fdbf046e966a5915b1f1e800e5d6ad0ea14c0"/>
   <project name="platform/development" path="development" revision="b1025ec93beeb480caaf3049d171283c3846461d"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -6,20 +6,20 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="317f25e0a4cb3e8e86e2b76c37a14081372f0307">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -6,22 +6,22 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0bc0e703df197d46dfffb9ac65cb85d2e3e10c4a"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -28,16 +28,19 @@ pref("browser.hiddenWindowChromeURL", "c
 pref("extensions.logging.enabled", false);
 
 // Disables strict compatibility, making addons compatible-by-default.
 pref("extensions.strictCompatibility", false);
 
 // Specifies a minimum maxVersion an addon needs to say it's compatible with
 // for it to be compatible by default.
 pref("extensions.minCompatibleAppVersion", "4.0");
+// Temporary preference to forcibly make themes more safe with Australis even if
+// extensions.checkCompatibility=false has been set.
+pref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "29.0a1");
 
 // Preferences for AMO integration
 pref("extensions.getAddons.cache.enabled", true);
 pref("extensions.getAddons.maxResults", 15);
 pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%");
 pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
 pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
 pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=firefox");
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -225,16 +225,18 @@ function init() {
     show("remote");
     wrapper.init();
   } else {
     // Check if we have a local account
     fxAccounts.getSignedInUser().then(user => {
       if (user) {
         show("stage");
         show("manage");
+        let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
+        document.title = sb.GetStringFromName("manage.pageTitle");
       } else {
         show("stage");
         show("intro");
         // load the remote frame in the background
         wrapper.init();
       }
     });
   }
--- a/browser/base/content/aboutaccounts/aboutaccounts.xhtml
+++ b/browser/base/content/aboutaccounts/aboutaccounts.xhtml
@@ -10,17 +10,17 @@
   <!ENTITY % aboutAccountsDTD SYSTEM "chrome://browser/locale/aboutAccounts.dtd">
   %aboutAccountsDTD;
   <!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
   %syncBrandDTD;
 ]>
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
-   <title>&syncBrand.fxa-plural.label;</title>
+   <title>&aboutAccounts.pageTitle;</title>
    <meta name="viewport" content="width=device-width"/>
 
 
    <link rel="icon" type="image/png" id="favicon"
          href="chrome://branding/content/icon32.png"/>
    <link rel="stylesheet"
      href="chrome://browser/content/aboutaccounts/fonts.css"
      type="text/css" />
@@ -50,19 +50,17 @@
             <div class="button-row">
               <a class="button" href="#" onclick="openPrefs()">Manage</a>
             </div>
         </section>
       </div>
 
       <div id="intro">
         <header>
-          <h1>&syncBrand.fxa-plural.label;</h1>
-
-          <h2>&syncBrand.shortName.label;</h2>
+          <h1>&aboutAccounts.pageTitle;</h1>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="description">&aboutAccountsSetup.description;</div>
 
             <div class="button-row">
--- a/browser/base/content/browser-charsetmenu.inc
+++ b/browser/base/content/browser-charsetmenu.inc
@@ -6,57 +6,16 @@
 
 #expand <menu id="__ID_PREFIX__charsetMenu"
     label="&charsetMenu.label;"
 #ifndef OMIT_ACCESSKEYS
     accesskey="&charsetMenu.accesskey;"
 #endif
     oncommand="MultiplexHandler(event)"
 #ifdef OMIT_ACCESSKEYS
-#expand    onpopupshowing="CharsetMenu.build(event, '__ID_PREFIX__', false);"
+#expand    onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__', false);"
 #else
-#expand    onpopupshowing="CharsetMenu.build(event, '__ID_PREFIX__');"
+#expand    onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__');"
 #endif
     onpopupshown="UpdateMenus(event);">
   <menupopup>
-    <menu label="&charsetMenuAutodet.label;"
-#ifndef OMIT_ACCESSKEYS
-          accesskey="&charsetMenuAutodet.accesskey;"
-#endif
-        >
-      <menupopup>
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.off"
-                  label="&charsetMenuAutodet.off.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.off.accesskey;"
-#endif
-                  />
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.ja_parallel_state_machine"
-                  label="&charsetMenuAutodet.ja.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.ja.accesskey;"
-#endif
-                  />
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.ruprob"
-                  label="&charsetMenuAutodet.ru.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.ru.accesskey;"
-#endif
-                  />
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.ukprob"
-                  label="&charsetMenuAutodet.uk.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.uk.accesskey;"
-#endif
-                  />
-      </menupopup>
-    </menu>
-    <menuseparator/>
   </menupopup>
 </menu>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -285,16 +285,24 @@ toolbarpaletteitem > #personal-bookmarks
 #urlbar-container {
   min-width: 28ch;
 }
 
 #search-container {
   min-width: 25ch;
 }
 
+#urlbar,
+.searchbar-textbox {
+  /* Setting a width and min-width to let the location & search bars maintain
+     a constant width in case they haven't be resized manually. (bug 965772) */
+  width: 1px;
+  min-width: 1px;
+}
+
 #main-window:-moz-lwtheme {
   background-repeat: no-repeat;
   background-position: top right;
 }
 
 %ifdef XP_MACOSX
 #main-window[inFullscreen="true"] {
   padding-top: 0; /* override drawintitlebar="true" */
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5216,22 +5216,33 @@ function UpdateCurrentCharset(target) {
 
     // Uncheck previous item
     if (gPrevCharset) {
         var pref_item = charsetMenuGetElement(target, "charset." + gPrevCharset);
         if (pref_item)
           pref_item.setAttribute('checked', 'false');
     }
 
-    var menuitem = charsetMenuGetElement(target, "charset." + wnd.document.characterSet);
+    var menuitem = charsetMenuGetElement(target, "charset." + FoldCharset(wnd.document.characterSet));
     if (menuitem) {
         menuitem.setAttribute('checked', 'true');
     }
 }
 
+function FoldCharset(charset) {
+  // For substantially similar encodings, treat two encodings as the same
+  // for the purpose of the check mark.
+  if (charset == "ISO-8859-8-I") {
+    return "windows-1255";
+  } else if (charset == "gb18030") {
+    return "gbk";
+  }
+  return charset;
+}
+
 function UpdateCharsetDetector(target) {
   var prefvalue;
 
   try {
     prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
   }
   catch (ex) {}
 
@@ -5244,17 +5255,17 @@ function UpdateCharsetDetector(target) {
 }
 
 function UpdateMenus(event) {
   UpdateCurrentCharset(event.target);
   UpdateCharsetDetector(event.target);
 }
 
 function charsetLoadListener() {
-  var charset = window.content.document.characterSet;
+  var charset = FoldCharset(window.content.document.characterSet);
 
   if (charset.length > 0 && (charset != gLastBrowserCharset)) {
     gPrevCharset = gLastBrowserCharset;
     gLastBrowserCharset = charset;
   }
 }
 
 var gPageStyleMenu = {
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
+  "resource://gre/modules/FxAccounts.jsm");
 
 registerCleanupFunction(function() {
   // Ensure we don't pollute prefs for next tests.
   Services.prefs.clearUserPref("identity.fxaccounts.enabled");
   Services.prefs.clearUserPref("identity.fxaccounts.remote.uri");
 });
 
 let gTests = [
@@ -42,17 +44,17 @@ let gTests = [
         }
 
       }, false, true);
 
     } catch(e) {
       ok(false, "Failed to get all commands");
       deferred.reject();
     }
-    return deferred.promise;
+    return deferred.promise.then(() => fxAccounts.signOut());
   }
 },
 
 
 ]; // gTests
 
 function test()
 {
@@ -69,23 +71,24 @@ function test()
 
       gBrowser.removeCurrentTab();
     }
 
     finish();
   });
 }
 
-function promiseNewTabLoadEvent(aUrl, aEventType="load")
+function promiseNewTabLoadEvent(aUrl)
 {
   let deferred = Promise.defer();
   let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
-  tab.linkedBrowser.addEventListener(aEventType, function load(event) {
-    tab.linkedBrowser.removeEventListener(aEventType, load, true);
-    let iframe = tab.linkedBrowser.contentDocument.getElementById("remote");
-      iframe.addEventListener("load", function frameLoad(e) {
-        iframe.removeEventListener("load", frameLoad, false);
-        deferred.resolve();
-      }, false);
-    }, true);
+  let browser = tab.linkedBrowser;
+
+  browser.addEventListener("load", function onLoad(event) {
+    let iframe = browser.contentDocument.getElementById("remote");
+    if (iframe && event.target == iframe.contentDocument) {
+      browser.removeEventListener("load", onLoad, true);
+      deferred.resolve();
+    }
+  }, true);
+
   return deferred.promise;
 }
-
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -30,9 +30,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('win
     DEFINES['CONTEXT_COPY_IMAGE_CONTENTS'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
     DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
     DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
 
-JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file
+JAR_MANIFESTS += ['jar.mn']
--- a/browser/components/customizableui/content/customizeMode.inc.xul
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -13,17 +13,24 @@
              onkeypress="BrowserOpenAddonsMgr('addons://discovery/');"
              id="customization-more-tools"
              class="text-link">
         &customizeMode.menuAndToolbars.emptyLink;
       </label>
     </hbox>
     <vbox id="customization-palette" class="customization-palette" flex="1"/>
     <spacer id="customization-spacer" flex="1"/>
-    <hbox>
+    <hbox id="customization-footer">
+#ifdef CAN_DRAW_IN_TITLEBAR
+      <button id="customization-titlebar-visibility-button" class="customizationmode-button"
+              label="&customizeMode.titlebar;" type="checkbox"
+#NB: because oncommand fires after click, by the time we've fired, the checkbox binding
+#    will already have switched the button's state, so this is correct:
+              oncommand="gCustomizeMode.toggleTitlebar(this.hasAttribute('checked'))"/>
+#endif
       <button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu">
         <menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
       </button>
       <spacer flex="1"/>
       <button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
     </hbox>
   </vbox>
   <vbox id="customization-panel-container">
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -145,17 +145,17 @@
     </panelview>
 
     <panelview id="PanelUI-characterEncodingView" flex="1">
       <label value="&charsetMenu.label;" class="panel-subview-header"/>
 
       <vbox id="PanelUI-characterEncodingView-customlist"
             class="PanelUI-characterEncodingView-list"/>
       <vbox>
-        <label value="&charsetMenuAutodet.label;"/>
+        <label id="PanelUI-characterEncodingView-autodetect-label"/>
         <vbox id="PanelUI-characterEncodingView-autodetect"
               class="PanelUI-characterEncodingView-list"/>
       </vbox>
     </panelview>
 
   </panelmultiview>
   <!-- These menupopups are located here to prevent flickering,
        see bug 492960 comment 20. -->
--- a/browser/components/customizableui/src/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/src/CustomizableWidgets.jsm
@@ -11,39 +11,59 @@ Cu.import("resource:///modules/Customiza
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
   "resource:///modules/PlacesUIUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils",
   "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
+  "resource://gre/modules/ShortcutUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "CharsetManager",
                                    "@mozilla.org/charset-converter-manager;1",
                                    "nsICharsetConverterManager");
+
+XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
+  const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
+  return Services.strings.createBundle(kCharsetBundle);
+});
 XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() {
   const kBrandBundle = "chrome://branding/locale/brand.properties";
   return Services.strings.createBundle(kBrandBundle);
 });
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
 const kWidePanelItemClass = "panel-wide-item";
 
 let gModuleName = "[CustomizableWidgets]";
 #include logging.js
 
 function setAttributes(aNode, aAttrs) {
+  let doc = aNode.ownerDocument;
   for (let [name, value] of Iterator(aAttrs)) {
     if (!value) {
       if (aNode.hasAttribute(name))
         aNode.removeAttribute(name);
     } else {
-      if (name == "label" || name == "tooltiptext")
-        value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, name);
+      if (name == "shortcutId") {
+        continue;
+      }
+      if (name == "label" || name == "tooltiptext") {
+        let stringId = (typeof value == "string") ? value : name;
+        let additionalArgs = [];
+        if (aAttrs.shortcutId) {
+          let shortcut = doc.getElementById(aAttrs.shortcutId);
+          if (doc) {
+            additionalArgs.push(ShortcutUtils.prettifyShortcut(shortcut));
+          }
+        }
+        value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, stringId, additionalArgs);
+      }
       aNode.setAttribute(name, value);
     }
   }
 }
 
 function updateCombinedWidgetStyle(aNode, aArea, aModifyCloseMenu) {
   let inPanel = (aArea == CustomizableUI.AREA_PANEL);
   let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
@@ -58,16 +78,17 @@ function updateCombinedWidgetStyle(aNode
   }
 }
 
 const CustomizableWidgets = [{
     id: "history-panelmenu",
     type: "view",
     viewId: "PanelUI-history",
     shortcutId: "key_gotoHistory",
+    tooltiptext: "history-panelmenu.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onViewShowing: function(aEvent) {
       // Populate our list of history
       const kMaxResults = 15;
       let doc = aEvent.detail.ownerDocument;
 
       let options = PlacesUtils.history.getNewQueryOptions();
       options.excludeQueries = true;
@@ -145,80 +166,84 @@ const CustomizableWidgets = [{
 
       if (PlacesUIUtils.shouldEnableTabsFromOtherComputersMenuitem()) {
         tabsFromOtherComputers.removeAttribute("disabled");
       } else {
         tabsFromOtherComputers.setAttribute("disabled", true);
       }
 #endif
 
-      let tabsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getTabsFragment(doc.defaultView, "toolbarbutton");
+      let utils = RecentlyClosedTabsAndWindowsMenuUtils;
+      let tabsFragment = utils.getTabsFragment(doc.defaultView, "toolbarbutton", true,
+                                               "menuRestoreAllTabsSubview.label");
       let separator = doc.getElementById("PanelUI-recentlyClosedTabs-separator");
       let elementCount = tabsFragment.childElementCount;
       separator.hidden = !elementCount;
       while (--elementCount >= 0) {
-        if (tabsFragment.children[elementCount].localName != "toolbarbutton")
-          continue;
-        tabsFragment.children[elementCount].setAttribute("class", "subviewbutton");
+        tabsFragment.children[elementCount].classList.add("subviewbutton");
       }
       recentlyClosedTabs.appendChild(tabsFragment);
 
-      let windowsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getWindowsFragment(doc.defaultView, "toolbarbutton");
+      let windowsFragment = utils.getWindowsFragment(doc.defaultView, "toolbarbutton", true,
+                                                     "menuRestoreAllWindowsSubview.label");
       separator = doc.getElementById("PanelUI-recentlyClosedWindows-separator");
       elementCount = windowsFragment.childElementCount;
       separator.hidden = !elementCount;
       while (--elementCount >= 0) {
-        if (windowsFragment.children[elementCount].localName != "toolbarbutton")
-          continue;
-        windowsFragment.children[elementCount].setAttribute("class", "subviewbutton");
+        windowsFragment.children[elementCount].classList.add("subviewbutton");
       }
       recentlyClosedWindows.appendChild(windowsFragment);
+      aEvent.target.addEventListener("command", win.PanelUI);
     },
     onViewHiding: function(aEvent) {
       LOG("History view is being hidden!");
+      aEvent.target.removeEventListener("command", win.PanelUI);
     }
   }, {
     id: "privatebrowsing-button",
     shortcutId: "key_privatebrowsing",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(e) {
       if (e.target && e.target.ownerDocument && e.target.ownerDocument.defaultView) {
         let win = e.target.ownerDocument.defaultView;
         if (typeof win.OpenBrowserWindow == "function") {
           win.OpenBrowserWindow({private: true});
         }
       }
     }
   }, {
     id: "save-page-button",
     shortcutId: "key_savePage",
+    tooltiptext: "save-page-button.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.saveDocument == "function") {
         win.saveDocument(win.content.document);
       }
     }
   }, {
     id: "find-button",
     shortcutId: "key_find",
+    tooltiptext: "find-button.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && win.gFindBar) {
         win.gFindBar.onFindCommand();
       }
     }
   }, {
     id: "open-file-button",
     shortcutId: "openFileKb",
+    tooltiptext: "open-file-button.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target
                 && aEvent.target.ownerDocument
                 && aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.BrowserOpenFileWindow == "function") {
         win.BrowserOpenFileWindow();
       }
@@ -284,31 +309,39 @@ const CustomizableWidgets = [{
       }
 
       parent.appendChild(items);
       aEvent.target.removeEventListener("command", win.PanelUI);
     }
   }, {
     id: "add-ons-button",
     shortcutId: "key_openAddons",
+    tooltiptext: "add-ons-button.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.BrowserOpenAddonsMgr == "function") {
         win.BrowserOpenAddonsMgr();
       }
     }
   }, {
     id: "preferences-button",
     defaultArea: CustomizableUI.AREA_PANEL,
 #ifdef XP_WIN
     label: "preferences-button.labelWin",
-    tooltiptext: "preferences-button.tooltipWin",
+    tooltiptext: "preferences-button.tooltipWin2",
+#else
+#ifdef XP_MACOSX
+    tooltiptext: "preferences-button.tooltiptext.withshortcut",
+    shortcutId: "key_preferencesCmdMac",
+#else
+    tooltiptext: "preferences-button.tooltiptext",
+#endif
 #endif
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.openPreferences == "function") {
         win.openPreferences();
       }
@@ -329,30 +362,33 @@ const CustomizableWidgets = [{
         cls = null;
 
       let buttons = [{
         id: "zoom-out-button",
         closemenu: closeMenu,
         command: "cmd_fullZoomReduce",
         class: cls,
         label: true,
-        tooltiptext: true
+        tooltiptext: "tooltiptext2",
+        shortcutId: "key_fullZoomReduce",
       }, {
         id: "zoom-reset-button",
         closemenu: closeMenu,
         command: "cmd_fullZoomReset",
         class: cls,
-        tooltiptext: true
+        tooltiptext: "tooltiptext2",
+        shortcutId: "key_fullZoomReset",
       }, {
         id: "zoom-in-button",
         closemenu: closeMenu,
         command: "cmd_fullZoomEnlarge",
         class: cls,
         label: true,
-        tooltiptext: true
+        tooltiptext: "tooltiptext2",
+        shortcutId: "key_fullZoomEnlarge",
       }];
 
       let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
       node.setAttribute("id", "zoom-controls");
       node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
       // Set this as an attribute in addition to the property to make sure we can style correctly.
       node.setAttribute("removable", "true");
       node.classList.add("chromeclass-toolbar-additional");
@@ -485,29 +521,32 @@ const CustomizableWidgets = [{
       if (!this.currentArea)
         cls = null;
 
       let buttons = [{
         id: "cut-button",
         command: "cmd_cut",
         class: cls,
         label: true,
-        tooltiptext: true
+        tooltiptext: "tooltiptext2",
+        shortcutId: "key_cut",
       }, {
         id: "copy-button",
         command: "cmd_copy",
         class: cls,
         label: true,
-        tooltiptext: true
+        tooltiptext: "tooltiptext2",
+        shortcutId: "key_copy",
       }, {
         id: "paste-button",
         command: "cmd_paste",
         class: cls,
         label: true,
-        tooltiptext: true
+        tooltiptext: "tooltiptext2",
+        shortcutId: "key_paste",
       }];
 
       let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
       node.setAttribute("id", "edit-controls");
       node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
       // Set this as an attribute in addition to the property to make sure we can style correctly.
       node.setAttribute("removable", "true");
       node.classList.add("chromeclass-toolbar-additional");
@@ -605,16 +644,17 @@ const CustomizableWidgets = [{
       if (!feeds || !feeds.length) {
         node.setAttribute("disabled", "true");
       }
     }
   }, {
     id: "characterencoding-button",
     type: "view",
     viewId: "PanelUI-characterEncodingView",
+    tooltiptext: "characterencoding-button.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     maybeDisableMenu: function(aDocument) {
       let window = aDocument.defaultView;
       return !(window.gBrowser &&
                window.gBrowser.docShell &&
                window.gBrowser.docShell.mayEnableCharacterEncodingMenu);
     },
     getCharsetList: function(aSection, aDocument) {
@@ -726,16 +766,21 @@ const CustomizableWidgets = [{
           elem.setAttribute("disabled", "true");
         elem.setAttribute("class", "subviewbutton");
         containerElem.appendChild(elem);
       }
     },
     onViewShowing: function(aEvent) {
       let document = aEvent.target.ownerDocument;
 
+      let autoDetectLabelId = "PanelUI-characterEncodingView-autodetect-label";
+      let autoDetectLabel = document.getElementById(autoDetectLabelId);
+      let label = CharsetBundle.GetStringFromName("charsetMenuAutodet");
+      autoDetectLabel.setAttribute("value", label);
+
       this.populateList(document,
                         "PanelUI-characterEncodingView-customlist",
                         "browser");
       this.populateList(document,
                         "PanelUI-characterEncodingView-autodetect",
                         "autodetect");
     },
     onCommand: function(aEvent) {
@@ -812,16 +857,17 @@ const CustomizableWidgets = [{
           let panel = aDoc.getElementById(kPanelId);
           panel.removeEventListener("popupshowing", updateButton);
         }
       };
       CustomizableUI.addListener(listener);
     }
   }, {
     id: "email-link-button",
+    tooltiptext: "email-link-button.tooltiptext2",
     onCommand: function(aEvent) {
       let win = aEvent.view;
       win.MailIntegration.sendLinkForWindow(win.content);
     }
   }];
 
 #ifdef XP_WIN
 #ifdef MOZ_METRO
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -11,16 +11,17 @@ const {classes: Cc, interfaces: Ci, util
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
 const kPrefCustomizationAnimation = "browser.uiCustomization.disableAnimation";
 const kPaletteId = "customization-palette";
 const kAboutURI = "about:customizing";
 const kDragDataTypePrefix = "text/toolbarwrapper-id/";
 const kPlaceholderClass = "panel-customization-placeholder";
 const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
 const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
+const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
 const kMaxTransitionDurationMs = 2000;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/CustomizableUI.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
@@ -45,16 +46,21 @@ function CustomizeMode(aWindow) {
 
   // There are two palettes - there's the palette that can be overlayed with
   // toolbar items in browser.xul. This is invisible, and never seen by the
   // user. Then there's the visible palette, which gets populated and displayed
   // to the user when in customizing mode.
   this.visiblePalette = this.document.getElementById(kPaletteId);
   this.paletteEmptyNotice = this.document.getElementById("customization-empty");
   this.paletteSpacer = this.document.getElementById("customization-spacer");
+#ifdef CAN_DRAW_IN_TITLEBAR
+  this._updateTitlebarButton();
+  Services.prefs.addObserver(kDrawInTitlebarPref, this, false);
+  this.window.addEventListener("unload", this);
+#endif
 };
 
 CustomizeMode.prototype = {
   _changed: false,
   _transitioning: false,
   window: null,
   document: null,
   // areas is used to cache the customizable areas when in customization mode.
@@ -75,16 +81,22 @@ CustomizeMode.prototype = {
   get panelUIContents() {
     return this.document.getElementById("PanelUI-contents");
   },
 
   get _handler() {
     return this.window.CustomizationHandler;
   },
 
+  uninit: function() {
+#ifdef CAN_DRAW_IN_TITLEBAR
+    Services.prefs.removeObserver(kDrawInTitlebarPref, this);
+#endif
+  },
+
   toggle: function() {
     if (this._handler.isEnteringCustomizeMode || this._handler.isExitingCustomizeMode) {
       this._wantToBeInCustomizeMode = !this._wantToBeInCustomizeMode;
       return;
     }
     if (this._customizing) {
       this.exit();
     } else {
@@ -962,19 +974,53 @@ CustomizeMode.prototype = {
       case "mouseup":
         this._onMouseUp(aEvent);
         break;
       case "keypress":
         if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
           this.exit();
         }
         break;
+#ifdef CAN_DRAW_IN_TITLEBAR
+      case "unload":
+        this.uninit();
+        break;
+#endif
+    }
+  },
+
+#ifdef CAN_DRAW_IN_TITLEBAR
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "nsPref:changed":
+        this._updateTitlebarButton();
+        break;
     }
   },
 
+  _updateTitlebarButton: function() {
+    let drawInTitlebar = true;
+    try {
+      drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref);
+    } catch (ex) { }
+    let button = this.document.getElementById("customization-titlebar-visibility-button");
+    // Drawing in the titlebar means 'hiding' the titlebar:
+    if (drawInTitlebar) {
+      button.removeAttribute("checked");
+    } else {
+      button.setAttribute("checked", "true");
+    }
+  },
+
+  toggleTitlebar: function(aShouldShowTitlebar) {
+    // Drawing in the titlebar means not showing the titlebar, hence the negation:
+    Services.prefs.setBoolPref(kDrawInTitlebarPref, !aShouldShowTitlebar);
+  },
+#endif
+
   _onDragStart: function(aEvent) {
     __dumpDragData(aEvent);
     let item = aEvent.target;
     while (item && item.localName != "toolbarpaletteitem") {
       if (item.localName == "toolbar") {
         return;
       }
       item = item.parentNode;
@@ -1322,16 +1368,20 @@ CustomizeMode.prototype = {
     }
 
     let draggedItemId =
       aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
 
     let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
     draggedWrapper.hidden = false;
     draggedWrapper.removeAttribute("mousedown");
+    if (this._dragOverItem) {
+      this._cancelDragActive(this._dragOverItem);
+      this._dragOverItem = null;
+    }
     this._showPanelCustomizationPlaceholders();
   },
 
   _isUnwantedDragDrop: function(aEvent) {
     // The simulated events generated by synthesizeDragStart/synthesizeDrop in
     // mochitests are used only for testing whether the right data is being put
     // into the dataTransfer. Neither cause a real drop to occur, so they don't
     // set the source node. There isn't a means of testing real drag and drops,
--- a/browser/components/customizableui/src/moz.build
+++ b/browser/components/customizableui/src/moz.build
@@ -4,14 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_JS_MODULES += [
     'DragPositionManager.jsm',
     'ScrollbarSampler.jsm',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
+    DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
+
 EXTRA_PP_JS_MODULES += [
     'CustomizableUI.jsm',
     'CustomizableWidgets.jsm',
     'CustomizeMode.jsm',
     'PanelWideWidgetTracker.jsm',
 ]
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -36,32 +36,16 @@ const DOWNLOAD_VIEW_SUPPORTED_COMMANDS =
  ["cmd_delete", "cmd_copy", "cmd_paste", "cmd_selectAll",
   "downloadsCmd_pauseResume", "downloadsCmd_cancel",
   "downloadsCmd_open", "downloadsCmd_show", "downloadsCmd_retry",
   "downloadsCmd_openReferrer", "downloadsCmd_clearDownloads"];
 
 const NOT_AVAILABLE = Number.MAX_VALUE;
 
 /**
- * Download a URL.
- *
- * @param aURL
- *        the url to download (nsIURI object)
- * @param [optional] aFileName
- *        the destination file name
- */
-function DownloadURL(aURL, aFileName) {
-  // For private browsing, try to get document out of the most recent browser
-  // window, or provide our own if there's no browser window.
-  let browserWin = RecentWindow.getMostRecentBrowserWindow();
-  let initiatingDoc = browserWin ? browserWin.document : document;
-  saveURL(aURL, aFileName, null, true, true, undefined, initiatingDoc);
-}
-
-/**
  * A download element shell is responsible for handling the commands and the
  * displayed data for a single download view element. The download element
  * could represent either a past download (for which we get data from places)  or
  * a "session" download (using a data-item object. See DownloadsCommon.jsm), or both.
  *
  * Once initialized with either a data item or a places node, the created richlistitem
  * can be accessed through the |element| getter, and can then be inserted/removed from
  * a richlistbox.
@@ -640,17 +624,20 @@ DownloadElementShell.prototype = {
     }
     return false;
   },
 
   _retryAsHistoryDownload: function DES__retryAsHistoryDownload() {
     // In future we may try to download into the same original target uri, when
     // we have it.  Though that requires verifying the path is still valid and
     // may surprise the user if he wants to be requested every time.
-    DownloadURL(this.downloadURI, this.getDownloadMetaData().fileName);
+    let browserWin = RecentWindow.getMostRecentBrowserWindow();
+    let initiatingDoc = browserWin ? browserWin.document : document;
+    DownloadURL(this.downloadURI, this.getDownloadMetaData().fileName,
+                initiatingDoc);
   },
 
   /* nsIController */
   doCommand: function DES_doCommand(aCommand) {
     switch (aCommand) {
       case "downloadsCmd_open": {
         let file = this._dataItem ?
           this.dataItem.localFile :
@@ -1428,17 +1415,19 @@ DownloadsPlacesView.prototype = {
 
   _canDownloadClipboardURL: function DPV__canDownloadClipboardURL() {
     let [url, name] = this._getURLFromClipboardData();
     return url != "";
   },
 
   _downloadURLFromClipboard: function DPV__downloadURLFromClipboard() {
     let [url, name] = this._getURLFromClipboardData();
-    DownloadURL(url, name);
+    let browserWin = RecentWindow.getMostRecentBrowserWindow();
+    let initiatingDoc = browserWin ? browserWin.document : document;
+    DownloadURL(url, name, initiatingDoc);
   },
 
   doCommand: function DPV_doCommand(aCommand) {
     switch (aCommand) {
       case "cmd_copy":
         this._copySelectedDownloadsToClipboard();
         break;
       case "cmd_selectAll":
@@ -1482,16 +1471,21 @@ DownloadsPlacesView.prototype = {
     // Set the state attribute so that only the appropriate items are displayed.
     let contextMenu = document.getElementById("downloadsContextMenu");
     let state = element._shell.getDownloadMetaData().state;
     if (state !== undefined)
       contextMenu.setAttribute("state", state);
     else
       contextMenu.removeAttribute("state");
 
+    if (state == nsIDM.DOWNLOAD_DOWNLOADING) {
+      // The resumable property of a download may change at any time, so
+      // ensure we update the related command now.
+      goUpdateCommand("downloadsCmd_pauseResume");
+    }
     return true;
   },
 
   onKeyPress: function DPV_onKeyPress(aEvent) {
     let selectedElements = this._richlistbox.selectedItems;
     if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) {
       // In the content tree, opening bookmarks by pressing return is only
       // supported when a single item is selected. To be consistent, do the
@@ -1574,18 +1568,21 @@ DownloadsPlacesView.prototype = {
     let dt = aEvent.dataTransfer;
     // If dragged item is from our source, do not try to
     // redownload already downloaded file.
     if (dt.mozGetDataAt("application/x-moz-file", 0))
       return;
 
     let name = { };
     let url = Services.droppedLinkHandler.dropLink(aEvent, name);
-    if (url)
-      DownloadURL(url, name.value);
+    if (url) {
+      let browserWin = RecentWindow.getMostRecentBrowserWindow();
+      let initiatingDoc = browserWin ? browserWin.document : document;
+      DownloadURL(url, name.value, initiatingDoc);
+    }
   }
 };
 
 for (let methodName of ["load", "applyFilter", "selectNode", "selectItems"]) {
   DownloadsPlacesView.prototype[methodName] = function() {
     throw new Error("|" + methodName + "| is not implemented by the downloads view.");
   }
 }
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -500,18 +500,17 @@ const DownloadsPanel = {
                             .data
                             .split("\n");
       if (!url) {
         return;
       }
 
       let uri = NetUtil.newURI(url);
       DownloadsCommon.log("Pasted URL seems valid. Starting download.");
-      saveURL(uri.spec, name || uri.spec, null, true, true,
-              undefined, document);
+      DownloadURL(uri.spec, name, document);
     } catch (ex) {}
   },
 
   /**
    * Move focus to the main element in the downloads panel, unless another
    * element in the panel is already focused.
    */
   _focusPanel: function DP_focusPanel()
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -215,16 +215,21 @@ let SyncHandler = {
  *   {entries: [{url: "about:mozilla", ...}, ...], index: 1}
  */
 let SessionHistoryListener = {
   init: function () {
     gFrameTree.addObserver(this);
     addEventListener("load", this, true);
     addEventListener("hashchange", this, true);
     Services.obs.addObserver(this, "browser:purge-session-history", false);
+
+    // Collect data if we start with a non-empty shistory.
+    if (!SessionHistory.isEmpty(docShell)) {
+      this.collect();
+    }
   },
 
   uninit: function () {
     Services.obs.removeObserver(this, "browser:purge-session-history");
   },
 
   observe: function () {
     // We need to use setTimeout() here because we listen for
--- a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm
+++ b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm
@@ -23,19 +23,25 @@ let ss = Cc["@mozilla.org/browser/sessio
 this.RecentlyClosedTabsAndWindowsMenuUtils = {
 
   /**
   * Builds up a document fragment of UI items for the recently closed tabs.
   * @param   aWindow
   *          The window that the tabs were closed in.
   * @param   aTagName
   *          The tag name that will be used when creating the UI items.
+  * @param   aPrefixRestoreAll (defaults to false)
+  *          Whether the 'restore all tabs' item is suffixed or prefixed to the list.
+  *          If suffixed (the default) a separator will be inserted before it.
+  * @param   aRestoreAllLabel (defaults to "menuRestoreAllTabs.label")
+  *          Which localizable string to use for the 'restore all tabs' item.
   * @returns A document fragment with UI items for each recently closed tab.
   */
-  getTabsFragment: function(aWindow, aTagName) {
+  getTabsFragment: function(aWindow, aTagName, aPrefixRestoreAll=false,
+                            aRestoreAllLabel="menuRestoreAllTabs.label") {
     let doc = aWindow.document;
     let fragment = doc.createDocumentFragment();
     if (ss.getClosedTabCount(aWindow) != 0) {
       let closedTabs = JSON.parse(ss.getClosedTabData(aWindow));
       for (let i = 0; i < closedTabs.length; i++) {
         let element = doc.createElementNS(kNSXUL, aTagName);
         element.setAttribute("label", closedTabs[i].title);
         if (closedTabs[i].image) {
@@ -57,34 +63,46 @@ this.RecentlyClosedTabsAndWindowsMenuUti
         }
 
         element.addEventListener("click", this._undoCloseMiddleClick, false);
         if (i == 0)
           element.setAttribute("key", "key_undoCloseTab");
         fragment.appendChild(element);
       }
 
-      fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
-      let restoreAllTabs = fragment.appendChild(doc.createElementNS(kNSXUL, aTagName));
-      restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName("menuRestoreAllTabs.label"));
+      let restoreAllTabs = doc.createElementNS(kNSXUL, aTagName);
+      restoreAllTabs.classList.add("restoreallitem");
+      restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
       restoreAllTabs.setAttribute("oncommand",
               "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab();");
+      if (!aPrefixRestoreAll) {
+        fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
+        fragment.appendChild(restoreAllTabs);
+      } else {
+        fragment.insertBefore(restoreAllTabs, fragment.firstChild);
+      }
     }
     return fragment;
   },
 
   /**
   * Builds up a document fragment of UI items for the recently closed windows.
   * @param   aWindow
   *          A window that can be used to create the elements and document fragment.
   * @param   aTagName
   *          The tag name that will be used when creating the UI items.
+  * @param   aPrefixRestoreAll (defaults to false)
+  *          Whether the 'restore all windows' item is suffixed or prefixed to the list.
+  *          If suffixed (the default) a separator will be inserted before it.
+  * @param   aRestoreAllLabel (defaults to "menuRestoreAllWindows.label")
+  *          Which localizable string to use for the 'restore all windows' item.
   * @returns A document fragment with UI items for each recently closed window.
   */
-  getWindowsFragment: function(aWindow, aTagName) {
+  getWindowsFragment: function(aWindow, aTagName, aPrefixRestoreAll=false,
+                            aRestoreAllLabel="menuRestoreAllWindows.label") {
     let closedWindowData = JSON.parse(ss.getClosedWindowData());
     let fragment = aWindow.document.createDocumentFragment();
     if (closedWindowData.length != 0) {
       let menuLabelString = navigatorBundle.GetStringFromName("menuUndoCloseWindowLabel");
       let menuLabelStringSingleTab =
         navigatorBundle.GetStringFromName("menuUndoCloseWindowSingleTabLabel");
 
       let doc = aWindow.document;
@@ -113,21 +131,27 @@ this.RecentlyClosedTabsAndWindowsMenuUti
           item.setAttribute("targetURI", selectedTab.entries[activeIndex].url);
 
         if (i == 0)
           item.setAttribute("key", "key_undoCloseWindow");
         fragment.appendChild(item);
       }
 
       // "Open All in Windows"
-      fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
-      let restoreAllWindows = fragment.appendChild(doc.createElementNS(kNSXUL, aTagName));
-      restoreAllWindows.setAttribute("label", navigatorBundle.GetStringFromName("menuRestoreAllWindows.label"));
+      let restoreAllWindows = doc.createElementNS(kNSXUL, aTagName);
+      restoreAllWindows.classList.add("restoreallitem");
+      restoreAllWindows.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
       restoreAllWindows.setAttribute("oncommand",
         "for (var i = 0; i < " + closedWindowData.length + "; i++) undoCloseWindow();");
+      if (!aPrefixRestoreAll) {
+        fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
+        fragment.appendChild(restoreAllWindows);
+      } else {
+        fragment.insertBefore(restoreAllWindows, fragment.firstChild);
+      }
     }
     return fragment;
   },
 
 
   /**
     * Re-open a closed tab and put it to the end of the tab strip.
     * Used for a middle click.
--- a/browser/components/sessionstore/src/SessionHistory.jsm
+++ b/browser/components/sessionstore/src/SessionHistory.jsm
@@ -19,30 +19,47 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 function debug(msg) {
   Services.console.logStringMessage("SessionHistory: " + msg);
 }
 
 /**
  * The external API exported by this module.
  */
 this.SessionHistory = Object.freeze({
+  isEmpty: function (docShell) {
+    return SessionHistoryInternal.isEmpty(docShell);
+  },
+
   collect: function (docShell) {
     return SessionHistoryInternal.collect(docShell);
   },
 
   restore: function (docShell, tabData) {
     SessionHistoryInternal.restore(docShell, tabData);
   }
 });
 
 /**
  * The internal API for the SessionHistory module.
  */
 let SessionHistoryInternal = {
   /**
+   * Returns whether the given docShell's session history is empty.
+   *
+   * @param docShell
+   *        The docShell that owns the session history.
+   */
+  isEmpty: function (docShell) {
+    let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
+    let history = webNavigation.sessionHistory;
+    let uri = webNavigation.currentURI.spec;
+    return uri == "about:blank" && history.count == 0;
+  },
+
+  /**
    * Collects session history data for a given docShell.
    *
    * @param docShell
    *        The docShell that owns the session history.
    */
   collect: function (docShell) {
     let data = {entries: []};
     let isPinned = docShell.isAppTab;
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -830,17 +830,19 @@ StackFrames.prototype = {
     let { environment, where } = frame;
     if (!environment) {
       return;
     }
 
     // Don't change the editor's location if the execution was paused by a
     // public client evaluation. This is useful for adding overlays on
     // top of the editor, like a variable inspection popup.
-    if (this._currentFrameDescription != FRAME_TYPE.PUBLIC_CLIENT_EVAL) {
+    let isClientEval = this._currentFrameDescription == FRAME_TYPE.PUBLIC_CLIENT_EVAL;
+    let isPopupShown = DebuggerView.VariableBubble.contentsShown();
+    if (!isClientEval && !isPopupShown) {
       // Move the editor's caret to the proper url and line.
       DebuggerView.setEditorLocation(where.url, where.line);
       // Highlight the breakpoint at the specified url and line if it exists.
       DebuggerView.Sources.highlightBreakpoint(where, { noEditorUpdate: true });
     }
 
     // Don't display the watch expressions textbox inputs in the pane.
     DebuggerView.WatchExpressions.toggleContents(false);
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -1904,16 +1904,26 @@ VariableBubbleView.prototype = {
    * Hides the inspection popup.
    */
   hideContents: function() {
     clearNamedTimeout("editor-mouse-move");
     this._tooltip.hide();
   },
 
   /**
+   * Checks whether the inspection popup is shown.
+   *
+   * @return boolean
+   *         True if the panel is shown or showing, false otherwise.
+   */
+  contentsShown: function() {
+    return this._tooltip.isShown();
+  },
+
+  /**
    * Functions for getting customized variables view evaluation macros.
    *
    * @param string aPrefix
    *        See the corresponding VariablesView.* functions.
    */
   _getSimpleValueEvalMacro: function(aPrefix) {
     return (item, string) =>
       VariablesView.simpleValueEvalMacro(item, string, aPrefix);
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -238,16 +238,17 @@ support-files =
 [browser_dbg_variables-view-popup-02.js]
 [browser_dbg_variables-view-popup-03.js]
 [browser_dbg_variables-view-popup-04.js]
 [browser_dbg_variables-view-popup-05.js]
 [browser_dbg_variables-view-popup-06.js]
 [browser_dbg_variables-view-popup-07.js]
 [browser_dbg_variables-view-popup-08.js]
 [browser_dbg_variables-view-popup-09.js]
+[browser_dbg_variables-view-popup-10.js]
 [browser_dbg_variables-view-reexpand-01.js]
 [browser_dbg_variables-view-reexpand-02.js]
 [browser_dbg_variables-view-webidl.js]
 [browser_dbg_watch-expressions-01.js]
 [browser_dbg_watch-expressions-02.js]
 [browser_dbg_chrome-create.js]
 skip-if = os == "linux" # Bug 847558
 [browser_dbg_on-pause-raise.js]
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-popup-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-popup-03.js
@@ -15,25 +15,29 @@ function test() {
 
     // Allow this generator function to yield first.
     executeSoon(() => debuggee.start());
     yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
 
     // Inspect variable.
     yield openVarPopup(panel, { line: 15, ch: 12 });
 
+    ok(bubble.contentsShown(),
+      "The variable should register as being shown.");
     ok(!bubble._tooltip.isEmpty(),
       "The variable inspection popup isn't empty.");
     ok(bubble._markedText,
       "There's some marked text in the editor.");
     ok(bubble._markedText.clear,
       "The marked text in the editor can be cleared.");
 
     yield hideVarPopup(panel);
 
+    ok(!bubble.contentsShown(),
+      "The variable should register as being hidden.");
     ok(bubble._tooltip.isEmpty(),
       "The variable inspection popup is now empty.");
     ok(!bubble._markedText,
       "The marked text in the editor was removed.");
 
     yield resumeDebuggerThenCloseAndFinish(panel);
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-popup-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-popup-08.js
@@ -45,22 +45,24 @@ function test() {
     yield waitForSourceAndCaretAndScopes(panel, ".html", 20);
     checkView(0, 20);
 
     // Inspect variable in topmost frame.
     yield openVarPopup(panel, { line: 18, ch: 12 });
     verifyContents("\"second scope\"", "token-string");
     checkView(0, 20);
 
-    // Change frame.
+    // Hide the popup and change the frame.
+    yield hideVarPopup(panel);
+
     let updatedFrame = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
     frames.selectedDepth = 1;
     yield updatedFrame;
     checkView(1, 15);
 
     // Inspect variable in oldest frame.
-    yield reopenVarPopup(panel, { line: 13, ch: 12 });
+    yield openVarPopup(panel, { line: 13, ch: 12 });
     verifyContents("\"first scope\"", "token-string");
     checkView(1, 15);
 
     yield resumeDebuggerThenCloseAndFinish(panel);
   });
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-popup-10.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Makes sure the source editor's scroll location doesn't change when
+ * a variable inspection popup is opened and a watch expression is
+ * also evaluated at the same time.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
+
+function test() {
+  Task.spawn(function() {
+    let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
+    let win = panel.panelWin;
+    let events = win.EVENTS;
+    let editor = win.DebuggerView.editor;
+    let editorContainer = win.document.getElementById("editor");
+    let bubble = win.DebuggerView.VariableBubble;
+    let expressions = win.DebuggerView.WatchExpressions;
+    let tooltip = bubble._tooltip.panel;
+
+    // Allow this generator function to yield first.
+    executeSoon(() => debuggee.start());
+    yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
+
+    let expressionsEvaluated = waitForDebuggerEvents(panel, events.FETCHED_WATCH_EXPRESSIONS);
+    expressions.addExpression("this");
+    editor.focus();
+    yield expressionsEvaluated;
+
+    // Scroll to the top of the editor and inspect variables.
+    let breakpointScrollPosition = editor.getScrollInfo().top;
+    editor.setFirstVisibleLine(0);
+    let topmostScrollPosition = editor.getScrollInfo().top;
+
+    ok(topmostScrollPosition < breakpointScrollPosition,
+      "The editor is now scrolled to the top (0).");
+    is(editor.getFirstVisibleLine(), 0,
+      "The editor is now scrolled to the top (1).");
+
+    let failPopup = () => ok(false, "The popup has got unexpectedly hidden.");
+    let failScroll = () => ok(false, "The editor has got unexpectedly scrolled.");
+    tooltip.addEventListener("popuphiding", failPopup);
+    editorContainer.addEventListener("scroll", failScroll);
+    editor.on("scroll", () => {
+      if (editor.getScrollInfo().top > topmostScrollPosition) {
+        ok(false, "The editor scrolled back to the breakpoint location.");
+      }
+    });
+
+    let expressionsEvaluated = waitForDebuggerEvents(panel, events.FETCHED_WATCH_EXPRESSIONS);
+    yield openVarPopup(panel, { line: 14, ch: 15 });
+    yield expressionsEvaluated;
+
+    tooltip.removeEventListener("popuphiding", failPopup);
+    editorContainer.removeEventListener("scroll", failScroll);
+
+    yield resumeDebuggerThenCloseAndFinish(panel);
+  });
+}
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -544,21 +544,23 @@ InspectorPanel.prototype = {
   hideNodeMenu: function InspectorPanel_hideNodeMenu() {
     this.nodemenu.hidePopup();
   },
 
   /**
    * Disable the delete item if needed. Update the pseudo classes.
    */
   _setupNodeMenu: function InspectorPanel_setupNodeMenu() {
+    let isSelectionElement = this.selection.isElementNode();
+
     // Set the pseudo classes
     for (let name of ["hover", "active", "focus"]) {
       let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name);
 
-      if (this.selection.isElementNode()) {
+      if (isSelectionElement) {
         let checked = this.selection.nodeFront.hasPseudoClassLock(":" + name);
         menu.setAttribute("checked", checked);
         menu.removeAttribute("disabled");
       } else {
         menu.setAttribute("disabled", "true");
       }
     }
 
@@ -570,33 +572,44 @@ InspectorPanel.prototype = {
       deleteNode.removeAttribute("disabled");
     }
 
     // Disable / enable "Copy Unique Selector", "Copy inner HTML" &
     // "Copy outer HTML" as appropriate
     let unique = this.panelDoc.getElementById("node-menu-copyuniqueselector");
     let copyInnerHTML = this.panelDoc.getElementById("node-menu-copyinner");
     let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter");
-    let selectionIsElement = this.selection.isElementNode();
-    if (selectionIsElement) {
+    if (isSelectionElement) {
       unique.removeAttribute("disabled");
       copyInnerHTML.removeAttribute("disabled");
       copyOuterHTML.removeAttribute("disabled");
     } else {
       unique.setAttribute("disabled", "true");
       copyInnerHTML.setAttribute("disabled", "true");
       copyOuterHTML.setAttribute("disabled", "true");
     }
 
+    // Enable the "edit HTML" item if the selection is an element and the root
+    // actor has the appropriate trait (isOuterHTMLEditable)
     let editHTML = this.panelDoc.getElementById("node-menu-edithtml");
-    if (this.isOuterHTMLEditable && selectionIsElement) {
+    if (this.isOuterHTMLEditable && isSelectionElement) {
       editHTML.removeAttribute("disabled");
     } else {
       editHTML.setAttribute("disabled", "true");
     }
+
+    // Enable the "copy image data-uri" item if the selection is previewable
+    // which essentially checks if it's an image or canvas tag
+    let copyImageData = this.panelDoc.getElementById("node-menu-copyimagedatauri");
+    let markupContainer = this.markup.getContainer(this.selection.nodeFront);
+    if (markupContainer && markupContainer.isPreviewable()) {
+      copyImageData.removeAttribute("disabled");
+    } else {
+      copyImageData.setAttribute("disabled", "true");
+    }
   },
 
   _resetNodeMenu: function InspectorPanel_resetNodeMenu() {
     // Remove any extra items
     while (this.lastNodemenuItem.nextSibling) {
       let toDelete = this.lastNodemenuItem.nextSibling;
       toDelete.parentNode.removeChild(toDelete);
     }
@@ -712,17 +725,29 @@ InspectorPanel.prototype = {
   {
     if (!this.selection.isNode()) {
       return;
     }
 
     this._copyLongStr(this.walker.outerHTML(this.selection.nodeFront));
   },
 
-  _copyLongStr: function(promise) {
+  /**
+   * Copy the data-uri for the currently selected image in the clipboard.
+   */
+  copyImageDataUri: function InspectorPanel_copyImageDataUri()
+  {
+    let container = this.markup.getContainer(this.selection.nodeFront);
+    if (container && container.isPreviewable()) {
+      container.copyImageDataUri();
+    }
+  },
+
+  _copyLongStr: function InspectorPanel_copyLongStr(promise)
+  {
     return promise.then(longstr => {
       return longstr.string().then(toCopy => {
         longstr.release().then(null, console.error);
         clipboardHelper.copyString(toCopy);
       });
     }).then(null, console.error);
   },
 
--- a/browser/devtools/inspector/inspector.xul
+++ b/browser/devtools/inspector/inspector.xul
@@ -47,16 +47,19 @@
       <menuitem id="node-menu-copyouter"
         label="&inspectorHTMLCopyOuter.label;"
         accesskey="&inspectorHTMLCopyOuter.accesskey;"
         oncommand="inspector.copyOuterHTML()"/>
       <menuitem id="node-menu-copyuniqueselector"
         label="&inspectorCopyUniqueSelector.label;"
         accesskey="&inspectorCopyUniqueSelector.accesskey;"
         oncommand="inspector.copyUniqueSelector()"/>
+      <menuitem id="node-menu-copyimagedatauri"
+        label="&inspectorCopyImageDataUri.label;"
+        oncommand="inspector.copyImageDataUri()"/>
       <menuseparator/>
       <menuitem id="node-menu-delete"
         label="&inspectorHTMLDelete.label;"
         accesskey="&inspectorHTMLDelete.accesskey;"
         oncommand="inspector.deleteNode()"/>
       <menuseparator/>
       <menuitem id="node-menu-pseudo-hover"
         label=":hover" type="checkbox"
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -23,16 +23,17 @@ const {gDevTools} = Cu.import("resource:
 const {HTMLEditor} = require("devtools/markupview/html-editor");
 const promise = require("sdk/core/promise");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Templater.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyGetter(this, "DOMParser", function() {
  return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
 });
 loader.lazyGetter(this, "AutocompletePopup", () => {
   return require("devtools/shared/autocomplete-popup").AutocompletePopup
 });
 
@@ -1274,46 +1275,64 @@ function MarkupContainer(aMarkupView, aN
   this._prepareImagePreview();
 }
 
 MarkupContainer.prototype = {
   toString: function() {
     return "[MarkupContainer for " + this.node + "]";
   },
 
-  _prepareImagePreview: function() {
+  isPreviewable: function() {
     if (this.node.tagName) {
       let tagName = this.node.tagName.toLowerCase();
       let srcAttr = this.editor.getAttributeElement("src");
       let isImage = tagName === "img" && srcAttr;
       let isCanvas = tagName === "canvas";
 
+      return isImage || isCanvas;
+    } else {
+      return false;
+    }
+  },
+
+  _prepareImagePreview: function() {
+    if (this.isPreviewable()) {
       // Get the image data for later so that when the user actually hovers over
       // the element, the tooltip does contain the image
-      if (isImage || isCanvas) {
-        let def = promise.defer();
+      let def = promise.defer();
 
-        this.tooltipData = {
-          target: isImage ? srcAttr : this.editor.tag,
-          data: def.promise
-        };
+      this.tooltipData = {
+        target: this.editor.getAttributeElement("src") || this.editor.tag,
+        data: def.promise
+      };
 
-        this.node.getImageData(IMAGE_PREVIEW_MAX_DIM).then(data => {
-          if (data) {
-            data.data.string().then(str => {
-              let res = {data: str, size: data.size};
-              // Resolving the data promise and, to always keep tooltipData.data
-              // as a promise, create a new one that resolves immediately
-              def.resolve(res);
-              this.tooltipData.data = promise.resolve(res);
-            });
-          }
+      this.node.getImageData(IMAGE_PREVIEW_MAX_DIM).then(data => {
+        if (data) {
+          data.data.string().then(str => {
+            let res = {data: str, size: data.size};
+            // Resolving the data promise and, to always keep tooltipData.data
+            // as a promise, create a new one that resolves immediately
+            def.resolve(res);
+            this.tooltipData.data = promise.resolve(res);
+          });
+        }
+      });
+    }
+  },
+
+  copyImageDataUri: function() {
+    // We need to send again a request to gettooltipData even if one was sent for
+    // the tooltip, because we want the full-size image
+    this.node.getImageData().then(data => {
+      if (data) {
+        data.data.string().then(str => {
+          clipboardHelper.copyString(str, this.markup.doc);
         });
       }
-    }
+    });
   },
 
   _buildTooltipContent: function(target, tooltip) {
     if (this.tooltipData && target === this.tooltipData.target) {
       this.tooltipData.data.then(({data, size}) => {
         tooltip.setImageContent(data, size);
       });
       return true;
@@ -2063,8 +2082,13 @@ function parseAttributeValues(attr, doc)
 
   // Attributes return from DOMParser in reverse order from how they are entered.
   return attributes.reverse();
 }
 
 loader.lazyGetter(MarkupView.prototype, "strings", () => Services.strings.createBundle(
   "chrome://browser/locale/devtools/inspector.properties"
 ));
+
+XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
+  return Cc["@mozilla.org/widget/clipboardhelper;1"].
+    getService(Ci.nsIClipboardHelper);
+});
--- a/browser/devtools/markupview/test/browser.ini
+++ b/browser/devtools/markupview/test/browser.ini
@@ -16,8 +16,9 @@ skip-if = true
 [browser_inspector_markup_edit_outerhtml.js]
 [browser_inspector_markup_edit_outerhtml2.js]
 [browser_inspector_markup_mutation.js]
 [browser_inspector_markup_mutation_flashing.js]
 [browser_inspector_markup_navigation.js]
 [browser_inspector_markup_subset.js]
 [browser_inspector_markup_765105_tooltip.js]
 [browser_inspector_markup_950732.js]
+[browser_inspector_markup_964014_copy_image_data.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_inspector_markup_964014_copy_image_data.js
@@ -0,0 +1,131 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that image nodes have the "copy data-uri" contextual menu item enabled
+// and that clicking it puts the image data into the clipboard
+
+let doc;
+let inspector;
+let markup;
+
+const PAGE_CONTENT = [
+  '<div></div>',
+  '<img class="data" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAdYElEQVRogVWYZ3QV5pWuz6y5694pKzNzZ26SyWQyccpkJo5jx8GOHXcbgzHYgOkgJECIYoQAIQkhoYoaoIKEQEK9HUnnqJxedXpvOk3l6OiogkB0XOLkzpo/z/2BV9a6P971re/ffvbe6/32twVB3QFC+oMEdcmMa/cT0uxnwpBCwnaEZc8J7npPMDO2n8Dop3iHNhIc2URUtoVp1XYS2iSiol3E5UeZ12YRNxSz6G1idXaU5Xk9E5MqEvNO4vMu4gk7iYSVpYSRlYSelVk1t+Mqlue1LM1pWE5ouJPQcG9WxaNZNU/ier6aNfF1wspKUM29yTGeLNq4k9CyEJdxa0HC4vwwgoDmWeBBXTJBXTIRXQqTxgPELanM2Y8QUe4krNhBWLadSfVO5saSWLaksGw5yLLpEM62j/F17SQ6cpQ5UzHLgSZuTw1yZ17L6oqd+TkLc3M25uYsLM2ZuTVnYGVOy524gltxOUuzapa+hVlNqFidkbASEbHg6SZubWfJPcgXcT1P5wysTCm5HVdxO6FkcVbK8oIMQXjsAOGxZCL6Z5mfNB5gynSQKUMKE/r9hBW7mBnbz5L1MCvOYzzwZ/A0coZH/gxuW45gbHwfbe372Dt2sWAu5PFkK6tTvdyeFnN3QcPctJK5mIaFuI7lhI47CQ2rCRX3EjJW43LuJLTcndPxYE7Dw1kJtyO9TBirMfZnMXw9FU3nGR5MDPD1kpqliJg7cQWri3rmZ+QsJlQIosYDhPT7CWn3EdYlEf0WJqzZS1C5i1lLKnc86Tzwn+Zh4AxfTeTyf+OFfBHMYslwGHvLR0grX2O44i08A4e467vC41gHD2N93J8Rczcu4e6snNWE4tk5K+VefJT7iVHuz0pYjal4mNDxJKHg3kQ/cVstNtFpBmu30pj3FvXn3sQnO8+TmX4ezo6yGpdzb2GM5cQYs1MqBCHtXsbVuwmodhHU7GFyLJmY6SAzpoPEjAdYdBznrv80D8fP8iScw59mCvnvuVIe+M8wpUjC2voR0srf0XX+VwxVvoVPdJhV7yW+jnfwzVwff1gU8/XiCE/nh3g0K+Z+TMj9mJBHs4M8mRvlaVzJFzNK7keExM3VeIcz0bXtRVSzlo7S16n8/Gf0XfmIeXcNf1yRc2d6hDszWh4uu0hM6hFMGJKZNKYwbT5IwnGEW9507oUyeRTN5slELg9CWTyOnufrqQL+OFPMfycq+GaqmAXTcdx9W9DUv81Yw7vorr6HqvY9LC1biclPcNtSyC17Kfd81dwPNHA/dIN7oSZWI83cn2zl6Ww3f1gU86c5GV9MiJg31WHvO4mi/jNGqtcjrnobYflr1Gf9B/U5L+KVneWb5WFWJgZZmlDwZMXP0owdwe1AOndDp3gYzebJVC5Pp/P4IpbPV7ELfD1TxNPJfL6YKuQP08V8Eyvhy2gRK85MIrJkHN2bMTV/yHjfZ0wO7SEo3IG/ZxvBvt2M9+/D07OH8FAaE9J0YqpsYrrzzBgKmDEXMeeoYNlTx21nPXFtOY7uEwxXfUpf4bsIC9+iO+8VmjKf50bWC9RkPI+u7QD3ox3cnxnh9qSCe3M2bsWsCJ7O5PFVooA/LZTwx/kSvojlcz+cw11/Jnd8Z7njzWbFk8WqJ4dVTw5L1jNMqtIIDCUREO1mSpJEXJbEzGgS0cGdBPs+I9K7g1D3Nvxdn+Fu34qvdzfjAwfwDR7E2ZeCqXs/hu5kjN0HMLYmoW7YzmDJOjpy3qAr5036z79N26k1XD74U+qOP09t+ouM1O5i3tXI10sq7ie0LExoWJwyIPhDoogvYvk8iGRzJ5DJHd8ZbnvOMG89ybT+CBOawwTlKbjFe3EM7MErSiYqP0pMk05ce4JF/XFmZSnMjO5lQrQDb9sGXE3r8bVuxNvyMYGOLQR7dxAS7iHQv4/AwAG8gwcxd+9B1bSZ0cvv0p3/Mo0n/pO2U2sQ5a2j9cQbFG/5MWfXfpfinT+jIf0N2gs2ccvdzJ9uabgbk3JrSsXqnAnBnUAmi650pg1pjCuScIl3Yuz+BG3bBlTNH6Fs2oD65kZ0bVuwCfcQlR9nwZjDsuU8C4ZMFvWfMzm6j1D/dvzdm3E0rcPRtA5/2yYC7Z/8GSLQvQ1vzw68vbvx9e/F0bcHc8dmdNffo6/wBerS/pXGY88zkv8xA2c3UrrleY68+vec3/RTrqW/R/uFzcwYr/J4ZoT7szLufWu/AlPPZnTtH6Ns/hBp43uIqt+kp/wVhFW/Z7T+QzQ3t+AWHWBae5o5Uw4LljwWrfksWPJYMGUzrzvB+MBObG0bsTStx3DtA6w31hPo2MJ451Y8LZvwdmzB27UVZ8dmHJ1bcXZvw927A3ffZ7h6N6C6+hqd2c/Tdvq3DJ/fRFf6enLX/Zyk5/+G8xv/k9asjfSU7CSivsTdSB8P5xQ8Xh5jZVaNoLvsVTpL19BT/jsGLr/JwOW3GbzyDpqbW/ANpzJvPseKq5i7nlKWrIXMGfNYMF9g0VLEoukccdVxHJ2b0TWuRX9tHWON67Hc3ISneye+rp14O3fg796Bt2cH7q5t2Ns+wdb+Cc6uLbh7P8E3sBFX90eMNa5HUfkRPZlrKdz0Cw6++B32/fKvqNy3hv6i7Ygu7WXSeIXVqQEezct5tKTndlyNoLf8Tfoq3mLw8vtI6jcgb9yEvm07gdFjLFrzeTR+mRVnKdOaLIKSDKY1OdxxlbPqqeKW9QLR0cOYbm5EffVDjDc3Y23fhqNzN57e/bi69+HuScLTm4Svbx/e3t24u7bhaP8UZ+eneHo+wdmzDo9wIz7hLhyt++jN/oBT7/4Te3/5Fxx74x+4fvL3DJR9ysDlLSSc1TydH+L+nJQ7CS0rCQOCgUsfILryISO1G5E1bEbXsgvXwGFi2mxu2UtZtpVw113BirOcOWMB86ZCVpzl3HaUMTeWi1e4D8219agbPsLevQfvYCo+0RE8omM4+9OwdR3A1p2EszcJr3AfPuEuvD3b8PdsIdC/FVffx7iFn+Lr38t4fxr6ut00HH6Zwi0/omr/v9N+7jW6S9+h4+K7xB3lfLE4xOqslNszelbnHQgGK9YxWr0JWd1mVNe2Ye05yLQmhxVn2bMs2y+ybCtlWn0en/gkvuEMprV5TGvzCEoyMLVtR167FnXjJ/jFaUQVmUQUZwkpsghIz2DpOYyxIxlzxx6cvfvwC/fi79tBULiNkGg740Pb8Yl34RXuIzhwmPDgCaxN+xkpW8dA8Rt0Fb1MZ+nLXM//NaGxHB7O9nF3RsbtmJG7CS+Ckas7kTfvR30zBU37IbySLG7763g61cLDyA2eTLZi78+g5vRrFCT/gq6SjwiMZjKlycc1cBRd0xZGrnyA5sZWxkczmNYVMKkvIaorIawpwiLMQN+Vhr4tCWvXPrzCffj7dhEU7iAs3sO4eA+h0RR8/Ul4e/YzLUknLs3A27kLzdUPGCx7icHy33A99z/wyT9nNXqT+zMj3IsbuBO3I1D3ZyPtPcXYUC4uXRlhey1LU518uSLlm1U1y+EeJC0nObPnBfa9+48UpLyAtH4fPvEZxofSsXbuxtmbhLM/jZD0HFO6SsZVldiHi3FKL+JXlSFpPkTf5c3Im7Zj696Lo2snnp5dTAynMC1NIyRKxte3j/GBZKZHj5CQHWV6aD+Rvm0Ya99EXPBLbp5+DkXtem45qngSE7I6KebxvA6BS3+Vcdt1pv3tTI23MR1sZ3VxlG8em/jqgYFEsA9x82kObvwJ7/xCQMZnP8HYcYw5XQkB8edMKdLxDBwgLDuDazCToKKCwfpj1Odv51TSGmovfIq84wRm0SlGr+9AXLMBW/deIsNp+Pr2MTFyhLA4lcDAAcZFKUREB5kaSmF6KIkZ0R58TeuQFf+G5s//lcHi3xNT5fAo0sKDSSGPZyUIJpxdxP19zIWETHrbiAU6eHJbxX89sfD0loqovZmumlRO7vwVJz/7KS0X1uEZPMmMOocJaTqhkTSiss+JyrMZrt1NXdZ6Sk+8T27aW+z44F9Y/+pfk536G7qufMbojd0om3dh7UkhIErDN3gQ/+BhxsVHCAwdwSc6hE+YQqB/P5PiZOKjKUwKd6C/8hbN6c/Rnv0ibuERVgPXeRzr52F8CMFiYJhZ3yAJ/wDz4X7uJST81xMLf3pgYnlyEK+2Bml7JoP1aZj6zjKlLWbOWMCCMZcVWx7zY2eJqU/RX7GB0sO/Zucb36Hy9Hoy9r7CKz8V8G/fEfDRa/+Tyqw30fQewz2agV2YhnvwKFFFJh7RMXzD6fhHTuId/hxnfyouYTLBwRQmRw4wM5KCrWkDfXkv0XHuZYztB1jxNvBlQszjxAiChEvElK2HeZ+I+3EVX67o+eaegYcLSmb83QT0DUxYbjBtbSA6Vsm0oYQlewl3nBdYsmSzZDnHtDaToZrNXDn9Ojvf/nuyU15l59of8fPvCnj9lwLOH/s9yr7TeFT5uGXnCCrPManJJ6o6z7g0h4D8HOOKXMYVufhkZ/BJ0hkfPsr4UCqRoUO4OrejrF6LuPw99C37WbBX82VCxOPEEIJZp5gpWx/zPjF3p+Xcm1XwaEnL09tjPF7UPhucwoPMutuIWa8SM1UQtxSTMOWSMGYxqc4gMHqMsDwHVUsa9ec/ISv5d5xLe4czh96grngX6sF8Io6rRGxXmLBUseC9yoKzhrCmiLDuIuGxMsJjZUQM5USMpUR0+YSVZwlJTzI+dBhPbxKW1u2oGj5F25LEtOEij2NCHsYHEMy6RcRdgyyGRrk7o+LRkp6v7jl4esfK4pQcn7EVnfgS8p5CrKNlhMYuMWmsJGEv5+74ZRZthbhEaXiGzmAWZuJXViHryEHdV4isJx+nthab6hJOXSUTrgaWwm0sh9tIOK8RNVwmarpCxFxD1FJLxFpL2FJN1FxJ1FDM5Nh5IvJT+ESHcfcdwNi2B21LEhFNIQ+mOng0I0RgkdbhUjcy5e7j9rSCp3esPL5jxWHqoK7qBAf3vs3xlLWkJ39AZupaGor3YhoqZtbVyIKnjthYHvGxfILK8/ikBfiVVbiV1ZhHKhkbqcAgKcesrMRjqCbqamTGd5O4t5mY8zqTtgaCxjqClnrCtusEbQ34zTUEzJeZsFQxY6tgxpBPUJKOR3QES3cyutZ9hFR5PJhs5VG8G4F3rBGLsppJfw8L0xLik6PcWTYh7C7jxz/6H/zz9/6CH37vL3nuB/+L//jx3/DRmz/jUn4ybt0NVqJ9rASuEjcWElTmE9JWELM0MmVrIWxqxme4jt/UiM/cQMBylbCjkZi3hYXxThaDPSwGe5gPdBHzthFxNBO03yBov07IXk/EWkPUXEbMXMSMMY+gJB2H8BDOgaN4JZnMOi7z1XwfAr2kFKu2hrmJQW7NK1iaU3P3roP+/su89OIP+NEP/44ffP9v+bu/+Qv++i8FPP+zf+JCVgpOXSfLkRHmnHXEzaVMGcqYNtcQdzQRd3Uw6+lhbryfCVcLEVczEed1JtxNxLwtxP0dzHo7iHvambQ3E7U3E3G0EHG2Eva0EHXfJOqsZ8JeRcx2kVlTAWHFKdyDabhERwlITzHnqOCPCz0I1EMFeK0N3JodZnlOTiIuZ3JSTnNzIW+8+Ut+/vN/4SfP/ZDvffd/8w/f+St+++K/U5yXjlbajlPbzIztKtPGMqJj5UyZa5mxNzPjbCXu7mTG20XM18GUp5VJVxMTzhtM2q8zYbtB1NxE2HidgK6OkOkaEUcLk54Oor42JjwtRF0NTDmqmTSVEjNdIKw4hWvwEM7+VHwjJ5izFPGHRCuCMUkREed1bsXELM4MMxESYTC0UFl5ik2fvMWPf/x9nvvxD/n5z37CmpdeYN+uT7l5rRyjug+T4joR4xX8qgu4ZBfwa8qZsFwn5mghYm7Cq69j2tPKlLuZCWcjE/YGIpYGwsYGxvUNBLX1BDTVhAz1TNqbmXC1EHY2M25rZNxSTdhcybgmn7AmG9/Icay9+7D0JOEcOEhMn8vTyXoE5pEiwuZ6liL9rMyMMhMeQiW9SnnJMVKSNvL273/Db196nt+teZFPN3zA2ZMHab9RhlrajF5ag01aiEOSi1NSgFdZTshQz4TlBiHTNXy6WqLWa0Rt9USsV4mY6wibrhIx1BPWXyM61kBUX8uksZ4Jy3XC5mt4xmpxaKtwaC7i0RThkmbhlWZgF6ZgbN+OqX0ntr4kosoM7geqEFjFxUQM9dwO93MvLmUuNIxe1kBF4VGSdnzA5g1v8e7rv+GDN9ewb8cGcs+kcKMum6Gei8j7CzGIc3HJ8vCqSvEoynDIKnArL+PT1RIYq8OprMClvohXU45XU45fXYlffYmgppaI9ioxQx3TY7VE9LX4NNXYVVVY5Bexygqxy/NwSs7iHD6OqWs3uptbMLZvx967l7DkGLccFxA4RaXMmJt4MCHmXnSEOe8gfkMnzdVZ7N38Fq/9+ie8+IsfsuZX/8bH773E8ZR11JSlIu7OQ9F/HllXOsquE8g70pG2ZqDszsE0VIJDVoFTUY55pACrpACXvAiXvAi3rBSPrJyA4gphZTWT2ktEVOX4FeW45eXY5WVYFaXYFUXYFbm4ZJk4xUfRtW5Ddf1jjK1bsXbvxic+yKz+DAK7uJSEo5VHk6MsBQZIeIXcisrQiGo5vv9D3lvzHK/88ru88JO/5YXn/ooPXvk+pw69w7WKJNpqD9JxeTeNhZu4nPUBV/M3IWo8jm2kFI+iAvNIATZpMU5FCV5VKT51GT5VBT5VFUFNLVHdVYKKMvyyYpySQhySYhyKctyaCtzqUjyqAryybByDR1E3f4a8YQP65k8xt2/D1beXCdlRBDPeDhbDQpajAyxEBliKDjEXHMCuuoqwKYuTyW+x/cOf8eGr/4cNr3+fjW/8Mx+/9k9sf/+HHN3+K5LX/wtJa7/LiW0/p+7cBmQ3T2AduoBjtACHtBC3ogSPshSPugKf9jI+TTVedTVuZTUuxSWc0ou4FSX4VBUE9JcJGqoJj1UzrinHryzGJz2HuecI2pu70d/cjq5pM/obH+Pu3cGs5giCh0sKHt9SsjonYWGin4WJfpYmBog6b2KWVtJ86QAlp9ZzOvlVsg6+TvbBNzm171VOJ/2OC0feoezE+1SdfJemC5tRNB/HMXwBt7QQ23Ae1qHzeJSluBUlOBUXcSkrcKku4VZfwaupw6utwaWqxKOtxK+7RMRQw4S5hgnjFSL6CsKaEkLyPKy9R9E070J/czvG5q0YbmzE1b2VKWkKgq/v6vjDPS2PFuUsRoXMjnexEO4l7u8gbG7ENFKKuOkkHVcOIqw7grjxJH21R+m6nEpfzWFGrh9H3nQMU28WflkxAdVFnKN5mMXZWIZycEgLcMqK/gzgUV3Gq67Gq63Bq615VhVtFaGxS0SMV5gwXmHCUPHsS6rOJyjNxNyVgvb6VsZubMbU/Anm5o24OrcQGdqL4NHCCF+syHmyJOH2VB+J8TYS/hamnU2EjM9szyEpxTiYj22kGLe0AvtwKebBQmziQhyiPOyiXJxDebhH8rAP52IaOINZnI1DkodtJBeHtACPshSftgq/7go+TTUu1SUcikq8mkt4NZUEtBUEtRWEtKWE1AUE5Nn4JRk4+1MxtG5H2/gxumvP9kfGxg9xtW4i0r8LwYPZAb64LeHr21Lux/tZDLWR8DYx7Wggaqpm1tXIhLGaoLaScU0FfsVFnCMF2MR5uIYu4B7JwyY6i0l4CmNfBmN9GRgHMrANZeOS5eFRXMCtKMSjLMGruohHXYFHXYVbVYlb9cxSfaoK/KqL+FSFBBR5+GRn8AwdxTV4AHP7NgzNH6O/thZ9/Xuor7yJ6vIb2BrXEe7ZjuDx3CBf3hriyyUxd6c6SfgaiTuvErPXErPVELPVMGW6TERfQUBVglt6AYf4HHZRDo6hbBxDZ7EOnsbcfxJz/0msg6ewDWfilOTgluUS0pcQ0BTjVhTikBZgkxbiUpbh01YxPnaFce0V/OpKfMpSvIo8vJJMXENHsAuTsHZvx9iyAUPzWgzX3kNT8zry8jXIyn6Lqe49Au2bETyZFfJkXsjj2R5uhZqIOaqZNFcxZakkZq0iMlZKVFfyrKyKPHyyXHzSc7hHsnCIT2EXZWAXZWAbPIllMB2r6CSO4dO4ZefwKs4T1BUT0BTjURXgkhfgVBbjUZcR0FcRNFwmqLvMuKYCn6IIjyQH99Dn2Pr3Y+78DEPbBvQ33kV77Q00Nb9DWfky0osvoSx/BWvDWsbbtyJ4ONPF49keHs50sBxsZMZ+mQlzGZOmZ5oyXGRirIigOo+AModxxTO5Rk5iEqZhEx3HLv4cm+g4xt4jjPWmYRlMxyXNYlyTj0t+Do8ij4CmmHF9KaGxCgJjlfj1FQS05QS1lQTUZXhk+ThHzmAfTMPcvRt960Z0N95H0/B7lNVrkFa+iPTir5GXr0F35S1czRsJ9+xCcH+ylccznTyYamPRV8+0pYKooZSorpiw9gJRfSFhbR5BVTYBxVnGZafxSzLwjqTjHvocu/gYdvExbKKjWAePYRk8hlV0Aos4A+vQKbyK8/jVBYT0F4mYKoiYKgjqy/GpS/AoS/DIi3FK87ENn8UuSsc+mIq1dy/mjs0YW9ahqHmNweL/pPf8TxkqeQH1lTcxNqzF1bKVUO9eBKvRVh5MtXF/spVFXz0xaxUTxotE9YWENPmENOcJqnMYV2bil58mIMvAJ0nHPXwch+gIloHDmPtTMQkPYew7xFhvKoa+wxiFxxkTHieoLSSkKyakv0hIf5FxfSk+TRFuRSFO2QXc8gIcklxsw5k4hk7gFKdh69uHsW0z2utrUda+wXDFbxkpexlt3TvYmjfh6tzOuHA/U5JjCFZCLaxGW1mNtrIcuMas4wpT5nIiY0UEtRcIqnMJKLMIKM4QUJxhXH6KgCwd7+hx3MNHcQ0dwSlOwypK/RbmMKb+I5j6P8c8cAK37BxuWS5ueT5ueT5OeR4O2XlsknNYR3Oe3SU5OEazcA2fwj18HFtfMvqWrSivrkVy5R1GL72Bsu49rK1b8PTtJTBwgNDIEaZVpxEs+2+yEmrhbqSNlVAzi54G4vbLTJrKiIwVEVDm4JWdwSM5iWvkBJ6Rz/+/wJ3iVByiQ1hFqZj7UzH3H8YsPPYMoP8kqs4jaLqPoRdmYBw8g3k4C8tI9p9ll+bglOXgkmbhGj2NY/A4xq5k9Dd3or62GVndRygbNmJs3Y6nP4XA0BECI58TVpxhSn8OwYKvmeXxFu5EOrgbbWcl1MK851krTZrKCChzcEtOYR08gqH3ILqu/Wg7k9B37kXXsQdN2w40bTtQt+5E1bILRctulC37UbUfRNWWymhzCrKWw6i70zEMZGIdPod99FkFbJJszMNncEjP4pJl4hjOwNSdhq41hbHWZMztB7B1peLqO4p/OJ2wPPPZukWdQ1Sfx5SlCMGcu5kFXzMr4Q7uT/Vwf6Kb5UATs/aaZxCGYsZV53AOp2PoPYi6fS+Kll0omrchb9rCSMNGRq5tYKThE0avbWH0+jZkTXuRtySjbE1F232Csb4MTKIsbCO5OKX5uOUFuBT5uBTnMY+cwiY9jVOWiXXoFKa+4xi7jmHvO4l3MJOINI+o4gJT6iIm9UVEdAUE9QWExgqJmIsQxJxNxN1N3Brv4GFMyMNYH7eCN5l11jJtrWLKXE5YX4hPnol9+Dgm4SH03cmoO3ajaH0GIW/+FOXN7ahbd6Pt3I+hJ+3bR+00Lsl5XJJ8XJICXJKCPwN4FIV4VPk4FWdxKTNxK7LwyHPwSXIJSi8QlRcxpSojrqsiPnaJ2NglpgwVhPQl+PRFeHUFuLTnEUzaGpl23GDJ38bDmJBHM/3fAlxl2lpFSFdMQJ2HT56FS5KOXXwMc38q+p4kNJ270HXsQt+1E2PPfqz9qdjFx/BKMgko8gipCwmoSvApinBJCrAPfzsbSfJwywvwqi8QNFzAqz+HX5NLSFdEzFTBnLWaJUs9C6Y65k1XmbPUM2e5SsxSTdR0iYCxFLe2AKs6l/8HXK32/y5m8HIAAAAASUVORK5CYII=" />',
+  '<canvas class="canvas" width="600" height="600"></canvas>'
+].join("\n");
+
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onload(evt) {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,markup view copy image as data-uri";
+}
+
+function createDocument() {
+  doc.body.innerHTML = PAGE_CONTENT;
+  let context = doc.querySelector(".canvas").getContext("2d");
+  context.beginPath();
+  context.moveTo(300, 0);
+  context.lineTo(600, 600);
+  context.lineTo(0, 600);
+  context.closePath();
+  context.fillStyle = "#ffc821";
+  context.fill();
+
+  openInspector().then(startTests);
+}
+
+function startTests(aInspector, aToolbox) {
+  inspector = aInspector;
+  markup = inspector.markup;
+
+  Task.spawn(function() {
+    yield selectNode("div", inspector);
+    yield assertCopyImageDataNotAvailable();
+
+    yield selectNode("img", inspector);
+    yield assertCopyImageDataAvailable();
+    yield triggerCopyImageUrlAndWaitForClipboard(doc.querySelector("img").src);
+
+    yield selectNode("canvas", inspector);
+    yield assertCopyImageDataAvailable();
+    let canvas = doc.querySelector(".canvas");
+    yield triggerCopyImageUrlAndWaitForClipboard(canvas.toDataURL());
+
+    // Check again that the menu isn't available on the DIV (to make sure our
+    // menu updating mechanism works)
+    yield selectNode("div", inspector);
+    yield assertCopyImageDataNotAvailable();
+  }).then(null, ok.bind(null, false)).then(endTests);
+}
+
+function endTests() {
+  doc = inspector = markup = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function assertCopyImageDataNotAvailable() {
+  return openNodeMenu().then(menu => {
+    let item = menu.getElementsByAttribute("id", "node-menu-copyimagedatauri")[0];
+    ok(item, "The menu item was found in the contextual menu");
+    is(item.getAttribute("disabled"), "true", "The menu item is disabled");
+  }).then(closeNodeMenu);
+}
+
+function assertCopyImageDataAvailable() {
+  return openNodeMenu().then(menu => {
+    let item = menu.getElementsByAttribute("id", "node-menu-copyimagedatauri")[0];
+    ok(item, "The menu item was found in the contextual menu");
+    is(item.getAttribute("disabled"), "", "The menu item is enabled");
+  }).then(closeNodeMenu);
+}
+
+function openNodeMenu() {
+  let deferred = promise.defer();
+
+  inspector.nodemenu.addEventListener("popupshown", function onOpen() {
+    inspector.nodemenu.removeEventListener("popupshown", onOpen, false);
+    deferred.resolve(inspector.nodemenu);
+  }, false);
+  inspector.nodemenu.hidden = false;
+  inspector.nodemenu.openPopup();
+
+  return deferred.promise;
+}
+
+function closeNodeMenu() {
+  let deferred = promise.defer();
+
+  inspector.nodemenu.addEventListener("popuphidden", function onClose() {
+    inspector.nodemenu.removeEventListener("popuphidden", onClose, false);
+    deferred.resolve(inspector.nodemenu);
+  }, false);
+  inspector.nodemenu.hidden = true;
+  inspector.nodemenu.hidePopup();
+
+  return deferred.promise;
+}
+
+function triggerCopyImageUrlAndWaitForClipboard(expected) {
+  let deferred = promise.defer();
+
+  SimpleTest.waitForClipboard(expected, () => {
+    markup.getContainer(inspector.selection.nodeFront).copyImageDataUri();
+  }, () => {
+    ok(true, "The clipboard contains the expected value " + expected.substring(0, 50) + "...");
+    deferred.resolve();
+  }, () => {
+    ok(false, "The clipboard doesn't contain the expected value " + expected.substring(0, 50) + "...");
+    deferred.resolve();
+  });
+
+  return deferred.promise;
+}
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils;
 
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let TargetFactory = devtools.TargetFactory;
 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
+let promise = devtools.require("sdk/core/promise");
 
 // Clear preferences that may be set during the course of tests.
 function clearUserPrefs() {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
 }
 
@@ -22,8 +23,43 @@ SimpleTest.registerCleanupFunction(() =>
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
 function getContainerForRawNode(markupView, rawNode) {
   let front = markupView.walker.frontForRawNode(rawNode);
   let container = markupView.getContainer(front);
   return container;
 }
+
+/**
+ * Open the toolbox, with the inspector tool visible.
+ * @return a promise that resolves when the inspector is ready
+ */
+function openInspector() {
+  let deferred = promise.defer();
+
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
+    let inspector = toolbox.getCurrentPanel();
+    inspector.once("inspector-updated", () => {
+      deferred.resolve(inspector, toolbox);
+    });
+  }).then(null, console.error);
+
+  return deferred.promise;
+}
+
+/**
+ * Set the inspector's current selection to the first match of the given css
+ * selector
+ * @return a promise that resolves when the inspector is updated with the new
+ * node
+ */
+function selectNode(selector, inspector) {
+  let deferred = promise.defer();
+  let node = content.document.querySelector(selector);
+  ok(node, "A node was found for selector " + selector + ". Selecting it now");
+  inspector.selection.setNode(node, "test");
+  inspector.once("inspector-updated", () => {
+    deferred.resolve(node);
+  });
+  return deferred.promise;
+}
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -84,16 +84,17 @@ const CM_MAPPING = [
   "getSelection",
   "replaceSelection",
   "extendSelection",
   "undo",
   "redo",
   "clearHistory",
   "openDialog",
   "refresh",
+  "getScrollInfo",
   "getOption",
   "setOption"
 ];
 
 const { cssProperties, cssValues, cssColors } = getCSSKeywords();
 
 const editors = new WeakMap();
 
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -61,8 +61,9 @@ support-files = browser_ruleview_pseudoe
 support-files =
   sourcemaps.html
   sourcemaps.css
   sourcemaps.css.map
   sourcemaps.scss
 [browser_computedview_original_source_link.js]
 [browser_bug946331_close_tooltip_on_new_selection.js]
 [browser_bug942297_user_property_reset.js]
+[browser_styleinspector_outputparser.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_outputparser.js
@@ -0,0 +1,194 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test expected outputs of the output-parser's parseCssProperty function.
+
+// This is more of a unit test than a mochitest-browser test, but can't be
+// tested with an xpcshell test as the output-parser requires the DOM to work.
+
+let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {OutputParser} = devtools.require("devtools/output-parser");
+
+const COLOR_CLASS = "color-class";
+const URL_CLASS = "url-class";
+
+function test() {
+  waitForExplicitFinish();
+
+  let testData = [
+    {
+      name: "width",
+      value: "100%",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 0);
+        is(fragment.textContent, "100%");
+      }
+    },
+    {
+      name: "width",
+      value: "blue",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 0);
+      }
+    },
+    {
+      name: "content",
+      value: "'red url(test.png) repeat top left'",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 0);
+      }
+    },
+    {
+      name: "content",
+      value: "\"blue\"",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 0);
+      }
+    },
+    {
+      name: "margin-left",
+      value: "url(something.jpg)",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 0);
+      }
+    },
+    {
+      name: "background-color",
+      value: "transparent",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 1);
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.textContent, "transparent");
+      }
+    },
+    {
+      name: "color",
+      value: "red",
+      test: fragment => {
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.textContent, "red");
+      }
+    },
+    {
+      name: "color",
+      value: "#F06",
+      test: fragment => {
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.textContent, "#F06");
+      }
+    },
+    {
+      name: "border-top-left-color",
+      value: "rgba(14, 255, 20, .5)",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 1);
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.textContent, "rgba(14, 255, 20, .5)");
+      }
+    },
+    {
+      name: "border",
+      value: "80em dotted pink",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 1);
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.querySelector("." + COLOR_CLASS).textContent, "pink");
+      }
+    },
+    {
+      name: "color",
+      value: "red !important",
+      test: fragment => {
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.textContent, "red !important");
+      }
+    },
+    {
+      name: "background",
+      value: "red url(test.png) repeat top left",
+      test: fragment => {
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.querySelectorAll("." + URL_CLASS).length, 1);
+        is(fragment.querySelector("." + COLOR_CLASS).textContent, "red");
+        is(fragment.querySelector("." + URL_CLASS).textContent, "test.png");
+        is(fragment.querySelectorAll("*").length, 2);
+      }
+    },
+    {
+      name: "background",
+      value: "blue url(test.png) repeat top left !important",
+      test: fragment => {
+        is(fragment.querySelectorAll("." + COLOR_CLASS).length, 1);
+        is(fragment.querySelectorAll("." + URL_CLASS).length, 1);
+        is(fragment.querySelector("." + COLOR_CLASS).textContent, "blue");
+        is(fragment.querySelector("." + URL_CLASS).textContent, "test.png");
+        is(fragment.querySelectorAll("*").length, 2);
+      }
+    },
+    {
+      name: "list-style-image",
+      value: "url(\"images/arrow.gif\")",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 1);
+        is(fragment.querySelector("." + URL_CLASS).textContent, "images/arrow.gif");
+      }
+    },
+    {
+      name: "list-style-image",
+      value: "url(\"images/arrow.gif\")!important",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 1);
+        is(fragment.querySelector("." + URL_CLASS).textContent, "images/arrow.gif");
+        is(fragment.textContent, "url('images/arrow.gif')!important");
+      }
+    },
+    {
+      name: "-moz-binding",
+      value: "url(http://somesite.com/path/to/binding.xml#someid)",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 1);
+        is(fragment.querySelectorAll("." + URL_CLASS).length, 1);
+        is(fragment.querySelector("." + URL_CLASS).textContent, "http://somesite.com/path/to/binding.xml#someid");
+      }
+    },
+    {
+      name: "background",
+      value: "linear-gradient(to right, rgba(183,222,237,1) 0%, rgba(33,180,226,1) 30%, rgba(31,170,217,.5) 44%, #F06 75%, red 100%)",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 5);
+        let allSwatches = fragment.querySelectorAll("." + COLOR_CLASS);
+        is(allSwatches.length, 5);
+        is(allSwatches[0].textContent, "rgba(183,222,237,1)");
+        is(allSwatches[1].textContent, "rgba(33,180,226,1)");
+        is(allSwatches[2].textContent, "rgba(31,170,217,.5)");
+        is(allSwatches[3].textContent, "#F06");
+        is(allSwatches[4].textContent, "red");
+      }
+    },
+    {
+      name: "background",
+      value: "-moz-radial-gradient(center 45deg, circle closest-side, orange 0%, red 100%)",
+      test: fragment => {
+        is(fragment.querySelectorAll("*").length, 2);
+        let allSwatches = fragment.querySelectorAll("." + COLOR_CLASS);
+        is(allSwatches.length, 2);
+        is(allSwatches[0].textContent, "orange");
+        is(allSwatches[1].textContent, "red");
+      }
+    }
+  ];
+
+  let parser = new OutputParser();
+  for (let i = 0; i < testData.length; i ++) {
+    let data = testData[i];
+    info("Output-parser test data " + i + ". {" + data.name + " : " + data.value + ";}");
+    data.test(parser.parseCssProperty(data.name, data.value, {
+      colorClass: COLOR_CLASS,
+      urlClass: URL_CLASS,
+      defaultColorType: false
+    }));
+  }
+
+  finish();
+}
--- a/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
@@ -1,7 +1,9 @@
 <!-- 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/. -->
 
-<!ENTITY aboutAccountsSetup.description "Sign in to backup and sync your tabs, bookmarks, and more.">
-<!ENTITY aboutAccountsSetup.startButton.label "Get Started">
-<!ENTITY aboutAccountsSetup.useOldSync.label "Using Sync on an older version of &brandShortName;?">
+<!ENTITY aboutAccounts.pageTitle "Welcome to Sync">
+
+<!ENTITY aboutAccountsSetup.description "Sign in to sync your tabs, bookmarks, passwords &amp; more.">
+<!ENTITY aboutAccountsSetup.startButton.label "Get started">
+<!ENTITY aboutAccountsSetup.useOldSync.label "Using an older version of Sync?">
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -688,16 +688,17 @@ just addresses the organization to follo
 
 <!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
 <!ENTITY customizeMode.menuAndToolbars.label "Menu and toolbars">
 <!ENTITY customizeMode.menuAndToolbars.header2 "Additional Tools and Features">
 <!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
 <!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
 <!ENTITY customizeMode.restoreDefaults "Restore Defaults">
 <!ENTITY customizeMode.toolbars "Show / Hide Toolbars">
+<!ENTITY customizeMode.titlebar "Title Bar">
 
 <!ENTITY social.chatBar.commandkey "c">
 <!ENTITY social.chatBar.label "Focus chats">
 <!ENTITY social.chatBar.accesskey "c">
 
 <!ENTITY social.markpageMenu.accesskey "P">
 <!ENTITY social.markpageMenu.label "Save Page To…">
 <!ENTITY social.marklinkMenu.accesskey "L">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -198,19 +198,25 @@ update.downloadAndInstallButton.accesske
 
 # RSS Pretty Print
 feedShowFeedNew=Subscribe to '%S'…
 
 menuOpenAllInTabs.label=Open All in Tabs
 
 # History menu
 menuRestoreAllTabs.label=Restore All Tabs
+# LOCALIZATION NOTE (menuRestoreAllTabsSubview.label): like menuRestoreAllTabs.label,
+# but used in the history subview in the panel UI, so needs to mention these are *closed* tabs.
+menuRestoreAllTabsSubview.label=Restore Closed Tabs
 # LOCALIZATION NOTE (menuRestoreAllWindows, menuUndoCloseWindowLabel, menuUndoCloseWindowSingleTabLabel):
 # see bug 394759
 menuRestoreAllWindows.label=Restore All Windows
+# LOCALIZATION NOTE (menuRestoreAllWindowsSubview.label): like menuRestoreAllWindows.label,
+# but used in the history subview in the panel UI, so needs to mention these are *closed* windows.
+menuRestoreAllWindowsSubview.label=Restore Closed Windows
 # LOCALIZATION NOTE (menuUndoCloseWindowLabel): Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 Window Title, #2 Number of tabs
 menuUndoCloseWindowLabel=#1 (and #2 other tab);#1 (and #2 other tabs)
 menuUndoCloseWindowSingleTabLabel=#1
 
 # Unified Back-/Forward Popup
 tabHistory.current=Stay on this page
@@ -494,13 +500,13 @@ mixedContentBlocked.unblock.accesskey = 
 slowStartup.message = %S seems slow… to… start.
 slowStartup.helpButton.label = Learn How to Speed It Up
 slowStartup.helpButton.accesskey = L
 slowStartup.disableNotificationButton.label = Don't Tell Me Again
 slowStartup.disableNotificationButton.accesskey = A
 
 
 # LOCALIZATION NOTE(tipSection.tip0): %1$S will be replaced with the text defined
-# in tipSection.tip0.hint, %2$S will be replaced with a hyperlink containing the
-# text defined in tipSection.tip0.learnMore.
-tipSection.tip0 = %1$S: You can customize Firefox to work the way you do. Simply drag any of the above to the menu or toolbar. %2$S about customizing Firefox.
+# in tipSection.tip0.hint, %2$S will be replaced with brandShortName, %3$S will
+# be replaced with a hyperlink containing the text defined in tipSection.tip0.learnMore.
+tipSection.tip0 = %1$S: You can customize %2$S to work the way you do. Simply drag any of the above to the menu or toolbar. %3$S about customizing %2$S.
 tipSection.tip0.hint = Hint
 tipSection.tip0.learnMore = Learn more
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -1,89 +1,98 @@
 # 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/.
 
 history-panelmenu.label = History
-# LOCALIZATION NOTE (history-panelmenu.tooltiptext): Use the unicode ellipsis char,
-# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
-history-panelmenu.tooltiptext = History… (%S)
+# LOCALIZATION NOTE(history-panelmenu.tooltiptext2): %S is the keyboard shortcut
+history-panelmenu.tooltiptext2 = Show your history (%S)
 
 privatebrowsing-button.label = New Private Window
 # LOCALIZATION NOTE(privatebrowsing-button.tooltiptext): %S is the keyboard shortcut
 privatebrowsing-button.tooltiptext = Open a new Private Browsing window (%S)
 
 save-page-button.label = Save Page
-# LOCALIZATION NOTE(save-page-button.tooltiptext): %S is the keyboard shortcut
-save-page-button.tooltiptext = Save this page (%S)
+# LOCALIZATION NOTE(save-page-button.tooltiptext2): %S is the keyboard shortcut
+# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
+save-page-button.tooltiptext2 = Save this page… (%S)
 
 find-button.label = Find
-# LOCALIZATION NOTE(find-button.tooltiptext): %S is the keyboard shortcut
-find-button.tooltiptext = Find in this page (%S)
+# LOCALIZATION NOTE(find-button.tooltiptext2): %S is the keyboard shortcut.
+# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
+find-button.tooltiptext2 = Find in this page… (%S)
 
 open-file-button.label = Open File
-# LOCALIZATION NOTE(open-file-button.tooltiptext): %S is the keyboard shortcut
-open-file-button.tooltiptext = Open file (%S)
+# LOCALIZATION NOTE (open-file-button.tooltiptext2): %S is the keyboard shortcut.
+# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
+open-file-button.tooltiptext2 = Open file… (%S)
 
 developer-button.label = Developer
 # LOCALIZATION NOTE(developer-button.tooltiptext): %S is the keyboard shortcut
 developer-button.tooltiptext = Web Developer Tools (%S)
 
 add-ons-button.label = Add-ons
-# LOCALIZATION NOTE(add-ons-button.tooltiptext): %S is the keyboard shortcut
-add-ons-button.tooltiptext = Add-ons Manager (%S)
+# LOCALIZATION NOTE(add-ons-button.tooltiptext2): %S is the keyboard shortcut
+add-ons-button.tooltiptext2 = Open Add-ons Manager (%S)
 
 switch-to-metro-button2.label = Windows 8 Touch
 # LOCALIZATION NOTE(switch-to-metro-button2.tooltiptext): %S is the brand short name
 switch-to-metro-button2.tooltiptext = Relaunch in %S for Windows 8 Touch
 
 preferences-button.label = Preferences
-# LOCALIZATION NOTE (preferences-button.tooltiptext): Use the unicode ellipsis char,
-# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
-preferences-button.tooltiptext = Preferences…
+preferences-button.tooltiptext2 = Open Preferences
+preferences-button.tooltiptext.withshortcut = Open Preferences (%S)
 # LOCALIZATION NOTE (preferences-button.labelWin): Windows-only label for Options
 preferences-button.labelWin = Options
 # LOCALIZATION NOTE (preferences-button.tooltipWin): Windows-only tooltip for Options
-preferences-button.tooltipWin = Options
+preferences-button.tooltipWin2 = Open Options
 
 zoom-controls.label = Zoom Controls
 zoom-controls.tooltiptext = Zoom Controls
 
 zoom-out-button.label = Zoom out
-zoom-out-button.tooltiptext = Zoom out
+# LOCALIZATION NOTE(zoom-out-button.tooltiptext2): %S is the keyboard shortcut.
+zoom-out-button.tooltiptext2 = Zoom out (%S)
 
 # LOCALIZATION NOTE(zoom-reset-button.label): %S is the current zoom level,
 # %% will be displayed as a single % character (% is commonly used to define
 # format specifiers, so it needs to be escaped).
 zoom-reset-button.label = %S%%
-zoom-reset-button.tooltiptext = Reset zoom level
+# LOCALIZATION NOTE(zoom-reset-button.tooltiptext2): %S is the keyboard shortcut.
+zoom-reset-button.tooltiptext2 = Reset zoom level (%S)
 
 zoom-in-button.label = Zoom in
-zoom-in-button.tooltiptext = Zoom in
+# LOCALIZATION NOTE(zoom-in-button.tooltiptext2): %S is the keyboard shortcut.
+zoom-in-button.tooltiptext2 = Zoom in (%S)
 
 edit-controls.label = Edit Controls
 edit-controls.tooltiptext = Edit Controls
 
 cut-button.label = Cut
-cut-button.tooltiptext = Cut
+# LOCALIZATION NOTE(cut-button.tooltiptext2): %S is the keyboard shortcut.
+cut-button.tooltiptext2 = Cut (%S)
 
 copy-button.label = Copy
-copy-button.tooltiptext = Copy
+# LOCALIZATION NOTE(copy-button.tooltiptext2): %S is the keyboard shortcut.
+copy-button.tooltiptext2 = Copy (%S)
 
 paste-button.label = Paste
-paste-button.tooltiptext = Paste
+# LOCALIZATION NOTE(paste-button.tooltiptext2): %S is the keyboard shortcut.
+paste-button.tooltiptext2 = Paste (%S)
 
 # LOCALIZATION NOTE (feed-button.tooltiptext): Use the unicode ellipsis char,
 # \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
 feed-button.label = Subscribe
 feed-button.tooltiptext = Subscribe to this page…
 
 characterencoding-button.label = Character Encoding
-characterencoding-button.tooltiptext = Character encoding
+characterencoding-button.tooltiptext2 = Show Character Encoding options
 
 email-link-button.label = Email Link
-email-link-button.tooltiptext = Email Link
+# LOCALIZATION NOTE (email-link-button.tooltiptext2): Use the unicode ellipsis char,
+# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
+email-link-button.tooltiptext2 = Email Link…
 
 # LOCALIZATION NOTE(quit-button.tooltiptext.linux): %S is the keyboard shortcut
 quit-button.tooltiptext.linux = Quit (%S)
 # LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.mac = Quit %1$S (%2$S)
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/inspector.dtd
@@ -12,8 +12,10 @@
 
 <!ENTITY inspectorHTMLDelete.label          "Delete Node">
 <!ENTITY inspectorHTMLDelete.accesskey      "D">
 
 <!ENTITY inspector.selectButton.tooltip     "Select element with mouse">
 
 <!ENTITY inspectorSearchHTML.label          "Search HTML">
 <!ENTITY inspectorSearchHTML.key            "F">
+
+<!ENTITY inspectorCopyImageDataUri.label       "Copy Image Data-URL">
--- a/browser/locales/en-US/chrome/browser/syncSetup.properties
+++ b/browser/locales/en-US/chrome/browser/syncSetup.properties
@@ -55,8 +55,11 @@ continue.label = Continue
 unlink.verify.title = Unlink Browser
 unlink.verify.heading = Are you sure?
 unlink.verify.description = This browser will stop syncing with your other computers, but won't delete any of your local browsing data.
 
 relink.verify.title = Merge Warning
 relink.verify.heading = Are you sure you want to sign in to Sync?
 # LOCALIZATION NOTE (relink.verify.description): Email address of a user previously signed into sync.
 relink.verify.description = A different user was previously signed in to Sync on this device. Signing in will merge this browser's bookmarks, passwords and other settings with %S
+
+
+manage.pageTitle = Manage Sync
--- a/browser/metro/base/content/appbar.js
+++ b/browser/metro/base/content/appbar.js
@@ -20,26 +20,35 @@ var Appbar = {
     window.addEventListener('MozContextActionsChange', this, false);
 
     // browser events we need to update button state on
     Elements.browsers.addEventListener('URLChanged', this, true);
     Elements.tabList.addEventListener('TabSelect', this, true);
 
     // tilegroup selection events for all modules get bubbled up
     window.addEventListener("selectionchange", this, false);
+    Services.obs.addObserver(this, "metro_on_async_tile_created", false);
 
     // gather appbar telemetry data
     try {
       UITelemetry.addSimpleMeasureFunction("metro-appbar",
                                            this.getAppbarMeasures.bind(this));
     } catch (ex) {
       // swallow exception that occurs if metro-appbar measure is already set up
     }
   },
 
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "metro_on_async_tile_created":
+        this._updatePinButton();
+        break;
+    }
+  },
+
   handleEvent: function Appbar_handleEvent(aEvent) {
     switch (aEvent.type) {
       case 'URLChanged':
       case 'TabSelect':
         this.update();
         this.flushActiveTileset(aEvent.lastTab);
         break;
 
--- a/browser/metro/base/content/contenthandlers/SelectionHandler.js
+++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js
@@ -295,22 +295,28 @@ var SelectionHandler = {
     }
     this.closeSelection();
   },
 
   /*
    * Called any time SelectionHelperUI would like us to
    * recalculate the selection bounds.
    */
-  _onSelectionUpdate: function _onSelectionUpdate() {
+  _onSelectionUpdate: function _onSelectionUpdate(aMsg) {
     if (!this._contentWindow) {
       this._onFail("_onSelectionUpdate was called without proper view set up");
       return;
     }
 
+    if (aMsg && aMsg.isInitiatedByAPZC) {
+      let {offset: offset} = Content.getCurrentWindowAndOffset(
+        this._targetCoordinates.x, this._targetCoordinates.y);
+      this._contentOffset = offset;
+    }
+
     // Update the position of our selection monocles
     this._updateSelectionUI("update", true, true);
   },
 
   /*
    * Called if for any reason we fail during the selection
    * process. Cancels the selection.
    */
@@ -382,16 +388,17 @@ var SelectionHandler = {
     this._clearTimers();
     this._cache = null;
     this._contentWindow = null;
     this._targetElement = null;
     this._selectionMoveActive = false;
     this._contentOffset = null;
     this._domWinUtils = null;
     this._targetIsEditable = false;
+    this._targetCoordinates = null;
     sendSyncMessage("Content:HandlerShutdown", {});
   },
 
   /*
    * Find content within frames - cache the target nsIDOMWindow,
    * client coordinate offset, target element, and dom utils interface.
    */
   _initTargetInfo: function _initTargetInfo(aX, aY) {
@@ -404,16 +411,21 @@ var SelectionHandler = {
     if (!contentWindow) {
       return false;
     }
     this._targetElement = element;
     this._contentWindow = contentWindow;
     this._contentOffset = offset;
     this._domWinUtils = utils;
     this._targetIsEditable = Util.isEditable(this._targetElement);
+    this._targetCoordinates = {
+      x: aX,
+      y: aY
+    };
+
     return true;
   },
 
   /*
    * _calcNewContentPosition - calculates the distance the browser should be
    * raised to move the focused form input out of the way of the soft
    * keyboard.
    *
@@ -528,17 +540,17 @@ var SelectionHandler = {
         this._onSelectionCopy(json);
         break;
 
       case "Browser:SelectionDebug":
         this._onSelectionDebug(json);
         break;
 
       case "Browser:SelectionUpdate":
-        this._onSelectionUpdate();
+        this._onSelectionUpdate(json);
         break;
 
       case "Browser:RepositionInfoRequest":
         // This message is sent simultaneously with a tap event.
         // Wait a bit to make sure we have the most up-to-date tap co-ordinates
         // before a call to _calcNewContentPosition() which accesses them.
         content.setTimeout (function () {
           SelectionHandler._repositionInfoRequest(json);
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectionHelperUI.js
@@ -356,17 +356,19 @@ var SelectionHelperUI = {
         break;
 
       case "apzc-transform-end":
         // The selection range callback will check to see if the new
         // position is off the screen, in which case it shuts down and
         // clears the selection.
         if (this.isActive && this.layerMode == kContentLayer) {
           this._showAfterUpdate = true;
-          this._sendAsyncMessage("Browser:SelectionUpdate", {});
+          this._sendAsyncMessage("Browser:SelectionUpdate", {
+            isInitiatedByAPZC: true
+          });
         }
         break;
       }
   },
 
   /*
    * Public apis
    */
--- a/browser/metro/base/tests/mochiperf/browser_apzc.js
+++ b/browser/metro/base/tests/mochiperf/browser_apzc.js
@@ -161,18 +161,18 @@ gTests.push({
                           "Measures frame rate of apzc fling for a large page of text using FTR.");
     PerfTest.declareNumericalResult(frameRate, "fps");
 
     PerfTest.declareTest("F5E83238-383F-4665-8415-878AA027B4A3",
                           "fling jank", "graphics", "apzc",
                           "Displays the low, high, and average FTR frame intervals when flinging a page of text.");
     PerfTest.declareNumericalResults([
       { value: low, desc: "low" },
-      { value: high, desc: "high" },
-      { value: ave, desc: "average" }
+      { value: high, desc: "high", shareAxis: 0 },
+      { value: ave, desc: "average", shareAxis: 0 }
     ]);
   }
 });
 
 /*
  * touch scroll the about:start page back and forth averaging
  * values across the entire set. Generally about:start should
  * only need to be painted once. Exercises composition and apzc.
@@ -208,13 +208,13 @@ gTests.push({
     PerfTest.declareFrameRateResult(intervals.length, msec, "fps");
 
     let results = PerfTest.computeHighLowBands(intervals, .1);
     PerfTest.declareTest("2E60F8B5-8925-4628-988E-E4C0BC6B34C7",
                           "about:start jank", "graphics", "apzc",
                           "Displays the low, high, and average FTR frame intervals for about:start.");
     PerfTest.declareNumericalResults([
       { value: results.low, desc: "low" },
-      { value: results.high, desc: "high" },
-      { value: results.ave, desc: "average" }
+      { value: results.high, desc: "high", shareAxis: 0 },
+      { value: results.ave, desc: "average", shareAxis: 0 }
     ]);
   }
-});
\ No newline at end of file
+});
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_selection_frame_in_scrollable_container.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      body {
+        padding: 40px;
+      }
+
+      .scrollable-container {
+        border: 1px solid blue;
+        overflow: auto;
+        max-height: 100px;
+        width: 200px;
+      }
+
+      .internal-frame {
+        display:block;
+        padding: 0px;
+        width: 150px;
+        visibility: visible;
+        height: 150px;
+        border: 1px solid red;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="scrollable-container">
+      <iframe id="frame1" src="res/documentindesignmode.html" tabindex="1" frameborder="0"
+              class="internal-frame">
+      </iframe>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_selection_frame_in_scrollable_container.js
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gWindow = null;
+var gFrame = null;
+
+function setUpAndTearDown() {
+  emptyClipboard();
+
+  if (gWindow) {
+    clearSelection(gWindow);
+  }
+
+  if (gFrame) {
+    clearSelection(gFrame);
+  }
+
+  yield waitForCondition(function () {
+    return !SelectionHelperUI.isSelectionUIVisible;
+  });
+
+  InputSourceHelper.isPrecise = false;
+  InputSourceHelper.fireUpdate();
+}
+
+gTests.push({
+  desc: "Selection monocles for frame content that is located inside " +
+        "scrollable container.",
+  setUp: setUpAndTearDown,
+  tearDown: setUpAndTearDown,
+  run: function test() {
+    let urlToLoad = chromeRoot +
+        "browser_selection_frame_in_scrollable_container.html";
+    info(urlToLoad);
+    yield addTab(urlToLoad);
+
+    ContextUI.dismiss();
+    yield waitForCondition(() => !ContextUI.navbarVisible);
+
+    gWindow = Browser.selectedTab.browser.contentWindow;
+    gFrame = gWindow.document.getElementById("frame1");
+
+    // Select some content inside frame.
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToWindow(gFrame.contentWindow, 10, 10);
+    yield promise;
+
+    let selectMenuItem = document.getElementById("context-select");
+    promise = waitForEvent(document, "popuphidden");
+    sendElementTap(gWindow, selectMenuItem);
+    yield promise;
+    yield waitForCondition(()=>SelectionHelperUI.isSelectionUIVisible);
+
+    // Scroll frame inside scrollable container.
+    let initialYPos = SelectionHelperUI.endMark.yPos;
+    let touchDrag = new TouchDragAndHold();
+    touchDrag.useNativeEvents = true;
+    yield touchDrag.start(gWindow, 100, 90, 100, 50);
+    touchDrag.end();
+
+    yield waitForCondition(() => !SelectionHelperUI.hasActiveDrag);
+    yield SelectionHelperUI.pingSelectionHandler();
+
+    yield waitForCondition(()=>SelectionHelperUI.isSelectionUIVisible);
+
+    ok(initialYPos - SelectionHelperUI.endMark.yPos > 10,
+        "Selection monocles followed scrolled content.");
+  }
+});
+
+function test() {
+  // We need this until bug 859742 is fully resolved.
+  setDevPixelEqualToPx();
+  runTests();
+}
--- a/browser/metro/base/tests/mochitest/metro.ini
+++ b/browser/metro/base/tests/mochitest/metro.ini
@@ -11,16 +11,17 @@ support-files =
   browser_link_click.html
   browser_onscreen_keyboard.html
   browser_progress_indicator.xul
   browser_selection_basic.html
   browser_selection_caretfocus.html
   browser_selection_contenteditable.html
   browser_selection_frame_content.html
   browser_selection_frame_inputs.html
+  browser_selection_frame_in_scrollable_container.html
   browser_selection_frame_textarea.html
   browser_selection_inputs.html
   browser_selection_textarea.html
   browser_tilegrid.xul
   head.js
   helpers/BookmarksHelper.js
   helpers/HistoryHelper.js
   helpers/ViewStateHelper.js
@@ -28,16 +29,17 @@ support-files =
   res/textblock01.html
   res/textdivs01.html
   res/textinput01.html
   res/textarea01.html
   res/testEngine.xml
   res/blankpage1.html
   res/blankpage2.html
   res/blankpage3.html
+  res/documentindesignmode.html
 
 [browser_apzc_basic.js]
 [browser_bookmarks.js]
 [browser_canonizeURL.js]
 [browser_circular_progress_indicator.js]
 [browser_colorUtils.js]
 [browser_crashprompt.js]
 [browser_context_menu_tests.js]
@@ -69,16 +71,18 @@ support-files =
 
 # These tests have known failures in debug builds
 [browser_selection_basic.js]
 skip-if = debug
 [browser_selection_textarea.js]
 skip-if = debug
 [browser_selection_frame_content.js]
 skip-if = debug
+[browser_selection_frame_in_scrollable_container.js]
+skip-if = debug
 [browser_selection_inputs.js]
 skip-if = debug
 [browser_selection_frame_textarea.js]
 skip-if = debug
 [browser_selection_frame_inputs.js]
 skip-if = debug
 [browser_selection_urlbar.js]
 skip-if = debug
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/res/documentindesignmode.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    body {
+      border: none;
+      background: none repeat scroll 0% 0% transparent;
+      margin: 0;
+      min-width: 0px;
+      min-height: 100px;
+      width: 100px;
+      height: 100%;
+      direction: ltr;
+      overflow: hidden;
+    }
+
+    .test-div {
+      font-size: 22px;
+    }
+  </style>
+</head>
+<body role="textbox" hidefocus="true">
+  <div class="test-div div1">1</div>
+  <div class="test-div div2">2</div>
+  <div class="test-div div3">3</div>
+  <div class="test-div div4">4</div>
+  <div class="test-div div5">5</div>
+</body>
+  <script type="text/javascript">
+    document.designMode = "on";
+  </script>
+</html>
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -189,16 +189,22 @@ documenttab[private] .documenttab-thumbn
 .documenttab-selection {
   background: transparent -moz-image-rect(url("chrome://browser/skin/images/tab-overlay.png"), 0%, 100%, 50%, 0%) 50% 50% no-repeat;
 }
 
 documenttab[selected] .documenttab-selection {
   background: transparent -moz-image-rect(url("chrome://browser/skin/images/tab-overlay.png"), 50%, 100%, 100%, 0%) 50% 50% no-repeat;
 }
 
+.documenttab-crop:-moz-locale-dir(rtl),
+.documenttab-selection:-moz-locale-dir(rtl),
+documenttab[selected] .documenttab-selection:-moz-locale-dir(rtl) {
+  transform: scaleX(-1);
+}
+
 .documenttab-close {
   background: none !important;
   padding: @metro_spacing_small@ !important;
   margin-top: @metro_spacing_snormal@;
   -moz-margin-end: @metro_spacing_xsmall@;
   border-color: transparent !important;
   list-style-image: url("chrome://browser/skin/images/closetab-default.png");
 }
@@ -384,18 +390,17 @@ documenttab[selected] .documenttab-selec
 }
 
 #overlay-back:-moz-locale-dir(ltr),
 #overlay-plus:-moz-locale-dir(rtl) {
   left: -70px;
   background-position: right 6px center;
 }
 
-#overlay-plus:-moz-locale-dir(ltr),
-#overlay-back:-moz-locale-dir(rtl) {
+#overlay-plus:-moz-locale-dir(ltr) {
   right: -70px;
   background-position: left 6px center;
 }
 
 #stack[viewstate="snapped"] > .overlay-button,
 #stack[keyboardVisible] > .overlay-button,
 #stack[autocomplete] > .overlay-button,
 #stack[fullscreen] > .overlay-button,
@@ -438,22 +443,31 @@ documenttab[selected] .documenttab-selec
 }
 
 #overlay-back:-moz-locale-dir(ltr):hover,
 #overlay-plus:-moz-locale-dir(rtl):hover {
   background-position: right 12px center;
   transform: translateX(40px) scale(1.2);
 }
 
-#overlay-plus:-moz-locale-dir(ltr):hover,
-#overlay-back:-moz-locale-dir(rtl):hover {
+#overlay-plus:-moz-locale-dir(ltr):hover {
   background-position: left 12px center;
   transform: translateX(-40px) scale(1.2);
 }
 
+#overlay-back:-moz-locale-dir(rtl):hover {
+  transform: translateX(-40px) scale(1.2) scaleX(-1);
+}
+
+#overlay-back:-moz-locale-dir(rtl) {
+  transform: scaleX(-1);
+  right: -70px;
+  background-position: right 9px center;
+}
+
 #overlay-back[mousedrag],
 #overlay-plus[mousedrag] {
   transition-property: left, right, transform, background-position,
                        background-color, background-size, border-color,
                        visibility, box-shadow;
 }
 
 /* Navigation bar ========================================================== */
@@ -530,16 +544,21 @@ documenttab[selected] .documenttab-selec
 
 #back-button {
   list-style-image: url(chrome://browser/skin/images/navbar-back.png);
   position: relative;
   z-index: 1;
   transition: opacity @forward_transition_length@ ease-out;
 }
 
+#back-button:-moz-locale-dir(rtl),
+#forward-button:-moz-locale-dir(rtl) {
+  transform: scaleX(-1);
+}
+
 #back-button[disabled] {
   visibility: visible;
   opacity: 0.5;
 }
 
 #forward-button {
   list-style-image: url(chrome://browser/skin/images/navbar-forward.png);
   transition: margin @forward_transition_length@ ease-out,
@@ -853,16 +872,20 @@ documenttab[selected] .documenttab-selec
 }
 
 #panel-close-button {
   -moz-margin-end: 40px;
   list-style-image: url(chrome://browser/skin/images/navbar-back.png);
   -moz-box-pack: center;
 }
 
+#panel-close-button:-moz-locale-dir(rtl) {
+  transform: scaleX(-1);
+}
+
 @media (min-resolution: @min_res_140pc@) {
   #panel-close-button {
     list-style-image: url(chrome://browser/skin/images/navbar-back@1.4x.png);
   }
 }
 
 @media (min-resolution: @min_res_180pc@) {
   #panel-close-button {
--- a/browser/metro/theme/flyoutpanel.css
+++ b/browser/metro/theme/flyoutpanel.css
@@ -74,15 +74,19 @@ flyoutpanel[visible] {
 
 .flyout-close-button {
   border: 0 none;
   -moz-appearance: none;
   list-style-image: url(chrome://browser/skin/images/flyout-back-button.png);
   -moz-image-region: rect(0 32px 32px 0);
 }
 
+.flyout-close-button:-moz-locale-dir(rtl) {
+  transform: scaleX(-1);
+}
+
 .flyout-close-button:hover {
   -moz-image-region: rect(0 64px 32px 32px);
 }
 
 .flyout-close-button:active {
   -moz-image-region: rect(0 96px 32px 64px);
 }
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -1,16 +1,14 @@
 /* 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/. */
 
 .searchbar-textbox {
   min-height: 22px;
-  width: 6em;
-  min-width: 6em;
   background-color: -moz-field;
 }
 
 .autocomplete-textbox-container {
   -moz-box-align: stretch;
 }
 
 .textbox-input-box {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -4051,16 +4051,31 @@ window > chatbox {
 
 #main-window[customize-entered] #TabsToolbar {
   margin-top: 9px;
   background-clip: padding-box;
   border-right: 3px solid transparent;
   border-left: 3px solid transparent;
 }
 
+@media (min-resolution: 2dppx) {
+  #customization-titlebar-visibility-button {
+    list-style-image: url("chrome://browser/skin/customizableui/customize-titleBar-toggle@2x.png");
+    -moz-image-region: rect(0, 48px, 48px, 0);
+  }
+
+  #customization-titlebar-visibility-button[checked] {
+    -moz-image-region: rect(0, 96px, 48px, 48px);
+  }
+
+  #customization-titlebar-visibility-button > .button-box > .button-icon {
+    width: 24px;
+  }
+}
+
 /* End customization mode */
 
 #main-window[privatebrowsingmode=temporary] {
   background-image: url("chrome://browser/skin/privatebrowsing-mask.png");
   background-position: top right;
   background-repeat: no-repeat;
   background-color: -moz-mac-chrome-active;
 }
new file mode 100644
index 0000000000000000000000000000000000000000..fb0edd2566a4f803e43914f431b4c2050f5aba18
GIT binary patch
literal 317
zc$@(}0mA-?P)<h;3K|Lk000e1NJLTq001xm000;W1ONa47WB4P00035Nkl<ZcmeIu
zu}T9$7{u}acP^U3a79cJPAr5xg)d-hXXP7+g++QRTk!>KtkWH&u-7_*U?CV16iguq
zlICt~Z;1!H#a8+&rW)9t&qypIF?aIJkZ><laebE$_Xs!Jhm6CKY;xJm-PXR#Sh4~-
zUhXh&zbFq=%uhi}-@)pa9|4Sx2j4V&ZeHBhTL7~Xx?V15;{@DKF?lJ(%^gdcwrd1j
z9TDto>_msx68c^W@$?~@j2r`~2qql#=mB^s3JTUOh~>nbbcBQJh?|~{;}^u;G=71A
zln-Pi_P^i{EJ%MMDEkDO<Ph7p^T)TM5<xa>@J<!iHyzL=+!w_4O{Db$0%dt!Va1}<
P00000NkvXXu0mjftImXR
new file mode 100644
index 0000000000000000000000000000000000000000..7200e74c2bdcbc6363d5f3903111c45572210b0e
GIT binary patch
literal 538
zc$@(m0_FXQP)<h;3K|Lk000e1NJLTq003YB001xu1ONa4L?5?A0005uNkl<ZcmeI!
zJ!=$E7{&45Y$oG`#bA6TsMy5DZy=;s38b)7!<Lo`sYHCiwXqOHKY}23BJnj}!qUcI
z5rSe6Eh0%aF<vDS2r{^n%}lXy|I?geIL|%Y^9+;db7FuPV3#!Xe}LA_R;LwecshTa
zwQBZHTin>6rK;y=_U*d6YW7dbv!fDa38mL6SkA$wf5##?Oo7^C=lu0gb3;&Sz*j5E
zFytV}w*^`>_V~ifr;~G23e<7EkR#0zx(+x7R-CzZ?!7L&IW>DPAZ)t}xb$MA(|Ueu
zsz?PpCrFSc&~*R=bUps&JF`MTDes>iY_H!gR=ty!ACTbAhS%K_C^-HhAEv`};v@yC
zf&gU(<UYPqf>HkxWKLMtlu@9<ZUO{>QUeZXSksNnh+xYX7HqNRd!g!`G=PAn=6@8h
zug6dA!%3BMbp}#`1^r0aeglstRnC=}_%l~_iR&S#mjD_;eZ&gI0C9=q62}{eOB^c{
zD-;970C9<HCsrs{C{`#|=+mI;C~&OLzX7x-<}(#CpS34|y8V;O27ctt&w^@6j9OhX
zj9UHE0pkzeogcQe`YWi6MzS|=7=H$8^-rF-c-EkLSoapS`X_-S#tf*Q4RgMz**^*T
co4yb58^&2}PxRWO-~a#s07*qoM6N<$g7u~JF#rGn
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -19,20 +19,22 @@ browser.jar:
 #endif
   skin/classic/browser/aboutTabCrashed.css
   skin/classic/browser/actionicon-tab.png
   skin/classic/browser/actionicon-tab@2x.png
 * skin/classic/browser/browser.css                          (browser.css)
 * skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
+  skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
+  skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
+  skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
-  skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
 * skin/classic/browser/engineManager.css                    (engineManager.css)
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-16@2x.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Geolocation-64@2x.png
   skin/classic/browser/home.png
   skin/classic/browser/identity.png
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -1,15 +1,14 @@
 /* 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/. */
 
 .searchbar-textbox {
   border-radius: 10000px;
-  min-width: 6em;
 }
 
 .searchbar-engine-button {
   -moz-padding-start: 6px;
   -moz-padding-end: 2px;
   margin: 0;
   -moz-margin-end: 2px;
   -moz-appearance: none;
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -31,33 +31,75 @@
   padding-left: 10px;
   padding-right: 10px;
 }
 
 #customization-container {
   background-color: rgb(247,247,247);
 }
 
-#customization-palette-container {
-  padding: 25px;
+#customization-palette,
+#customization-empty {
+  padding: 0 25px 25px;
 }
 
 #customization-header {
   font-size: 1.5em;
   line-height: 1.5em;
   color: rgb(64,100,128);
   font-weight: lighter;
   margin-bottom: 1em;
+  padding: 25px 25px 0;
 }
 
 #customization-panel-container {
   padding: 15px 25px 25px;
   background-image: linear-gradient(to bottom, #3e86ce, #3878ba);
 }
 
+#customization-footer {
+  background-color: rgb(236,236,236);
+  border-top: 1px solid rgb(221,221,221);
+  padding: 15px;
+}
+
+.customizationmode-button {
+  border: 1px solid rgb(192,192,192);
+  border-radius: 3px;
+  margin: 0;
+  padding: 2px 12px;
+  background-color: rgb(251,251,251);
+  color: rgb(71,71,71);
+  box-shadow: 0 1px rgba(255, 255, 255, 0.5),
+              inset 0 1px rgba(255, 255, 255, 0.5);
+  -moz-appearance: none;
+}
+
+#customization-titlebar-visibility-button {
+  list-style-image: url("chrome://browser/skin/customizableui/customize-titleBar-toggle.png");
+  -moz-image-region: rect(0, 24px, 24px, 0);
+  padding: 2px 7px;
+  -moz-margin-end: 10px;
+}
+
+#customization-titlebar-visibility-button > .button-box > .button-text {
+  /* Sadly, button.css thinks its margins are perfect for everyone. */
+  -moz-margin-start: 6px !important;
+}
+
+#customization-titlebar-visibility-button[checked] {
+  -moz-image-region: rect(0, 48px, 24px, 24px);
+  background-color: rgb(218, 218, 218);
+  border-color: rgb(168, 168, 168);
+  text-shadow: 0 1px rgb(236, 236, 236);
+  box-shadow: 0 1px rgba(255, 255, 255, 0.5),
+              inset 0 1px rgb(196, 196, 196);
+}
+
+
 #main-window[customize-entered] #customization-panel-container {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-separatorHorizontal.png"),
                     url("chrome://browser/skin/customizableui/customizeMode-separatorVertical.png"),
                     url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png"),
                     url("chrome://browser/skin/customizableui/background-noise-toolbar.png"),
                     linear-gradient(to bottom, #3e86ce, #3878ba);
   background-position: center top, left center, left top, left top, left top;
   background-repeat: no-repeat, no-repeat, repeat, repeat, no-repeat;
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -246,23 +246,23 @@ toolbarpaletteitem[place="palette"] > to
   margin: 0;
 }
 
 #PanelUI-footer-inner {
   display: flex;
   box-shadow: 0 -1px 0 rgba(0,0,0,.15);
 }
 
-#PanelUI-footer > toolbarseparator {
+#PanelUI-footer-inner > toolbarseparator {
   border: 0;
   border-left: 1px solid rgba(0,0,0,0.1);
   margin: 7px 0 7px;
 }
 
-#PanelUI-footer:hover > toolbarseparator {
+#PanelUI-footer-inner:hover > toolbarseparator {
   margin: 0;
 }
 
 #PanelUI-help,
 #PanelUI-fxa-status,
 #PanelUI-customize,
 #PanelUI-quit {
   margin: 0;
@@ -274,39 +274,46 @@ toolbarpaletteitem[place="palette"] > to
   border: 1px solid transparent;
   border-bottom-style: none;
   border-radius: 0;
   transition: background-color;
   -moz-box-orient: horizontal;
 }
 
 #PanelUI-fxa-status {
-  width: calc(@menuPanelWidth@ + 20px);
   border-bottom-style: solid;
 }
 
+#PanelUI-fxa-status > .toolbarbutton-text {
+  width: 0; /* Fancy cropping solution for flexbox. */
+}
+
 #PanelUI-fxa-status[signedin] {
   font-weight: bold;
 }
 
 #PanelUI-help,
 #PanelUI-quit {
   min-width: 46px;
 }
 
 #PanelUI-fxa-status > .toolbarbutton-text,
 #PanelUI-customize > .toolbarbutton-text {
+  margin: 0;
+  padding: 0 6px;
   text-align: start;
 }
 
 #PanelUI-help > .toolbarbutton-text,
 #PanelUI-quit > .toolbarbutton-text {
   display: none;
 }
 
+#PanelUI-fxa-status > .toolbarbutton-icon,
+#PanelUI-customize > .toolbarbutton-icon,
 #PanelUI-help > .toolbarbutton-icon,
 #PanelUI-quit > .toolbarbutton-icon {
   -moz-margin-end: 0;
 }
 
 #PanelUI-fxa-status,
 #PanelUI-customize {
   flex: 1;
@@ -386,17 +393,16 @@ toolbarpaletteitem[place="palette"] > to
 #customization-palette .toolbarbutton-multiline-text,
 #customization-palette .toolbarbutton-text {
   display: none;
 }
 
 panelview .toolbarbutton-1,
 .subviewbutton,
 .widget-overflow-list .toolbarbutton-1,
-.customizationmode-button,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 #edit-controls@inAnyPanel@ > toolbarbutton,
 #zoom-controls@inAnyPanel@ > toolbarbutton {
   -moz-appearance: none;
   padding: 2px 6px;
   background-color: hsla(210,4%,10%,0);
   border-radius: 2px;
   border: 1px solid;
@@ -445,31 +451,29 @@ panelview .toolbarbutton-1,
 
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   border: 0;
 }
 
 panelview .toolbarbutton-1@buttonStateHover@,
 .subviewbutton@buttonStateHover@,
 .widget-overflow-list .toolbarbutton-1@buttonStateHover@,
-.customizationmode-button,
 #edit-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@,
 #zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
   background-color: hsla(210,4%,10%,.08);
   border-color: hsla(210,4%,10%,.1);
 }
 
 #edit-controls@inAnyPanel@@buttonStateHover@,
 #zoom-controls@inAnyPanel@@buttonStateHover@ {
   border-color: hsla(210,4%,10%,.1);
 }
 
 panelview .toolbarbutton-1@buttonStateActive@,
 .subviewbutton@buttonStateActive@,
-.customizationmode-button@buttonStateActive@,
 .widget-overflow-list .toolbarbutton-1@buttonStateActive@,
 #edit-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@,
 #zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
   background-color: hsla(210,4%,10%,.15);
   border-color: hsla(210,4%,10%,.15);
   box-shadow: 0 1px 0 0 hsla(210,4%,10%,.05) inset;
 }
 
@@ -503,16 +507,20 @@ panelview toolbarseparator,
   -moz-margin-end: auto;
   color: hsl(0,0%,50%);
 }
 
 #PanelUI-historyItems > toolbarbutton {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
+.restoreallitem.subviewbutton > .toolbarbutton-icon {
+  display: none;
+}
+
 #PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
 #PanelUI-footer.panel-multiview-anchor,
new file mode 100644
index 0000000000000000000000000000000000000000..01dcf09a2b7db12a4d8af85ee05ddfeb2134e4bc
GIT binary patch
literal 550
zc$@(y0@?kEP)<h;3K|Lk000e1NJLTq001xm000;W1^@s6y#0RM0005)Nkl<ZSi|j@
z$xgyh6o&5$I6R9(RFpWaUAf{7RMa8`3N27thEhZZ2gFx0afmU|xYf9HE3U5RZ){==
z8YP?%6K?XSKk2#O$<Mi$1wsthfLAnS-De6_nJpglRbUePp%=bkS7ahn^pwh#yL#Pn
zJ-$d2+-;t*2<%??cI8mU(uL)ik)Jd2`;wkZq`-$?gty$L$EE=LvQ#^fxpG}*iq&t0
z_o#E5ZRylo)THUDP(AAEQRDB$p1%nDV}>0hP3yJ`KTn!5@)h_1JZiueNM$<pj-r`k
zKOHrCKTdVB+-OUrSB?Cts@n@Xjr)sVAs_U=A&edS{Av6~I3DqI!INe>UA@m1NM$;;
zS4GQ?{2U-VN_7<AlmaxQs$26>-I~y?GW;z0$xnU?HJC1ambBJ51hz0}L>PUxM3;xc
zd?&m|9garv;MhA-5JuU}qXzqwM20N@*aA1+skK-Hwi0IyKy0CzCGRp|68#jIVDHA$
zYyrR)k)$afpPoY;0(*xs0N7$DX7Dv2HaA~vt9%W>=H`pAs<Sl!o0~7f(G*_;VsrCF
zcrVG<fY{u8vBlT`Y;OMl53m{0*cyP%&Hw)aaFfq3u0s8WUtZs=`@ce*e6gV<Zs{z&
o(etIBB4&r4cijBpp8|tk?|1m$=MZ@D6951J07*qoM6N<$f|6nb!T<mO
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -21,20 +21,21 @@ browser.jar:
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/aboutTabCrashed.css
         skin/classic/browser/actionicon-tab.png
 *       skin/classic/browser/browser.css
 *       skin/classic/browser/browser-lightweightTheme.css
         skin/classic/browser/click-to-play-warning-stripes.png
         skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
+        skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
+        skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
         skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
         skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
         skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
-        skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
 *       skin/classic/browser/engineManager.css
         skin/classic/browser/fullscreen-darknoise.png
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png
         skin/classic/browser/identity.png
         skin/classic/browser/identity-icons-generic.png
         skin/classic/browser/identity-icons-https.png
@@ -347,20 +348,21 @@ browser.jar:
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/aboutTabCrashed.css
         skin/classic/aero/browser/actionicon-tab.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
 *       skin/classic/aero/browser/browser-lightweightTheme.css
         skin/classic/aero/browser/click-to-play-warning-stripes.png
         skin/classic/aero/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
+        skin/classic/aero/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
+        skin/classic/aero/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
         skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
         skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
         skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
-        skin/classic/aero/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
 *       skin/classic/aero/browser/engineManager.css
         skin/classic/aero/browser/fullscreen-darknoise.png
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/identity-icons-generic.png
         skin/classic/aero/browser/identity-icons-https.png
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -1,17 +1,12 @@
 /* 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/. */
 
-.searchbar-textbox {
-  width: 6em;
-  min-width: 6em;
-}
-
 .autocomplete-textbox-container {
   -moz-box-align: stretch;
 }
 
 .textbox-input-box {
   margin: 0;
 }
 
--- a/build/unix/elfhack/Makefile.in
+++ b/build/unix/elfhack/Makefile.in
@@ -1,17 +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/.
 
 INTERNAL_TOOLS = 1
 
-VPATH += $(topsrcdir)/build
-
 OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions
 
 WRAP_LDFLAGS=
 
 include $(topsrcdir)/config/rules.mk
 
 test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack
 	$(MKSHLIB) $(LDFLAGS) $< -nostartfiles
--- a/build/valgrind/mach_commands.py
+++ b/build/valgrind/mach_commands.py
@@ -14,17 +14,17 @@ from mach.decorators import (
 )
 from mozbuild.base import (
     MachCommandBase,
     MachCommandConditions as conditions,
 )
 
 
 def is_valgrind_build(cls):
-    """Must be a build with --enable-valgrind and --disable-jemalloc."""
+    '''Must be a build with --enable-valgrind and --disable-jemalloc.'''
     defines = cls.config_environment.defines
     return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines
 
 
 @CommandProvider
 class MachCommands(MachCommandBase):
     '''
     Run Valgrind tests.
@@ -37,16 +37,17 @@ class MachCommands(MachCommandBase):
         description='Run the Valgrind test job.')
     @CommandArgument('--suppressions', default=[], action='append',
         metavar='FILENAME',
         help='Specify a suppression file for Valgrind to use. Use '
             '--suppression multiple times to specify multiple suppression '
             'files.')
     def valgrind_test(self, suppressions):
         import json
+        import re
         import sys
         import tempfile
 
         from mozbuild.base import MozbuildObject
         from mozfile import TemporaryDirectory
         from mozhttpd import MozHttpd
         from mozprofile import FirefoxProfile, Preferences
         from mozprofile.permissions import ServerLocations
@@ -86,23 +87,35 @@ class MachCommands(MachCommandBase):
             firefox_args = [httpd.get_url()]
 
             env = os.environ.copy()
             env['G_SLICE'] = 'always-malloc'
             env['XPCOM_CC_RUN_DURING_SHUTDOWN'] = '1'
             env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
             env['XPCOM_DEBUG_BREAK'] = 'warn'
 
+            class OutputHandler(object):
+                def __init__(self):
+                    self.found_errors = False
+
+                def __call__(self, line):
+                    print(line)
+                    m = re.match(r'.*ERROR SUMMARY: [1-9]\d* errors from \d+ contexts', line)
+                    if m:
+                        self.found_errors = True
+
+            outputHandler = OutputHandler()
+            kp_kwargs = {'processOutputLine': [outputHandler]}
+
             valgrind = 'valgrind'
             if not os.path.exists(valgrind):
                 valgrind = findInPath(valgrind)
 
             valgrind_args = [
                 valgrind,
-                '--error-exitcode=1',
                 '--smc-check=all-non-file',
                 '--vex-iropt-register-updates=allregs-at-mem-access',
                 '--gen-suppressions=all',
                 '--num-callers=20',
                 '--leak-check=full',
                 '--show-possibly-lost=no',
                 '--track-origins=yes'
             ]
@@ -116,23 +129,33 @@ class MachCommands(MachCommandBase):
 
             # MACHTYPE is an odd bash-only environment variable that doesn't
             # show up in os.environ, so we have to get it another way.
             machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
             supps_file2 = os.path.join(supps_dir, machtype + '.sup')
             if os.path.isfile(supps_file2):
                 valgrind_args.append('--suppressions=' + supps_file2)
 
+            exitcode = None
             try:
                 runner = FirefoxRunner(profile=profile,
                                        binary=self.get_binary_path(),
                                        cmdargs=firefox_args,
-                                       env=env)
+                                       env=env,
+                                       kp_kwargs=kp_kwargs)
                 runner.start(debug_args=valgrind_args)
-                status = runner.wait()
+                exitcode = runner.wait()
 
             finally:
+                if not outputHandler.found_errors:
+                    status = 0
+                    print('TEST-PASS | valgrind-test | valgrind found no errors')
+                else:
+                    status = 1  # turns the TBPL job orange
+                    print('TEST-UNEXPECTED-FAIL | valgrind-test | valgrind found errors')
+
+                if exitcode != 0:
+                    status = 2  # turns the TBPL job red
+                    print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')
+
                 httpd.stop()
-                if status != 0:
-                    status = 1 # normalize status, in case it's larger than 127
-                    print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code')
 
             return status
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -3216,17 +3216,17 @@ nsObjectLoadingContent::LegacyCall(JSCon
   }
 
   if (!pi_obj) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return JS::UndefinedValue();
   }
 
   JS::Rooted<JS::Value> retval(aCx);
-  bool ok = JS::Call(aCx, thisVal, pi_obj, args.Length(), rooter.array,
+  bool ok = JS::Call(aCx, thisVal, pi_obj, rooter.length(), rooter.start(),
                      &retval);
   if (!ok) {
     aRv.Throw(NS_ERROR_FAILURE);
     return JS::UndefinedValue();
   }
 
   Telemetry::Accumulate(Telemetry::PLUGIN_CALLED_DIRECTLY, true);
   return retval;
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -895,16 +895,17 @@ protected:
     // WebGL extensions (implemented in WebGLContextExtensions.cpp)
     enum WebGLExtensionID {
         EXT_sRGB,
         EXT_texture_filter_anisotropic,
         OES_element_index_uint,
         OES_standard_derivatives,
         OES_texture_float,
         OES_texture_float_linear,
+        OES_texture_half_float,
         OES_vertex_array_object,
         WEBGL_compressed_texture_atc,
         WEBGL_compressed_texture_pvrtc,
         WEBGL_compressed_texture_s3tc,
         WEBGL_debug_renderer_info,
         WEBGL_depth_texture,
         WEBGL_lose_context,
         WEBGL_draw_buffers,
--- a/content/canvas/src/WebGLContextExtensions.cpp
+++ b/content/canvas/src/WebGLContextExtensions.cpp
@@ -18,16 +18,17 @@ using namespace mozilla::gl;
 // must match WebGLContext::WebGLExtensionID
 static const char *sExtensionNames[] = {
     "EXT_sRGB",
     "EXT_texture_filter_anisotropic",
     "OES_element_index_uint",
     "OES_standard_derivatives",
     "OES_texture_float",
     "OES_texture_float_linear",
+    "OES_texture_half_float",
     "OES_vertex_array_object",
     "WEBGL_compressed_texture_atc",
     "WEBGL_compressed_texture_pvrtc",
     "WEBGL_compressed_texture_s3tc",
     "WEBGL_debug_renderer_info",
     "WEBGL_depth_texture",
     "WEBGL_lose_context",
     "WEBGL_draw_buffers",
@@ -81,16 +82,22 @@ bool WebGLContext::IsExtensionSupported(
             return gl->IsSupported(GLFeature::standard_derivatives);
         case WEBGL_lose_context:
             // We always support this extension.
             return true;
         case OES_texture_float:
             return gl->IsSupported(GLFeature::texture_float);
         case OES_texture_float_linear:
             return gl->IsSupported(GLFeature::texture_float_linear);
+        case OES_texture_half_float:
+            // If we have Feature::texture_half_float, we must not be on ES2
+            // and need to translate HALF_FLOAT_OES -> HALF_FLOAT.  We do that
+            // right before making the relevant calls.
+            return gl->IsExtensionSupported(GLContext::OES_texture_half_float) ||
+                   gl->IsSupported(GLFeature::texture_half_float);
         case OES_vertex_array_object:
             return WebGLExtensionVertexArray::IsSupported(this);
         case EXT_texture_filter_anisotropic:
             return gl->IsExtensionSupported(GLContext::EXT_texture_filter_anisotropic);
         case WEBGL_compressed_texture_s3tc:
             if (gl->IsExtensionSupported(GLContext::EXT_texture_compression_s3tc)) {
                 return true;
             }
@@ -248,16 +255,19 @@ WebGLContext::EnableExtension(WebGLExten
             obj = new WebGLExtensionDepthTexture(this);
             break;
         case OES_texture_float:
             obj = new WebGLExtensionTextureFloat(this);
             break;
         case OES_texture_float_linear:
             obj = new WebGLExtensionTextureFloatLinear(this);
             break;
+        case OES_texture_half_float:
+            obj = new WebGLExtensionTextureHalfFloat(this);
+            break;
         case WEBGL_draw_buffers:
             obj = new WebGLExtensionDrawBuffers(this);
             break;
         case OES_vertex_array_object:
             obj = new WebGLExtensionVertexArray(this);
             break;
         case ANGLE_instanced_arrays:
             obj = new WebGLExtensionInstancedArrays(this);
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3669,26 +3669,35 @@ GLenum WebGLContext::CheckedTexImage2D(G
     if (tex->HasImageInfoAt(target, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
                         format != imageInfo.InternalFormat() ||
                         type != imageInfo.Type();
     }
 
+    // convert type for half float if not on GLES2
+    GLenum realType = type;
+    if (realType == LOCAL_GL_HALF_FLOAT_OES && !gl->IsGLES2()) {
+        realType = LOCAL_GL_HALF_FLOAT;
+    }
+
     if (sizeMayChange) {
         UpdateWebGLErrorAndClearGLError();
-        gl->fTexImage2D(target, level, internalFormat, width, height, border, format, type, data);
+
+        gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
+
         GLenum error = LOCAL_GL_NO_ERROR;
         UpdateWebGLErrorAndClearGLError(&error);
         return error;
-    } else {
-        gl->fTexImage2D(target, level, internalFormat, width, height, border, format, type, data);
-        return LOCAL_GL_NO_ERROR;
     }
+
+    gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
+
+    return LOCAL_GL_NO_ERROR;
 }
 
 void
 WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
                               GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                               GLint border,
                               GLenum format, GLenum type,
                               void *data, uint32_t byteLength,
@@ -3967,34 +3976,40 @@ WebGLContext::TexSubImage2D_base(GLenum 
     MakeContextCurrent();
 
     size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
 
     size_t dstPlainRowSize = dstTexelSize * width;
     // There are checks above to ensure that this won't overflow.
     size_t dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
 
+    // convert type for half float if not on GLES2
+    GLenum realType = type;
+    if (realType == LOCAL_GL_HALF_FLOAT_OES && !gl->IsGLES2()) {
+        realType = LOCAL_GL_HALF_FLOAT;
+    }
+
     if (actualSrcFormat == dstFormat &&
         srcPremultiplied == mPixelStorePremultiplyAlpha &&
         srcStride == dstStride &&
         !mPixelStoreFlipY)
     {
         // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, realType, pixels);
     }
     else
     {
         size_t convertedDataSize = height * dstStride;
         nsAutoArrayPtr<uint8_t> convertedData(new uint8_t[convertedDataSize]);
         ConvertImage(width, height, srcStride, dstStride,
                     static_cast<const uint8_t*>(pixels), convertedData,
                     actualSrcFormat, srcPremultiplied,
                     dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
 
-        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, convertedData);
+        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, realType, convertedData);
     }
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum target, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLsizei width, GLsizei height,
                             GLenum format, GLenum type,
@@ -4196,16 +4211,33 @@ WebGLTexelFormat mozilla::GetWebGLTexelF
                 return WebGLTexelFormat::A32F;
             case LOCAL_GL_LUMINANCE:
                 return WebGLTexelFormat::R32F;
             case LOCAL_GL_LUMINANCE_ALPHA:
                 return WebGLTexelFormat::RA32F;
         }
 
         MOZ_CRASH("Invalid WebGL texture format/type?");
+    } else if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        // OES_texture_half_float
+        switch (internalformat) {
+            case LOCAL_GL_RGBA:
+                return WebGLTexelFormat::RGBA16F;
+            case LOCAL_GL_RGB:
+                return WebGLTexelFormat::RGB16F;
+            case LOCAL_GL_ALPHA:
+                return WebGLTexelFormat::A16F;
+            case LOCAL_GL_LUMINANCE:
+                return WebGLTexelFormat::R16F;
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                return WebGLTexelFormat::RA16F;
+            default:
+                MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
+                return WebGLTexelFormat::BadFormat;
+        }
     }
 
     switch (type) {
         case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
            return WebGLTexelFormat::RGBA4444;
         case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
            return WebGLTexelFormat::RGBA5551;
         case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
@@ -4256,16 +4288,31 @@ InternalFormatForFormatAndType(GLenum fo
             return LOCAL_GL_ALPHA32F_ARB;
         case LOCAL_GL_LUMINANCE:
             return LOCAL_GL_LUMINANCE32F_ARB;
         case LOCAL_GL_LUMINANCE_ALPHA:
             return LOCAL_GL_LUMINANCE_ALPHA32F_ARB;
         }
         break;
 
+    case LOCAL_GL_HALF_FLOAT_OES:
+        switch (format) {
+        case LOCAL_GL_RGBA:
+            return LOCAL_GL_RGBA16F;
+        case LOCAL_GL_RGB:
+            return LOCAL_GL_RGB16F;
+        case LOCAL_GL_ALPHA:
+            return LOCAL_GL_ALPHA16F_ARB;
+        case LOCAL_GL_LUMINANCE:
+            return LOCAL_GL_LUMINANCE16F_ARB;
+        case LOCAL_GL_LUMINANCE_ALPHA:
+            return LOCAL_GL_LUMINANCE_ALPHA16F_ARB;
+        }
+        break;
+
     default:
         break;
     }
 
     NS_ASSERTION(false, "Coding mistake -- bad format/type passed?");
     return 0;
 }
 
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -564,54 +564,19 @@ bool WebGLContext::ValidateTexFormatAndT
                 }
             }
 
             *texelSize = 4;
             return true;
         }
     }
 
-
-    if (type == LOCAL_GL_UNSIGNED_BYTE ||
-        (IsExtensionEnabled(OES_texture_float) && type == LOCAL_GL_FLOAT))
-    {
-        if (jsArrayType != -1) {
-            if ((type == LOCAL_GL_UNSIGNED_BYTE && jsArrayType != js::ArrayBufferView::TYPE_UINT8) ||
-                (type == LOCAL_GL_FLOAT && jsArrayType != js::ArrayBufferView::TYPE_FLOAT32))
-            {
-                ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
-                return false;
-            }
-        }
+    const char invalidTypedArray[] = "%s: invalid typed array type for given texture data type";
 
-        int texMultiplier = type == LOCAL_GL_FLOAT ? 4 : 1;
-        switch (format) {
-            case LOCAL_GL_ALPHA:
-            case LOCAL_GL_LUMINANCE:
-                *texelSize = 1 * texMultiplier;
-                return true;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                *texelSize = 2 * texMultiplier;
-                return true;
-            case LOCAL_GL_RGB:
-            case LOCAL_GL_SRGB_EXT:
-                *texelSize = 3 * texMultiplier;
-                return true;
-            case LOCAL_GL_RGBA:
-            case LOCAL_GL_SRGB_ALPHA_EXT:
-                *texelSize = 4 * texMultiplier;
-                return true;
-            default:
-                break;
-        }
-
-        ErrorInvalidEnum("%s: invalid format 0x%x", info, format);
-        return false;
-    }
-
+    // First, we check for packed types
     switch (type) {
         case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
         case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
             if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_UINT16) {
                 ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
                 return false;
             }
 
@@ -634,17 +599,77 @@ bool WebGLContext::ValidateTexFormatAndT
             }
             ErrorInvalidOperation("%s: mutually incompatible format and type", info);
             return false;
 
         default:
             break;
         }
 
-    ErrorInvalidEnum("%s: invalid type 0x%x", info, type);
+    int texMultiplier = 1;
+
+    // If not a packed types, then it's might be a standard type.
+    if (type == LOCAL_GL_UNSIGNED_BYTE) {
+        if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_UINT8) {
+            ErrorInvalidEnum(invalidTypedArray, info);
+            return false;
+        }
+    } else if (type == LOCAL_GL_FLOAT) {
+        if (!IsExtensionEnabled(OES_texture_float)) {
+            ErrorInvalidEnum("%s: invalid format FLOAT: need OES_texture_float enabled", info);
+            return false;
+        }
+
+        if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_FLOAT32) {
+            ErrorInvalidOperation(invalidTypedArray, info);
+            return false;
+        }
+
+        texMultiplier = 4;
+    } else if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        if (!IsExtensionEnabled(OES_texture_half_float)) {
+            ErrorInvalidEnum("%s: invalid format HALF_FLOAT_OES: need OES_texture_half_float enabled", info);
+            return false;
+        }
+
+        if (jsArrayType != -1)
+        {
+            ErrorInvalidOperation(invalidTypedArray, info);
+            return false;
+        }
+
+        texMultiplier = 2;
+    } else {
+        // We don't know the type
+        ErrorInvalidEnum("%s: invalid type 0x%x", info, type);
+        return false;
+    }
+
+    // Ok we know that is a standard type.
+    switch (format) {
+        case LOCAL_GL_ALPHA:
+        case LOCAL_GL_LUMINANCE:
+            *texelSize = 1 * texMultiplier;
+            return true;
+        case LOCAL_GL_LUMINANCE_ALPHA:
+            *texelSize = 2 * texMultiplier;
+            return true;
+        case LOCAL_GL_RGB:
+        case LOCAL_GL_SRGB_EXT:
+            *texelSize = 3 * texMultiplier;
+            return true;
+        case LOCAL_GL_RGBA:
+        case LOCAL_GL_SRGB_ALPHA_EXT:
+            *texelSize = 4 * texMultiplier;
+            return true;
+        default:
+            break;
+    }
+
+    ErrorInvalidEnum("%s: invalid format 0x%x", info, format);
     return false;
 }
 
 bool
 WebGLContext::ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object)
 {
     if (!ValidateObjectAllowNull(info, location_object))
         return false;
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLExtensionTextureHalfFloat.cpp
@@ -0,0 +1,20 @@
+/* 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 "WebGLContext.h"
+#include "WebGLExtensions.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+
+using namespace mozilla;
+
+WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* context)
+    : WebGLExtensionBase(context)
+{
+}
+
+WebGLExtensionTextureHalfFloat::~WebGLExtensionTextureHalfFloat()
+{
+}
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureHalfFloat)
--- a/content/canvas/src/WebGLExtensions.h
+++ b/content/canvas/src/WebGLExtensions.h
@@ -156,32 +156,42 @@ class WebGLExtensionTextureFloatLinear
 {
 public:
     WebGLExtensionTextureFloatLinear(WebGLContext*);
     virtual ~WebGLExtensionTextureFloatLinear();
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionTextureHalfFloat
+    : public WebGLExtensionBase
+{
+public:
+    WebGLExtensionTextureHalfFloat(WebGLContext*);
+    virtual ~WebGLExtensionTextureHalfFloat();
+
+    DECL_WEBGL_EXTENSION_GOOP
+};
+
 class WebGLExtensionDrawBuffers
     : public WebGLExtensionBase
 {
 public:
     WebGLExtensionDrawBuffers(WebGLContext*);
     virtual ~WebGLExtensionDrawBuffers();
 
     void DrawBuffersWEBGL(const dom::Sequence<GLenum>& buffers);
 
     static bool IsSupported(const WebGLContext*);
 
     static const size_t sMinColorAttachments = 4;
     static const size_t sMinDrawBuffers = 4;
     /*
      WEBGL_draw_buffers does not give a minal value for GL_MAX_DRAW_BUFFERS. But, we request
-     for GL_MAX_DRAW_BUFFERS = 4 at least to be able to use all requested color attachements.
+     for GL_MAX_DRAW_BUFFERS = 4 at least to be able to use all requested color attachments.
      See DrawBuffersWEBGL in WebGLExtensionDrawBuffers.cpp inner comments for more informations.
      */
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
 class WebGLExtensionVertexArray
     : public WebGLExtensionBase
--- a/content/canvas/src/WebGLFramebuffer.cpp
+++ b/content/canvas/src/WebGLFramebuffer.cpp
@@ -129,32 +129,46 @@ WebGLFramebuffer::Attachment::RectangleO
 
 /* The following IsValidFBOTextureXXX functions check the internal
    format that is used by GL or GL ES texture formats.  This
    corresponds to the state that is stored in
    WebGLTexture::ImageInfo::InternalFormat()*/
 static inline bool
 IsValidFBOTextureColorFormat(GLenum internalFormat)
 {
+    /* These formats are internal formats for each texture -- the actual
+     * low level format, which we might have to do conversions for when
+     * running against desktop GL (e.g. GL_RGBA + GL_FLOAT -> GL_RGBA32F).
+     *
+     * This function just handles all of them whether desktop GL or ES.
+     */
+
     return (
         /* linear 8-bit formats */
         internalFormat == LOCAL_GL_ALPHA ||
         internalFormat == LOCAL_GL_LUMINANCE ||
         internalFormat == LOCAL_GL_LUMINANCE_ALPHA ||
         internalFormat == LOCAL_GL_RGB ||
         internalFormat == LOCAL_GL_RGBA ||
         /* sRGB 8-bit formats */
         internalFormat == LOCAL_GL_SRGB_EXT ||
         internalFormat == LOCAL_GL_SRGB_ALPHA_EXT ||
         /* linear float32 formats */
         internalFormat == LOCAL_GL_ALPHA32F_ARB ||
         internalFormat == LOCAL_GL_LUMINANCE32F_ARB ||
         internalFormat == LOCAL_GL_LUMINANCE_ALPHA32F_ARB ||
         internalFormat == LOCAL_GL_RGB32F_ARB ||
-        internalFormat == LOCAL_GL_RGBA32F_ARB);
+        internalFormat == LOCAL_GL_RGBA32F_ARB ||
+        /* texture_half_float formats */
+        internalFormat == LOCAL_GL_ALPHA16F_ARB ||
+        internalFormat == LOCAL_GL_LUMINANCE16F_ARB ||
+        internalFormat == LOCAL_GL_LUMINANCE_ALPHA16F_ARB ||
+        internalFormat == LOCAL_GL_RGB16F_ARB ||
+        internalFormat == LOCAL_GL_RGBA16F_ARB
+    );
 }
 
 static inline bool
 IsValidFBOTextureDepthFormat(GLenum internalFormat)
 {
     return (
         internalFormat == LOCAL_GL_DEPTH_COMPONENT ||
         internalFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
--- a/content/canvas/src/WebGLTexelConversions.cpp
+++ b/content/canvas/src/WebGLTexelConversions.cpp
@@ -37,31 +37,36 @@ class WebGLImageConverter
      * texels with typed pointers and this value will tell us by how much we need
      * to increment these pointers to advance to the next texel.
      */
     template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
     static size_t NumElementsPerTexelForFormat() {
         switch (Format) {
             case WebGLTexelFormat::R8:
             case WebGLTexelFormat::A8:
+            case WebGLTexelFormat::R16F:
+            case WebGLTexelFormat::A16F:
             case WebGLTexelFormat::R32F:
             case WebGLTexelFormat::A32F:
             case WebGLTexelFormat::RGBA5551:
             case WebGLTexelFormat::RGBA4444:
             case WebGLTexelFormat::RGB565:
                 return 1;
             case WebGLTexelFormat::RA8:
+            case WebGLTexelFormat::RA16F:
             case WebGLTexelFormat::RA32F:
                 return 2;
             case WebGLTexelFormat::RGB8:
+            case WebGLTexelFormat::RGB16F:
             case WebGLTexelFormat::RGB32F:
                 return 3;
             case WebGLTexelFormat::RGBA8:
             case WebGLTexelFormat::BGRA8:
             case WebGLTexelFormat::BGRX8:
+            case WebGLTexelFormat::RGBA16F:
             case WebGLTexelFormat::RGBA32F:
                 return 4;
             default:
                 MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
                 return 0;
         }
     }
 
@@ -238,26 +243,31 @@ class WebGLImageConverter
     {
         #define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
             case DstFormat: \
                 return run<SrcFormat, DstFormat>(premultiplicationOp);
 
         switch (dstFormat) {
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R8)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A8)
+            WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R16F)
+            WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A16F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::R32F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::A32F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA8)
+            WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA16F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RA32F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB8)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB565)
+            WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB16F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGB32F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA8)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA5551)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA4444)
+            WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA16F)
             WEBGLIMAGECONVERTER_CASE_DSTFORMAT(WebGLTexelFormat::RGBA32F)
             default:
                 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
         }
 
         #undef WEBGLIMAGECONVERTER_CASE_DSTFORMAT
     }
 
@@ -269,28 +279,33 @@ public:
     {
         #define WEBGLIMAGECONVERTER_CASE_SRCFORMAT(SrcFormat) \
             case SrcFormat: \
                 return run<SrcFormat>(dstFormat, premultiplicationOp);
 
         switch (srcFormat) {
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R8)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A8)
+            WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R16F)
+            WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A16F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::R32F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::A32F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA8)
+            WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA16F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RA32F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB8)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRX8) // source format only
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB565)
+            WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB16F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGB32F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA8)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::BGRA8)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA5551)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA4444)
+            WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA16F)
             WEBGLIMAGECONVERTER_CASE_SRCFORMAT(WebGLTexelFormat::RGBA32F)
             default:
                 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
         }
 
         #undef WEBGLIMAGECONVERTER_CASE_SRCFORMAT
     }
 
--- a/content/canvas/src/WebGLTexelConversions.h
+++ b/content/canvas/src/WebGLTexelConversions.h
@@ -33,16 +33,104 @@
 #endif
 
 #include "WebGLTypes.h"
 #include <stdint.h>
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 
+// single precision float
+// seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
+
+// half precision float
+// seeeeemmmmmmmmmm
+
+// IEEE 16bits floating point:
+const uint16_t kFloat16Value_Zero     = 0x0000; // = 0000000000000000b
+const uint16_t kFloat16Value_One      = 0x3C00; // = 0011110000000000b
+const uint16_t kFloat16Value_Infinity = 0x7C00; // = 0111110000000000b
+const uint16_t kFloat16Value_NaN      = 0x7FFF; // = 011111yyyyyyyyyyb (nonzero y)
+
+MOZ_ALWAYS_INLINE uint16_t
+packToFloat16(float v)
+{
+    union {
+        float f32Value;
+        uint32_t f32Bits;
+    };
+
+    f32Value = v;
+
+    // pull the sign from v into f16bits
+    uint16_t f16Bits = uint16_t(f32Bits >> 16) & 0x8000;
+
+    // handle +/- 0
+    if ((f32Bits & 0x7FFFFFFF) == 0x00000000) {
+        return f16Bits;
+    }
+
+    // handle NaN
+    if (f32Value != f32Value) {
+        return f16Bits | kFloat16Value_NaN;
+    }
+
+    int32_t exp = int32_t(f32Bits >> 23) - 127;
+
+    // too small, we clamp it to -0 or +0
+    if (exp < -14) {
+        return f16Bits;
+    }
+
+    // too big, we clamp it to -inf/+inf
+    if (exp > 15) {
+        return f16Bits | kFloat16Value_Infinity;
+    }
+
+    f16Bits |= uint16_t(exp + 15) << 10;
+    f16Bits |= uint16_t(f32Bits >> 13) & 0x03FF;
+
+    return f16Bits;
+}
+
+MOZ_ALWAYS_INLINE float
+unpackFromFloat16(uint16_t v)
+{
+    union
+    {
+        float f32Value;
+        uint32_t f32Bits;
+    };
+
+    // grab sign bit
+    f32Bits = uint32_t(v & 0x8000) << 16;
+
+    if ((v & 0x7FFF) == 0x0000) {
+        // +0 or -0
+        return f32Value;
+    }
+
+    uint16_t exp = (v >> 10) & 0x001F;
+    if (exp == 0x001F) {
+        if (v & 0x03FF) {
+            // this is a NaN
+            f32Bits |= 0x7FFFFFFF;
+        } else {
+            // this is -inf or +inf
+            f32Bits |= 0x7F800000;
+        }
+        return f32Value;
+    }
+
+    f32Bits |= uint32_t(exp + (-15 + 127)) << 10;
+    f32Bits |= uint32_t(v & 0x03FF) << 13;
+
+    return f32Value;
+}
+
 MOZ_BEGIN_ENUM_CLASS(WebGLTexelPremultiplicationOp, int)
     None,
     Premultiply,
     Unpremultiply
 MOZ_END_ENUM_CLASS(WebGLTexelPremultiplicationOp)
 
 namespace WebGLTexelConversions {
 
@@ -53,51 +141,70 @@ struct IsFloatFormat
         Format == WebGLTexelFormat::RGBA32F ||
         Format == WebGLTexelFormat::RGB32F ||
         Format == WebGLTexelFormat::RA32F ||
         Format == WebGLTexelFormat::R32F ||
         Format == WebGLTexelFormat::A32F;
 };
 
 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
+struct IsHalfFloatFormat
+{
+    static const bool Value =
+        Format == WebGLTexelFormat::RGBA16F ||
+        Format == WebGLTexelFormat::RGB16F ||
+        Format == WebGLTexelFormat::RA16F ||
+        Format == WebGLTexelFormat::R16F ||
+        Format == WebGLTexelFormat::A16F;
+};
+
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
 struct Is16bppFormat
 {
     static const bool Value =
         Format == WebGLTexelFormat::RGBA4444 ||
         Format == WebGLTexelFormat::RGBA5551 ||
         Format == WebGLTexelFormat::RGB565;
 };
 
 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format,
          bool IsFloat = IsFloatFormat<Format>::Value,
-         bool Is16bpp = Is16bppFormat<Format>::Value>
+         bool Is16bpp = Is16bppFormat<Format>::Value,
+         bool IsHalfFloat = IsHalfFloatFormat<Format>::Value>
 struct DataTypeForFormat
 {
     typedef uint8_t Type;
 };
 
 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
-struct DataTypeForFormat<Format, true, false>
+struct DataTypeForFormat<Format, true, false, false>
 {
     typedef float Type;
 };
 
 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
-struct DataTypeForFormat<Format, false, true>
+struct DataTypeForFormat<Format, false, true, false>
+{
+    typedef uint16_t Type;
+};
+
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
+struct DataTypeForFormat<Format, false, false, true>
 {
     typedef uint16_t Type;
 };
 
 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
 struct IntermediateFormat
 {
     static const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Value
         = IsFloatFormat<Format>::Value
           ? WebGLTexelFormat::RGBA32F
-          : WebGLTexelFormat::RGBA8;
+          : IsHalfFloatFormat<Format>::Value ? WebGLTexelFormat::RGBA16F
+                                             : WebGLTexelFormat::RGBA8;
 };
 
 inline GLenum
 GLFormatForTexelFormat(WebGLTexelFormat format) {
     switch (format) {
         case WebGLTexelFormat::R8:          return LOCAL_GL_LUMINANCE;
         case WebGLTexelFormat::A8:          return LOCAL_GL_ALPHA;
         case WebGLTexelFormat::RA8:         return LOCAL_GL_LUMINANCE_ALPHA;
@@ -111,78 +218,96 @@ GLFormatForTexelFormat(WebGLTexelFormat 
         case WebGLTexelFormat::BGRX8:       return LOCAL_GL_BGR;
         case WebGLTexelFormat::R32F:        return LOCAL_GL_LUMINANCE;
         case WebGLTexelFormat::A32F:        return LOCAL_GL_ALPHA;
         case WebGLTexelFormat::D32:         return LOCAL_GL_DEPTH_COMPONENT;
         case WebGLTexelFormat::D24S8:       return LOCAL_GL_DEPTH_STENCIL;
         case WebGLTexelFormat::RA32F:       return LOCAL_GL_LUMINANCE_ALPHA;
         case WebGLTexelFormat::RGB32F:      return LOCAL_GL_RGB;
         case WebGLTexelFormat::RGBA32F:     return LOCAL_GL_RGBA;
+        case WebGLTexelFormat::R16F:        return LOCAL_GL_LUMINANCE;
+        case WebGLTexelFormat::A16F:        return LOCAL_GL_ALPHA;
+        case WebGLTexelFormat::RA16F:       return LOCAL_GL_LUMINANCE_ALPHA;
+        case WebGLTexelFormat::RGB16F:      return LOCAL_GL_RGB;
+        case WebGLTexelFormat::RGBA16F:     return LOCAL_GL_RGBA;
         default:
             MOZ_CRASH("Unknown texel format. Coding mistake?");
             return LOCAL_GL_INVALID_ENUM;
     }
 }
 
 inline size_t TexelBytesForFormat(WebGLTexelFormat format) {
     switch (format) {
         case WebGLTexelFormat::R8:
         case WebGLTexelFormat::A8:
             return 1;
         case WebGLTexelFormat::RA8:
         case WebGLTexelFormat::RGBA5551:
         case WebGLTexelFormat::RGBA4444:
         case WebGLTexelFormat::RGB565:
+        case WebGLTexelFormat::R16F:
+        case WebGLTexelFormat::A16F:
         case WebGLTexelFormat::D16:
             return 2;
         case WebGLTexelFormat::RGB8:
             return 3;
         case WebGLTexelFormat::RGBA8:
         case WebGLTexelFormat::BGRA8:
         case WebGLTexelFormat::BGRX8:
         case WebGLTexelFormat::R32F:
         case WebGLTexelFormat::A32F:
         case WebGLTexelFormat::D32:
         case WebGLTexelFormat::D24S8:
+        case WebGLTexelFormat::RA16F:
             return 4;
+        case WebGLTexelFormat::RGB16F:
+            return 6;
+        case WebGLTexelFormat::RGBA16F:
         case WebGLTexelFormat::RA32F:
             return 8;
         case WebGLTexelFormat::RGB32F:
             return 12;
         case WebGLTexelFormat::RGBA32F:
             return 16;
         default:
             MOZ_ASSERT(false, "Unknown texel format. Coding mistake?");
             return 0;
     }
 }
 
 MOZ_ALWAYS_INLINE bool HasAlpha(WebGLTexelFormat format) {
     return format == WebGLTexelFormat::A8 ||
+           format == WebGLTexelFormat::A16F ||
            format == WebGLTexelFormat::A32F ||
            format == WebGLTexelFormat::RA8 ||
+           format == WebGLTexelFormat::RA16F ||
            format == WebGLTexelFormat::RA32F ||
            format == WebGLTexelFormat::RGBA8 ||
            format == WebGLTexelFormat::BGRA8 ||
+           format == WebGLTexelFormat::RGBA16F ||
            format == WebGLTexelFormat::RGBA32F ||
            format == WebGLTexelFormat::RGBA4444 ||
            format == WebGLTexelFormat::RGBA5551;
 }
 
 MOZ_ALWAYS_INLINE bool HasColor(WebGLTexelFormat format) {
     return format == WebGLTexelFormat::R8 ||
+           format == WebGLTexelFormat::R16F ||
            format == WebGLTexelFormat::R32F ||
            format == WebGLTexelFormat::RA8 ||
+           format == WebGLTexelFormat::RA16F ||
            format == WebGLTexelFormat::RA32F ||
            format == WebGLTexelFormat::RGB8 ||
            format == WebGLTexelFormat::BGRX8 ||
            format == WebGLTexelFormat::RGB565 ||
+           format == WebGLTexelFormat::RGB16F ||
            format == WebGLTexelFormat::RGB32F ||
            format == WebGLTexelFormat::RGBA8 ||
            format == WebGLTexelFormat::BGRA8 ||
+           format == WebGLTexelFormat::RGBA16F ||
            format == WebGLTexelFormat::RGBA32F ||
            format == WebGLTexelFormat::RGBA4444 ||
            format == WebGLTexelFormat::RGBA5551;
 }
 
 
 /****** BEGIN CODE SHARED WITH WEBKIT ******/
 
@@ -343,16 +468,61 @@ template<> MOZ_ALWAYS_INLINE void
 unpack<WebGLTexelFormat::A32F, float, float>(const float* __restrict src, float* __restrict dst)
 {
     dst[0] = 0;
     dst[1] = 0;
     dst[2] = 0;
     dst[3] = src[0];
 }
 
+template<> MOZ_ALWAYS_INLINE void
+unpack<WebGLTexelFormat::RGBA16F, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[1];
+    dst[2] = src[2];
+    dst[3] = src[3];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+unpack<WebGLTexelFormat::RGB16F, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[1];
+    dst[2] = src[2];
+    dst[3] = kFloat16Value_One;
+}
+
+template<> MOZ_ALWAYS_INLINE void
+unpack<WebGLTexelFormat::R16F, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[0];
+    dst[2] = src[0];
+    dst[3] = kFloat16Value_One;
+}
+
+template<> MOZ_ALWAYS_INLINE void
+unpack<WebGLTexelFormat::RA16F, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[0];
+    dst[2] = src[0];
+    dst[3] = src[1];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+unpack<WebGLTexelFormat::A16F, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = kFloat16Value_Zero;
+    dst[1] = kFloat16Value_Zero;
+    dst[2] = kFloat16Value_Zero;
+    dst[3] = src[0];
+}
+
 //----------------------------------------------------------------------
 // Pixel packing routines.
 //
 
 template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format,
          MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp,
          typename SrcType,
          typename DstType>
@@ -673,16 +843,92 @@ pack<WebGLTexelFormat::RA32F, WebGLTexel
 template<> MOZ_ALWAYS_INLINE void
 pack<WebGLTexelFormat::RA32F, WebGLTexelPremultiplicationOp::Premultiply, float, float>(const float* __restrict src, float* __restrict dst)
 {
     float scaleFactor = src[3];
     dst[0] = src[0] * scaleFactor;
     dst[1] = scaleFactor;
 }
 
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::RGB16F, WebGLTexelPremultiplicationOp::None, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[1];
+    dst[2] = src[2];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::RGB16F, WebGLTexelPremultiplicationOp::Premultiply, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    float scaleFactor = unpackFromFloat16(src[3]);
+    dst[0] = packToFloat16(unpackFromFloat16(src[0]) * scaleFactor);
+    dst[1] = packToFloat16(unpackFromFloat16(src[1]) * scaleFactor);
+    dst[2] = packToFloat16(unpackFromFloat16(src[2]) * scaleFactor);
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::RGBA16F, WebGLTexelPremultiplicationOp::None, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[1];
+    dst[2] = src[2];
+    dst[3] = src[3];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::RGBA16F, WebGLTexelPremultiplicationOp::Premultiply, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    float scaleFactor = unpackFromFloat16(src[3]);
+    dst[0] = packToFloat16(unpackFromFloat16(src[0]) * scaleFactor);
+    dst[1] = packToFloat16(unpackFromFloat16(src[1]) * scaleFactor);
+    dst[2] = packToFloat16(unpackFromFloat16(src[2]) * scaleFactor);
+    dst[3] = src[3];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::A16F, WebGLTexelPremultiplicationOp::None, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[3];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::A16F, WebGLTexelPremultiplicationOp::Premultiply, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[3];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::R16F, WebGLTexelPremultiplicationOp::None, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::R16F, WebGLTexelPremultiplicationOp::Premultiply, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    float scaleFactor = unpackFromFloat16(src[3]);
+    dst[0] = packToFloat16(unpackFromFloat16(src[0]) * scaleFactor);
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::RA16F, WebGLTexelPremultiplicationOp::None, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[3];
+}
+
+template<> MOZ_ALWAYS_INLINE void
+pack<WebGLTexelFormat::RA16F, WebGLTexelPremultiplicationOp::Premultiply, uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    float scaleFactor = unpackFromFloat16(src[3]);
+    dst[0] = packToFloat16(unpackFromFloat16(src[0]) * scaleFactor);
+    dst[1] = scaleFactor;
+}
+
 /****** END CODE SHARED WITH WEBKIT ******/
 
 template<typename SrcType, typename DstType> MOZ_ALWAYS_INLINE void
 convertType(const SrcType* __restrict src, DstType* __restrict dst)
 {
     MOZ_ASSERT(false, "Unimplemented texture format conversion");
 }
 
@@ -691,16 +937,25 @@ convertType<uint8_t, uint8_t>(const uint
 {
     dst[0] = src[0];
     dst[1] = src[1];
     dst[2] = src[2];
     dst[3] = src[3];
 }
 
 template<> MOZ_ALWAYS_INLINE void
+convertType<uint16_t, uint16_t>(const uint16_t* __restrict src, uint16_t* __restrict dst)
+{
+    dst[0] = src[0];
+    dst[1] = src[1];
+    dst[2] = src[2];
+    dst[3] = src[3];
+}
+
+template<> MOZ_ALWAYS_INLINE void
 convertType<float, float>(const float* __restrict src, float* __restrict dst)
 {
     dst[0] = src[0];
     dst[1] = src[1];
     dst[2] = src[2];
     dst[3] = src[3];
 }
 
@@ -709,13 +964,23 @@ convertType<uint8_t, float>(const uint8_
 {
     const float scaleFactor = 1.f / 255.0f;
     dst[0] = src[0] * scaleFactor;
     dst[1] = src[1] * scaleFactor;
     dst[2] = src[2] * scaleFactor;
     dst[3] = src[3] * scaleFactor;
 }
 
+template<> MOZ_ALWAYS_INLINE void
+convertType<uint8_t, uint16_t>(const uint8_t* __restrict src, uint16_t* __restrict dst)
+{
+    const float scaleFactor = 1.f / 255.0f;
+    dst[0] = packToFloat16(src[0] * scaleFactor);
+    dst[1] = packToFloat16(src[1] * scaleFactor);
+    dst[2] = packToFloat16(src[2] * scaleFactor);
+    dst[3] = packToFloat16(src[3] * scaleFactor);
+}
+
 } // end namespace WebGLTexelConversions
 
 } // end namespace mozilla
 
 #endif // WEBGLTEXELCONVERSIONS_H_
--- a/content/canvas/src/WebGLTexture.cpp
+++ b/content/canvas/src/WebGLTexture.cpp
@@ -346,16 +346,36 @@ WebGLTexture::ResolvedFakeBlackStatus() 
         else if (mMagFilter == LOCAL_GL_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
                                       "which is not compatible with gl.FLOAT by default. "
                                       "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
             mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
         }
     }
+    else if (ImageInfoBase().mType == LOCAL_GL_HALF_FLOAT_OES)
+    {
+        if (mMinFilter == LOCAL_GL_LINEAR ||
+            mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
+            mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
+            mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
+        {
+            mContext->GenerateWarning("%s is a texture with a linear minification filter, "
+                                      "which is not compatible with gl.HALF_FLOAT by default. "
+                                      "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black);
+            mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
+        }
+        else if (mMagFilter == LOCAL_GL_LINEAR)
+        {
+            mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
+                                      "which is not compatible with gl.HALF_FLOAT by default. "
+                                      "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black);
+            mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
+        }
+    }
 
     // We have exhausted all cases of incomplete textures, where we would need opaque black.
     // We may still need transparent black in case of uninitialized image data.
     bool hasUninitializedImageData = false;
     for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
         for (size_t face = 0; face < mFacesCount; ++face) {
             hasUninitializedImageData |= (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
         }
--- a/content/canvas/src/WebGLTypes.h
+++ b/content/canvas/src/WebGLTypes.h
@@ -103,32 +103,37 @@ MOZ_BEGIN_ENUM_CLASS(WebGLTexelFormat, i
     BadFormat,
     // dummy pseudo-format meaning "use the other format".
     // For example, if SrcFormat=Auto and DstFormat=RGB8, then the source
     // is implicitly treated as being RGB8 itself.
     Auto,
     // 1-channel formats
     R8,
     A8,
-    D16, // used for WEBGL_depth_texture extension
-    D32, // used for WEBGL_depth_texture extension
-    R32F, // used for OES_texture_float extension
-    A32F, // used for OES_texture_float extension
+    D16, // WEBGL_depth_texture
+    D32, // WEBGL_depth_texture
+    R16F, // OES_texture_half_float
+    A16F, // OES_texture_half_float
+    R32F, // OES_texture_float
+    A32F, // OES_texture_float
     // 2-channel formats
     RA8,
-    RA32F,
-    D24S8, // used for WEBGL_depth_texture extension
+    RA16F, // OES_texture_half_float
+    RA32F, // OES_texture_float
+    D24S8, // WEBGL_depth_texture
     // 3-channel formats
     RGB8,
     BGRX8, // used for DOM elements. Source format only.
     RGB565,
-    RGB32F, // used for OES_texture_float extension
+    RGB16F, // OES_texture_half_float
+    RGB32F, // OES_texture_float
     // 4-channel formats
     RGBA8,
     BGRA8, // used for DOM elements
     RGBA5551,
     RGBA4444,
-    RGBA32F // used for OES_texture_float extension
+    RGBA16F, // OES_texture_half_float
+    RGBA32F // OES_texture_float
 MOZ_END_ENUM_CLASS(WebGLTexelFormat)
 
 } // namespace mozilla
 
 #endif
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -53,16 +53,17 @@ if CONFIG['MOZ_WEBGL']:
         'WebGLExtensionElementIndexUint.cpp',
         'WebGLExtensionInstancedArrays.cpp',
         'WebGLExtensionLoseContext.cpp',
         'WebGLExtensionSRGB.cpp',
         'WebGLExtensionStandardDerivatives.cpp',
         'WebGLExtensionTextureFilterAnisotropic.cpp',
         'WebGLExtensionTextureFloat.cpp',
         'WebGLExtensionTextureFloatLinear.cpp',
+        'WebGLExtensionTextureHalfFloat.cpp',
         'WebGLExtensionVertexArray.cpp',
         'WebGLFramebuffer.cpp',
         'WebGLObjectModel.cpp',
         'WebGLProgram.cpp',
         'WebGLQuery.cpp',
         'WebGLRenderbuffer.cpp',
         'WebGLShader.cpp',
         'WebGLShaderPrecisionFormat.cpp',
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -5,16 +5,17 @@
 
 #include "AudioNodeStream.h"
 
 #include "MediaStreamGraphImpl.h"
 #include "AudioNodeEngine.h"
 #include "ThreeDPoint.h"
 #include "AudioChannelFormat.h"
 #include "AudioParamTimeline.h"
+#include "AudioContext.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 /**
  * An AudioNodeStream produces a single audio track with ID
  * AUDIO_TRACK. This track has rate AudioContext::sIdealAudioRate
@@ -25,17 +26,17 @@ namespace mozilla {
  */
 
 AudioNodeStream::~AudioNodeStream()
 {
   MOZ_COUNT_DTOR(AudioNodeStream);
 }
 
 void
-AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, MediaStream* aRelativeToStream,
+AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
                                         double aStreamTime)
 {
   class Message : public ControlMessage {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
             double aStreamTime)
       : ControlMessage(aStream), mStreamTime(aStreamTime),
         mRelativeToStream(aRelativeToStream), mIndex(aIndex) {}
@@ -45,17 +46,19 @@ AudioNodeStream::SetStreamTimeParameter(
           SetStreamTimeParameterImpl(mIndex, mRelativeToStream, mStreamTime);
     }
     double mStreamTime;
     MediaStream* mRelativeToStream;
     uint32_t mIndex;
   };
 
   MOZ_ASSERT(this);
-  GraphImpl()->AppendMessage(new Message(this, aIndex, aRelativeToStream, aStreamTime));
+  GraphImpl()->AppendMessage(new Message(this, aIndex,
+      aContext->DestinationStream(),
+      aContext->DOMTimeToStreamTime(aStreamTime)));
 }
 
 void
 AudioNodeStream::SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
                                             double aStreamTime)
 {
   TrackTicks ticks =
       WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime(
@@ -402,17 +405,22 @@ AudioNodeStream::ProduceOutput(GraphTime
 {
   EnsureTrack(AUDIO_TRACK, mSampleRate);
   // No more tracks will be coming
   mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
   uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
   mLastChunks.SetLength(outputCount);
 
-  if (mMuted || IsFinishedOnGraphThread()) {
+  // Consider this stream blocked if it has already finished output. Normally
+  // mBlocked would reflect this, but due to rounding errors our audio track may
+  // appear to extend slightly beyond aFrom, so we might not be blocked yet.
+  bool blocked = mFinished || mBlocked.GetAt(aFrom);
+  // If the stream has finished at this time, it will be blocked.
+  if (mMuted || blocked) {
     for (uint16_t i = 0; i < outputCount; ++i) {
       mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
   } else {
     // We need to generate at least one input
     uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
     OutputChunks inputChunks;
     inputChunks.SetLength(maxInputs);
@@ -434,26 +442,26 @@ AudioNodeStream::ProduceOutput(GraphTime
     }
     for (uint16_t i = 0; i < outputCount; ++i) {
       NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
                    "Invalid WebAudio chunk size");
     }
     if (finished) {
       mMarkAsFinishedAfterThisBlock = true;
     }
-  }
 
-  if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
-    for (uint32_t i = 0; i < mLastChunks.Length(); ++i) {
-      mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
+    if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
+      for (uint32_t i = 0; i < outputCount; ++i) {
+        mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
+      }
     }
   }
 
-  if (!IsFinishedOnGraphThread()) {
-    // Don't output anything after we've finished!
+  if (!blocked) {
+    // Don't output anything while blocked
     AdvanceOutputSegment();
     if (mMarkAsFinishedAfterThisBlock && (aFlags & ALLOW_FINISH)) {
       // This stream was finished the last time that we looked at it, and all
       // of the depending streams have finished their output as well, so now
       // it's time to mark this stream as finished.
       FinishOutput();
     }
   }
--- a/content/media/AudioNodeStream.h
+++ b/content/media/AudioNodeStream.h
@@ -11,16 +11,17 @@
 #include "AudioSegment.h"
 
 namespace mozilla {
 
 namespace dom {
 struct ThreeDPoint;
 class AudioParamTimeline;
 class DelayNodeEngine;
+class AudioContext;
 }
 
 class ThreadSharedFloatArrayBufferList;
 class AudioNodeEngine;
 
 /**
  * An AudioNodeStream produces one audio track with ID AUDIO_TRACK.
  * The start time of the AudioTrack is aligned to the start time of the
@@ -28,16 +29,18 @@ class AudioNodeEngine;
  * samples.
  *
  * An AudioNodeStream has an AudioNodeEngine plugged into it that does the
  * actual audio processing. AudioNodeStream contains the glue code that
  * integrates audio processing with the MediaStreamGraph.
  */
 class AudioNodeStream : public ProcessedMediaStream {
 public:
+  typedef mozilla::dom::AudioContext AudioContext;
+
   enum { AUDIO_TRACK = 1 };
 
   typedef nsAutoTArray<AudioChunk, 1> OutputChunks;
 
   /**
    * Transfers ownership of aEngine to the new AudioNodeStream.
    */
   AudioNodeStream(AudioNodeEngine* aEngine,
@@ -61,17 +64,17 @@ public:
   }
   ~AudioNodeStream();
 
   // Control API
   /**
    * Sets a parameter that's a time relative to some stream's played time.
    * This time is converted to a time relative to this stream when it's set.
    */
-  void SetStreamTimeParameter(uint32_t aIndex, MediaStream* aRelativeToStream,
+  void SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
                               double aStreamTime);
   void SetDoubleParameter(uint32_t aIndex, double aValue);
   void SetInt32Parameter(uint32_t aIndex, int32_t aValue);
   void SetTimelineParameter(uint32_t aIndex, const dom::AudioParamTimeline& aValue);
   void SetThreeDPointParameter(uint32_t aIndex, const dom::ThreeDPoint& aValue);
   void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer);
   // This consumes the contents of aData.  aData will be emptied after this returns.
   void SetRawArrayData(nsTArray<float>& aData);
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -167,45 +167,47 @@ class MediaRecorder::Session: public nsI
       MOZ_ASSERT(NS_IsMainThread() && mSession.get());
       MediaRecorder *recorder = mSession->mRecorder;
 
       // SourceMediaStream is ended, and send out TRACK_EVENT_END notification.
       // Read Thread will be terminate soon.
       // We need to switch MediaRecorder to "Stop" state first to make sure
       // MediaRecorder is not associated with this Session anymore, then, it's
       // safe to delete this Session.
-      if (recorder->mState != RecordingState::Inactive) {
+      // Also avoid to run if this session already call stop before
+      if (!mSession->mStopIssued) {
         ErrorResult result;
         recorder->Stop(result);
         NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
 
         return NS_OK;
       }
 
       // Dispatch stop event and clear MIME type.
       recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
-      recorder->SetMimeType(NS_LITERAL_STRING(""));
-
+      mSession->mMimeType = NS_LITERAL_STRING("");
+      recorder->SetMimeType(mSession->mMimeType);
       return NS_OK;
     }
 
   private:
     // Call mSession::Release automatically while DestroyRunnable be destroy.
     nsRefPtr<Session> mSession;
   };
 
   friend class PushBlobRunnable;
   friend class ExtractRunnable;
   friend class DestroyRunnable;
   friend class TracksAvailableCallback;
 
 public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
     : mRecorder(aRecorder),
-      mTimeSlice(aTimeSlice)
+      mTimeSlice(aTimeSlice),
+      mStopIssued(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     AddRef();
     mEncodedBufferCache = new EncodedBufferCache(MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
 
@@ -221,16 +223,17 @@ public:
 
     SetupStreams();
   }
 
   void Stop()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
+    mStopIssued = true;
     CleanupStreams();
     nsContentUtils::UnregisterShutdownObserver(this);
   }
 
   nsresult Pause()
   {
     NS_ENSURE_TRUE(NS_IsMainThread() && mTrackUnionStream, NS_ERROR_FAILURE);
     mTrackUnionStream->ChangeExplicitBlockerCount(-1);
@@ -243,20 +246,17 @@ public:
     NS_ENSURE_TRUE(NS_IsMainThread() && mTrackUnionStream, NS_ERROR_FAILURE);
     mTrackUnionStream->ChangeExplicitBlockerCount(1);
 
     return NS_OK;
   }
 
   already_AddRefed<nsIDOMBlob> GetEncodedData()
   {
-    nsString mimeType;
-    mRecorder->GetMimeType(mimeType);
-
-    return mEncodedBufferCache->ExtractBlob(mimeType);
+    return mEncodedBufferCache->ExtractBlob(mMimeType);
   }
 
   bool IsEncoderError()
   {
     if (mEncoder && mEncoder->HasError()) {
       return true;
     }
     return false;
@@ -269,20 +269,18 @@ private:
   {
     MOZ_ASSERT(NS_GetCurrentThread() == mReadThread);
 
     // Whether push encoded data back to onDataAvailable automatically.
     const bool pushBlob = (mTimeSlice > 0) ? true : false;
 
     // Pull encoded media data from MediaEncoder
     nsTArray<nsTArray<uint8_t> > encodedBuf;
-    nsString mimeType;
-    mEncoder->GetEncodedData(&encodedBuf, mimeType);
-
-    mRecorder->SetMimeType(mimeType);
+    mEncoder->GetEncodedData(&encodedBuf, mMimeType);
+    mRecorder->SetMimeType(mMimeType);
 
     // Append pulled data into cache buffer.
     for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
       mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
     }
 
     if (pushBlob) {
       if ((TimeStamp::Now() - mLastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
@@ -320,18 +318,20 @@ private:
     // At this stage, the API doesn't allow UA to choose the output mimeType format.
     mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes);
 
     if (!mEncoder) {
       DoSessionEndTask(NS_ERROR_ABORT);
       return;
     }
 
-    // media stream is ready but has been issued stop command
-    if (mRecorder->mState == RecordingState::Inactive) {
+    // Media stream is ready but UA issues a stop method follow by start method.
+    // The Session::stop would clean the mTrackUnionStream. If the AfterTracksAdded
+    // comes after stop command, this function would crash.
+    if (!mTrackUnionStream) {
       DoSessionEndTask(NS_OK);
       return;
     }
     mTrackUnionStream->AddListener(mEncoder);
     // Create a thread to read encode media data from MediaEncoder.
     if (!mReadThread) {
       nsresult rv = NS_NewNamedThread("Media Encoder", getter_AddRefs(mReadThread));
       if (NS_FAILED(rv)) {
@@ -395,23 +395,27 @@ private:
   nsRefPtr<MediaInputPort> mInputPort;
 
   // Runnable thread for read data from MediaEncode.
   nsCOMPtr<nsIThread> mReadThread;
   // MediaEncoder pipeline.
   nsRefPtr<MediaEncoder> mEncoder;
   // A buffer to cache encoded meda data.
   nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
+  // Current session mimeType
+  nsString mMimeType;
   // Timestamp of the last fired dataavailable event.
   TimeStamp mLastBlobTimeStamp;
   // The interval of passing encoded data from EncodedBufferCache to onDataAvailable
   // handler. "mTimeSlice < 0" means Session object does not push encoded data to
   // onDataAvailable, instead, it passive wait the client side pull encoded data
   // by calling requestData API.
   const int32_t mTimeSlice;
+  // Indicate this session's stop has been called.
+  bool mStopIssued;
 };
 
 NS_IMPL_ISUPPORTS1(MediaRecorder::Session, nsIObserver)
 
 MediaRecorder::~MediaRecorder()
 {
   MOZ_ASSERT(mSession == nullptr);
 }
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1112,16 +1112,38 @@ MediaStreamGraphImpl::AllFinishedStreams
     if (s->mFinished && !s->mNotifiedFinished) {
       return false;
     }
   }
   return true;
 }
 
 void
+MediaStreamGraphImpl::PauseAllAudioOutputs()
+{
+  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+    MediaStream* s = mStreams[i];
+    for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
+      s->mAudioOutputStreams[j].mStream->Pause();
+    }
+  }
+}
+
+void
+MediaStreamGraphImpl::ResumeAllAudioOutputs()
+{
+  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+    MediaStream* s = mStreams[i];
+    for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
+      s->mAudioOutputStreams[j].mStream->Resume();
+    }
+  }
+}
+
+void
 MediaStreamGraphImpl::RunThread()
 {
   nsTArray<MessageBlock> messageQueue;
   {
     MonitorAutoLock lock(mMonitor);
     messageQueue.SwapElements(mMessageQueue);
   }
   NS_ASSERTION(!messageQueue.IsEmpty(),
@@ -1175,17 +1197,16 @@ MediaStreamGraphImpl::RunThread()
       }
     }
 
     // Figure out which streams are blocked and when.
     GraphTime prevComputedTime = mStateComputedTime;
     RecomputeBlocking(endBlockingDecisions);
 
     // Play stream contents.
-    uint32_t audioStreamsActive = 0;
     bool allBlockedForever = true;
     // True when we've done ProduceOutput for all processed streams.
     bool doneAllProducing = false;
     // Figure out what each stream wants to do
     for (uint32_t i = 0; i < mStreams.Length(); ++i) {
       MediaStream* stream = mStreams[i];
       if (!doneAllProducing) {
         ProcessedMediaStream* ps = stream->AsProcessedStream();
@@ -1216,29 +1237,28 @@ MediaStreamGraphImpl::RunThread()
           }
         }
       }
       NotifyHasCurrentData(stream);
       if (mRealtime) {
         // Only playback audio and video in real-time mode
         CreateOrDestroyAudioStreams(prevComputedTime, stream);
         PlayAudio(stream, prevComputedTime, mStateComputedTime);
-        audioStreamsActive += stream->mAudioOutputStreams.Length();
         PlayVideo(stream);
       }
       SourceMediaStream* is = stream->AsSourceStream();
       if (is) {
         UpdateBufferSufficiencyState(is);
       }
       GraphTime end;
       if (!stream->mBlocked.GetAt(mCurrentTime, &end) || end < GRAPH_TIME_MAX) {
         allBlockedForever = false;
       }
     }
-    if (ensureNextIteration || !allBlockedForever || audioStreamsActive > 0) {
+    if (ensureNextIteration || !allBlockedForever) {
       EnsureNextIteration();
     }
 
     // Send updates to the main thread and wait for the next control loop
     // iteration.
     {
       MonitorAutoLock lock(mMonitor);
       bool finalUpdate = mForceShutDown ||
@@ -1256,35 +1276,41 @@ MediaStreamGraphImpl::RunThread()
         return;
       }
 
       // No need to wait in non-realtime mode, just churn through the input as soon
       // as possible.
       if (mRealtime) {
         PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
         TimeStamp now = TimeStamp::Now();
+        bool pausedOutputs = false;
         if (mNeedAnotherIteration) {
           int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
             int64_t((now - mCurrentTimeStamp).ToMilliseconds());
           // Make sure timeoutMS doesn't overflow 32 bits by waking up at
           // least once a minute, if we need to wake up at all
           timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
           timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
           STREAM_LOG(PR_LOG_DEBUG+1, ("Waiting for next iteration; at %f, timeout=%f",
                                      (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
           mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
         } else {
           mWaitState = WAITSTATE_WAITING_INDEFINITELY;
+          PauseAllAudioOutputs();
+          pausedOutputs = true;
         }
         if (timeout > 0) {
           mMonitor.Wait(timeout);
           STREAM_LOG(PR_LOG_DEBUG+1, ("Resuming after timeout; at %f, elapsed=%f",
                                      (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
                                      (TimeStamp::Now() - now).ToSeconds()));
         }
+        if (pausedOutputs) {
+          ResumeAllAudioOutputs();
+        }
       }
       mWaitState = WAITSTATE_RUNNING;
       mNeedAnotherIteration = false;
       messageQueue.SwapElements(mMessageQueue);
     }
   }
 
   profiler_unregister_thread();
@@ -1526,17 +1552,18 @@ MediaStreamGraphImpl::RunInStableState()
         // Revive the MediaStreamGraph since we have more messages going to it.
         // Note that we need to put messages into its queue before reviving it,
         // or it might exit immediately.
         nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable(this);
         mThread->Dispatch(event, 0);
       }
     }
 
-    if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
+    if (mForceShutDown &&
+        mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
       // Defer calls to RunDuringShutdown() to happen while mMonitor is not held.
       for (uint32_t i = 0; i < mMessageQueue.Length(); ++i) {
         MessageBlock& mb = mMessageQueue[i];
         controlMessagesToRunDuringShutdown.MoveElementsFrom(mb.mMessages);
       }
       mMessageQueue.Clear();
       MOZ_ASSERT(mCurrentTaskMessageQueue.IsEmpty());
       // Stop MediaStreamGraph threads. Do not clear gGraph since
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -365,16 +365,24 @@ public:
   void DestroyPort(MediaInputPort* aPort);
   /**
    * Mark the media stream order as dirty.
    */
   void SetStreamOrderDirty()
   {
     mStreamOrderDirty = true;
   }
+  /**
+   * Pause all AudioStreams being written to by MediaStreams
+   */
+  void PauseAllAudioOutputs();
+  /**
+   * Resume all AudioStreams being written to by MediaStreams
+   */
+  void ResumeAllAudioOutputs();
 
   // Data members
 
   /**
    * Media graph thread.
    * Readonly after initialization on the main thread.
    */
   nsCOMPtr<nsIThread> mThread;
--- a/content/media/RtspMediaResource.cpp
+++ b/content/media/RtspMediaResource.cpp
@@ -435,29 +435,65 @@ RtspMediaResource::OnMediaDataAvailable(
   if (mRealTime) {
     time = 0;
   }
   mTrackBuffer[aTrackIdx]->WriteBuffer(data.BeginReading(), length, time,
                                        frameType);
   return NS_OK;
 }
 
+// Bug 962309 - Video RTSP support should be disabled in 1.3
+bool
+RtspMediaResource::IsVideoEnabled()
+{
+  return Preferences::GetBool("media.rtsp.video.enabled", false);
+}
+
+bool
+RtspMediaResource::IsVideo(uint8_t tracks, nsIStreamingProtocolMetaData *meta)
+{
+  bool isVideo = false;
+  for (int i = 0; i < tracks; ++i) {
+    nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta;
+    mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta));
+    MOZ_ASSERT(trackMeta);
+    uint32_t w = 0, h = 0;
+    trackMeta->GetWidth(&w);
+    trackMeta->GetHeight(&h);
+    if (w > 0 || h > 0) {
+      isVideo = true;
+      break;
+    }
+  }
+  return isVideo;
+}
+
 nsresult
 RtspMediaResource::OnConnected(uint8_t aTrackIdx,
                                nsIStreamingProtocolMetaData *meta)
 {
   if (mIsConnected) {
     for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
       mTrackBuffer[i]->Start();
     }
     return NS_OK;
   }
 
   uint8_t tracks;
   mMediaStreamController->GetTotalTracks(&tracks);
+
+  // If the preference of RTSP video feature is not enabled and the streaming is
+  // video, we give up moving forward.
+  if (!IsVideoEnabled() && IsVideo(tracks, meta)) {
+    // Give up, report error to media element.
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
+    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+    return NS_ERROR_FAILURE;
+  }
   uint64_t duration = 0;
   for (int i = 0; i < tracks; ++i) {
     nsCString rtspTrackId("RtspTrack");
     rtspTrackId.AppendInt(i);
     nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta;
     mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta));
     MOZ_ASSERT(trackMeta);
     trackMeta->GetDuration(&duration);
@@ -535,20 +571,19 @@ RtspMediaResource::OnDisconnected(uint8_
   // triggered when media element was destroyed and mDecoder was already
   // shutdown.
   if (!mDecoder) {
     return NS_OK;
   }
 
   if (aReason == NS_ERROR_NOT_INITIALIZED ||
       aReason == NS_ERROR_CONNECTION_REFUSED ||
-      aReason == NS_ERROR_NOT_CONNECTED) {
-
+      aReason == NS_ERROR_NOT_CONNECTED ||
+      aReason == NS_ERROR_NET_TIMEOUT) {
     RTSPMLOG("Error in OnDisconnected 0x%x", aReason);
-
     mDecoder->NetworkError();
     return NS_OK;
   }
 
   // Resetting the decoder and media element when the connection
   // between Rtsp client and server goes down.
   mDecoder->ResetConnectionState();
   return NS_OK;
--- a/content/media/RtspMediaResource.h
+++ b/content/media/RtspMediaResource.h
@@ -211,16 +211,18 @@ public:
 protected:
   // Main thread access only.
   // These are called on the main thread by Listener.
   NS_DECL_NSISTREAMINGPROTOCOLLISTENER
 
   nsRefPtr<Listener> mListener;
 
 private:
+  bool IsVideoEnabled();
+  bool IsVideo(uint8_t tracks, nsIStreamingProtocolMetaData *meta);
   // These two members are created at |RtspMediaResource::OnConnected|.
   nsCOMPtr<nsIStreamingProtocolController> mMediaStreamController;
   nsTArray<nsAutoPtr<RtspTrackBuffer>> mTrackBuffer;
 
   // A flag that indicates the |RtspMediaResource::OnConnected| has already been
   // called.
   bool mIsConnected;
   // live stream
--- a/content/media/TextTrackCue.h
+++ b/content/media/TextTrackCue.h
@@ -84,31 +84,33 @@ public:
 
   void SetStartTime(double aStartTime)
   {
     if (mStartTime == aStartTime) {
       return;
     }
 
     mStartTime = aStartTime;
+    mReset = true;
     CueChanged();
   }
 
   double EndTime() const
   {
     return mEndTime;
   }
 
   void SetEndTime(double aEndTime)
   {
     if (mEndTime == aEndTime) {
       return;
     }
 
     mEndTime = aEndTime;
+    mReset = true;
     CueChanged();
   }
 
   bool PauseOnExit()
   {
     return mPauseOnExit;
   }
 
--- a/content/media/TimeVarying.h
+++ b/content/media/TimeVarying.h
@@ -39,16 +39,19 @@ protected:
  * There is also a "current time" which must always advance (not go backward).
  * The function is constant for all times less than the current time.
  * When the current time is advanced, the value of the function at the new
  * current time replaces the values for all previous times.
  *
  * The implementation records a mCurrent (the value at the current time)
  * and an array of "change times" (greater than the current time) and the
  * new value for each change time. This is a simple but dumb implementation.
+ * We maintain the invariant that each change entry in the array must have
+ * a different value to the value in the previous change entry (or, for
+ * the first change entry, mCurrent).
  */
 template <typename Time, typename T, uint32_t ReservedChanges>
 class TimeVarying : public TimeVaryingBase {
 public:
   TimeVarying(const T& aInitial) : mCurrent(aInitial) {}
   /**
    * This constructor can only be called if mCurrent has a no-argument
    * constructor.
@@ -74,16 +77,19 @@ public:
           mChanges.RemoveElementAt(i);
           return;
         }
         mChanges[i].mValue = aValue;
         return;
       }
       mChanges.RemoveElementAt(i);
     }
+    if (mCurrent == aValue) {
+      return;
+    }
     mChanges.InsertElementAt(0, Entry(aTime, aValue));
   }
   /**
    * Returns the final value of the function. If aTime is non-null,
    * sets aTime to the time at which the function changes to that final value.
    * If there are no changes after the current time, returns INT64_MIN in aTime.
    */
   const T& GetLast(Time* aTime = nullptr) const
--- a/content/media/encoder/ContainerWriter.h
+++ b/content/media/encoder/ContainerWriter.h
@@ -19,18 +19,18 @@ class ContainerWriter {
 public:
   ContainerWriter()
     : mInitialized(false)
     , mIsWritingComplete(false)
   {}
   virtual ~ContainerWriter() {}
   // Mapping to DOMLocalMediaStream::TrackTypeHints
   enum {
-    HAS_AUDIO = 1 << 0,
-    HAS_VIDEO = 1 << 1,
+    CREATE_AUDIO_TRACK = 1 << 0,
+    CREATE_VIDEO_TRACK = 1 << 1,
   };
   enum {
     END_OF_STREAM = 1 << 0
   };
 
   /**
    * Writes encoded track data from aBuffer to a packet, and insert this packet
    * into the internal stream of container writer. aDuration is the playback
--- a/content/media/encoder/EncodedFrameContainer.h
+++ b/content/media/encoder/EncodedFrameContainer.h
@@ -51,24 +51,35 @@ public:
     I_FRAME,      // intraframe
     P_FRAME,      // predicted frame
     B_FRAME,      // bidirectionally predicted frame
     AUDIO_FRAME,  // audio frame
     AAC_CSD,      // AAC codec specific data
     AVC_CSD,      // AVC codec specific data
     UNKNOW        // FrameType not set
   };
+  nsresult SwapInFrameData(nsTArray<uint8_t>& aData)
+  {
+    mFrameData.SwapElements(aData);
+    return NS_OK;
+  }
+  nsresult SwapOutFrameData(nsTArray<uint8_t>& aData)
+  {
+    if (mFrameType != UNKNOW) {
+      // Reset this frame type to UNKNOW once the data is swapped out.
+      mFrameData.SwapElements(aData);
+      mFrameType = UNKNOW;
+      return NS_OK;
+    }
+    return NS_ERROR_FAILURE;
+  }
   const nsTArray<uint8_t>& GetFrameData() const
   {
     return mFrameData;
   }
-  void SetFrameData(nsTArray<uint8_t> *aData)
-  {
-    mFrameData.SwapElements(*aData);
-  }
   uint64_t GetTimeStamp() const { return mTimeStamp; }
   void SetTimeStamp(uint64_t aTimeStamp) { mTimeStamp = aTimeStamp; }
 
   uint64_t GetDuration() const { return mDuration; }
   void SetDuration(uint64_t aDuration) { mDuration = aDuration; }
 
   FrameType GetFrameType() const { return mFrameType; }
   void SetFrameType(FrameType aFrameType) { mFrameType = aFrameType; }
--- a/content/media/encoder/MediaEncoder.cpp
+++ b/content/media/encoder/MediaEncoder.cpp
@@ -95,47 +95,47 @@ MediaEncoder::CreateEncoder(const nsAStr
   nsString mimeType;
   if (!aTrackTypes) {
     LOG(PR_LOG_ERROR, ("NO TrackTypes!!!"));
     return nullptr;
   }
 #ifdef MOZ_WEBM_ENCODER
   else if (MediaEncoder::IsWebMEncoderEnabled() &&
           (aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
-          (aTrackTypes & ContainerWriter::HAS_VIDEO))) {
-    if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
+          (aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
+    if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
       audioEncoder = new VorbisTrackEncoder();
       NS_ENSURE_TRUE(audioEncoder, nullptr);
     }
     videoEncoder = new VP8TrackEncoder();
     writer = new WebMWriter(aTrackTypes);
     NS_ENSURE_TRUE(writer, nullptr);
     NS_ENSURE_TRUE(videoEncoder, nullptr);
     mimeType = NS_LITERAL_STRING(VIDEO_WEBM);
   }
 #endif //MOZ_WEBM_ENCODER
 #ifdef MOZ_OMX_ENCODER
   else if (MediaEncoder::IsOMXEncoderEnabled() &&
           (aMIMEType.EqualsLiteral(VIDEO_MP4) ||
-          (aTrackTypes & ContainerWriter::HAS_VIDEO))) {
-    if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
+          (aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
+    if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
       audioEncoder = new OmxAudioTrackEncoder();
       NS_ENSURE_TRUE(audioEncoder, nullptr);
     }
     videoEncoder = new OmxVideoTrackEncoder();
     writer = new ISOMediaWriter(aTrackTypes);
     NS_ENSURE_TRUE(writer, nullptr);
     NS_ENSURE_TRUE(videoEncoder, nullptr);
     mimeType = NS_LITERAL_STRING(VIDEO_MP4);
   }
 #endif // MOZ_OMX_ENCODER
 #ifdef MOZ_OGG
   else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
            (aMIMEType.EqualsLiteral(AUDIO_OGG) ||
-           (aTrackTypes & ContainerWriter::HAS_AUDIO))) {
+           (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK))) {
     writer = new OggWriter();
     audioEncoder = new OpusTrackEncoder();
     NS_ENSURE_TRUE(writer, nullptr);
     NS_ENSURE_TRUE(audioEncoder, nullptr);
     mimeType = NS_LITERAL_STRING(AUDIO_OGG);
   }
 #endif  // MOZ_OGG
   else {
--- a/content/media/encoder/MediaEncoder.h
+++ b/content/media/encoder/MediaEncoder.h
@@ -88,17 +88,17 @@ public :
   virtual void NotifyRemoved(MediaStreamGraph* aGraph);
 
   /**
    * Creates an encoder with a given MIME type. Returns null if we are unable
    * to create the encoder. For now, default aMIMEType to "audio/ogg" and use
    * Ogg+Opus if it is empty.
    */
   static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
-                                                      uint8_t aTrackTypes = ContainerWriter::HAS_AUDIO);
+                                                      uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK);
   /**
    * Encodes the raw track data and returns the final container data. Assuming
    * it is called on a single worker thread. The buffer of container data is
    * allocated in ContainerWriter::GetContainerData(), and is appended to
    * aOutputBufs. aMIMEType is the valid mime-type of this returned container
    * data.
    */
   void GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
--- a/content/media/encoder/OmxTrackEncoder.cpp
+++ b/content/media/encoder/OmxTrackEncoder.cpp
@@ -116,30 +116,32 @@ OmxVideoTrackEncoder::GetEncodedTrack(En
     uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
     layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack())
                          ? nullptr : mLastFrame.GetImage();
     mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs,
                      OMXCodecWrapper::BUFFER_EOS);
   }
 
   // Dequeue an encoded frame from the output buffers of OMXCodecWrapper.
+  nsresult rv;
   nsTArray<uint8_t> buffer;
   int outFlags = 0;
   int64_t outTimeStampUs = 0;
   mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags,
                                 GET_ENCODED_VIDEO_FRAME_TIMEOUT);
   if (!buffer.IsEmpty()) {
     nsRefPtr<EncodedFrame> videoData = new EncodedFrame();
     if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) {
       videoData->SetFrameType(EncodedFrame::AVC_CSD);
     } else {
       videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ?
                               EncodedFrame::I_FRAME : EncodedFrame::P_FRAME);
     }
-    videoData->SetFrameData(&buffer);
+    rv = videoData->SwapInFrameData(buffer);
+    NS_ENSURE_SUCCESS(rv, rv);
     videoData->SetTimeStamp(outTimeStampUs);
     aData.AppendEncodedFrame(videoData);
   }
 
   if (outFlags & OMXCodecWrapper::BUFFER_EOS) {
     mEncodingComplete = true;
     OMX_LOG("Done encoding video.");
   }
@@ -208,17 +210,18 @@ OmxAudioTrackEncoder::AppendEncodedFrame
     } else if (outFlags & OMXCodecWrapper::BUFFER_EOS) { // last frame
       mEncodingComplete = true;
     }
 
     nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
     audiodata->SetFrameType(isCSD ?
       EncodedFrame::AAC_CSD : EncodedFrame::AUDIO_FRAME);
     audiodata->SetTimeStamp(outTimeUs);
-    audiodata->SetFrameData(&frameData);
+    rv = audiodata->SwapInFrameData(frameData);
+    NS_ENSURE_SUCCESS(rv, rv);
     aContainer.AppendEncodedFrame(audiodata);
   }
 
   return NS_OK;
 }
 
 nsresult
 OmxAudioTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
--- a/content/media/encoder/OpusTrackEncoder.cpp
+++ b/content/media/encoder/OpusTrackEncoder.cpp
@@ -369,14 +369,14 @@ OpusTrackEncoder::GetEncodedTrack(Encode
   }
   if (mEncodingComplete) {
     if (mResampler) {
       speex_resampler_destroy(mResampler);
       mResampler = nullptr;
     }
   }
 
-  audiodata->SetFrameData(&frameData);
+  audiodata->SwapInFrameData(frameData);
   aData.AppendEncodedFrame(audiodata);
   return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
 }
 
 }
--- a/content/media/encoder/VP8TrackEncoder.cpp
+++ b/content/media/encoder/VP8TrackEncoder.cpp
@@ -159,16 +159,17 @@ VP8TrackEncoder::GetMetadata()
 }
 
 nsresult
 VP8TrackEncoder::GetEncodedPartitions(EncodedFrameContainer& aData)
 {
   vpx_codec_iter_t iter = nullptr;
   EncodedFrame::FrameType frameType = EncodedFrame::P_FRAME;
   nsTArray<uint8_t> frameData;
+  nsresult rv;
   const vpx_codec_cx_pkt_t *pkt = nullptr;
   while ((pkt = vpx_codec_get_cx_data(mVPXContext, &iter)) != nullptr) {
     switch (pkt->kind) {
       case VPX_CODEC_CX_FRAME_PKT: {
         // Copy the encoded data from libvpx to frameData
         frameData.AppendElements((uint8_t*)pkt->data.frame.buf,
                                  pkt->data.frame.sz);
         break;
@@ -197,17 +198,18 @@ VP8TrackEncoder::GetEncodedPartitions(En
       videoData->SetTimeStamp(
         (uint64_t)FramesToUsecs(mEncodedTimestamp, mTrackRate).value());
     }
     CheckedInt64 duration = FramesToUsecs(pkt->data.frame.duration, mTrackRate);
     if (duration.isValid()) {
       videoData->SetDuration(
         (uint64_t)FramesToUsecs(pkt->data.frame.duration, mTrackRate).value());
     }
-    videoData->SetFrameData(&frameData);
+    rv = videoData->SwapInFrameData(frameData);
+    NS_ENSURE_SUCCESS(rv, rv);
     VP8LOG("GetEncodedPartitions TimeStamp %lld Duration %lld\n",
            videoData->GetTimeStamp(), videoData->GetDuration());
     VP8LOG("frameType %d\n", videoData->GetFrameType());
     aData.AppendEncodedFrame(videoData);
   }
 
   return NS_OK;
 }
--- a/content/media/encoder/VorbisTrackEncoder.cpp
+++ b/content/media/encoder/VorbisTrackEncoder.cpp
@@ -142,17 +142,17 @@ VorbisTrackEncoder::GetEncodedFrames(Enc
   while (vorbis_analysis_blockout(&mVorbisDsp, &mVorbisBlock) == 1) {
     ogg_packet oggPacket;
     if (vorbis_analysis(&mVorbisBlock, &oggPacket) == 0) {
       VORBISLOG("vorbis_analysis_blockout block size %d", oggPacket.bytes);
       EncodedFrame* audiodata = new EncodedFrame();
       audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME);
       nsTArray<uint8_t> frameData;
       frameData.AppendElements(oggPacket.packet, oggPacket.bytes);
-      audiodata->SetFrameData(&frameData);
+      audiodata->SwapInFrameData(frameData);
       aData.AppendEncodedFrame(audiodata);
     }
   }
 }
 
 nsresult
 VorbisTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
 {
--- a/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
+++ b/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
@@ -23,20 +23,20 @@ namespace mozilla {
 const static uint32_t FRAG_DURATION = 2000000;    // microsecond per unit
 
 ISOMediaWriter::ISOMediaWriter(uint32_t aType)
   : ContainerWriter()
   , mState(MUXING_HEAD)
   , mBlobReady(false)
   , mType(0)
 {
-  if (aType & HAS_AUDIO) {
+  if (aType & CREATE_AUDIO_TRACK) {
     mType |= Audio_Track;
   }
-  if (aType & HAS_VIDEO) {
+  if (aType & CREATE_VIDEO_TRACK) {
     mType |= Video_Track;
   }
   mControl = new ISOControl();
   MOZ_COUNT_CTOR(ISOMediaWriter);
 }
 
 ISOMediaWriter::~ISOMediaWriter()
 {
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -251,16 +251,17 @@ support-files =
 [test_mozHasAudio.html]
 [test_source_media.html]
 [test_autoplay_contentEditable.html]
 [test_decoder_disable.html]
 [test_mediarecorder_record_no_timeslice.html]
 [test_mediarecorder_reload_crash.html]
 [test_mediarecorder_record_immediate_stop.html]
 [test_mediarecorder_record_session.html]
+[test_mediarecorder_record_startstopstart.html]
 [test_mediarecorder_unsupported_src.html]
 [test_playback.html]
 [test_seekLies.html]
 [test_media_sniffer.html]
 [test_streams_srcObject.html]
 [test_reset_src.html]
 [test_streams_autoplay.html]
 [test_streams_element_capture.html]
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_mediarecorder_record_startstopstart.html
@@ -0,0 +1,76 @@
+ <!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test MediaRecorder crash on sequence start stop start method</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<div id="content" style="display: none">
+</div>
+<script class="testbody" type="text/javascript">
+
+function startTest() {
+  var ac = new window.AudioContext();
+  var dest = ac.createMediaStreamDestination();
+  var recorder = new MediaRecorder(dest.stream);
+  var stopCount = 0;
+  var dataavailable = 0;
+  // mobile device may produce another format, but not landed.
+  // In audio only case, we should produce opus type.
+  var expectedMimeType = 'audio/ogg';
+  recorder.onstop = function (e) {
+    info('onstop fired');
+    is(recorder.stream, dest.stream,
+       'Media recorder stream = element stream post recording');
+    stopCount++;
+    if (stopCount == 2) {
+      is(2, dataavailable, 'Should has two dataavailable event');
+      SimpleTest.finish();
+    }
+  }
+  recorder.ondataavailable = function (evt) {
+    info('ondataavailable fired');
+    ok(evt instanceof BlobEvent,
+       'Events fired from ondataavailable should be BlobEvent');
+    is(evt.type, 'dataavailable',
+       'Event type should dataavailable');
+    // If script runs slower, it may generate header data in blob from encoder
+    if (evt.data.size > 0) {
+     info('blob size = ' + evt.data.size);
+     is(evt.data.type, expectedMimeType,
+       'Blob data received should have type = ' + expectedMimeType);
+    } else {
+      is(evt.data.type, '',
+         'Blob data received should have empty type');
+    }
+    dataavailable++;
+  }
+  recorder.onerror = function (e) {
+    ok(false, 'it should execute normally without exception');
+  }
+  recorder.onwarning = function() {
+    ok(false, 'onwarning unexpectedly fired');
+  };
+
+  recorder.start(2000);
+  is(recorder.state, 'recording', 'Media recorder should be recording');
+  recorder.stop();
+  is(recorder.state, 'inactive', 'check recording status is inactive');
+  recorder.start(10000); // This bug would crash on this line without this fix.
+  is(recorder.state, 'recording', 'check recording status is recording');
+  // Simulate delay stop, only delay stop no no stop can trigger crash.
+  setTimeout(function() {
+    recorder.stop();
+    is(recorder.state, 'inactive','check recording status is recording');
+  }, 1000);
+}
+
+startTest();
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
--- a/content/media/test/test_texttrack.html
+++ b/content/media/test/test_texttrack.html
@@ -15,17 +15,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
   function() {
     var video = document.createElement("video");
-    video.src = "seek.webm";
 
     isnot(video.textTracks, undefined, "HTMLMediaElement::TextTrack() property should be available.")
 
     var trackList = video.textTracks;
     is(trackList.length, 0, "Length should be 0.");
 
     ok(typeof video.addTextTrack == "function", "HTMLMediaElement::AddTextTrack() function should be available.")
     video.addTextTrack("subtitles", "label", "en-CA");
--- a/content/media/webaudio/AudioBufferSourceNode.cpp
+++ b/content/media/webaudio/AudioBufferSourceNode.cpp
@@ -527,17 +527,17 @@ AudioBufferSourceNode::Start(double aWhe
   // We can't send these parameters without a buffer because we don't know the
   // buffer's sample rate or length.
   if (mBuffer) {
     SendOffsetAndDurationParametersToStream(ns);
   }
 
   // Don't set parameter unnecessarily
   if (aWhen > 0.0) {
-    ns->SetStreamTimeParameter(START, Context()->DestinationStream(), aWhen);
+    ns->SetStreamTimeParameter(START, Context(), aWhen);
   }
 
   MarkActive();
 }
 
 void
 AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx)
 {
@@ -611,18 +611,17 @@ AudioBufferSourceNode::Stop(double aWhen
   }
 
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   if (!ns || !Context()) {
     // We've already stopped and had our stream shut down
     return;
   }
 
-  ns->SetStreamTimeParameter(STOP, Context()->DestinationStream(),
-                             std::max(0.0, aWhen));
+  ns->SetStreamTimeParameter(STOP, Context(), std::max(0.0, aWhen));
 }
 
 void
 AudioBufferSourceNode::NotifyMainThreadStateChanged()
 {
   if (mStream->IsFinished()) {
     class EndedEventDispatcher : public nsRunnable
     {
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -79,27 +79,32 @@ float GetSampleRateForAudioContext(bool 
 AudioContext::AudioContext(nsPIDOMWindow* aWindow,
                            bool aIsOffline,
                            uint32_t aNumberOfChannels,
                            uint32_t aLength,
                            float aSampleRate)
   : nsDOMEventTargetHelper(aWindow)
   , mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate))
   , mNumberOfChannels(aNumberOfChannels)
+  , mNodeCount(0)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)
   , mIsShutDown(false)
 {
   aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aNumberOfChannels,
                                           aLength, aSampleRate);
   mDestination->Stream()->AddAudioOutput(&gWebAudioOutputKey);
+  // We skip calling SetIsOnlyNodeForContext during mDestination's constructor,
+  // because we can only call SetIsOnlyNodeForContext after mDestination has
+  // been set up.
+  mDestination->SetIsOnlyNodeForContext(true);
 }
 
 AudioContext::~AudioContext()
 {
   nsPIDOMWindow* window = GetOwner();
   if (window) {
     window->RemoveAudioContext(this);
   }
@@ -538,17 +543,18 @@ AudioContext::DestinationStream() const
     return Destination()->Stream();
   }
   return nullptr;
 }
 
 double
 AudioContext::CurrentTime() const
 {
-  return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime());
+  return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime()) +
+      ExtraCurrentTime();
 }
 
 void
 AudioContext::Shutdown()
 {
   mIsShutDown = true;
 
   // We mute rather than suspending, because the delay between the ::Shutdown
@@ -584,16 +590,28 @@ void
 AudioContext::Resume()
 {
   MediaStream* ds = DestinationStream();
   if (ds) {
     ds->ChangeExplicitBlockerCount(-1);
   }
 }
 
+void
+AudioContext::UpdateNodeCount(int32_t aDelta)
+{
+  bool firstNode = mNodeCount == 0;
+  mNodeCount += aDelta;
+  MOZ_ASSERT(mNodeCount >= 0);
+  // mDestinationNode may be null when we're destroying nodes unlinked by CC
+  if (!firstNode && mDestination) {
+    mDestination->SetIsOnlyNodeForContext(mNodeCount == 1);
+  }
+}
+
 JSContext*
 AudioContext::GetJSContext() const
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
     do_QueryInterface(GetParentObject());
   if (!scriptGlobal) {
@@ -674,10 +692,16 @@ NS_IMETHODIMP
 AudioContext::CollectReports(nsIHandleReportCallback* aHandleReport,
                              nsISupports* aData)
 {
   int64_t amount = SizeOfIncludingThis(MallocSizeOf);
   return MOZ_COLLECT_REPORT("explicit/webaudio/audiocontext", KIND_HEAP, UNITS_BYTES,
                             amount, "Memory used by AudioContext objects (Web Audio).");
 }
 
+double
+AudioContext::ExtraCurrentTime() const
+{
+  return mDestination->ExtraCurrentTime();
+}
+
 }
 }
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -241,17 +241,33 @@ public:
   void Mute() const;
   void Unmute() const;
 
   JSContext* GetJSContext() const;
 
   AudioChannel MozAudioChannelType() const;
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
+  void UpdateNodeCount(int32_t aDelta);
+
+  double DOMTimeToStreamTime(double aTime) const
+  {
+    return aTime - ExtraCurrentTime();
+  }
+
 private:
+  /**
+   * Returns the amount of extra time added to the current time of the
+   * AudioDestinationNode's MediaStream to get this AudioContext's currentTime.
+   * Must be subtracted from all DOM API parameter times that are on the same
+   * timeline as AudioContext's currentTime to get times we can pass to the
+   * MediaStreamGraph.
+   */
+  double ExtraCurrentTime() const;
+
   void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
   void ShutdownDecoder();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                             nsISupports* aData);
 
   friend struct ::mozilla::WebAudioDecodeJob;
@@ -267,16 +283,18 @@ private:
   // See RegisterActiveNode.  These will keep the AudioContext alive while it
   // is rendering and the window remains alive.
   nsTHashtable<nsRefPtrHashKey<AudioNode> > mActiveNodes;
   // Hashsets containing all the PannerNodes, to compute the doppler shift.
   // These are weak pointers.
   nsTHashtable<nsPtrHashKey<PannerNode> > mPannerNodes;
   // Number of channels passed in the OfflineAudioContext ctor.
   uint32_t mNumberOfChannels;
+  // Number of nodes that currently exist for this AudioContext
+  int32_t mNodeCount;
   bool mIsOffline;
   bool mIsStarted;
   bool mIsShutDown;
 };
 
 }
 }
 
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -12,16 +12,18 @@
 #include "AudioNodeStream.h"
 #include "MediaStreamGraph.h"
 #include "OfflineAudioCompletionEvent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDocShell.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsServiceManagerUtils.h"
+#include "nsIAppShell.h"
+#include "nsWidgetsCID.h"
 
 namespace mozilla {
 namespace dom {
 
 class OfflineDestinationNodeEngine : public AudioNodeEngine
 {
 public:
   typedef AutoFallibleTArray<nsAutoArrayPtr<float>, 2> InputChannels;
@@ -212,16 +214,19 @@ AudioDestinationNode::AudioDestinationNo
   : AudioNode(aContext,
               aIsOffline ? aNumberOfChannels : 2,
               ChannelCountMode::Explicit,
               ChannelInterpretation::Speakers)
   , mFramesToProduce(aLength)
   , mAudioChannel(AudioChannel::Normal)
   , mIsOffline(aIsOffline)
   , mHasFinished(false)
+  , mExtraCurrentTime(0)
+  , mExtraCurrentTimeSinceLastStartedBlocking(0)
+  , mExtraCurrentTimeUpdatedSinceLastStableState(false)
 {
   MediaStreamGraph* graph = aIsOffline ?
                             MediaStreamGraph::CreateNonRealtimeInstance() :
                             MediaStreamGraph::GetInstance();
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
@@ -481,11 +486,81 @@ AudioDestinationNode::CreateAudioChannel
     docshell->GetIsActive(&isActive);
     mAudioChannelAgent->SetVisibilityState(isActive);
   }
 
   int32_t state = 0;
   mAudioChannelAgent->StartPlaying(&state);
   SetCanPlay(state == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
 }
+
+void
+AudioDestinationNode::NotifyStableState()
+{
+  mExtraCurrentTimeUpdatedSinceLastStableState = false;
+}
+
+static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+void
+AudioDestinationNode::ScheduleStableStateNotification()
+{
+  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+  if (appShell) {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState);
+    appShell->RunInStableState(event);
+  }
+}
+
+double
+AudioDestinationNode::ExtraCurrentTime()
+{
+  if (!mStartedBlockingDueToBeingOnlyNode.IsNull() &&
+      !mExtraCurrentTimeUpdatedSinceLastStableState) {
+    mExtraCurrentTimeUpdatedSinceLastStableState = true;
+    mExtraCurrentTimeSinceLastStartedBlocking =
+      (TimeStamp::Now() - mStartedBlockingDueToBeingOnlyNode).ToSeconds();
+    ScheduleStableStateNotification();
+  }
+  return mExtraCurrentTime + mExtraCurrentTimeSinceLastStartedBlocking;
+}
+
+void
+AudioDestinationNode::SetIsOnlyNodeForContext(bool aIsOnlyNode)
+{
+  if (!mStartedBlockingDueToBeingOnlyNode.IsNull() == aIsOnlyNode) {
+    // Nothing changed.
+    return;
+  }
+
+  if (!mStream) {
+    // DestroyMediaStream has been called, presumably during CC Unlink().
+    return;
+  }
+
+  if (mIsOffline) {
+    // Don't block the destination stream for offline AudioContexts, since
+    // we expect the zero data produced when there are no other nodes to
+    // show up in its result buffer. Also, we would get confused by adding
+    // ExtraCurrentTime before StartRendering has even been called.
+    return;
+  }
+
+  if (aIsOnlyNode) {
+    mStream->ChangeExplicitBlockerCount(1);
+    mStartedBlockingDueToBeingOnlyNode = TimeStamp::Now();
+    mExtraCurrentTimeSinceLastStartedBlocking = 0;
+    // Don't do an update of mExtraCurrentTimeSinceLastStartedBlocking until the next stable state.
+    mExtraCurrentTimeUpdatedSinceLastStableState = true;
+    ScheduleStableStateNotification();
+  } else {
+    // Force update of mExtraCurrentTimeSinceLastStartedBlocking if necessary
+    ExtraCurrentTime();
+    mExtraCurrentTime += mExtraCurrentTimeSinceLastStartedBlocking;
+    mStream->ChangeExplicitBlockerCount(-1);
+    mStartedBlockingDueToBeingOnlyNode = TimeStamp();
+  }
 }
 
 }
+
+}
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -65,30 +65,45 @@ public:
   NS_IMETHOD CanPlayChanged(int32_t aCanPlay);
 
   AudioChannel MozAudioChannelType() const;
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
   void FireOfflineCompletionEvent();
 
+  // An amount that should be added to the MediaStream's current time to
+  // get the AudioContext.currentTime.
+  double ExtraCurrentTime();
+
+  // When aIsOnlyNode is true, this is the only node for the AudioContext.
+  void SetIsOnlyNodeForContext(bool aIsOnlyNode);
+
 private:
   bool CheckAudioChannelPermissions(AudioChannel aValue);
   void CreateAudioChannelAgent();
 
   void SetCanPlay(bool aCanPlay);
 
+  void NotifyStableState();
+  void ScheduleStableStateNotification();
+
   SelfReference<AudioDestinationNode> mOfflineRenderingRef;
   uint32_t mFramesToProduce;
 
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 
   // Audio Channel Type.
   AudioChannel mAudioChannel;
   bool mIsOffline;
   bool mHasFinished;
+
+  TimeStamp mStartedBlockingDueToBeingOnlyNode;
+  double mExtraCurrentTime;
+  double mExtraCurrentTimeSinceLastStartedBlocking;
+  bool mExtraCurrentTimeUpdatedSinceLastStableState;
 };
 
 }
 }
 
 #endif
 
--- a/content/media/webaudio/AudioNode.cpp
+++ b/content/media/webaudio/AudioNode.cpp
@@ -14,16 +14,17 @@ namespace mozilla {
 namespace dom {
 
 static const uint32_t INVALID_PORT = 0xffffffff;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioNode)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
   tmp->DisconnectFromGraph();
+  tmp->mContext->UpdateNodeCount(-1);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputParams)
@@ -53,23 +54,29 @@ AudioNode::AudioNode(AudioContext* aCont
                      ChannelInterpretation aChannelInterpretation)
   : nsDOMEventTargetHelper(aContext->GetParentObject())
   , mContext(aContext)
   , mChannelCount(aChannelCount)
   , mChannelCountMode(aChannelCountMode)
   , mChannelInterpretation(aChannelInterpretation)
 {
   MOZ_ASSERT(aContext);
+  nsDOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
+  SetIsDOMBinding();
+  aContext->UpdateNodeCount(1);
 }
 
 AudioNode::~AudioNode()
 {
   MOZ_ASSERT(mInputNodes.IsEmpty());
   MOZ_ASSERT(mOutputNodes.IsEmpty());
   MOZ_ASSERT(mOutputParams.IsEmpty());
+  if (mContext) {
+    mContext->UpdateNodeCount(-1);
+  }
 }
 
 template <class InputNode>
 static uint32_t
 FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode)
 {
   for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
     if (aInputNodes[i].mInputNode == aNode) {
--- a/content/media/webaudio/AudioParam.h
+++ b/content/media/webaudio/AudioParam.h
@@ -37,29 +37,34 @@ public:
   NS_IMETHOD_(nsrefcnt) Release(void);
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioParam)
 
   AudioContext* GetParentObject() const
   {
     return mNode->Context();
   }
 
+  double DOMTimeToStreamTime(double aTime) const
+  {
+    return mNode->Context()->DOMTimeToStreamTime(aTime);
+  }
+
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   // We override SetValueCurveAtTime to convert the Float32Array to the wrapper
   // object.
   void SetValueCurveAtTime(const Float32Array& aValues, double aStartTime, double aDuration, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
     AudioParamTimeline::SetValueCurveAtTime(aValues.Data(), aValues.Length(),
-                                            aStartTime, aDuration, aRv);
+                                            DOMTimeToStreamTime(aStartTime), aDuration, aRv);
     mCallback(mNode);
   }
 
   // We override the rest of the mutating AudioParamTimeline methods in order to make
   // sure that the callback is called every time that this object gets mutated.
   void SetValue(float aValue)
   {
     // Optimize away setting the same value on an AudioParam
@@ -71,58 +76,58 @@ public:
     mCallback(mNode);
   }
   void SetValueAtTime(float aValue, double aStartTime, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
-    AudioParamTimeline::SetValueAtTime(aValue, aStartTime, aRv);
+    AudioParamTimeline::SetValueAtTime(aValue, DOMTimeToStreamTime(aStartTime), aRv);
     mCallback(mNode);
   }
   void LinearRampToValueAtTime(float aValue, double aEndTime, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aEndTime)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
-    AudioParamTimeline::LinearRampToValueAtTime(aValue, aEndTime, aRv);
+    AudioParamTimeline::LinearRampToValueAtTime(aValue, DOMTimeToStreamTime(aEndTime), aRv);
     mCallback(mNode);
   }
   void ExponentialRampToValueAtTime(float aValue, double aEndTime, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aEndTime)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
-    AudioParamTimeline::ExponentialRampToValueAtTime(aValue, aEndTime, aRv);
+    AudioParamTimeline::ExponentialRampToValueAtTime(aValue, DOMTimeToStreamTime(aEndTime), aRv);
     mCallback(mNode);
   }
   void SetTargetAtTime(float aTarget, double aStartTime, double aTimeConstant, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime) ||
         !WebAudioUtils::IsTimeValid(aTimeConstant)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
-    AudioParamTimeline::SetTargetAtTime(aTarget, aStartTime, aTimeConstant, aRv);
+    AudioParamTimeline::SetTargetAtTime(aTarget, DOMTimeToStreamTime(aStartTime), aTimeConstant, aRv);
     mCallback(mNode);
   }
   void SetTargetValueAtTime(float aTarget, double aStartTime, double aTimeConstant, ErrorResult& aRv)
   {
     SetTargetAtTime(aTarget, aStartTime, aTimeConstant, aRv);
   }
   void CancelScheduledValues(double aStartTime, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
-    AudioParamTimeline::CancelScheduledValues(aStartTime);
+    AudioParamTimeline::CancelScheduledValues(DOMTimeToStreamTime(aStartTime));
     mCallback(mNode);
   }
 
   float DefaultValue() const
   {
     return mDefaultValue;
   }
 
--- a/content/media/webaudio/OscillatorNode.cpp
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -573,18 +573,17 @@ OscillatorNode::Start(double aWhen, Erro
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   if (!ns) {
     // Nothing to play, or we're already dead for some reason
     return;
   }
 
   // TODO: Perhaps we need to do more here.
   ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
-                             Context()->DestinationStream(),
-                             aWhen);
+                             Context(), aWhen);
 
   MarkActive();
 }
 
 void
 OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
 {
   if (!WebAudioUtils::IsTimeValid(aWhen)) {
@@ -600,18 +599,17 @@ OscillatorNode::Stop(double aWhen, Error
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   if (!ns || !Context()) {
     // We've already stopped and had our stream shut down
     return;
   }
 
   // TODO: Perhaps we need to do more here.
   ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
-                             Context()->DestinationStream(),
-                             std::max(0.0, aWhen));
+                             Context(), std::max(0.0, aWhen));
 }
 
 void
 OscillatorNode::NotifyMainThreadStateChanged()
 {
   if (mStream->IsFinished()) {
     class EndedEventDispatcher : public nsRunnable
     {
--- a/content/media/webaudio/moz.build
+++ b/content/media/webaudio/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 PARALLEL_DIRS += ['blink', 'test']
 
 TEST_TOOL_DIRS += ['compiledtest']
 
 EXPORTS += [
+    'AudioContext.h',
     'AudioParamTimeline.h',
     'MediaBufferDecoder.h',
     'ThreeDPoint.h',
     'WebAudioUtils.h',
 ]
 
 EXPORTS.mozilla += [
     'FFTBlock.h',
--- a/content/media/webaudio/test/webaudio.js
+++ b/content/media/webaudio/test/webaudio.js
@@ -48,20 +48,21 @@ function compareChannels(buf1, buf2,
       difference++;
       maxDifference = Math.max(maxDifference, Math.abs(buf1[i + sourceOffset] - buf2[i + destOffset]));
       if (firstBadIndex == -1) {
         firstBadIndex = i;
       }
     }
   };
 
-  is(difference, 0, "Found " + difference + " different samples, maxDifference: " +
-     maxDifference + ", first bad index: " + firstBadIndex +
-     " with source offset " + sourceOffset + " and destination offset " +
-     destOffset);
+  is(difference, 0, "maxDifference: " + maxDifference +
+     ", first bad index: " + firstBadIndex +
+     " with test-data offset " + sourceOffset + " and expected-data offset " +
+     destOffset + "; corresponding values " + buf1[firstBadIndex + sourceOffset] +
+     " and " + buf2[firstBadIndex + destOffset] + " --- differences");
 }
 
 function compareBuffers(got, expected) {
   if (got.numberOfChannels != expected.numberOfChannels) {
     is(got.numberOfChannels, expected.numberOfChannels,
        "Correct number of buffer channels");
     return;
   }
--- a/content/media/webm/WebMWriter.cpp
+++ b/content/media/webm/WebMWriter.cpp
@@ -39,25 +39,25 @@ nsresult
 WebMWriter::SetMetadata(TrackMetadataBase* aMetadata)
 {
   MOZ_ASSERT(aMetadata);
   if (aMetadata->GetKind() == TrackMetadataBase::METADATA_VP8) {
     VP8Metadata* meta = static_cast<VP8Metadata*>(aMetadata);
     MOZ_ASSERT(meta, "Cannot find vp8 encoder metadata");
     mEbmlComposer->SetVideoConfig(meta->mWidth, meta->mHeight,
                                   meta->mEncodedFrameRate);
-    mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::HAS_VIDEO;
+    mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::CREATE_VIDEO_TRACK;
   }
 
   if (aMetadata->GetKind() == TrackMetadataBase::METADATA_VORBIS) {
     VorbisMetadata* meta = static_cast<VorbisMetadata*>(aMetadata);
     MOZ_ASSERT(meta, "Cannot find vorbis encoder metadata");
     mEbmlComposer->SetAudioConfig(meta->mSamplingFrequency, meta->mChannels, meta->mBitDepth);
     mEbmlComposer->SetAudioCodecPrivateData(meta->mData);
-    mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::HAS_AUDIO;
+    mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::CREATE_AUDIO_TRACK;
   }
 
   if (!mMetadataRequiredFlag) {
     mEbmlComposer->GenerateHeader();
   }
   return NS_OK;
 }
 
--- a/content/media/webvtt/vtt.jsm
+++ b/content/media/webvtt/vtt.jsm
@@ -3,18 +3,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 this.EXPORTED_SYMBOLS = ["WebVTTParser"];
 
 /**
  * Code below is vtt.js the JS WebVTTParser.
  * Current source code can be found at http://github.com/andreasgal/vtt.js
  *
- * Code taken from commit 33a837b1ceef138a61a3b2f6fac90d5c70bd90d9
+ * Code taken from commit d819872e198d051dfcebcfb7ecf260462c9a9c6f
  */
+/**
+ * Copyright 2013 vtt.js Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
 (function(global) {
 
   function ParsingError(message) {
     this.name = "ParsingError";
     this.message = message || "";
   }
   ParsingError.prototype = Object.create(Error.prototype);
   ParsingError.prototype.constructor = ParsingError;
@@ -125,17 +142,17 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"]
   function parseCue(input, cue) {
     // 4.1 WebVTT timestamp
     function consumeTimeStamp() {
       var ts = parseTimeStamp(input);
       if (ts === null) {
         throw new ParsingError("Malformed time stamp.");
       }
       // Remove time stamp from input.
-      input = input.replace(/^[^\s-]+/, "");
+      input = input.replace(/^[^\sa-zA-Z-]+/, "");
       return ts;
     }
 
     // 4.4.2 WebVTT cue settings
     function consumeCueSettings(input, cue) {
       var settings = new Settings();
 
       parseOptions(input, function (k, v) {
@@ -627,23 +644,29 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"]
     return "ltr";
   }
 
   function computeLinePos(cue) {
     if (typeof cue.line === "number" &&
         (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
       return cue.line;
     }
-    if (!cue.track) {
+    if (!cue.track || !cue.track.textTrackList ||
+        !cue.track.textTrackList.mediaElement) {
       return -1;
     }
-    // TODO: Have to figure out a way to determine what the position of the
-    // Track is in the Media element's list of TextTracks and return that + 1,
-    // negated.
-    return 100;
+    var track = cue.track,
+        trackList = track.textTrackList,
+        count = 0;
+    for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
+      if (trackList[i].mode === "showing") {
+        count++;
+      }
+    }
+    return ++count * -1;
   }
 
   function BoundingBox() {
   }
 
   BoundingBox.prototype.applyStyles = function(styles) {
     var div = this.div;
     Object.keys(styles).forEach(function(style) {
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -25,15 +25,17 @@ enum StructuredCloneTags {
   SCTAG_DOM_FILELIST,
   SCTAG_DOM_FILEHANDLE,
   SCTAG_DOM_FILE,
 
   // These tags are used for both main thread and workers.
   SCTAG_DOM_IMAGEDATA,
   SCTAG_DOM_MESSAGEPORT,
 
+  SCTAG_DOM_FUNCTION,
+
   SCTAG_DOM_MAX
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // StructuredCloneTags_h__
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1625,18 +1625,18 @@ GetExternalClassInfo(nsScriptNameSpaceMa
 
 
 static nsresult
 ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
                  JS::Handle<JSObject*> obj, const char16_t *name,
                  const nsDOMClassInfoData *ci_data,
                  const nsGlobalNameStruct *name_struct,
                  nsScriptNameSpaceManager *nameSpaceManager,
-                 JSObject *dot_prototype, bool install, bool *did_resolve);
-
+                 JSObject *dot_prototype,
+                 JS::MutableHandle<JSPropertyDescriptor> ctorDesc);
 
 NS_IMETHODIMP
 nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto)
 {
   uint32_t flags = (mData->mScriptableFlags & DONT_ENUM_STATIC_PROPS)
                    ? 0
                    : JSPROP_ENUMERATE;
 
@@ -1722,29 +1722,40 @@ nsDOMClassInfo::PostCreatePrototype(JSCo
 
     if (!win || !(global = win->GetGlobalJSObject()) ||
         win->IsClosedOrClosing()) {
       return NS_OK;
     }
   }
 
   // Don't overwrite a property set by content.
-  bool found;
+  bool contentDefinedProperty;
   if (!::JS_AlreadyHasOwnUCProperty(cx, global, reinterpret_cast<const jschar*>(mData->mNameUTF16),
-                                    NS_strlen(mData->mNameUTF16), &found)) {
+                                    NS_strlen(mData->mNameUTF16),
+                                    &contentDefinedProperty)) {
     return NS_ERROR_FAILURE;
   }
 
   nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_OK);
 
-  bool unused;
-  return ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16,
-                          mData, nullptr, nameSpaceManager, proto, !found,
-                          &unused);
+  JS::Rooted<JSPropertyDescriptor> desc(cx);
+  nsresult rv = ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16,
+                                 mData, nullptr, nameSpaceManager, proto,
+                                 &desc);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() &&
+      !JS_DefineUCProperty(cx, global, mData->mNameUTF16,
+                           NS_strlen(mData->mNameUTF16),
+                           desc.value(), desc.getter(), desc.setter(),
+                           desc.attributes())) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
 }
 
 // static
 nsIClassInfo *
 NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID)
 {
   if (aID >= eDOMClassInfoIDCount) {
     NS_ERROR("Bad ID!");
@@ -2176,30 +2187,16 @@ public:
   nsresult Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                      JS::Handle<JSObject*> obj, const JS::CallArgs &args,
                      bool *_retval);
 
   nsresult HasInstance(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JS::Handle<JSObject*> obj, const jsval &val, bool *bp,
                        bool *_retval);
 
-  nsresult Install(JSContext *cx, JS::Handle<JSObject*> target,
-                   JS::Handle<JS::Value> aThisAsVal)
-  {
-    JS::Rooted<JS::Value> thisAsVal(cx, aThisAsVal);
-    // The 'attrs' argument used to be JSPROP_PERMANENT. See bug 628612.
-    bool ok = JS_WrapValue(cx, &thisAsVal) &&
-      ::JS_DefineUCProperty(cx, target,
-                            reinterpret_cast<const jschar *>(mClassName),
-                            NS_strlen(mClassName), thisAsVal, JS_PropertyStub,
-                            JS_StrictPropertyStub, 0);
-
-    return ok ? NS_OK : NS_ERROR_UNEXPECTED;
-  }
-
   nsresult ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj);
 
 private:
   const nsGlobalNameStruct *GetNameStruct()
   {
     if (!mClassName) {
       NS_ERROR("Can't get name");
       return nullptr;
@@ -2611,17 +2608,18 @@ GetXPCProto(nsIXPConnect *aXPConnect, JS
 // Either ci_data must be non-null or name_struct must be non-null and of type
 // eTypeClassProto.
 static nsresult
 ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
                  JS::Handle<JSObject*> obj, const char16_t *name,
                  const nsDOMClassInfoData *ci_data,
                  const nsGlobalNameStruct *name_struct,
                  nsScriptNameSpaceManager *nameSpaceManager,
-                 JSObject* aDot_prototype, bool install, bool *did_resolve)
+                 JSObject* aDot_prototype,
+                 JS::MutableHandle<JSPropertyDescriptor> ctorDesc)
 {
   JS::Rooted<JSObject*> dot_prototype(cx, aDot_prototype);
   NS_ASSERTION(ci_data ||
                (name_struct &&
                 name_struct->mType == nsGlobalNameStruct::eTypeClassProto),
                "Wrong type or missing ci_data!");
 
   nsRefPtr<nsDOMConstructor> constructor;
@@ -2630,19 +2628,22 @@ ResolvePrototype(nsIXPConnect *aXPConnec
   NS_ENSURE_SUCCESS(rv, rv);
 
   JS::Rooted<JS::Value> v(cx);
 
   rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
                   false, &v);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (install) {
-    rv = constructor->Install(cx, obj, v);
-    NS_ENSURE_SUCCESS(rv, rv);
+  FillPropertyDescriptor(ctorDesc, obj, 0, v);
+  // And make sure we wrap the value into the right compartment.  Note that we
+  // do this with ctorDesc.value(), not with v, because we need v to be in the
+  // right compartment (that of the reflector of |constructor|) below.
+  if (!JS_WrapValue(cx, ctorDesc.value())) {
+    return NS_ERROR_UNEXPECTED;
   }
 
   JS::Rooted<JSObject*> class_obj(cx, &v.toObject());
 
   const nsIID *primary_iid = &NS_GET_IID(nsISupports);
 
   if (!ci_data) {
     primary_iid = &name_struct->mIID;
@@ -2761,18 +2762,16 @@ ResolvePrototype(nsIXPConnect *aXPConnec
   // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly}
   if (!JS_WrapValue(cx, &v) ||
       !JS_DefineProperty(cx, class_obj, "prototype", v,
                          JS_PropertyStub, JS_StrictPropertyStub,
                          JSPROP_PERMANENT | JSPROP_READONLY)) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  *did_resolve = true;
-
   return NS_OK;
 }
 
 static bool
 OldBindingConstructorEnabled(const nsGlobalNameStruct *aStruct,
                              nsGlobalWindow *aWin, JSContext *cx)
 {
   MOZ_ASSERT(aStruct->mType == nsGlobalNameStruct::eTypeClassConstructor ||
@@ -2795,37 +2794,47 @@ OldBindingConstructorEnabled(const nsGlo
   // Don't expose CSSFontFeatureValuesRule unless the pref is enabled
   if (aStruct->mDOMClassInfoID == eDOMClassInfo_CSSFontFeatureValuesRule_id) {
     return nsCSSFontFeatureValuesRule::PrefEnabled();
   }
 
   return true;
 }
 
+static nsresult
+LookupComponentsShim(JSContext *cx, JS::Handle<JSObject*> global,
+                     nsPIDOMWindow *win,
+                     JS::MutableHandle<JSPropertyDescriptor> desc);
+
 // static
 nsresult
 nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
                           JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                          bool *did_resolve)
+                          JS::MutableHandle<JSPropertyDescriptor> desc)
 {
-  *did_resolve = false;
+  if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
+    return LookupComponentsShim(cx, obj, aWin, desc);
+  }
 
   nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nsDependentJSString name(id);
 
   const char16_t *class_name = nullptr;
   const nsGlobalNameStruct *name_struct =
     nameSpaceManager->LookupName(name, &class_name);
 
   if (!name_struct) {
     return NS_OK;
   }
 
+  // The class_name had better match our name
+  MOZ_ASSERT(name.Equals(class_name));
+
   NS_ENSURE_TRUE(class_name, NS_ERROR_UNEXPECTED);
 
   nsresult rv = NS_OK;
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeExternalClassInfoCreator) {
     rv = GetExternalClassInfo(nameSpaceManager, name, name_struct,
                               &name_struct);
     if (NS_FAILED(rv) || !name_struct) {
@@ -2833,74 +2842,96 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
     }
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeNewDOMBinding ||
       name_struct->mType == nsGlobalNameStruct::eTypeInterface ||
       name_struct->mType == nsGlobalNameStruct::eTypeClassProto ||
       name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
     // Lookup new DOM bindings.
-    mozilla::dom::DefineInterface define =
+    DefineInterface getOrCreateInterfaceObject =
       name_struct->mDefineDOMInterface;
-    if (define) {
+    if (getOrCreateInterfaceObject) {
       if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
           !OldBindingConstructorEnabled(name_struct, aWin, cx)) {
         return NS_OK;
       }
 
-      Maybe<JSAutoCompartment> ac;
-      JS::Rooted<JSObject*> global(cx);
-      bool defineOnXray = xpc::WrapperFactory::IsXrayWrapper(obj);
-      if (defineOnXray) {
-        // Check whether to define this property on the Xray first.  This allows
-        // consumers to opt in to defining on the xray even if they don't want
-        // to define on the underlying global.
-        if (name_struct->mConstructorEnabled &&
-            !(*name_struct->mConstructorEnabled)(cx, obj)) {
-          return NS_OK;
-        }
-
-        global = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
+      ConstructorEnabled* checkEnabledForScope = name_struct->mConstructorEnabled;
+      if (checkEnabledForScope && !checkEnabledForScope(cx, obj)) {
+        return NS_OK;
+      }
+
+      // The DOM constructor resolve machinery interacts with Xrays in tricky
+      // ways, and there are some asymmetries that are important to understand.
+      //
+      // In the regular (non-Xray) case, we only want to resolve constructors
+      // once (so that if they're deleted, they don't reappear). We do this by
+      // stashing the constructor in a slot on the global, such that we can see
+      // during resolve whether we've created it already. This is rather
+      // memory-intensive, so we don't try to maintain these semantics when
+      // manipulating a global over Xray (so the properties just re-resolve if
+      // they've been deleted).
+      //
+      // Unfortunately, there's a bit of an impedance-mismatch between the Xray
+      // and non-Xray machinery. The Xray machinery wants an API that returns a
+      // JSPropertyDescriptor, so that the resolve hook doesn't have to get
+      // snared up with trying to define a property on the Xray holder. At the
+      // same time, the DefineInterface callbacks are set up to define things
+      // directly on the global.  And re-jiggering them to return property
+      // descriptors is tricky, because some DefineInterface callbacks define
+      // multiple things (like the Image() alias for HTMLImageElement).
+      //
+      // So the setup is as-follows:
+      //
+      // * The resolve function takes a JSPropertyDescriptor, but in the
+      //   non-Xray case, callees may define things directly on the global, and
+      //   set the value on the property descriptor to |undefined| to indicate
+      //   that there's nothing more for the caller to do. We assert against
+      //   this behavior in the Xray case.
+      //
+      // * We make sure that we do a non-Xray resolve first, so that all the
+      //   slots are set up. In the Xray case, this means unwrapping and doing
+      //   a non-Xray resolve before doing the Xray resolve.
+      //
+      // This all could use some grand refactoring, but for now we just limp
+      // along.
+      if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+        JS::Rooted<JSObject*> global(cx,
+          js::CheckedUnwrap(obj, /* stopAtOuter = */ false));
         if (!global) {
           return NS_ERROR_DOM_SECURITY_ERR;
         }
-        ac.construct(cx, global);
-      } else {
-        global = obj;
-      }
-
-      // Check whether to define on the global too.  Note that at this point cx
-      // is in the compartment of global even if we were coming in via an Xray.
-      bool defineOnGlobal = !name_struct->mConstructorEnabled ||
-        (*name_struct->mConstructorEnabled)(cx, global);
-
-      if (!defineOnGlobal && !defineOnXray) {
-        return NS_OK;
-      }
-
-      JS::Rooted<JSObject*> interfaceObject(cx, define(cx, global, id,
-                                                       defineOnGlobal));
-      if (!interfaceObject) {
-        return NS_ERROR_FAILURE;
-      }
-
-      if (defineOnXray) {
-        // This really should be handled by the Xray for the window.
-        ac.destroy();
-        if (!JS_WrapObject(cx, &interfaceObject) ||
-            !JS_DefinePropertyById(cx, obj, id,
-                                   JS::ObjectValue(*interfaceObject), JS_PropertyStub,
-                                   JS_StrictPropertyStub, 0)) {
+        JS::Rooted<JSObject*> interfaceObject(cx);
+        {
+          JSAutoCompartment ac(cx, global);
+          interfaceObject = getOrCreateInterfaceObject(cx, global, id, false);
+        }
+        if (NS_WARN_IF(!interfaceObject)) {
+          return NS_ERROR_FAILURE;
+        }
+        if (!JS_WrapObject(cx, &interfaceObject)) {
           return NS_ERROR_FAILURE;
         }
 
+        FillPropertyDescriptor(desc, obj, 0, JS::ObjectValue(*interfaceObject));
+      } else {
+        JS::Rooted<JSObject*> interfaceObject(cx,
+          getOrCreateInterfaceObject(cx, obj, id, true));
+        if (NS_WARN_IF(!interfaceObject)) {
+          return NS_ERROR_FAILURE;
+        }
+        // We've already defined the property.  We indicate this to the caller
+        // by filling a property descriptor with JS::UndefinedValue() as the
+        // value.  We still have to fill in a property descriptor, though, so
+        // that the caller knows the property is in fact on this object.  It
+        // doesn't matter what we pass for the "readonly" argument here.
+        FillPropertyDescriptor(desc, obj, JS::UndefinedValue(), false);
       }
 
-      *did_resolve = true;
-
       return NS_OK;
     }
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeInterface) {
     // We're resolving a name of a DOM interface for which there is no
     // direct DOM class, create a constructor object...
     nsRefPtr<nsDOMConstructor> constructor;
@@ -2911,72 +2942,78 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
                                   getter_AddRefs(constructor));
     NS_ENSURE_SUCCESS(rv, rv);
 
     JS::Rooted<JS::Value> v(cx);
     rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
                     false, &v);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = constructor->Install(cx, obj, v);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     JS::Rooted<JSObject*> class_obj(cx, &v.toObject());
 
     // ... and define the constants from the DOM interface on that
     // constructor object.
 
-    JSAutoCompartment ac(cx, class_obj);
-    rv = DefineInterfaceConstants(cx, class_obj, &name_struct->mIID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    *did_resolve = true;
-
+    {
+      JSAutoCompartment ac(cx, class_obj);
+      rv = DefineInterfaceConstants(cx, class_obj, &name_struct->mIID);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    if (!JS_WrapValue(cx, &v)) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    FillPropertyDescriptor(desc, obj, 0, v);
     return NS_OK;
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor ||
       name_struct->mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
     if (!OldBindingConstructorEnabled(name_struct, aWin, cx)) {
       return NS_OK;
     }
 
     // Create the XPConnect prototype for our classinfo, PostCreateProto will
-    // set up the prototype chain.
+    // set up the prototype chain.  This will go ahead and define things on the
+    // actual window's global.
     nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
     rv = GetXPCProto(sXPConnect, cx, aWin, name_struct,
                      getter_AddRefs(proto_holder));
-
-    if (NS_SUCCEEDED(rv) && obj != aWin->GetGlobalJSObject()) {
-      JS::Rooted<JSObject*> dot_prototype(cx, proto_holder->GetJSObject());
-      NS_ENSURE_STATE(dot_prototype);
-
-      const nsDOMClassInfoData *ci_data;
-      if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
-        ci_data = &sClassInfoData[name_struct->mDOMClassInfoID];
-      } else {
-        ci_data = name_struct->mData;
-      }
-
-      return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data,
-                              name_struct, nameSpaceManager, dot_prototype,
-                              true, did_resolve);
+    NS_ENSURE_SUCCESS(rv, rv);
+    bool isXray = xpc::WrapperFactory::IsXrayWrapper(obj);
+    MOZ_ASSERT_IF(obj != aWin->GetGlobalJSObject(), isXray);
+    if (!isXray) {
+      // GetXPCProto already defined the property for us
+      FillPropertyDescriptor(desc, obj, JS::UndefinedValue(), false);
+      return NS_OK;
     }
 
-    *did_resolve = NS_SUCCEEDED(rv);
-
-    return rv;
+    // This is the Xray case.  Look up the constructor object for this
+    // prototype.
+    JS::Rooted<JSObject*> dot_prototype(cx, proto_holder->GetJSObject());
+    NS_ENSURE_STATE(dot_prototype);
+
+    const nsDOMClassInfoData *ci_data;
+    if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
+      ci_data = &sClassInfoData[name_struct->mDOMClassInfoID];
+    } else {
+      ci_data = name_struct->mData;
+    }
+
+    return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data,
+                            name_struct, nameSpaceManager, dot_prototype,
+                            desc);
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeClassProto) {
     // We don't have a XPConnect prototype object, let ResolvePrototype create
     // one.
     return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, nullptr,
-                            name_struct, nameSpaceManager, nullptr, true,
-                            did_resolve);
+                            name_struct, nameSpaceManager, nullptr, desc);
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
     const nsGlobalNameStruct *alias_struct =
       nameSpaceManager->GetConstructorProto(name_struct);
     NS_ENSURE_TRUE(alias_struct, NS_ERROR_UNEXPECTED);
 
     // We need to use the XPConnect prototype for the DOM class that this
@@ -2995,38 +3032,34 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
       ci_data = &sClassInfoData[alias_struct->mDOMClassInfoID];
     } else if (alias_struct->mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
       ci_data = alias_struct->mData;
     } else {
       return NS_ERROR_UNEXPECTED;
     }
 
     return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data,
-                            name_struct, nameSpaceManager, nullptr, true,
-                            did_resolve);
+                            name_struct, nameSpaceManager, nullptr, desc);
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
     nsRefPtr<nsDOMConstructor> constructor;
     rv = nsDOMConstructor::Create(class_name, nullptr, name_struct,
                                   static_cast<nsPIDOMWindow*>(aWin),
                                   getter_AddRefs(constructor));
     NS_ENSURE_SUCCESS(rv, rv);
 
     JS::Rooted<JS::Value> val(cx);
     rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
-                    false, &val);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = constructor->Install(cx, obj, val);
+                    true, &val);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(val.isObject(), "Why didn't we get a JSObject?");
 
-    *did_resolve = true;
+    FillPropertyDescriptor(desc, obj, 0, val);
 
     return NS_OK;
   }
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeProperty) {
     if (name_struct->mChromeOnly &&
         (name_struct->mAllowXBL ? !IsChromeOrXBL(cx, nullptr) :
          !nsContentUtils::IsCallerChrome()))
@@ -3065,36 +3098,19 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
     }
 
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!JS_WrapValue(cx, &prop_val)) {
       return NS_ERROR_UNEXPECTED;
     }
 
-    bool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val,
-                                      JS_PropertyStub, JS_StrictPropertyStub,
-                                      JSPROP_ENUMERATE);
-
-    *did_resolve = true;
-
-    return ok ? NS_OK : NS_ERROR_FAILURE;
-  }
-
-  if (name_struct->mType == nsGlobalNameStruct::eTypeDynamicNameSet) {
-    nsCOMPtr<nsIScriptExternalNameSet> nameset =
-      do_CreateInstance(name_struct->mCID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsIScriptContext *context = aWin->GetContext();
-    NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
-
-    rv = nameset->InitializeNameSet(context);
-
-    *did_resolve = true;
+    FillPropertyDescriptor(desc, obj, prop_val, false);
+
+    return NS_OK;
   }
 
   return rv;
 }
 
 template<class Interface>
 static nsresult
 LocationSetterGuts(JSContext *cx, JSObject *obj, JS::MutableHandle<JS::Value> vp)
@@ -3196,40 +3212,40 @@ const InterfaceShimEntry kInterfaceShimM
   { "nsIDOMOfflineResourceList", "OfflineResourceList" },
   { "nsIDOMRange", "Range" },
   { "nsIDOMSVGLength", "SVGLength" },
   { "nsIDOMNodeFilter", "NodeFilter" },
   { "nsIDOMXPathNamespace", "XPathNamespace" },
   { "nsIDOMXPathResult", "XPathResult" } };
 
 static nsresult
-DefineComponentsShim(JSContext *cx, JS::Handle<JSObject*> global, nsPIDOMWindow *win)
+LookupComponentsShim(JSContext *cx, JS::Handle<JSObject*> global,
+                     nsPIDOMWindow *win,
+                     JS::MutableHandle<JSPropertyDescriptor> desc)
 {
   // Keep track of how often this happens.
   Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
 
   // Warn once.
   nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
   if (doc) {
     doc->WarnOnceAbout(nsIDocument::eComponents, /* asError = */ true);
   }
 
   // Create a fake Components object.
   JS::Rooted<JSObject*> components(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
   NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY);
-  bool ok = JS_DefineProperty(cx, global, "Components", JS::ObjectValue(*components),
-                              JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
-  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   // Create a fake interfaces object.
   JS::Rooted<JSObject*> interfaces(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
   NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY);
-  ok = JS_DefineProperty(cx, components, "interfaces", JS::ObjectValue(*interfaces),
-                         JS_PropertyStub, JS_StrictPropertyStub,
-                         JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
+  bool ok =
+    JS_DefineProperty(cx, components, "interfaces", JS::ObjectValue(*interfaces),
+                      JS_PropertyStub, JS_StrictPropertyStub,
+                      JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
   // interfaces with constants.
   for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
 
     // Grab the names from the table.
     const char *geckoName = kInterfaceShimMap[i].geckoName;
@@ -3246,16 +3262,18 @@ DefineComponentsShim(JSContext *cx, JS::
 
     // Define the shim on the interfaces object.
     ok = JS_DefineProperty(cx, interfaces, geckoName, v,
                            JS_PropertyStub, JS_StrictPropertyStub,
                            JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
     NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   }
 
+  FillPropertyDescriptor(desc, global, JS::ObjectValue(*components), false);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj_, jsid id_, uint32_t flags,
                        JSObject **objp, bool *_retval)
 {
@@ -3266,21 +3284,16 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     return NS_OK;
   }
 
   MOZ_ASSERT(*_retval == true); // guaranteed by XPC_WN_Helper_NewResolve
 
   nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
   MOZ_ASSERT(win->IsInnerWindow());
 
-  if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
-    *objp = obj;
-    return DefineComponentsShim(cx, obj, win);
-  }
-
   // Don't resolve standard classes on XrayWrappers, only resolve them if we're
   // resolving on the real global object.
   bool isXray = xpc::WrapperFactory::IsXrayWrapper(obj);
   if (!isXray) {
     bool did_resolve = false;
     if (!JS_ResolveStandardClass(cx, obj, id, &did_resolve)) {
       // Return NS_OK to avoid stomping over the exception that was passed
       // down from the ResolveStandardClass call.
@@ -3347,30 +3360,69 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
                                JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
     *objp = obj;
 
     return NS_OK;
   }
 
-  bool did_resolve = false;
-  nsresult rv = GlobalResolve(win, cx, obj, id, &did_resolve);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (did_resolve) {
+  if (isXray) {
+    // We promise to resolve on the underlying object first.  That will create
+    // the actual interface object if needed and store it in a data structure
+    // hanging off the global.  Then our second call will wrap up in an Xray as
+    // needed.  We do things this way because we use the existence of the
+    // object in that data structure as a flag that indicates that its name
+    // (and any relevant named constructor names) has been resolved before;
+    // this allows us to avoid re-resolving in the Xray case if the property is
+    // deleted by page script.
+    JS::Rooted<JSObject*> global(cx,
+      js::UncheckedUnwrap(obj, /* stopAtOuter = */ false));
+    JSAutoCompartment ac(cx, global);
+    JS::Rooted<JSPropertyDescriptor> desc(cx);
+    if (!win->DoNewResolve(cx, global, id, &desc)) {
+      return NS_ERROR_FAILURE;
+    }
+    // If we have an object here, that means we resolved the property.
+    // But if the value is undefined, that means that GlobalResolve
+    // also already defined it, so we don't have to.
+    if (desc.object() && !desc.value().isUndefined() &&
+        !JS_DefinePropertyById(cx, global, id, desc.value(),
+                               desc.getter(), desc.setter(),
+                               desc.attributes())) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  JS::Rooted<JSPropertyDescriptor> desc(cx);
+  if (!win->DoNewResolve(cx, obj, id, &desc)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (desc.object()) {
+    // If we have an object here, that means we resolved the property.
+    // But if the value is undefined, that means that GlobalResolve
+    // also already defined it, so we don't have to.  Note that in the
+    // Xray case we should never see undefined.
+    MOZ_ASSERT_IF(isXray, !desc.value().isUndefined());
+    if (!desc.value().isUndefined() &&
+        !JS_DefinePropertyById(cx, obj, id, desc.value(),
+                               desc.getter(), desc.setter(),
+                               desc.attributes())) {
+      return NS_ERROR_FAILURE;
+    }
+
     *objp = obj;
     return NS_OK;
   }
 
   if (!(flags & JSRESOLVE_ASSIGNING) && sDocument_id == id) {
     nsCOMPtr<nsIDocument> document = win->GetDoc();
     JS::Rooted<JS::Value> v(cx);
-    rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), document, document,
-                    &NS_GET_IID(nsIDOMDocument), &v, false);
+    nsresult rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), document, document,
+                             &NS_GET_IID(nsIDOMDocument), &v, false);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // nsIDocument::WrapObject will handle defining the property.
     *objp = obj;
 
     // NB: We need to do this for any Xray wrapper.
     if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
       *_retval = JS_WrapValue(cx, &v) &&
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -251,18 +251,19 @@ protected:
   }
 
   virtual ~nsWindowSH()
   {
   }
 
   static nsresult GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
                                 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                                bool *did_resolve);
+                                JS::MutableHandle<JSPropertyDescriptor> desc);
 
+  friend class nsGlobalWindow;
 public:
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj) MOZ_OVERRIDE;
   NS_IMETHOD PostCreatePrototype(JSContext * cx, JSObject * proto) MOZ_OVERRIDE;
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj) MOZ_OVERRIDE;
   NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj, bool *_retval) MOZ_OVERRIDE;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -29,16 +29,17 @@
 #include "nsDOMWindowList.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptTimeoutHandler.h"
 #include "nsIController.h"
+#include "nsScriptNameSpaceManager.h"
 
 // Helper Classes
 #include "nsJSUtils.h"
 #include "jsapi.h"              // for JSAutoRequest
 #include "js/OldDebugAPI.h"     // for JS_ClearWatchPointsForObject
 #include "jswrapper.h"
 #include "nsReadableUtils.h"
 #include "nsDOMClassInfo.h"
@@ -582,20 +583,16 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogV
 // nsOuterWindowProxy: Outer Window Proxy
 //*****************************************************************************
 
 class nsOuterWindowProxy : public js::Wrapper
 {
 public:
   nsOuterWindowProxy() : js::Wrapper(0) { }
 
-  virtual bool isOuterWindow() {
-    return true;
-  }
-
   virtual bool finalizeInBackground(JS::Value priv) {
     return false;
   }
 
   virtual const char *className(JSContext *cx,
                                 JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
   virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
 
@@ -673,16 +670,30 @@ protected:
   already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx,
                                                    JS::Handle<JSObject*> proxy,
                                                    JS::Handle<jsid> id);
 
   bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
                                   JS::AutoIdVector &props);
 };
 
+const js::Class OuterWindowProxyClass =
+    PROXY_CLASS_WITH_EXT(
+        "Proxy",
+        0, /* additional slots */
+        0, /* additional class flags */
+        nullptr, /* call */
+        nullptr, /* construct */
+        PROXY_MAKE_EXT(
+            nullptr, /* outerObject */
+            js::proxy_innerObject,
+            nullptr, /* iteratorObject */
+            false   /* isWrappedNative */
+        ));
+
 bool
 nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
                                  bool *extensible)
 {
   // If [[Extensible]] could be false, then navigating a window could navigate
   // to a window that's [[Extensible]] after being at one that wasn't: an
   // invariant violation.  So always report true for this.
   *extensible = true;
@@ -1019,19 +1030,23 @@ nsChromeOuterWindowProxy::className(JSCo
 
 nsChromeOuterWindowProxy
 nsChromeOuterWindowProxy::singleton;
 
 static JSObject*
 NewOuterWindowProxy(JSContext *cx, JS::Handle<JSObject*> parent, bool isChrome)
 {
   JSAutoCompartment ac(cx, parent);
+  js::WrapperOptions options;
+  options.setClass(&OuterWindowProxyClass);
+  options.setSingleton(true);
   JSObject *obj = js::Wrapper::New(cx, parent, parent,
                                    isChrome ? &nsChromeOuterWindowProxy::singleton
-                                            : &nsOuterWindowProxy::singleton);
+                                            : &nsOuterWindowProxy::singleton,
+                                   &options);
 
   NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class");
   return obj;
 }
 
 //*****************************************************************************
 //***    nsGlobalWindow: Object Management
 //*****************************************************************************
@@ -4033,16 +4048,53 @@ nsGlobalWindow::GetSupportedNames(nsTArr
     for (uint32_t i = 0; i < length; ++i, ++name) {
       nsCOMPtr<nsIDocShellTreeItem> item =
         windows->GetDocShellTreeItemAt(i);
       item->GetName(*name);
     }
   }
 }
 
+bool
+nsGlobalWindow::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
+                             JS::Handle<jsid> aId,
+                             JS::MutableHandle<JSPropertyDescriptor> aDesc)
+{
+  MOZ_ASSERT(IsInnerWindow());
+
+  if (!JSID_IS_STRING(aId)) {
+    return true;
+  }
+
+  nsresult rv = nsWindowSH::GlobalResolve(this, aCx, aObj, aId, aDesc);
+  if (NS_FAILED(rv)) {
+    return Throw(aCx, rv);
+  }
+
+  return true;
+}
+
+static PLDHashOperator
+EnumerateGlobalName(const nsAString& aName, void* aClosure)
+{
+  nsTArray<nsString>* arr = static_cast<nsTArray<nsString>*>(aClosure);
+  arr->AppendElement(aName);
+  return PL_DHASH_NEXT;
+}
+
+void
+nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
+                                    ErrorResult& aRv)
+{
+  nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
+  if (nameSpaceManager) {
+    nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &aNames);
+  }
+}
+
 nsIDOMOfflineResourceList*
 nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetApplicationCache, (aError), aError, nullptr);
 
   if (!mApplicationCache) {
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
     if (!webNav) {
@@ -4111,23 +4163,16 @@ nsGlobalWindow::GetCrypto(nsIDOMCrypto**
 {
   ErrorResult rv;
   nsCOMPtr<nsIDOMCrypto> crypto = GetCrypto(rv);
   crypto.forget(aCrypto);
 
   return rv.ErrorCode();
 }
 
-NS_IMETHODIMP
-nsGlobalWindow::GetPkcs11(nsIDOMPkcs11** aPkcs11)
-{
-  *aPkcs11 = nullptr;
-  return NS_OK;
-}
-
 nsIControllers*
 nsGlobalWindow::GetControllers(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetControllers, (aError), aError, nullptr);
 
   if (!mControllers) {
     nsresult rv;
     mControllers = do_CreateInstance(kXULControllersCID, &rv);
@@ -10472,16 +10517,21 @@ nsGlobalWindow::ShowSlowScriptDialog()
   // If it isn't safe to run script, then it isn't safe to bring up the prompt
   // (since that spins the event loop). In that (rare) case, we just kill the
   // script and report a warning.
   if (!nsContentUtils::IsSafeToRunScript()) {
     JS_ReportWarning(cx, "A long running script was terminated");
     return KillSlowScript;
   }
 
+  // If our document is not active, just kill the script: we've been unloaded
+  if (!HasActiveDocument()) {
+    return KillSlowScript;
+  }
+
   // Get the nsIPrompt interface from the docshell
   nsCOMPtr<nsIDocShell> ds = GetDocShell();
   NS_ENSURE_TRUE(ds, KillSlowScript);
   nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
   NS_ENSURE_TRUE(prompt, KillSlowScript);
 
   // Check if we should offer the option to debug
   JS::Rooted<JSScript*> script(cx);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -470,16 +470,23 @@ public:
   // nsIInterfaceRequestor
   NS_DECL_NSIINTERFACEREQUESTOR
 
   // WebIDL interface.
   already_AddRefed<nsIDOMWindow> IndexedGetter(uint32_t aIndex, bool& aFound);
 
   void GetSupportedNames(nsTArray<nsString>& aNames);
 
+  bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
+                    JS::Handle<jsid> aId,
+                    JS::MutableHandle<JSPropertyDescriptor> aDesc);
+
+  void GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
+                           mozilla::ErrorResult& aRv);
+
   // Object Management
   nsGlobalWindow(nsGlobalWindow *aOuterWindow);
 
   static nsGlobalWindow *FromSupports(nsISupports *supports)
   {
     // Make sure this matches the casts we do in QueryInterface().
     return (nsGlobalWindow *)(mozilla::dom::EventTarget *)supports;
   }
@@ -897,20 +904,16 @@ public:
   void MozCancelRequestAnimationFrame(int32_t aHandle,
                                       mozilla::ErrorResult& aError)
   {
     return CancelAnimationFrame(aHandle, aError);
   }
   int64_t GetMozAnimationStartTime(mozilla::ErrorResult& aError);
   void SizeToContent(mozilla::ErrorResult& aError);
   nsIDOMCrypto* GetCrypto(mozilla::ErrorResult& aError);
-  nsIDOMPkcs11* GetPkcs11()
-  {
-    return nullptr;
-  }
   nsIControllers* GetControllers(mozilla::ErrorResult& aError);
   float GetMozInnerScreenX(mozilla::ErrorResult& aError);
   float GetMozInnerScreenY(mozilla::ErrorResult& aError);
   float GetDevicePixelRatio(mozilla::ErrorResult& aError);
   int32_t GetScrollMaxX(mozilla::ErrorResult& aError);
   int32_t GetScrollMaxY(mozilla::ErrorResult& aError);
   bool GetFullScreen(mozilla::ErrorResult& aError);
   void SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError);
--- a/dom/base/nsIScriptNameSpaceManager.h
+++ b/dom/base/nsIScriptNameSpaceManager.h
@@ -20,18 +20,15 @@
   "JavaScript-global-privileged-property"
 
 #define JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY \
   "JavaScript-navigator-property"
 
 #define JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY \
   "JavaScript-global-static-nameset"
 
-#define JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY \
-  "JavaScript-global-dynamic-nameset"
-
 #define JAVASCRIPT_DOM_CLASS \
   "JavaScript-DOM-class"
 
 #define JAVASCRIPT_DOM_INTERFACE \
   "JavaScript-DOM-interface"
 
 #endif /* nsIScriptNameSpaceManager_h__ */
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1177,17 +1177,17 @@ nsJSContext::SetProperty(JS::Handle<JSOb
   // got the arguments, now attach them.
 
   for (uint32_t i = 0; i < argc; ++i) {
     if (!JS_WrapValue(mContext, array.handleAt(i))) {
       return NS_ERROR_FAILURE;
     }
   }
 
-  JSObject *args = ::JS_NewArrayObject(mContext, argc, array.array);
+  JSObject *args = ::JS_NewArrayObject(mContext, argc, array.start());
   if (!args) {
     return NS_ERROR_FAILURE;
   }
   JS::Value vargs = OBJECT_TO_JSVAL(args);
 
   return JS_DefineProperty(mContext, aTarget, aPropName, vargs,
   						   nullptr, nullptr, 0) ? NS_OK : NS_ERROR_FAILURE;
 }
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -364,19 +364,16 @@ nsScriptNameSpaceManager::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = FillHash(cm, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = FillHash(cm, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = FillHash(cm, JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   rv = FillHash(cm, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Initial filling of the has table has been done.
   // Now, listen for changes.
   nsCOMPtr<nsIObserverService> serv = 
     mozilla::services::GetObserverService();
 
@@ -621,18 +618,16 @@ nsScriptNameSpaceManager::OperateCategor
     type = nsGlobalNameStruct::eTypeExternalConstructor;
   } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY) == 0 ||
              strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0) {
     type = nsGlobalNameStruct::eTypeProperty;
   } else if (strcmp(aCategory, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY) == 0) {
     type = nsGlobalNameStruct::eTypeNavigatorProperty;
   } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY) == 0) {
     type = nsGlobalNameStruct::eTypeStaticNameSet;
-  } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY) == 0) {
-    type = nsGlobalNameStruct::eTypeDynamicNameSet;
   } else {
     return NS_OK;
   }
 
   nsCOMPtr<nsISupportsCString> strWrapper = do_QueryInterface(aEntry);
 
   if (!strWrapper) {
     NS_WARNING("Category entry not an nsISupportsCString!");
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -45,17 +45,16 @@ struct nsGlobalNameStruct
   enum nametype {
     eTypeNotInitialized,
     eTypeNewDOMBinding,
     eTypeInterface,
     eTypeProperty,
     eTypeNavigatorProperty,
     eTypeExternalConstructor,
     eTypeStaticNameSet,
-    eTypeDynamicNameSet,
     eTypeClassConstructor,
     eTypeClassProto,
     eTypeExternalClassInfoCreator,
     eTypeExternalClassInfo,
     eTypeExternalConstructorAlias
   } mType;
 
   // mChromeOnly is only used for structs that define non-WebIDL things
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2163,17 +2163,17 @@ void
 FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj)
 {
   MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
   mozilla::dom::DestroyProtoAndIfaceCache(aObj);
 }
 
 bool
 ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
-              JS::MutableHandle<jsid> aId, unsigned aFlags,
+              JS::Handle<jsid> aId, unsigned aFlags,
               JS::MutableHandle<JSObject*> aObjp)
 {
   bool resolved;
   if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
     return false;
   }
 
   aObjp.set(resolved ? aObj.get() : nullptr);
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2352,17 +2352,17 @@ ThreadsafeCheckIsChrome(JSContext* aCx, 
 void
 TraceGlobal(JSTracer* aTrc, JSObject* aObj);
 
 void
 FinalizeGlobal(JSFreeOp* aFop, JSObject* aObj);
 
 bool
 ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
-              JS::MutableHandle<jsid> aId, unsigned aFlags,
+              JS::Handle<jsid> aId, unsigned aFlags,
               JS::MutableHandle<JSObject*> aObjp);
 
 bool
 EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj);
 
 template <class T, JS::Handle<JSObject*> (*ProtoGetter)(JSContext*,
                                                         JS::Handle<JSObject*>)>
 JSObject*
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1402,16 +1402,21 @@ DOMInterfaces = {
     'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionTextureFloatLinear': {
     'nativeType': 'mozilla::WebGLExtensionTextureFloatLinear',
     'headerFile': 'WebGLExtensions.h'
 },
 
+'WebGLExtensionTextureHalfFloat': {
+   'nativeType': 'mozilla::WebGLExtensionTextureHalfFloat',
+   'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLExtensionDrawBuffers': {
     'nativeType': 'mozilla::WebGLExtensionDrawBuffers',
     'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionVertexArray': {
     'nativeType': 'mozilla::WebGLExtensionVertexArray',
     'headerFile': 'WebGLExtensions.h'
@@ -1925,17 +1930,16 @@ addExternalIface('nsIInputStreamCallback
 addExternalIface('nsISelectionListener', nativeType='nsISelectionListener')
 addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True)
 addExternalIface('nsISupports', nativeType='nsISupports')
 addExternalIface('nsIDocShell', nativeType='nsIDocShell', notflattened=True)
 addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True)
 addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True)
 addExternalIface('OutputStream', nativeType='nsIOutputStream',
                  notflattened=True)
-addExternalIface('Pkcs11')
 addExternalIface('Principal', nativeType='nsIPrincipal',
                  headerFile='nsIPrincipal.h', notflattened=True)
 addExternalIface('StackFrame', nativeType='nsIStackFrame',
                  headerFile='nsIException.h', notflattened=True)
 addExternalIface('StyleSheetList')
 addExternalIface('SVGLength')
 addExternalIface('SVGNumber')
 addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2472,16 +2472,17 @@ class CGClearCachedValueMethod(CGAbstrac
             declObj = "JS::Rooted<JSObject*> obj(aCx);"
             noopRetval = " true"
             saveMember = (
                 "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" %
                 slotIndex)
             regetMember = ("\n"
                            "JS::Rooted<JS::Value> temp(aCx);\n"
                            "JSJitGetterCallArgs args(&temp);\n"
+                           "JSAutoCompartment ac(aCx, obj);\n"
                            "if (!get_%s(aCx, obj, aObject, args)) {\n"
                            "  js::SetReservedSlot(obj, %s, oldValue);\n"
                            "  nsJSUtils::ReportPendingException(aCx);\n"
                            "  return false;\n"
                            "}\n"
                            "return true;" % (self.member.identifier.name, slotIndex))
         else:
             declObj = "JSObject* obj;"
@@ -6016,16 +6017,30 @@ class CGNewResolveHook(CGAbstractBinding
                 "    !JS_DefinePropertyById(cx, obj, id, desc.value(),\n"
                 "                           desc.getter(), desc.setter(),\n"
                 "                           desc.attributes())) {\n"
                 "  return false;\n"
                 "}\n"
                 "objp.set(obj);\n"
                 "return true;"))
 
+    def definition_body(self):
+        if self.descriptor.interface.getExtendedAttribute("Global"):
+            # Resolve standard classes
+            prefix = CGIndenter(CGGeneric(
+                    "if (!ResolveGlobal(cx, obj, id, flags, objp)) {\n"
+                    "  return false;\n"
+                    "}\n"
+                    "if (objp) {\n"
+                    "  return true;\n"
+                    "}\n\n")).define()
+        else:
+            prefix = ""
+        return prefix + CGAbstractBindingMethod.definition_body(self)
+
 class CGEnumerateHook(CGAbstractBindingMethod):
     """
     Enumerate hook for objects with custom hooks.
     """
     def __init__(self, descriptor):
         assert descriptor.interface.getExtendedAttribute("NeedNewResolve")
 
         args = [Argument('JSContext*', 'cx'),
@@ -6047,16 +6062,27 @@ class CGEnumerateHook(CGAbstractBindingM
                 "JS::Rooted<JS::Value> dummy(cx);\n"
                 "for (uint32_t i = 0; i < names.Length(); ++i) {\n"
                 "  if (!JS_LookupUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) {\n"
                 "    return false;\n"
                 "  }\n"
                 "}\n"
                 "return true;"))
 
+    def definition_body(self):
+        if self.descriptor.interface.getExtendedAttribute("Global"):
+            # Enumerate standard classes
+            prefix = CGIndenter(CGGeneric(
+                    "if (!EnumerateGlobal(cx, obj)) {\n"
+                    "  return false;\n"
+                    "}\n\n")).define()
+        else:
+            prefix = ""
+        return prefix + CGAbstractBindingMethod.definition_body(self)
+
 class CppKeywords():
     """
     A class for checking if method names declared in webidl
     are not in conflict with C++ keywords.
     """
     keywords = frozenset(['alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool',
     'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const', 'constexpr',
     'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else', 'enum',
@@ -7727,16 +7753,39 @@ class CGResolveOwnPropertyViaNewresolve(
                 Argument('unsigned', 'flags'),
                 ]
         CGAbstractBindingMethod.__init__(self, descriptor,
                                          "ResolveOwnPropertyViaNewresolve",
                                          args, getThisObj="",
                                          callArgs="")
     def generate_code(self):
         return CGIndenter(CGGeneric(
+                "{\n"
+                "  // Since we're dealing with an Xray, do the resolve on the\n"
+                "  // underlying object first.  That gives it a chance to\n"
+                "  // define properties on the actual object as needed, and\n"
+                "  // then use the fact that it created the objects as a flag\n"
+                "  // o avoid re-resolving the properties if someone deletes\n"
+                "  // them.\n"
+                "  JSAutoCompartment ac(cx, obj);\n"
+                "  JS::Rooted<JSPropertyDescriptor> objDesc(cx);\n"
+                "  if (!self->DoNewResolve(cx, obj, id, &objDesc)) {\n"
+                "    return false;\n"
+                "  }\n"
+                "  // If desc.value() is undefined, then the DoNewResolve call\n"
+                "  // has already defined the property on the object.  Don't\n"
+                "  // try to also define it.\n"
+                "  if (objDesc.object() &&\n"
+                "      !objDesc.value().isUndefined() &&\n"
+                "      !JS_DefinePropertyById(cx, obj, id, objDesc.value(),\n"
+                "                             objDesc.getter(), objDesc.setter(),\n"
+                "                             objDesc.attributes())) {\n"
+                "    return false;\n"
+                "  }\n"
+                "}\n"
                 "return self->DoNewResolve(cx, wrapper, id, desc);"))
 
 class CGEnumerateOwnProperties(CGAbstractStaticMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'wrapper'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::AutoIdVector&', 'props')]
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -160,12 +160,24 @@ FillPropertyDescriptor(JS::MutableHandle
 inline void
 FillPropertyDescriptor(JS::MutableHandle<JSPropertyDescriptor> desc, JSObject* obj, JS::Value v,
                        bool readonly)
 {
   desc.value().set(v);
   FillPropertyDescriptor(desc, obj, readonly);
 }
 
+inline void
+FillPropertyDescriptor(JS::MutableHandle<JSPropertyDescriptor> desc,
+                       JSObject* obj, unsigned attributes, JS::Value v)
+{
+  desc.object().set(obj);
+  desc.value().set(v);
+  desc.setAttributes(attributes);
+  desc.setGetter(nullptr);
+  desc.setSetter(nullptr);
+  desc.setShortId(0);
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_DOMProxyHandler_h */
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -373,20 +373,17 @@ NS_IMETHODIMP JSStackFrame::GetLanguageN
 }
 
 /* readonly attribute string filename; */
 NS_IMETHODIMP JSStackFrame::GetFilename(nsACString& aFilename)
 {
   if (!mFilenameInitialized) {
     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
     if (desc.script()) {
-      // This cx dance is silly, since JS_GetScriptFilename ignores
-      // its cx argument.
-      JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
-      JSAutoRequest ar(cx);
+      ThreadsafeAutoSafeJSContext cx;
       JSAutoCompartment ac(cx, desc.script());
       const char* filename = JS_GetScriptFilename(cx, desc.script());
       if (filename) {
         mFilename.Assign(filename);
       }
     }
     mFilenameInitialized = true;
   }
@@ -402,18 +399,17 @@ NS_IMETHODIMP JSStackFrame::GetFilename(
 }
 
 /* readonly attribute string name; */
 NS_IMETHODIMP JSStackFrame::GetName(nsACString& aFunction)
 {
   if (!mFunnameInitialized) {
     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
     if (desc.fun() && desc.script()) {
-      JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
-      JSAutoRequest ar(cx);
+      ThreadsafeAutoSafeJSContext cx;
       JSAutoCompartment ac(cx, desc.script());
       JS::Rooted<JSFunction*> fun(cx, desc.fun());
       JS::Rooted<JSString*> funid(cx, JS_GetFunctionDisplayId(fun));
       if (funid) {
         CopyUTF16toUTF8(JS_GetStringCharsZ(cx, funid), mFunname);
       }
     }
     mFunnameInitialized = true;
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -57,17 +57,18 @@ StaticRefPtr<BluetoothOppManager> sBluet
 static bool sInShutdown = false;
 }
 
 class mozilla::dom::bluetooth::SendFileBatch {
 public:
   SendFileBatch(const nsAString& aDeviceAddress, BlobParent* aActor)
     : mDeviceAddress(aDeviceAddress)
   {
-    mBlobs.AppendElement(aActor->GetBlob().get());
+    nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
+    mBlobs.AppendElement(blob);
   }
 
   nsString mDeviceAddress;
   nsCOMArray<nsIDOMBlob> mBlobs;
 };
 
 NS_IMETHODIMP
 BluetoothOppManager::Observe(nsISupports* aSubject,
@@ -369,17 +370,18 @@ BluetoothOppManager::AppendBlobToSend(co
    * - mBatches is empty, or
    * - aDeviceAddress differs from mDeviceAddress of the last batch
    */
   if (mBatches.IsEmpty() ||
       aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
     SendFileBatch batch(aDeviceAddress, aActor);
     mBatches.AppendElement(batch);
   } else {
-    mBatches[indexTail].mBlobs.AppendElement(aActor->GetBlob().get());
+    nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
+    mBatches[indexTail].mBlobs.AppendElement(blob);
   }
 }
 
 void
 BluetoothOppManager::DiscardBlobsToSend()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/bluetooth/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp
@@ -57,17 +57,18 @@ StaticRefPtr<BluetoothOppManager> sBluet
 static bool sInShutdown = false;
 }
 
 class mozilla::dom::bluetooth::SendFileBatch {
 public:
   SendFileBatch(const nsAString& aDeviceAddress, BlobParent* aActor)
     : mDeviceAddress(aDeviceAddress)
   {
-    mBlobs.AppendElement(aActor->GetBlob().get());
+    nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
+    mBlobs.AppendElement(blob);
   }
 
   nsString mDeviceAddress;
   nsCOMArray<nsIDOMBlob> mBlobs;
 };
 
 NS_IMETHODIMP
 BluetoothOppManager::Observe(nsISupports* aSubject,
@@ -385,17 +386,18 @@ BluetoothOppManager::AppendBlobToSend(co
    * - mBatches is empty, or
    * - aDeviceAddress differs from mDeviceAddress of the last batch
    */
   if (mBatches.IsEmpty() ||
       aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
     SendFileBatch batch(aDeviceAddress, aActor);
     mBatches.AppendElement(batch);
   } else {
-    mBatches[indexTail].mBlobs.AppendElement(aActor->GetBlob().get());
+    nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
+    mBatches[indexTail].mBlobs.AppendElement(blob);
   }
 }
 
 void
 BluetoothOppManager::DiscardBlobsToSend()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/datastore/DataStore.jsm
+++ b/dom/datastore/DataStore.jsm
@@ -20,17 +20,16 @@ const REVISION_REMOVED = "removed";
 const REVISION_VOID = "void";
 
 // This value has to be tuned a bit. Currently it's just a guess
 // and yet we don't know if it's too low or too high.
 const MAX_REQUESTS = 25;
 
 Cu.import("resource://gre/modules/DataStoreCursor.jsm");
 Cu.import("resource://gre/modules/DataStoreDB.jsm");
-Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.importGlobalProperties(["indexedDB"]);
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
@@ -137,17 +136,17 @@ this.DataStore.prototype = {
     // We're going to create this amount of requests.
     let pendingIds = aIds.length;
     let indexPos = 0;
 
     let self = this;
 
     function getInternalSuccess(aEvent, aPos) {
       debug("GetInternal success. Record: " + aEvent.target.result);
-      results[aPos] = ObjectWrapper.wrap(aEvent.target.result, self._window);
+      results[aPos] = Cu.cloneInto(aEvent.target.result, self._window);
       if (!--pendingIds) {
         aCallback(results);
         return;
       }
 
       if (indexPos < aIds.length) {
         // Just MAX_REQUESTS requests at the same time.
         let count = 0;
--- a/dom/datastore/DataStoreCursor.jsm
+++ b/dom/datastore/DataStoreCursor.jsm
@@ -22,17 +22,16 @@ const STATE_REVISION_SEND = 4;
 const STATE_DONE = 5;
 
 const REVISION_ADDED = 'added';
 const REVISION_UPDATED = 'updated';
 const REVISION_REMOVED = 'removed';
 const REVISION_VOID = 'void';
 const REVISION_SKIP = 'skip'
 
-Cu.import('resource://gre/modules/ObjectWrapper.jsm');
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 /**
  * legend:
  * - RID = revision ID
  * - R = revision object (with the internalRevisionId that is a number)
  * - X = current object ID.
  * - L = the list of revisions that we have to send
@@ -146,17 +145,17 @@ this.DataStoreCursor.prototype = {
     }
 
     let self = this;
     let request = aRevisionStore.openCursor(null, 'prev');
     request.onsuccess = function(aEvent) {
       self._revision = aEvent.target.result.value;
       self._objectId = 0;
       self._state = STATE_SEND_ALL;
-      aResolve(ObjectWrapper.wrap({ operation: 'clear' }, self._window));
+      aResolve(Cu.cloneInto({ operation: 'clear' }, self._window));
     }
   },
 
   stateMachineRevisionInit: function(aStore, aRevisionStore, aResolve, aReject) {
     debug('StateMachineRevisionInit');
 
     let self = this;
     let request = this._dataStore._db.getInternalRevisionId(
@@ -286,32 +285,32 @@ this.DataStoreCursor.prototype = {
     debug('StateMachineSendAll');
 
     let self = this;
     let request = aRevisionStore.openCursor(null, 'prev');
     request.onsuccess = function(aEvent) {
       if (self._revision.revisionId != aEvent.target.result.value.revisionId) {
         self._revision = aEvent.target.result.value;
         self._objectId = 0;
-        aResolve(ObjectWrapper.wrap({ operation: 'clear' }, self._window));
+        aResolve(Cu.cloneInto({ operation: 'clear' }, self._window));
         return;
       }
 
       let request = aStore.openCursor(self._window.IDBKeyRange.lowerBound(self._objectId, true));
       request.onsuccess = function(aEvent) {
         let cursor = aEvent.target.result;
         if (!cursor) {
           self._state = STATE_REVISION_CHECK;
           self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
           return;
         }
 
         self._objectId = cursor.key;
-        aResolve(ObjectWrapper.wrap({ operation: 'add', id: self._objectId,
-                                      data: cursor.value }, self._window));
+        aResolve(Cu.cloneInto({ operation: 'add', id: self._objectId,
+                                data: cursor.value }, self._window));
       };
     };
   },
 
   stateMachineRevisionSend: function(aStore, aRevisionStore, aResolve, aReject) {
     debug('StateMachineRevisionSend');
 
     if (!this._revisionsList.length) {
@@ -319,31 +318,31 @@ this.DataStoreCursor.prototype = {
       this.stateMachine(aStore, aRevisionStore, aResolve, aReject);
       return;
     }
 
     this._revision = this._revisionsList.shift();
 
     switch (this._revision.operation) {
       case REVISION_REMOVED:
-        aResolve(ObjectWrapper.wrap({ operation: 'remove', id: this._revision.objectId },
-                                    this._window));
+        aResolve(Cu.cloneInto({ operation: 'remove', id: this._revision.objectId },
+                              this._window));
         break;
 
       case REVISION_ADDED: {
         let request = aStore.get(this._revision.objectId);
         let self = this;
         request.onsuccess = function(aEvent) {
           if (aEvent.target.result == undefined) {
             self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
             return;
           }
 
-          aResolve(ObjectWrapper.wrap({ operation: 'add', id: self._revision.objectId,
-                                        data: aEvent.target.result }, self._window));
+          aResolve(Cu.cloneInto({ operation: 'add', id: self._revision.objectId,
+                                  data: aEvent.target.result }, self._window));
         }
         break;
       }
 
       case REVISION_UPDATED: {
         let request = aStore.get(this._revision.objectId);
         let self = this;
         request.onsuccess = function(aEvent) {
@@ -352,18 +351,18 @@ this.DataStoreCursor.prototype = {
             return;
           }
 
           if (aEvent.target.result.revisionId >  self._revision.internalRevisionId) {
             self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
             return;
           }
 
-          aResolve(ObjectWrapper.wrap({ operation: 'update', id: self._revision.objectId,
-                                        data: aEvent.target.result }, self._window));
+          aResolve(Cu.cloneInto({ operation: 'update', id: self._revision.objectId,
+                                  data: aEvent.target.result }, self._window));
         }
         break;
       }
 
       case REVISION_VOID:
         // Internal error!
         dump('Internal error: Revision "' + REVISION_VOID + '" should not be found!!!\n');
         break;
@@ -372,18 +371,18 @@ this.DataStoreCursor.prototype = {
         // This revision contains data that has already been sent by another one.
         this.stateMachine(aStore, aRevisionStore, aResolve, aReject);
         break;
     }
   },
 
   stateMachineDone: function(aStore, aRevisionStore, aResolve, aReject) {
     this.close();
-    aResolve(ObjectWrapper.wrap({ revisionId: this._revision.revisionId,
-                                  operation: 'done' }, this._window));
+    aResolve(Cu.cloneInto({ revisionId: this._revision.revisionId,
+                            operation: 'done' }, this._window));
   },
 
   // public interface
 
   get store() {
     return this._dataStore.exposedObject;
   },
 
--- a/dom/identity/DOMIdentity.jsm
+++ b/dom/identity/DOMIdentity.jsm
@@ -1,15 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const PREF_FXA_ENABLED = "identity.fxaccounts.enabled";
+let _fxa_enabled = false;
+try {
+  if (Services.prefs.getPrefType(PREF_FXA_ENABLED) === Ci.nsIPrefBranch.PREF_BOOL) {
+    _fxa_enabled = Services.prefs.getBoolPref(PREF_FXA_ENABLED);
+  }
+} catch(noPref) {
+}
+const FXA_ENABLED = _fxa_enabled;
 
 // This is the parent process corresponding to nsDOMIdentity.
 this.EXPORTED_SYMBOLS = ["DOMIdentity"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "objectCopy",
@@ -17,16 +26,22 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
 #ifdef MOZ_B2G_VERSION
                                   "resource://gre/modules/identity/MinimalIdentity.jsm");
 #else
                                   "resource://gre/modules/identity/Identity.jsm");
 #endif
 
+XPCOMUtils.defineLazyModuleGetter(this, "FirefoxAccounts",
+                                  "resource://gre/modules/identity/FirefoxAccounts.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject",
+                                  "resource://gre/modules/identity/IdentityUtils.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this,
                                   "Logger",
                                   "resource://gre/modules/identity/LogUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
@@ -96,19 +111,27 @@ function RPWatchContext(aOptions, aTarge
   // id and origin are required
   if (! (this.id && this.origin)) {
     throw new Error("id and origin are required for RP watch context");
   }
 
   // default for no loggedInUser is undefined, not null
   this.loggedInUser = aOptions.loggedInUser;
 
-  // Maybe internal
+  // Maybe internal.  For hosted b2g identity shim.
   this._internal = aOptions._internal;
 
+  // By default, set the audience of the assertion to the origin of the RP. Bug
+  // 947374 will make it possible for certified apps and packaged apps on
+  // FirefoxOS to request a different audience from their origin.
+  //
+  // For BrowserID on b2g, this audience value is consumed by a hosted identity
+  // shim, set up by b2g/components/SignInToWebsite.jsm.
+  this.audience = this.origin;
+
   this._mm = aTargetMM;
 }
 
 RPWatchContext.prototype = {
   doLogin: function RPWatchContext_onlogin(aAssertion, aMaybeInternalParams) {
     log("doLogin: " + this.id);
     let message = new IDDOMMessage({id: this.id, assertion: aAssertion});
     if (aMaybeInternalParams) {
@@ -136,16 +159,82 @@ RPWatchContext.prototype = {
   },
 
   doError: function RPWatchContext_onerror(aMessage) {
     log("doError: " + aMessage);
   }
 };
 
 this.DOMIdentity = {
+  /*
+   * When relying parties (RPs) invoke the watch() method, they can request
+   * to use Firefox Accounts as their auth service or BrowserID (the default).
+   * For each RP, we create an RPWatchContext to store the parameters given to
+   * watch(), and to provide hooks to invoke the onlogin(), onlogout(), etc.
+   * callbacks held in the nsDOMIdentity state.
+   *
+   * The serviceContexts map associates the window ID of the RP with the
+   * context object.  The mmContexts map associates a message manager with a
+   * window ID.  We use the mmContexts map when child-process-shutdown is
+   * observed, and all we have is a message manager to identify the window in
+   * question.
+   */
+  _serviceContexts: new Map(),
+  _mmContexts: new Map(),
+
+  /*
+   * Create a new RPWatchContext, and update the context maps.
+   */
+  newContext: function(message, targetMM) {
+    let context = new RPWatchContext(message, targetMM);
+    this._serviceContexts.set(message.id, context);
+    this._mmContexts.set(targetMM, message.id);
+    return context;
+  },
+
+  /*
+   * Get the identity service used for an RP.
+   *
+   * @object message
+   *         A message received from an RP.  Will include the id of the window
+   *         whence the message originated.
+   *
+   * Returns FirefoxAccounts or IdentityService
+   */
+  getService: function(message) {
+    if (!this._serviceContexts.has(message.id)) {
+      throw new Error("getService called before newContext for " + message.id);
+    }
+
+    let context = this._serviceContexts.get(message.id);
+    if (context.wantIssuer == "firefox-accounts") {
+      if (FXA_ENABLED) {
+        return FirefoxAccounts;
+      }
+      log("WARNING: Firefox Accounts is not enabled; Defaulting to BrowserID");
+    }
+    return IdentityService;
+  },
+
+  /*
+   * Get the RPWatchContext object for a given message manager.
+   */
+  getContextForMM: function(targetMM) {
+    return this._serviceContexts.get(this._mmContexts.get(targetMM));
+  },
+
+  /*
+   * Delete the RPWatchContext object for a given message manager.  Removes the
+   * mapping both from _serviceContexts and _mmContexts.
+   */
+  deleteContextForMM: function(targetMM) {
+    this._serviceContexts.delete(this._mmContexts.get(targetMM));
+    this._mmContexts.delete(targetMM);
+  },
+
   // nsIMessageListener
   receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
     let msg = aMessage.json;
 
     // Target is the frame message manager that called us and is
     // used to send replies back to the proper window.
     let targetMM = aMessage.target;
 
@@ -230,75 +319,69 @@ this.DOMIdentity = {
 
   _unsubscribeListeners: function DOMIdentity__unsubscribeListeners() {
     for (let message of this.messages) {
       ppmm.removeMessageListener(message, this);
     }
     ppmm = null;
   },
 
-  _resetFrameState: function(aContext) {
-    log("_resetFrameState: ", aContext.id);
-    if (!aContext._mm) {
-      throw new Error("ERROR: Trying to reset an invalid context");
-    }
-    let message = new IDDOMMessage({id: aContext.id});
-    aContext._mm.sendAsyncMessage("Identity:ResetState", message);
-  },
-
   _watch: function DOMIdentity__watch(message, targetMM) {
     log("DOMIdentity__watch: " + message.id);
-    // Pass an object with the watch members to Identity.jsm so it can call the
-    // callbacks.
-    let context = new RPWatchContext(message, targetMM);
-    IdentityService.RP.watch(context);
+    let context = this.newContext(message, targetMM);
+    this.getService(message).RP.watch(context);
   },
 
   _unwatch: function DOMIdentity_unwatch(message, targetMM) {
-    IdentityService.RP.unwatch(message.id, targetMM);
+    this.getService(message).RP.unwatch(message.id, targetMM);
   },
 
   _request: function DOMIdentity__request(message) {
-    IdentityService.RP.request(message.id, message);
+    this.getService(message).RP.request(message.id, message);
   },
 
   _logout: function DOMIdentity__logout(message) {
-    IdentityService.RP.logout(message.id, message.origin, message);
+    log("logout " + message + "\n");
+    this.getService(message).RP.logout(message.id, message.origin, message);
   },
 
   _childProcessShutdown: function DOMIdentity__childProcessShutdown(targetMM) {
-    IdentityService.RP.childProcessShutdown(targetMM);
+    this.getContextForMM(targetMM).RP.childProcessShutdown(targetMM);
+    this.deleteContextForMM(targetMM);
+
+    let options = makeMessageObject({messageManager: targetMM, id: null, origin: null});
+    Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null);
   },
 
   _beginProvisioning: function DOMIdentity__beginProvisioning(message, targetMM) {
     let context = new IDPProvisioningContext(message.id, message.origin,
                                              targetMM);
-    IdentityService.IDP.beginProvisioning(context);
+    this.getService(message).IDP.beginProvisioning(context);
   },
 
   _genKeyPair: function DOMIdentity__genKeyPair(message) {
-    IdentityService.IDP.genKeyPair(message.id);
+    this.getService(message).IDP.genKeyPair(message.id);
   },
 
   _registerCertificate: function DOMIdentity__registerCertificate(message) {
-    IdentityService.IDP.registerCertificate(message.id, message.cert);
+    this.getService(message).IDP.registerCertificate(message.id, message.cert);
   },
 
   _provisioningFailure: function DOMIdentity__provisioningFailure(message) {
-    IdentityService.IDP.raiseProvisioningFailure(message.id, message.reason);
+    this.getService(message).IDP.raiseProvisioningFailure(message.id, message.reason);
   },
 
   _beginAuthentication: function DOMIdentity__beginAuthentication(message, targetMM) {
     let context = new IDPAuthenticationContext(message.id, message.origin,
                                                targetMM);
-    IdentityService.IDP.beginAuthentication(context);
+    this.getService(message).IDP.beginAuthentication(context);
   },
 
   _completeAuthentication: function DOMIdentity__completeAuthentication(message) {
-    IdentityService.IDP.completeAuthentication(message.id);
+    this.getService(message).IDP.completeAuthentication(message.id);
   },
 
   _authenticationFailure: function DOMIdentity__authenticationFailure(message) {
-    IdentityService.IDP.cancelAuthentication(message.id);
+    this.getService(message).IDP.cancelAuthentication(message.id);
   }
 };
 
 // Object is initialized by nsIDService.js
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -71,36 +71,50 @@ nsDOMIdentity.prototype = {
   },
 
   /**
    * Relying Party (RP) APIs
    */
 
   watch: function nsDOMIdentity_watch(aOptions) {
     if (this._rpWatcher) {
+      // For the initial release of Firefox Accounts, we support callers who
+      // invoke watch() either for Firefox Accounts, or Persona, but not both.
+      // In the future, we may wish to support the dual invocation (say, for
+      // packaged apps so they can sign users in who reject the app's request
+      // to sign in with their Firefox Accounts identity).
       throw new Error("navigator.id.watch was already called");
     }
 
     if (!aOptions || typeof(aOptions) !== "object") {
       throw new Error("options argument to watch is required");
     }
 
-    // Check for required callbacks
-    let requiredCallbacks = ["onlogin", "onlogout"];
-    for (let cbName of requiredCallbacks) {
-      if ((!(cbName in aOptions))
-          || typeof(aOptions[cbName]) !== "function") {
-           throw new Error(cbName + " callback is required.");
-         }
+    // The relying party (RP) provides callbacks on watch().
+    //
+    // In the future, BrowserID will probably only require an onlogin()
+    // callback [1], lifting the requirement that BrowserID handle logged-in
+    // state management for RPs.  See
+    // https://github.com/mozilla/id-specs/blob/greenfield/browserid/api-rp.md
+    //
+    // However, Firefox Accounts will almost certainly require RPs to provide
+    // onlogout(), onready(), and possibly an onerror() callback.
+    // XXX Bug 945278
+    //
+    // To accomodate the more and less lenient uses of the API, we will simply
+    // be strict about checking for onlogin here.
+    if (typeof(aOptions["onlogin"]) != "function") {
+      throw new Error("onlogin() callback is required.");
     }
 
-    // Optional callback "onready"
-    if (aOptions["onready"]
-        && typeof(aOptions['onready']) !== "function") {
-      throw new Error("onready must be a function");
+    // Optional callbacks
+    for (let cb of ["onready", "onlogout"]) {
+      if (aOptions[cb] && typeof(aOptions[cb]) != "function") {
+        throw new Error(cb + " must be a function");
+      }
     }
 
     let message = this.DOMIdentityMessage(aOptions);
 
     // loggedInUser vs loggedInEmail
     // https://developer.mozilla.org/en-US/docs/DOM/navigator.id.watch
     // This parameter, loggedInUser, was renamed from loggedInEmail in early
     // September, 2012. Both names will continue to work for the time being,
@@ -374,16 +388,17 @@ nsDOMIdentity.prototype = {
   // Private.
   _init: function nsDOMIdentity__init(aWindow) {
 
     this._initializeState();
 
     // Store window and origin URI.
     this._window = aWindow;
     this._origin = aWindow.document.nodePrincipal.origin;
+    this._appStatus = aWindow.document.nodePrincipal.appStatus;
 
     // Setup identifiers for current window.
     let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);
 
     // We need to inherit the id from the internalIdentity service.
     // See comments below in that service's init.
     this._id = this._identityInternal._id;
@@ -531,16 +546,21 @@ nsDOMIdentity.prototype = {
     objectCopy(aOptions, message);
 
     // outer window id
     message.id = this._id;
 
     // window origin
     message.origin = this._origin;
 
+    // On b2g, an app's status can be NOT_INSTALLED, INSTALLED, PRIVILEGED, or
+    // CERTIFIED.  Compare the appStatus value to the constants enumerated in
+    // Ci.nsIPrincipal.APP_STATUS_*.
+    message.appStatus = this._appStatus;
+
     return message;
   },
 
   uninit: function DOMIdentity_uninit() {
     this._log("nsDOMIdentity uninit()");
     this._identityInternal._mm.sendAsyncMessage(
       "Identity:RP:Unwatch",
       { id: this._id }
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -72,13 +72,12 @@ interface nsIDOMCSSStyleRule;
 interface nsIDOMCSSStyleRuleCollection;
 interface nsIDOMHTMLTableCaptionElement;
 
 // Range
 interface nsIDOMRange;
 
 // Crypto
 interface nsIDOMCrypto;
-interface nsIDOMPkcs11;
 
 // Used font face (for inspector)
 interface nsIDOMFontFace;
 interface nsIDOMFontFaceList;
--- a/dom/interfaces/base/moz.build
+++ b/dom/interfaces/base/moz.build
@@ -18,17 +18,16 @@ XPIDL_SOURCES += [
     'nsIDOMConstructor.idl',
     'nsIDOMGlobalObjectConstructor.idl',
     'nsIDOMGlobalPropertyInitializer.idl',
     'nsIDOMHistory.idl',
     'nsIDOMJSWindow.idl',
     'nsIDOMLocation.idl',
     'nsIDOMModalContentWindow.idl',
     'nsIDOMNavigator.idl',
-    'nsIDOMPkcs11.idl',
     'nsIDOMScreen.idl',
     'nsIDOMWindow.idl',
     'nsIDOMWindowCollection.idl',
     'nsIDOMWindowUtils.idl',
     'nsIFocusManager.idl',
     'nsIFrameRequestCallback.idl',
     'nsIIdleObserver.idl',
     'nsIQueryContentEventResult.idl',
deleted file mode 100644
--- a/dom/interfaces/base/nsIDOMPkcs11.idl
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "domstubs.idl"
-
-[scriptable, uuid(9fd42950-25e7-11d4-8a7d-006008c844c3)]
-interface nsIDOMPkcs11 : nsISupports
-{
-  long                      deletemodule(in DOMString moduleName);
-  long                      addmodule(in DOMString moduleName,
-                                      in DOMString libraryFullPath,
-                                      in long cryptoMechanismFlags,
-                                      in long cipherFlags);
-};
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -19,17 +19,17 @@ interface nsIVariant;
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(f2d1d383-f7b5-46f8-aadf-b69a0ebfb16f)]
+[scriptable, uuid(97b6784b-ab12-4f79-8422-d7868a4cc7dc)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -369,17 +369,16 @@ interface nsIDOMWindow : nsISupports
   [noscript] readonly attribute nsIDOMWindow            content;
 
   /* [replaceable] prompter */
   [noscript] readonly attribute nsIPrompt               prompter;
 
   readonly attribute boolean                            closed;
 
   readonly attribute nsIDOMCrypto                       crypto;
-  readonly attribute nsIDOMPkcs11                       pkcs11;
 
   // Note: this is [ChromeOnly] scriptable via WebIDL.
   [noscript] readonly attribute nsIControllers          controllers;
 
   readonly attribute float                              mozInnerScreenX;
   readonly attribute float                              mozInnerScreenY;
   readonly attribute float                              devicePixelRatio;
 
--- a/dom/ipc/AppProcessChecker.cpp
+++ b/dom/ipc/AppProcessChecker.cpp
@@ -184,22 +184,26 @@ AssertAppPrincipal(PContentParent* aActo
   return false;
 }
 
 already_AddRefed<nsIPrincipal>
 GetAppPrincipal(uint32_t aAppId)
 {
   nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
 
-  nsString manifestURL;
-  nsresult rv = appsService->GetManifestURLByLocalId(aAppId, manifestURL);
+  nsCOMPtr<mozIApplication> app;
+  nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  nsString origin;
+  rv = app->GetOrigin(origin);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsCOMPtr<nsIURI> uri;
-  NS_NewURI(getter_AddRefs(uri), manifestURL);
+  NS_NewURI(getter_AddRefs(uri), origin);
 
   nsCOMPtr<nsIScriptSecurityManager> secMan =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
 
   nsCOMPtr<nsIPrincipal> appPrincipal;
   rv = secMan->GetAppCodebasePrincipal(uri, aAppId, false,
                                        getter_AddRefs(appPrincipal));
   NS_ENSURE_SUCCESS(rv, nullptr);
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -159,35 +159,37 @@ mozNfc.prototype = {
   _window: null,
   _wrap: function _wrap(obj) {
     return ObjectWrapper.wrap(obj, this._window);
   },
 
   init: function init(aWindow) {
     debug("mozNfc init called");
     this._window = aWindow;
-    let origin = this._window.document.nodePrincipal.origin;
-    // Only System Process should listen on 'nfc-p2p-user-accept' event
-    if (origin !== 'app://system.gaiamobile.org') {
-      return;
-    }
-    let self = this;
-    this._window.addEventListener("nfc-p2p-user-accept", function (event) {
-      let appID = appsService.getAppLocalIdByManifestURL(event.detail.manifestUrl);
-      // Notify Chrome process of User's acknowledgement
-      self._nfcContentHelper.notifyUserAcceptedP2P(self._window, appID);
-    });
   },
 
+  // Only System Process can call the following interfaces
+  // 'checkP2PRegistration' , 'notifyUserAcceptedP2P' , 'notifySendFileStatus'
   checkP2PRegistration: function checkP2PRegistration(manifestUrl) {
     // Get the AppID and pass it to ContentHelper
     let appID = appsService.getAppLocalIdByManifestURL(manifestUrl);
     return this._nfcContentHelper.checkP2PRegistration(this._window, appID);
   },
 
+  notifyUserAcceptedP2P: function notifyUserAcceptedP2P(manifestUrl) {
+    let appID = appsService.getAppLocalIdByManifestURL(manifestUrl);
+    // Notify chrome process of user's acknowledgement
+    this._nfcContentHelper.notifyUserAcceptedP2P(this._window, appID);
+  },
+
+  notifySendFileStatus: function notifySendFileStatus(status, requestId) {
+    this._nfcContentHelper.notifySendFileStatus(this._window,
+                                                status, requestId);
+  },
+
   getNFCTag: function getNFCTag(sessionToken) {
     let obj = new MozNFCTag();
     let nfcTag = this._window.MozNFCTag._create(this._window, obj);
     if (nfcTag) {
       obj.initialize(this._window, sessionToken);
       return nfcTag;
     } else {
       debug("Error: Unable to create NFCTag");
--- a/dom/system/gonk/Nfc.js
+++ b/dom/system/gonk/Nfc.js
@@ -54,17 +54,18 @@ const NFC_IPC_MSG_NAMES = [
   "NFC:Close",
   "NFC:SendFile"
 ];
 
 const NFC_IPC_PEER_MSG_NAMES = [
   "NFC:RegisterPeerTarget",
   "NFC:UnregisterPeerTarget",
   "NFC:CheckP2PRegistration",
-  "NFC:NotifyUserAcceptedP2P"
+  "NFC:NotifyUserAcceptedP2P",
+  "NFC:NotifySendFileStatus"
 ];
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
@@ -304,20 +305,22 @@ XPCOMUtils.defineLazyGetter(this, "gMess
         }
       } else if (NFC_IPC_PEER_MSG_NAMES.indexOf(msg.name) != -1) {
         if (!msg.target.assertPermission("nfc-write")) {
           debug("Nfc Peer message  " + msg.name +
                 " from a content process with no 'nfc-write' privileges.");
           return null;
         }
 
-        // Add extra permission check for below IPC Peer events:
-        // 'NFC:CheckP2PRegistration' , 'NFC:NotifyUserAcceptedP2P'
+        // Add extra permission check for below events:
+        // 'NFC:CheckP2PRegistration' , 'NFC:NotifyUserAcceptedP2P',
+        // 'NFC:NotifySendFileStatus'
         if ((msg.name == "NFC:CheckP2PRegistration") ||
-            (msg.name == "NFC:NotifyUserAcceptedP2P")) {
+            (msg.name == "NFC:NotifyUserAcceptedP2P") ||
+            (msg.name == "NFC:NotifySendFileStatus")) {
           // ONLY privileged Content can send these events
           if (!msg.target.assertPermission("nfc-manager")) {
             debug("NFC message " + message.name +
                   " from a content process with no 'nfc-manager' privileges.");
             return null;
           }
         }
       } else {
@@ -351,16 +354,21 @@ XPCOMUtils.defineLazyGetter(this, "gMess
             status: status,
             requestId: msg.json.requestId
           });
           break;
         case "NFC:NotifyUserAcceptedP2P":
           // Notify the 'NFC_PEER_EVENT_READY' since user has acknowledged
           this.notifyPeerEvent(msg.json.appId, NFC.NFC_PEER_EVENT_READY);
           break;
+        case "NFC:NotifySendFileStatus":
+          // Upon receiving the status of sendFile operation, send the response
+          // to appropriate content process.
+          this.sendNfcResponseMessage(msg.name + "Response", msg.json);
+          break;
       }
       return null;
     },
 
     /**
      * nsIObserver interface methods.
      */
 
--- a/dom/system/gonk/NfcContentHelper.js
+++ b/dom/system/gonk/NfcContentHelper.js
@@ -47,17 +47,19 @@ const NFCCONTENTHELPER_CID =
 const NFC_IPC_MSG_NAMES = [
   "NFC:ReadNDEFResponse",
   "NFC:WriteNDEFResponse",
   "NFC:GetDetailsNDEFResponse",
   "NFC:MakeReadOnlyNDEFResponse",
   "NFC:ConnectResponse",
   "NFC:CloseResponse",
   "NFC:CheckP2PRegistrationResponse",
-  "NFC:PeerEvent"
+  "NFC:PeerEvent",
+  "NFC:NotifySendFileStatusResponse",
+  "NFC:SendFileResponse"
 ];
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 function NfcContentHelper() {
   this.initDOMRequestHelper(/* aWindow */ null, NFC_IPC_MSG_NAMES);
@@ -230,16 +232,29 @@ NfcContentHelper.prototype = {
     cpmm.sendAsyncMessage("NFC:SendFile", {
       requestId: requestId,
       sessionToken: sessionToken,
       blob: data.blob
     });
     return request;
   },
 
+  notifySendFileStatus: function notifySendFileStatus(window, status,
+                                                      requestId) {
+    if (window == null) {
+      throw Components.Exception("Can't get window object",
+                                  Cr.NS_ERROR_UNEXPECTED);
+    }
+
+    cpmm.sendAsyncMessage("NFC:NotifySendFileStatus", {
+      status: status,
+      requestId: requestId
+    });
+  },
+
   registerTargetForPeerEvent: function registerTargetForPeerEvent(window,
                                                   appId, event, callback) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     this.peerEventsCallbackMap[event] = callback;
     cpmm.sendAsyncMessage("NFC:RegisterPeerTarget", {
@@ -336,16 +351,17 @@ NfcContentHelper.prototype = {
         this.handleReadNDEFResponse(message.json);
         break;
       case "NFC:ConnectResponse": // Fall through.
       case "NFC:CloseResponse":
       case "NFC:WriteNDEFResponse":
       case "NFC:MakeReadOnlyNDEFResponse":
       case "NFC:GetDetailsNDEFResponse":
       case "NFC:CheckP2PRegistrationResponse":
+      case "NFC:NotifySendFileStatusResponse":
         this.handleResponse(message.json);
         break;
       case "NFC:PeerEvent":
         let callback = this.peerEventsCallbackMap[message.json.event];
         if (callback) {
           callback.peerNotification(message.json.event,
                                     message.json.sessionToken);
         } else {
--- a/dom/system/gonk/nsINfcContentHelper.idl
+++ b/dom/system/gonk/nsINfcContentHelper.idl
@@ -19,17 +19,17 @@ interface nsINfcPeerCallback : nsISuppor
    *
    * @param sessionToken
    *        SessionToken received from Chrome process
    */
    void peerNotification(in unsigned long event,
                          in DOMString sessionToken);
 };
 
-[scriptable, uuid(91c2760a-f41c-4174-ad68-614840d4e201)]
+[scriptable, uuid(70cac000-7e3c-11e3-baa7-0800200c9a66)]
 interface nsINfcContentHelper : nsISupports
 {
   const long NFC_EVENT_PEER_READY = 0x01;
   const long NFC_EVENT_PEER_LOST  = 0x02;
 
   void setSessionToken(in DOMString sessionToken);
 
   nsIDOMDOMRequest getDetailsNDEF(in nsIDOMWindow window, in DOMString sessionToken);
@@ -113,9 +113,26 @@ interface nsINfcContentHelper : nsISuppo
   *
   * @param window
   *        Current window
   *
   * @param appId
   *        Application ID that is capable of handling NFC_EVENT_PEER_READY event
   */
   void notifyUserAcceptedP2P(in nsIDOMWindow window, in unsigned long appId);
+
+ /**
+  * Notify the status of sendFile operation to Chrome process
+  *
+  * @param window
+  *        Current window
+  *
+  * @param status
+  *        Status of sendFile operation
+  *        (GECKO_NFC_ERROR_SUCCESS, GECKO_NFC_ERROR_GENERIC_FAILURE)
+  *
+  * @param requestId
+  *        Request ID of SendFile DOM Request
+  */
+  void notifySendFileStatus(in nsIDOMWindow window,
+                            in octet status,
+                            in DOMString requestId);
 };
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -213,18 +213,17 @@ Telephony::ProvidedOrDefaultServiceId(co
   }
 }
 
 bool
 Telephony::HasDialingCall()
 {
   for (uint32_t i = 0; i < mCalls.Length(); i++) {
     const nsRefPtr<TelephonyCall>& call = mCalls[i];
-    if (call->IsOutgoing() &&
-        call->CallState() > nsITelephonyProvider::CALL_STATE_UNKNOWN &&
+    if (call->CallState() > nsITelephonyProvider::CALL_STATE_UNKNOWN &&
         call->CallState() < nsITelephonyProvider::CALL_STATE_CONNECTED) {
       return true;
     }
   }
 
   return false;
 }
 
--- a/dom/telephony/TelephonyCall.cpp
+++ b/dom/telephony/TelephonyCall.cpp
@@ -41,18 +41,17 @@ TelephonyCall::Create(Telephony* aTeleph
 
   return call.forget();
 }
 
 TelephonyCall::TelephonyCall(nsPIDOMWindow* aOwner)
   : nsDOMEventTargetHelper(aOwner),
     mCallIndex(kOutgoingPlaceholderCallIndex),
     mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN),
-    mLive(false),
-    mOutgoing(false)
+    mLive(false)
 {
 }
 
 TelephonyCall::~TelephonyCall()
 {
 }
 
 JSObject*
@@ -100,20 +99,16 @@ TelephonyCall::ChangeStateInternal(uint1
       break;
     default:
       NS_NOTREACHED("Unknown state!");
   }
 
   mState = stateString;
   mCallState = aCallState;
 
-  if (aCallState == nsITelephonyProvider::CALL_STATE_DIALING) {
-    mOutgoing = true;
-  }
-
   if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
     NS_ASSERTION(mLive, "Should be live!");
     if (mGroup) {
       mGroup->RemoveCall(this);
     } else {
       mTelephony->RemoveCall(this);
     }
     mLive = false;
--- a/dom/telephony/TelephonyCall.h
+++ b/dom/telephony/TelephonyCall.h
@@ -26,17 +26,16 @@ class TelephonyCall MOZ_FINAL : public n
   nsString mSecondNumber;
   nsString mState;
   bool mEmergency;
   nsRefPtr<DOMError> mError;
 
   uint32_t mCallIndex;
   uint16_t mCallState;
   bool mLive;
-  bool mOutgoing;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TelephonyCall,
                                            nsDOMEventTargetHelper)
 
   friend class Telephony;
@@ -152,22 +151,16 @@ public:
   }
 
   void
   UpdateSecondNumber(const nsAString& aNumber)
   {
     mSecondNumber = aNumber;
   }
 
-  bool
-  IsOutgoing() const
-  {
-    return mOutgoing;
-  }
-
   void
   NotifyError(const nsAString& aError);
 
   void
   ChangeGroup(TelephonyCallGroup* aGroup);
 
 private:
   TelephonyCall(nsPIDOMWindow* aOwner);
--- a/dom/webidl/MozNfc.webidl
+++ b/dom/webidl/MozNfc.webidl
@@ -8,21 +8,28 @@
  Func="Navigator::HasNfcManagerSupport"]
 interface MozNfcManager {
    /**
     * API to check if the given application's manifest
     * URL is registered with the Chrome Process or not.
     *
     * Returns success if given manifestUrl is registered for 'onpeerready',
     * otherwise error
-    *
-    * Users of this API should have valid permissions 'nfc-manager'
-    * and 'nfc-write'
     */
    DOMRequest checkP2PRegistration(DOMString manifestUrl);
+
+   /**
+    * Notify that user has accepted to share nfc message on P2P UI
+    */
+   void notifyUserAcceptedP2P(DOMString manifestUrl);
+
+   /**
+    * Notify the status of sendFile operation
+    */
+   void notifySendFileStatus(octet status, DOMString requestId);
 };
 
 [JSImplementation="@mozilla.org/navigatorNfc;1",
  NavigatorProperty="mozNfc",
  Func="Navigator::HasNfcSupport"]
 interface MozNfc : EventTarget {
    MozNFCTag getNFCTag(DOMString sessionId);
    MozNFCPeer getNFCPeer(DOMString sessionId);
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -891,16 +891,22 @@ interface WebGLExtensionDrawBuffers {
 };
 
 [NoInterfaceObject]
 interface WebGLExtensionTextureFloatLinear
 {
 };
 
 [NoInterfaceObject]
+interface WebGLExtensionTextureHalfFloat
+{
+    const GLenum HALF_FLOAT_OES = 0x8D61;
+};
+
+[NoInterfaceObject]
 interface WebGLExtensionVertexArray {
     const GLenum VERTEX_ARRAY_BINDING_OES = 0x85B5;
 
     WebGLVertexArray? createVertexArrayOES();
     void deleteVertexArrayOES(WebGLVertexArray? arrayObject);
     [WebGLHandlesContextLoss] GLboolean isVertexArrayOES(WebGLVertexArray? arrayObject);
     void bindVertexArrayOES(WebGLVertexArray? arrayObject);
 };
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -13,21 +13,20 @@
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
  * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
  */
 
 interface ApplicationCache;
 interface MozFrameRequestCallback;
 interface nsIDOMCrypto;
-interface Pkcs11;
 typedef any Transferable;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
-[Global]
+[Global, NeedNewResolve]
 /*sealed*/ interface Window : EventTarget {
   // the current browsing context
   [Unforgeable, Throws,
    CrossOriginReadable] readonly attribute WindowProxy window;
   [Replaceable, Throws,
    CrossOriginReadable] readonly attribute WindowProxy self;
   //[Unforgeable] readonly attribute Document? document;
   [Throws] attribute DOMString name; 
@@ -252,18 +251,16 @@ partial interface Window {
    */
   void                      scrollByPages(long numPages);
 
   /**
    * Method for sizing this window to the content in the window.
    */
   [Throws] void             sizeToContent();
 
-  readonly attribute Pkcs11?                      pkcs11;
-
   // XXX Shouldn't this be in nsIDOMChromeWindow?
   [ChromeOnly, Replaceable, Throws] readonly attribute MozControllers controllers;
 
   [Throws] readonly attribute float               mozInnerScreenX;
   [Throws] readonly attribute float               mozInnerScreenY;
   [Throws] readonly attribute float               devicePixelRatio;
 
   /* The maximum offset that the window can be scrolled to
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -1065,17 +1065,17 @@ Proxy::HandleEvent(nsIDOMEvent* aEvent)
       mMainThreadSeenLoadStart = true;
     }
     else if (mMainThreadSeenLoadStart &&
              type.EqualsASCII(sEventStrings[STRING_loadend])) {
       mMainThreadSeenLoadStart = false;
 
       nsRefPtr<LoadStartDetectionRunnable> runnable =
         new LoadStartDetectionRunnable(this, mXMLHttpRequestPrivate);
-      if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+      if (!runnable->RegisterAndDispatch()) {
         NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
       }
     }
   }
 
   return NS_OK;
 }
 
--- a/dom/workers/test/promise_worker.js
+++ b/dom/workers/test/promise_worker.js
@@ -26,30 +26,67 @@ function promiseResolve() {
     is(what, 42, "ResolveCb received 42");
     runTest();
   }, function() {
     ok(false, "Then - rejectCb has been called");
     runTest();
   });
 }
 
+function promiseResolveNoArg() {
+  var promise = new Promise(function(resolve, reject) {
+    ok(resolve, "Promise.resolve exists");
+    ok(reject, "Promise.reject exists");
+
+    resolve();
+  }).then(function(what) {
+    ok(true, "Then - resolveCb has been called");
+    is(what, undefined, "ResolveCb received undefined");
+    runTest();
+  }, function() {
+    ok(false, "Then - rejectCb has been called");
+    runTest();
+  });
+}
+
+function promiseRejectNoHandler() {
+  // This test only checks that the code that reports unhandled errors in the
+  // Promises implementation does not crash or leak.
+  var promise = new Promise(function(res, rej) {
+    noSuchMethod();
+  });
+  runTest();
+}
 
 function promiseReject() {
   var promise = new Promise(function(resolve, reject) {
     reject(42);
   }).then(function(what) {
     ok(false, "Then - resolveCb has been called");
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
     is(what, 42, "RejectCb received 42");
     runTest();
   });
 }
 
+function promiseRejectNoArg() {
+  var promise = new Promise(function(resolve, reject) {
+    reject();
+  }).then(function(what) {
+    ok(false, "Then - resolveCb has been called");
+    runTest();
+  }, function(what) {
+    ok(true, "Then - rejectCb has been called");
+    is(what, undefined, "RejectCb received undefined");
+    runTest();
+  });
+}
+
 function promiseException() {
   var promise = new Promise(function(resolve, reject) {
     throw 42;
   }).then(function(what) {
     ok(false, "Then - resolveCb has been called");
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
@@ -255,16 +292,76 @@ function promiseThenCatchOrderingReject(
     });
     setTimeout(function() {
       is(global, 2, "Many steps... should return 2");
       runTest();
     }, 0);
   });
 }
 
+function promiseThenNoArg() {
+  var promise = new Promise(function(resolve, reject) {
+    resolve(42);
+  });
+
+  var clone = promise.then();
+  isnot(promise, clone, "These 2 promise objs are different");
+  promise.then(function(v) {
+    clone.then(function(cv) {
+      is(v, cv, "Both resolve to the same value");
+      runTest();
+    });
+  });
+}
+
+function promiseThenUndefinedResolveFunction() {
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
+  });
+
+  try {
+    promise.then(undefined, function(v) {
+      is(v, 42, "Promise rejected with 42");
+      runTest();
+    });
+  } catch (e) {
+    ok(false, "then should not throw on undefined resolve function");
+  }
+}
+
+function promiseThenNullResolveFunction() {
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
+  });
+
+  try {
+    promise.then(null, function(v) {
+      is(v, 42, "Promise rejected with 42");
+      runTest();
+    });
+  } catch (e) {
+    ok(false, "then should not throw on null resolve function");
+  }
+}
+
+function promiseCatchNoArg() {
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
+  });
+
+  var clone = promise.catch();
+  isnot(promise, clone, "These 2 promise objs are different");
+  promise.catch(function(v) {
+    clone.catch(function(cv) {
+      is(v, cv, "Both reject to the same value");
+      runTest();
+    });
+  });
+}
+
 function promiseNestedPromise() {
   new Promise(function(resolve, reject) {
     resolve(new Promise(function(resolve, reject) {
       ok(true, "Nested promise is executed");
       resolve(42);
     }));
   }).then(function(value) {
     is(value, 42, "Nested promise is executed and then == 42");
@@ -352,16 +449,227 @@ function promiseRejectNoHandler() {
   // This test only checks that the code that reports unhandled errors in the
   // Promises implementation does not crash or leak.
   var promise = new Promise(function(res, rej) {
     noSuchMethod();
   });
   runTest();
 }
 
+function promiseUtilitiesDefined() {
+  ok(Promise.all, "Promise.all must be defined when Promise is enabled.");
+  ok(Promise.cast, "Promise.cast must be defined when Promise is enabled.");
+  ok(Promise.race, "Promise.race must be defined when Promise is enabled.");
+  runTest();
+}
+
+function promiseAllArray() {
+  var p = Promise.all([1, new Date(), Promise.resolve("firefox")]);
+  ok(p instanceof Promise, "Return value of Promise.all should be a Promise.");
+  p.then(function(values) {
+    ok(Array.isArray(values), "Resolved value should be an array.");
+    is(values.length, 3, "Resolved array length should match iterable's length.");
+    is(values[0], 1, "Array values should match.");
+    ok(values[1] instanceof Date, "Array values should match.");
+    is(values[2], "firefox", "Array values should match.");
+    runTest();
+  }, function() {
+    ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
+    runTest();
+  });
+}
+
+function promiseAllWaitsForAllPromises() {
+  var arr = [
+    new Promise(function(resolve) {
+      setTimeout(resolve.bind(undefined, 1), 50);
+    }),
+    new Promise(function(resolve) {
+      setTimeout(resolve.bind(undefined, 2), 10);
+    }),
+    new Promise(function(resolve) {
+      setTimeout(resolve.bind(undefined, new Promise(function(resolve2) {
+        resolve2(3);
+      })), 10);
+    }),
+    new Promise(function(resolve) {
+      setTimeout(resolve.bind(undefined, 4), 20);
+    })
+  ];
+
+  var p = Promise.all(arr);
+  p.then(function(values) {
+    ok(Array.isArray(values), "Resolved value should be an array.");
+    is(values.length, 4, "Resolved array length should match iterable's length.");
+    is(values[0], 1, "Array values should match.");
+    is(values[1], 2, "Array values should match.");
+    is(values[2], 3, "Array values should match.");
+    is(values[3], 4, "Array values should match.");
+    runTest();
+  }, function() {
+    ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
+    runTest();
+  });
+}
+
+function promiseAllRejectFails() {
+  var arr = [
+    new Promise(function(resolve) {
+      setTimeout(resolve.bind(undefined, 1), 50);
+    }),
+    new Promise(function(resolve, reject) {
+      setTimeout(reject.bind(undefined, 2), 10);
+    }),
+    new Promise(function(resolve) {
+      setTimeout(resolve.bind(undefined, 3), 10);
+    }),
+    new Promise(function(resolve) {
+      setTimeout(resolve.bind(undefined, 4), 20);
+    })
+  ];
+
+  var p = Promise.all(arr);
+  p.then(function(values) {
+    ok(false, "Promise.all shouldn't resolve when iterable has rejected Promises.");
+    runTest();
+  }, function(e) {
+    ok(true, "Promise.all should reject when iterable has rejected Promises.");
+    is(e, 2, "Rejection value should match.");
+    runTest();
+  });
+}
+
+function promiseCastNoArg() {
+  var p = Promise.cast();
+  ok(p instanceof Promise, "Should cast to a Promise.");
+  p.then(function(v) {
+    is(v, undefined, "Resolved value should be undefined.");
+    runTest();
+  });
+}
+
+function promiseCastInteger() {
+  var p = Promise.cast(5);
+  ok(p instanceof Promise, "Should cast to a Promise.");
+  p.then(function(v) {
+    is(v, 5, "Resolved value should match original.");
+    runTest();
+  });
+}
+
+function promiseCastArray() {
+  var p = Promise.cast([1,2,3]);
+  ok(p instanceof Promise, "Should cast to a Promise.");
+  p.then(runTest);
+}
+
+// We don't support thenables, but if we did
+// they'd have to be cast to a trusted Promise.
+function promiseCastThenable() {
+  var p = Promise.cast({ then: function(onFulfill, onReject) { onFulfill(2); } });
+  ok(p instanceof Promise, "Should cast to a Promise.");
+  p.then(function(v) {
+    is(v, 2, "Should resolve to 2.");
+    runTest();
+  }, function(e) {
+    ok(false, "promiseCastThenable should've resolved");
+    runTest();
+  });
+}
+
+function promiseCastPromise() {
+  var original = Promise.resolve(true);
+  var cast = Promise.cast(original);
+
+  ok(cast instanceof Promise, "Should cast to a Promise.");
+  is(cast, original, "Should return original Promise.");
+  runTest();
+}
+
+function promiseRaceEmpty() {
+  var p = Promise.race([]);
+  ok(p instanceof Promise, "Should return a Promise.");
+  // An empty race never resolves!
+  runTest();
+}
+
+function promiseRaceValuesArray() {
+  var p = Promise.race([true, new Date(), 3]);
+  ok(p instanceof Promise, "Should return a Promise.");
+  p.then(function(winner) {
+    is(winner, true, "First value should win.");
+    runTest();
+  }, function(err) {
+    ok(false, "Should not fail " + err + ".");
+    runTest();
+  });
+}
+
+function promiseRacePromiseArray() {
+  function timeoutPromise(n) {
+    return new Promise(function(resolve) {
+      setTimeout(function() {
+        resolve(n);
+      }, n);
+    });
+  }
+
+  var arr = [
+    timeoutPromise(50),
+    timeoutPromise(20),
+    timeoutPromise(30),
+    timeoutPromise(100)
+  ];
+
+  var p = Promise.race(arr);
+  p.then(function(winner) {
+    is(winner, 20, "Fastest timeout should win.");
+    runTest();
+  });
+}
+
+function promiseRaceReject() {
+  var p = Promise.race([
+    Promise.reject(new Error("Fail bad!")),
+    new Promise(function(resolve) {
+      setTimeout(resolve, 0);
+    })
+  ]);
+
+  p.then(function() {
+    ok(false, "Should not resolve when winning Promise rejected.");
+    runTest();
+  }, function(e) {
+    ok(true, "Should be rejected");
+    ok(e instanceof Error, "Should reject with Error.");
+    ok(e.message == "Fail bad!", "Message should match.");
+    runTest();
+  });
+}
+
+function promiseRaceThrow() {
+  var p = Promise.race([
+    new Promise(function(resolve) {
+      nonExistent();
+    }),
+    new Promise(function(resolve) {
+      setTimeout(resolve, 0);
+    })
+  ]);
+
+  p.then(function() {
+    ok(false, "Should not resolve when winning Promise had an error.");
+    runTest();
+  }, function(e) {
+    ok(true, "Should be rejected");
+    ok(e instanceof ReferenceError, "Should reject with ReferenceError for function nonExistent().");
+    runTest();
+  });
+}
+
 var tests = [
     promiseResolve,
     promiseReject,
     promiseException,
     promiseAsync,
     promiseDoubleThen,
     promiseThenException,
     promiseThenCatchThen,
@@ -372,17 +680,42 @@ var tests = [
     promiseThenCatchOrderingReject,
     promiseNestedPromise,
     promiseNestedNestedPromise,
     promiseWrongNestedPromise,
     promiseLoop,
     promiseStaticReject,
     promiseStaticResolve,
     promiseResolveNestedPromise,
+    promiseResolveNoArg,
+    promiseRejectNoArg,
+
+    promiseThenNoArg,
+    promiseThenUndefinedResolveFunction,
+    promiseThenNullResolveFunction,
+    promiseCatchNoArg,
     promiseRejectNoHandler,
+
+    promiseUtilitiesDefined,
+
+    promiseAllArray,
+    promiseAllWaitsForAllPromises,
+    promiseAllRejectFails,
+
+    promiseCastNoArg,
+    promiseCastInteger,
+    promiseCastArray,
+    promiseCastThenable,
+    promiseCastPromise,
+
+    promiseRaceEmpty,
+    promiseRaceValuesArray,
+    promiseRacePromiseArray,
+    promiseRaceReject,
+    promiseRaceThrow,
 ];
 
 function runTest() {
   if (!tests.length) {
     postMessage({ type: 'finish' });
     return;
   }
 
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -78,16 +78,18 @@ static const char *sExtensionNames[] = {
     "GL_APPLE_client_storage",
     "GL_ARB_texture_non_power_of_two",
     "GL_ARB_pixel_buffer_object",
     "GL_ARB_ES2_compatibility",
     "GL_ARB_ES3_compatibility",
     "GL_OES_texture_float",
     "GL_OES_texture_float_linear",
     "GL_ARB_texture_float",
+    "GL_OES_texture_half_float",
+    "GL_NV_half_float",
     "GL_EXT_unpack_subimage",
     "GL_OES_standard_derivatives",
     "GL_EXT_texture_filter_anisotropic",
     "GL_EXT_texture_compression_s3tc",
     "GL_EXT_texture_compression_dxt1",
     "GL_ANGLE_texture_compression_dxt3",
     "GL_ANGLE_texture_compression_dxt5",
     "GL_AMD_compressed_ATC_texture",
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -101,16 +101,17 @@ MOZ_BEGIN_ENUM_CLASS(GLFeature)
     occlusion_query2,
     packed_depth_stencil,
     query_objects,
     robustness,
     sRGB,
     standard_derivatives,
     texture_float,
     texture_float_linear,
+    texture_half_float,
     texture_non_power_of_two,
     transform_feedback,
     vertex_array_object,
     EnumMax
 MOZ_END_ENUM_CLASS(GLFeature)
 
 MOZ_BEGIN_ENUM_CLASS(ContextProfile, uint8_t)
     Unknown = 0,
@@ -353,16 +354,18 @@ public:
         APPLE_client_storage,
         ARB_texture_non_power_of_two,
         ARB_pixel_buffer_object,
         ARB_ES2_compatibility,
         ARB_ES3_compatibility,
         OES_texture_float,
         OES_texture_float_linear,
         ARB_texture_float,
+        OES_texture_half_float,
+        NV_half_float,
         EXT_unpack_subimage,
         OES_standard_derivatives,
         EXT_texture_filter_anisotropic,
         EXT_texture_compression_s3tc,
         EXT_texture_compression_dxt1,
         ANGLE_texture_compression_dxt3,
         ANGLE_texture_compression_dxt5,
         AMD_compressed_ATC_texture,
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -287,16 +287,34 @@ static const FeatureInfo sFeatureInfoArr
         300, // OpenGL ES version
         {
             GLContext::ARB_texture_float,
             GLContext::OES_texture_float_linear,
             GLContext::Extensions_End
         }
     },
     {
+        "texture_half_float",
+        310, // OpenGL version
+        300, // OpenGL ES version
+        {
+            GLContext::ARB_texture_float,
+            GLContext::NV_half_float,
+            GLContext::Extensions_End
+        }
+        /**
+         * We are not including OES_texture_half_float in this feature, because:
+         *   GL_HALF_FLOAT     = 0x140B
+         *   GL_HALF_FLOAT_ARB = 0x140B == GL_HALF_FLOAT
+         *   GL_HALF_FLOAT_NV  = 0x140B == GL_HALF_FLOAT
+         *   GL_HALF_FLOAT_OES = 0x8D61 != GL_HALF_FLOAT
+         * WebGL handles this specifically with an OES_texture_half_float check.
+         */
+    },
+    {
         "texture_non_power_of_two",
         200, // OpenGL version
         300, // OpenGL ES version
         {
             GLContext::ARB_texture_non_power_of_two,
             GLContext::OES_texture_npot,
             GLContext::Extensions_End
         }
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -22,17 +22,16 @@ HBSOURCES =  \
 	hb-blob.cc \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
 	hb-buffer-private.hh \
 	hb-buffer-serialize.cc \
 	hb-buffer.cc \
 	hb-cache-private.hh \
 	hb-common.cc \
-	hb-fallback-shape.cc \
 	hb-face-private.hh \
 	hb-face.cc \
 	hb-font-private.hh \
 	hb-font.cc \
 	hb-mutex-private.hh \
 	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
@@ -87,39 +86,47 @@ HBSOURCES += \
 	hb-ot-layout-private.hh \
 	hb-ot-map.cc \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-default.cc \
+	hb-ot-shape-complex-hangul.cc \
+	hb-ot-shape-complex-hebrew.cc \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.cc \
 	hb-ot-shape-complex-myanmar.cc \
 	hb-ot-shape-complex-myanmar-machine.hh \
 	hb-ot-shape-complex-sea.cc \
 	hb-ot-shape-complex-sea-machine.hh \
 	hb-ot-shape-complex-thai.cc \
+	hb-ot-shape-complex-tibetan.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
 	hb-ot-shape-normalize.cc \
 	hb-ot-shape-fallback-private.hh \
 	hb-ot-shape-fallback.cc \
 	hb-ot-shape-private.hh \
 	$(NULL)
 HBHEADERS += \
 	hb-ot.h \
 	hb-ot-layout.h \
+	hb-ot-shape.h \
 	hb-ot-tag.h \
 	$(NULL)
 endif
 
+if HAVE_FALLBACK
+HBSOURCES += hb-fallback-shape.cc
+endif
+
 if HAVE_PTHREAD
 HBCFLAGS += $(PTHREAD_CFLAGS)
 HBLIBS   += $(PTHREAD_LIBS)
 endif
 
 if HAVE_GLIB
 HBCFLAGS += $(GLIB_CFLAGS)
 HBLIBS   += $(GLIB_LIBS)
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -24,17 +24,16 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
 /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
 #define _POSIX_C_SOURCE 199309L
 
 #include "hb-private.hh"
 
-#include "hb-blob.h"
 #include "hb-object-private.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <sys/mman.h>
 #endif /* HAVE_SYS_MMAN_H */
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -26,17 +26,16 @@
  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_BUFFER_PRIVATE_HH
 #define HB_BUFFER_PRIVATE_HH
 
 #include "hb-private.hh"
-#include "hb-buffer.h"
 #include "hb-object-private.hh"
 #include "hb-unicode-private.hh"
 
 
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
 
 
--- a/gfx/harfbuzz/src/hb-buffer-serialize.cc
+++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc
@@ -199,17 +199,17 @@ static unsigned int
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
       if (pos[i].x_offset || pos[i].y_offset)
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset));
 
       *p++ = '+';
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
-      if (pos->y_advance)
+      if (pos[i].y_advance)
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
     }
 
     unsigned int l = p - b;
     if (buf_size > l)
     {
       memcpy (buf, b, l);
       buf += l;
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -23,18 +23,16 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
-#include "hb-version.h"
-
 #include "hb-mutex-private.hh"
 #include "hb-object-private.hh"
 
 #include <locale.h>
 
 
 /* hb_options_t */
 
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -89,16 +89,17 @@ typedef union _hb_var_int_t {
 /* hb_tag_t */
 
 typedef uint32_t hb_tag_t;
 
 #define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
 #define HB_UNTAG(tag)   ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_