Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Fri, 24 Jan 2014 11:59:00 -0800
changeset 181060 76f4b3a1015450e9ce411635516e696a0eef508d
parent 181059 b21ef8cb0db658ae277beb1b6a6e81c3f10bc2a1 (current diff)
parent 181051 bfe4ed6d47cec0efbc671cf48175e3a36b012b11 (diff)
child 181061 86d2f0068a499f4fd1b3548955306eacfd73957e
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
dom/tests/mochitest/webapps/test_install_receipts.xul
js/src/jit-test/tests/basic/offThreadCompileScript.js
js/src/jit-test/tests/debug/Source-elementProperty.js
media/libvorbis/bug944977-r19028.patch
mobile/android/base/resources/drawable-hdpi/copy.png
mobile/android/base/resources/drawable-hdpi/cut.png
mobile/android/base/resources/drawable-hdpi/paste.png
mobile/android/base/resources/drawable-hdpi/select_all.png
mobile/android/base/resources/drawable-mdpi/copy.png
mobile/android/base/resources/drawable-mdpi/cut.png
mobile/android/base/resources/drawable-mdpi/paste.png
mobile/android/base/resources/drawable-mdpi/select_all.png
mobile/android/base/resources/drawable-xhdpi/copy.png
mobile/android/base/resources/drawable-xhdpi/cut.png
mobile/android/base/resources/drawable-xhdpi/paste.png
services/docs/healthreport.rst
toolkit/components/telemetry/TelemetryPing.js
toolkit/components/telemetry/TelemetryPing.manifest
toolkit/components/telemetry/nsITelemetryPing.idl
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-JS build system changes are apparently requiring clobbers.
+Bug 948583, first part, apparently requires a clobber.  (Ideas for fixing this involve removing jsopcode.tbl, which is a bit too big to do while holding up this patch.)
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -326,28 +326,26 @@ HyperTextAccessible::DOMPointToHypertext
   Accessible* childAccAtOffset = nullptr;
   while (descendantAcc) {
     Accessible* parentAcc = descendantAcc->Parent();
     if (parentAcc == this) {
       childAccAtOffset = descendantAcc;
       break;
     }
 
-    // This offset no longer applies because the passed-in text object is not a child
-    // of the hypertext. This happens when there are nested hypertexts, e.g.
-    // <div>abc<h1>def</h1>ghi</div>
-    // If the passed-in DOM point was not on a direct child of the hypertext, we will
-    // return the offset for that entire hypertext
-    if (aIsEndOffset) {
-      // Not inclusive, the indicated char comes at index before this offset
-      // If the end offset is after the first character of the passed in object, use 1 for
-    // addTextOffset, to put us after the embedded object char. We'll only treat the offset as
-    // before the embedded object char if we end at the very beginning of the child.
-    addTextOffset = addTextOffset > 0;
-    } else
+    // This offset no longer applies because the passed-in text object is not
+    // a child of the hypertext. This happens when there are nested hypertexts,
+    // e.g. <div>abc<h1>def</h1>ghi</div>. Thus we need to adjust the offset
+    // to make it relative the hypertext.
+    // If the end offset is not supposed to be inclusive and the original point
+    // is not at 0 offset then the returned offset should be after an embedded
+    // character the original point belongs to.
+    if (aIsEndOffset)
+      addTextOffset = (addTextOffset > 0 || descendantAcc->IndexInParent() > 0) ? 1 : 0;
+    else
       addTextOffset = 0;
 
     descendantAcc = parentAcc;
   }
 
   // Loop through, adding offsets until we reach childAccessible
   // If childAccessible is null we will end up adding up the entire length of
   // the hypertext, which is good -- it just means our offset node
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -501,71 +501,109 @@ var Output = {
               'clicked.ogg'],
 
     earconBuffers: {},
 
     inited: false,
 
     webspeechEnabled: false,
 
+    deferredOutputs: [],
+
     init: function init() {
       let window = Utils.win;
       this.webspeechEnabled = !!window.speechSynthesis;
 
+      let settingsToGet = 2;
+      let settingsCallback = (aName, aSetting) => {
+        if (--settingsToGet > 0) {
+          return;
+        }
+
+        this.inited = true;
+
+        for (let actions of this.deferredOutputs) {
+          this.output(actions);
+        }
+      };
+
+      this._volumeSetting = new SettingCache(
+        'accessibility.screenreader-volume', settingsCallback,
+        { defaultValue: 1, callbackNow: true, callbackOnce: true });
+      this._rateSetting = new SettingCache(
+        'accessibility.screenreader-rate', settingsCallback,
+        { defaultValue: 0, callbackNow: true, callbackOnce: true });
+
       for (let earcon of this.EARCONS) {
         let earconName = /(^.*)\..*$/.exec(earcon)[1];
         this.earconBuffers[earconName] = new WeakMap();
         this.earconBuffers[earconName].set(
           window, new window.Audio('chrome://global/content/accessibility/' + earcon));
       }
+    },
 
-      this.inited = true;
+    uninit: function uninit() {
+      if (this.inited) {
+        delete this._volumeSetting;
+        delete this._rateSetting;
+      }
+      this.inited = false;
     },
 
     output: function output(aActions) {
       if (!this.inited) {
-        this.init();
+        this.deferredOutputs.push(aActions);
+        return;
       }
 
       for (let action of aActions) {
         let window = Utils.win;
         Logger.debug('tts.' + action.method, '"' + action.data + '"',
                      JSON.stringify(action.options));
 
         if (!action.options.enqueue && this.webspeechEnabled) {
           window.speechSynthesis.cancel();
         }
 
         if (action.method === 'speak' && this.webspeechEnabled) {
-          window.speechSynthesis.speak(
-            new window.SpeechSynthesisUtterance(action.data));
+          let utterance = new window.SpeechSynthesisUtterance(action.data);
+          let requestedRate = this._rateSetting.value;
+          utterance.volume = this._volumeSetting.value;
+          utterance.rate = requestedRate >= 0 ?
+            requestedRate + 1 : 1 / (Math.abs(requestedRate) + 1);
+          window.speechSynthesis.speak(utterance);
         } else if (action.method === 'playEarcon') {
           let audioBufferWeakMap = this.earconBuffers[action.data];
           if (audioBufferWeakMap) {
-            audioBufferWeakMap.get(window).cloneNode(false).play();
+            let node = audioBufferWeakMap.get(window).cloneNode(false);
+            node.volume = this._volumeSetting.value;
+            node.play();
           }
         }
       }
     }
   },
 
   start: function start() {
     Cu.import('resource://gre/modules/Geometry.jsm');
+    this.speechHelper.init();
   },
 
   stop: function stop() {
     if (this.highlightBox) {
       Utils.win.document.documentElement.removeChild(this.highlightBox.get());
       delete this.highlightBox;
     }
 
     if (this.announceBox) {
       Utils.win.document.documentElement.removeChild(this.announceBox.get());
       delete this.announceBox;
     }
+
+    this.speechHelper.uninit();
   },
 
   Speech: function Speech(aDetails, aBrowser) {
     this.speechHelper.output(aDetails.actions);
   },
 
   Visual: function Visual(aDetails, aBrowser) {
     switch (aDetails.method) {
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -15,17 +15,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   'resource://gre/modules/Geometry.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Events',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Relations',
   'resource://gre/modules/accessibility/Constants.jsm');
 
-this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache'];
+this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache', 'SettingCache'];
 
 this.Utils = {
   _buildAppMap: {
     '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g',
     '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'browser',
     '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android',
     '{a23983c0-fd0e-11dc-95ff-0800200c9a66}': 'mobile/xul'
   },
@@ -769,8 +769,45 @@ PrefCache.prototype = {
         Logger.logException(x);
       }
     }
   },
 
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsISupportsWeakReference])
 };
+
+this.SettingCache = function SettingCache(aName, aCallback, aOptions = {}) {
+  this.value = aOptions.defaultValue;
+  let runCallback = () => {
+    if (aCallback) {
+      aCallback(aName, this.value);
+      if (aOptions.callbackOnce) {
+        runCallback = () => {};
+      }
+    }
+  };
+
+  let settings = Utils.win.navigator.mozSettings;
+  if (!settings) {
+    if (aOptions.callbackNow) {
+      runCallback();
+    }
+    return;
+  }
+
+
+  let lock = settings.createLock();
+  let req = lock.get(aName);
+
+  req.addEventListener('success', () => {
+    this.value = req.result[aName] == undefined ? aOptions.defaultValue : req.result[aName];
+    if (aOptions.callbackNow) {
+      runCallback();
+    }
+  });
+
+  settings.addObserver(aName,
+                       (evt) => {
+                         this.value = evt.settingValue;
+                         runCallback();
+                       });
+};
--- a/accessible/src/jsat/content-script.js
+++ b/accessible/src/jsat/content-script.js
@@ -338,23 +338,24 @@ function scroll(aMessage) {
   let position = Utils.getVirtualCursor(content.document).position;
   if (!forwardToChild(aMessage, scroll, position)) {
     sendScrollCoordinates(position);
   }
 }
 
 function adjustRange(aMessage) {
   function sendUpDownKey(aAccessible) {
-    let evt = content.document.createEvent('KeyboardEvent');
-    let keycode = aMessage.json.direction == 'forward' ?
-      content.KeyEvent.DOM_VK_DOWN : content.KeyEvent.DOM_VK_UP;
-    evt.initKeyEvent(
-      "keypress", false, true, null, false, false, false, false, keycode, 0);
-    if (aAccessible.DOMNode) {
-      aAccessible.DOMNode.dispatchEvent(evt);
+    let acc = Utils.getEmbeddedControl(aAccessible) || aAccessible;
+    if (acc.DOMNode) {
+      let evt = content.document.createEvent('KeyboardEvent');
+      let keycode = aMessage.json.direction == 'forward' ?
+        content.KeyEvent.DOM_VK_DOWN : content.KeyEvent.DOM_VK_UP;
+      evt.initKeyEvent(
+        "keypress", false, true, null, false, false, false, false, keycode, 0);
+      acc.DOMNode.dispatchEvent(evt);
     }
   }
 
   let position = Utils.getVirtualCursor(content.document).position;
   if (!forwardToChild(aMessage, adjustRange, position)) {
     sendUpDownKey(position);
   }
 }
--- a/accessible/tests/mochitest/text/test_lineboundary.html
+++ b/accessible/tests/mochitest/text/test_lineboundary.html
@@ -115,16 +115,22 @@
                        [ [ 0, 12, "Hello world ", 0, 12 ] ]);
 
       //////////////////////////////////////////////////////////////////////////
       // list items
 
       testTextAtOffset([ "li1" ], BOUNDARY_LINE_START,
                        [ [ 0, 5, kDiscBulletChar + "Item", 0, 5 ] ]);
 
+      //////////////////////////////////////////////////////////////////////////
+      // Nested hypertexts
+
+      testTextAtOffset(["ht_5" ], BOUNDARY_LINE_START,
+                       [ [ 0, 0, kEmbedChar, 0, 1 ] ]);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -185,10 +191,17 @@ two words
   <iframe id="ht_3" src="data:text/html,<div contentEditable='true'>foo<br/><br/></div>"></iframe>
 
   <p id="ht_4">Hello world
 </p>
 
   <ul>
     <li id="li1">Item</li>
   </ul>
+
+  <div id="ht_5">
+    <div>
+      <p>sectiounus</p>
+      <p>seciofarus</p>
+    </div>
+  </div>
 </body>
 </html>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -396,16 +396,17 @@ pref("browser.link.open_newwindow.restri
 // work), but make in-process browser frames the default.
 pref("dom.mozBrowserFramesEnabled", true);
 
 // Enable a (virtually) unlimited number of mozbrowser processes.
 // We'll run out of PIDs on UNIX-y systems before we hit this limit.
 pref("dom.ipc.processCount", 100000);
 
 pref("dom.ipc.browser_frames.oop_by_default", false);
+pref("dom.browser_frames.useAsyncPanZoom", false);
 
 // SMS/MMS
 pref("dom.sms.enabled", true);
 
 //The waiting time in network manager.
 pref("network.gonk.ms-release-mms-connection", 30000);
 
 // WebContacts
--- a/b2g/chrome/content/desktop.js
+++ b/b2g/chrome/content/desktop.js
@@ -1,10 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 window.addEventListener("ContentStart", function(evt) {
   // Enable touch event shim on desktop that translates mouse events
   // into touch ones
   let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {})
                   .devtools.require;
   let { TouchEventHandler } = require("devtools/touch-events");
-  let touchEventHandler = new TouchEventHandler(shell.contentBrowser);
+  let chromeEventHandler = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsIWebNavigation)
+                                 .QueryInterface(Ci.nsIDocShell)
+                                 .chromeEventHandler || window;
+  let touchEventHandler = new TouchEventHandler(chromeEventHandler);
   touchEventHandler.start();
 });
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -646,8 +646,15 @@ SettingsListener.observe("accessibility.
   themingPrefs.forEach(function(pref) {
     SettingsListener.observe('gaia.' + pref, null, function(value) {
       if (value) {
         Services.prefs.setCharPref(pref, value);
       }
     });
   });
 })();
+
+// =================== AsyncPanZoom ======================
+
+SettingsListener.observe('apz.force-enable', false, function(value) {
+  Services.prefs.setBoolPref('dom.browser_frames.useAsyncPanZoom', value);
+});
+
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -23,16 +23,19 @@ Cu.import('resource://gre/modules/Networ
 
 // Identity
 Cu.import('resource://gre/modules/SignInToWebsite.jsm');
 SignInToWebsiteController.init();
 Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
 
 Cu.import('resource://gre/modules/DownloadsAPI.jsm');
 
+Cu.import('resource://gre/modules/Webapps.jsm');
+DOMApplicationRegistry.allAppsLaunchable = true;
+
 XPCOMUtils.defineLazyServiceGetter(Services, 'env',
                                    '@mozilla.org/process/environment;1',
                                    'nsIEnvironment');
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'ss',
                                    '@mozilla.org/content/style-sheet-service;1',
                                    'nsIStyleSheetService');
 
@@ -263,17 +266,16 @@ var shell = {
     }
 
     let homeURL = this.homeURL;
     if (!homeURL) {
       let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
       alert(msg);
       return;
     }
-
     let manifestURL = this.manifestURL;
     // <html:iframe id="systemapp"
     //              mozbrowser="true" allowfullscreen="true"
     //              style="overflow: hidden; height: 100%; width: 100%; border: none;"
     //              src="data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;'>"/>
     let systemAppFrame =
       document.createElementNS('http://www.w3.org/1999/xhtml', 'html:iframe');
     systemAppFrame.setAttribute('id', 'systemapp');
@@ -289,24 +291,32 @@ var shell = {
     container.appendChild(systemAppFrame);
 
     systemAppFrame.contentWindow
                   .QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIWebNavigation)
                   .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
                                       .createInstance(Ci.nsISHistory);
 
+    // On firefox mulet, shell.html is loaded in a tab
+    // and we have to listen on the chrome event handler
+    // to catch key events
+    let chromeEventHandler = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIWebNavigation)
+                                   .QueryInterface(Ci.nsIDocShell)
+                                   .chromeEventHandler || window;
     // Capture all key events so we can filter out hardware buttons
     // And send them to Gaia via mozChromeEvents.
     // Ideally, hardware buttons wouldn't generate key events at all, or
     // if they did, they would use keycodes that conform to DOM 3 Events.
     // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362
-    window.addEventListener('keydown', this, true);
-    window.addEventListener('keypress', this, true);
-    window.addEventListener('keyup', this, true);
+    chromeEventHandler.addEventListener('keydown', this, true);
+    chromeEventHandler.addEventListener('keypress', this, true);
+    chromeEventHandler.addEventListener('keyup', this, true);
+
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('mozfullscreenchange', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
 
     CustomEventManager.init();
     WebappsHelper.init();
@@ -608,19 +618,16 @@ var shell = {
   notifyContentStart: function shell_notifyContentStart() {
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserlocationchange', this, true);
 
     let content = this.contentBrowser.contentWindow;
 
     this.reportCrash(true);
 
-    Cu.import('resource://gre/modules/Webapps.jsm');
-    DOMApplicationRegistry.allAppsLaunchable = true;
-
     this.sendEvent(window, 'ContentStart');
 
     Services.obs.notifyObservers(null, 'content-start', null);
 
 #ifdef MOZ_WIDGET_GONK
     Cu.import('resource://gre/modules/OperatorApps.jsm');
 #endif
 
@@ -663,23 +670,22 @@ Services.obs.addObserver(function onInte
                           peers: data.appsToSelect });
 }, 'inter-app-comm-select-app', false);
 
 Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data) {
   shell.sendChromeEvent({ type: "fullscreenoriginchange",
                           fullscreenorigin: data });
 }, "fullscreen-origin-change", false);
 
-Services.obs.addObserver(function onWebappsStart(subject, topic, data) {
+DOMApplicationRegistry.registryStarted.then(function () {
   shell.sendChromeEvent({ type: 'webapps-registry-start' });
-}, 'webapps-registry-start', false);
-
-Services.obs.addObserver(function onWebappsReady(subject, topic, data) {
+});
+DOMApplicationRegistry.registryReady.then(function () {
   shell.sendChromeEvent({ type: 'webapps-registry-ready' });
-}, 'webapps-registry-ready', false);
+});
 
 Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
   shell.sendChromeEvent({
     type: "bluetooth-volumeset",
     value: data
   });
 }, 'bluetooth-volume-change', false);
 
@@ -1092,16 +1098,18 @@ let RemoteDebugger = {
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/gcli.js");
         if ("nsIProfiler" in Ci) {
           DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
         }
         DebuggerServer.registerModule("devtools/server/actors/inspector");
         DebuggerServer.registerModule("devtools/server/actors/styleeditor");
         DebuggerServer.registerModule("devtools/server/actors/stylesheets");
+        DebuggerServer.registerModule("devtools/server/actors/tracer");
+        DebuggerServer.registerModule("devtools/server/actors/webgl");
       }
       DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
       DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
       DebuggerServer.registerModule("devtools/server/actors/device");
 
 #ifdef MOZ_WIDGET_GONK
       DebuggerServer.onConnectionChange = function(what) {
         AdbController.updateState();
--- 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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <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="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <!-- 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="fce1a137746dbd354bca1918f02f96d51c40bad2">
     <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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <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="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <!-- 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": "806d5b997ec768914eaff1c46143c55de2612dbf", 
+    "revision": "5116c92a2905f6646d7049ddd1e1ab68eeb278d9", 
     "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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <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="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <!-- 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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <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="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <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="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <!-- 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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <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="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <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="fce1a137746dbd354bca1918f02f96d51c40bad2">
     <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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <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="afa75c6f431b00a2cee83c48f1bf986fc1cd6df1"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="48637bedf20a7d1b8cc3f1638e72eeb44728f467"/>
   <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="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4addd530e2dc1708745d11d81de21b5d1230ed41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a887bfabaed83c4588b40c845535c0388c8da0f3"/>
   <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/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -520,18 +520,16 @@
 #ifdef MOZ_SERVICES_HEALTHREPORT
 @BINPATH@/components/HealthReportComponents.manifest
 @BINPATH@/components/HealthReportService.js
 #endif
 #ifdef MOZ_CAPTIVEDETECT
 @BINPATH@/components/CaptivePortalDetectComponents.manifest
 @BINPATH@/components/captivedetect.js
 #endif
-@BINPATH@/components/TelemetryPing.js
-@BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/TelemetryStartup.js
 @BINPATH@/components/TelemetryStartup.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
 @BINPATH@/components/Push.js
 @BINPATH@/components/Push.manifest
--- a/browser/app/macbuild/Contents/_CodeSignature/CodeResources
+++ b/browser/app/macbuild/Contents/_CodeSignature/CodeResources
@@ -49,11 +49,23 @@
                 <real>10</real>
             </dict>
             <key>^MacOS/updates.xml$</key><dict>
                 <key>omit</key>
                 <true/>
                 <key>weight</key>
                 <real>10</real>
             </dict>
+            <key>^Updated.app/.*</key><dict>
+                <key>omit</key>
+                <true/>
+                <key>weight</key>
+                <real>10</real>
+            </dict>
+            <key>^updating/.*</key><dict>
+                <key>omit</key>
+                <true/>
+                <key>weight</key>
+                <real>10</real>
+            </dict>
         </dict>
     </dict>
 </plist>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1332,12 +1332,12 @@ pref("geo.wifi.uri", "https://www.google
 // currently irrelevant for desktop e10s
 pref("network.disable.ipc.security", true);
 
 // CustomizableUI debug logging.
 pref("browser.uiCustomization.debug", false);
 
 // The URL where remote content that composes the UI for Firefox Accounts should
 // be fetched. Must use HTTPS.
-pref("firefox.accounts.remoteUrl", "https://accounts.dev.lcip.org/?service=sync");
+pref("identity.fxaccounts.remote.uri", "https://accounts.dev.lcip.org/?service=sync");
 
 // The URL of the Firefox Accounts auth server backend
 pref("identity.fxaccounts.auth.uri", "https://api-accounts.dev.lcip.org/v1");
--- a/browser/base/content/browser-feeds.js
+++ b/browser/base/content/browser-feeds.js
@@ -29,36 +29,42 @@ var FeedHandler = {
       // set (because it thinks it's already open).  onpopupshowing gets
       // called after the attribute is unset, and it doesn't get unset
       // if we return false.  so we unset it here; otherwise, the menu
       // refuses to work past this point.
       container.parentNode.removeAttribute("open");
       return false;
     }
 
-    while (container.firstChild)
-      container.removeChild(container.firstChild);
+    for (let i = container.childNodes.length - 1; i >= 0; --i) {
+      let node = container.childNodes[i];
+      if (isSubview && node.localName == "label")
+        continue;
+      container.removeChild(node);
+    }
 
     if (!feeds || feeds.length <= 1)
       return false;
 
     // Build the menu showing the available feed choices for viewing.
     var itemNodeType = isSubview ? "toolbarbutton" : "menuitem";
     for (let feedInfo of feeds) {
       var item = document.createElement(itemNodeType);
       var baseTitle = feedInfo.title || feedInfo.href;
       var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
-      item.setAttribute("class", "feed-" + itemNodeType);
       item.setAttribute("label", labelStr);
       item.setAttribute("feed", feedInfo.href);
       item.setAttribute("tooltiptext", feedInfo.href);
       item.setAttribute("crop", "center");
+      let className = "feed-" + itemNodeType;
       if (isSubview) {
         item.setAttribute("tabindex", "0");
+        className += " subviewbutton";
       }
+      item.setAttribute("class", className);
       container.appendChild(item);
     }
     return true;
   },
 
   /**
    * Subscribe to a given feed.  Called when
    *   1. Page has a single feed and user clicks feed icon in location bar
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -278,17 +278,23 @@ let gSyncUI = {
    *          "reset" -- reset sync
    */
 
   openSetup: function SUI_openSetup(wizardType) {
     let xps = Components.classes["@mozilla.org/weave/service;1"]
                                 .getService(Components.interfaces.nsISupports)
                                 .wrappedJSObject;
     if (xps.fxAccountsEnabled) {
-      switchToTabHavingURI("about:accounts", true);
+      fxAccounts.getSignedInUser().then(userData => {
+        if (userData) {
+          this.openPrefs();
+        } else {
+          switchToTabHavingURI("about:accounts", true);
+        }
+      });
     } else {
       let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
       if (win)
         win.focus();
       else {
         window.openDialog("chrome://browser/content/sync/setup.xul",
                           "weaveSetup", "centerscreen,chrome,resizable=no",
                           wizardType);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -260,17 +260,16 @@ toolbarpaletteitem > #personal-bookmarks
 #personal-bookmarks[cui-areatype="toolbar"].overflowedItem > #bookmarks-toolbar-placeholder {
   display: -moz-box;
 }
 
 #zoom-controls[cui-areatype="toolbar"]:not(.overflowedItem) > #zoom-reset-button > .toolbarbutton-text {
   display: -moz-box;
 }
 
-#wrapper-urlbar-container > #urlbar-container > #urlbar-wrapper > #urlbar > toolbarbutton,
 #urlbar-reload-button:not([displaystop]) + #urlbar-stop-button,
 #urlbar-reload-button[displaystop] {
   visibility: collapse;
 }
 
 #PanelUI-feeds > .feed-toolbarbutton:-moz-locale-dir(rtl) {
   direction: rtl;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3443,16 +3443,31 @@ var XULBrowserWindow = {
 
     if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
       url = trimURL(url);
 
     this.overLink = url;
     LinkTargetDisplay.update();
   },
 
+  showTooltip: function (x, y, tooltip) {
+    // The x,y coordinates are relative to the <browser> element using
+    // the chrome zoom level.
+    let elt = document.getElementById("remoteBrowserTooltip");
+    elt.label = tooltip;
+
+    let anchor = gBrowser.selectedBrowser;
+    elt.openPopupAtScreen(anchor.boxObject.screenX + x, anchor.boxObject.screenY + y, false, null);
+  },
+
+  hideTooltip: function () {
+    let elt = document.getElementById("remoteBrowserTooltip");
+    elt.hidePopup();
+  },
+
   updateStatusField: function () {
     var text, type, types = ["overLink"];
     if (this._busyUI)
       types.push("status");
     types.push("defaultStatus");
     for (type of types) {
       text = this[type];
       if (text)
@@ -6413,20 +6428,20 @@ var gIdentityHandler = {
     let unknown = false;
     try {
       uri.host;
     } catch (e) { unknown = true; }
 
     // Chrome URIs however get special treatment. Some chrome URIs are
     // whitelisted to provide a positive security signal to the user.
     let chromeWhitelist = ["about:addons", "about:app-manager", "about:config",
-                           "about:crashes", "about:healthreport", "about:home",
-                           "about:newaddon", "about:permissions", "about:preferences",
-                           "about:privatebrowsing", "about:sessionstore",
-                           "about:support", "about:welcomeback"];
+                           "about:crashes", "about:customizing", "about:healthreport",
+                           "about:home", "about:newaddon", "about:permissions",
+                           "about:preferences", "about:privatebrowsing",
+                           "about:sessionstore", "about:support", "about:welcomeback"];
     let lowercaseSpec = uri.spec.toLowerCase();
     if (chromeWhitelist.some(function(whitelistedSpec) lowercaseSpec.startsWith(whitelistedSpec))) {
       this.setMode(this.IDENTITY_MODE_CHROMEUI);
     } else if (unknown) {
       this.setMode(this.IDENTITY_MODE_UNKNOWN);
     } else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       this.setMode(this.IDENTITY_MODE_IDENTIFIED);
     } else if (state & nsIWebProgressListener.STATE_IS_SECURE) {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -124,16 +124,17 @@
 
     <!-- bug 415444/582485: event.stopPropagation is here for the cloned version
          of this menupopup -->
     <menupopup id="backForwardMenu"
                onpopupshowing="return FillHistoryMenu(event.target);"
                oncommand="gotoHistoryIndex(event); event.stopPropagation();"
                onclick="checkForMiddleClick(this, event);"/>
     <tooltip id="aHTMLTooltip" page="true"/>
+    <tooltip id="remoteBrowserTooltip"/>
 
     <!-- for search and content formfill/pw manager -->
     <panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for url bar autocomplete -->
     <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
 
     <!-- for select dropdowns -->
@@ -212,16 +213,17 @@
           <hbox id="UITourTooltipButtons" flex="1" align="end"/>
         </vbox>
       </hbox>
     </panel>
     <panel id="UITourHighlightContainer"
            hidden="true"
            noautofocus="true"
            noautohide="true"
+           flip="none"
            consumeoutsideclicks="false">
       <box id="UITourHighlight"></box>
     </panel>
 
     <panel id="social-share-panel"
            class="social-panel"
            type="arrow"
            orient="horizontal"
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -1,89 +1,89 @@
-/* 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");
-
-registerCleanupFunction(function() {
-  // Ensure we don't pollute prefs for next tests.
-  Services.prefs.clearUserPref("firefox.accounts.remoteUrl");
-});
-
-let gTests = [
-
-{
-  desc: "Test the remote commands",
-  setup: function ()
-  {
-    Services.prefs.setCharPref("firefox.accounts.remoteUrl",
-                               "https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
-  },
-  run: function ()
-  {
-    let deferred = Promise.defer();
-
-    let results = 0;
-    try {
-      let win = gBrowser.contentWindow;
-      win.addEventListener("message", function testLoad(e) {
-        if (e.data.type == "testResult") {
-          ok(e.data.pass, e.data.info);
-          results++;
-        }
-        else if (e.data.type == "testsComplete") {
-          is(results, e.data.count, "Checking number of results received matches the number of tests that should have run");
-          win.removeEventListener("message", testLoad, false, true);
-          deferred.resolve();
-        }
-
-      }, false, true);
-
-    } catch(e) {
-      ok(false, "Failed to get all commands");
-      deferred.reject();
-    }
-    return deferred.promise;
-  }
-},
-
-
-]; // gTests
-
-function test()
-{
-  waitForExplicitFinish();
-
-  Task.spawn(function () {
-    for (let test of gTests) {
-      info(test.desc);
-      test.setup();
-
-      yield promiseNewTabLoadEvent("about:accounts");
-
-      yield test.run();
-
-      gBrowser.removeCurrentTab();
-    }
-
-    finish();
-  });
-}
-
-function promiseNewTabLoadEvent(aUrl, aEventType="load")
-{
-  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);
-  return deferred.promise;
-}
-
+/* 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");
+
+registerCleanupFunction(function() {
+  // Ensure we don't pollute prefs for next tests.
+  Services.prefs.clearUserPref("identity.fxaccounts.remote.uri");
+});
+
+let gTests = [
+
+{
+  desc: "Test the remote commands",
+  setup: function ()
+  {
+    Services.prefs.setCharPref("identity.fxaccounts.remote.uri",
+                               "https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
+  },
+  run: function ()
+  {
+    let deferred = Promise.defer();
+
+    let results = 0;
+    try {
+      let win = gBrowser.contentWindow;
+      win.addEventListener("message", function testLoad(e) {
+        if (e.data.type == "testResult") {
+          ok(e.data.pass, e.data.info);
+          results++;
+        }
+        else if (e.data.type == "testsComplete") {
+          is(results, e.data.count, "Checking number of results received matches the number of tests that should have run");
+          win.removeEventListener("message", testLoad, false, true);
+          deferred.resolve();
+        }
+
+      }, false, true);
+
+    } catch(e) {
+      ok(false, "Failed to get all commands");
+      deferred.reject();
+    }
+    return deferred.promise;
+  }
+},
+
+
+]; // gTests
+
+function test()
+{
+  waitForExplicitFinish();
+
+  Task.spawn(function () {
+    for (let test of gTests) {
+      info(test.desc);
+      test.setup();
+
+      yield promiseNewTabLoadEvent("about:accounts");
+
+      yield test.run();
+
+      gBrowser.removeCurrentTab();
+    }
+
+    finish();
+  });
+}
+
+function promiseNewTabLoadEvent(aUrl, aEventType="load")
+{
+  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);
+  return deferred.promise;
+}
+
--- a/browser/base/content/test/general/browser_popupNotification.js
+++ b/browser/base/content/test/general/browser_popupNotification.js
@@ -977,17 +977,17 @@ var tests = [
       // checkPopup checks for the matching label. Note that this assumes that
       // this.notifyObj.mainAction is the same as notification.mainAction,
       // which could be a problem if we ever decided to deep-copy.
       checkPopup(popup, this.notifyObj);
       triggerMainCommand(popup);
     },
     onHidden: function() { }
   },
-  { // Test #31 - Moving a tab to a new window should remove non-swappable
+  { // Test #34 - Moving a tab to a new window should remove non-swappable
     // notifications.
     run: function() {
       gBrowser.selectedTab = gBrowser.addTab("about:blank");
       let notifyObj = new basicNotification();
       showNotification(notifyObj);
       let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
       whenDelayedStartupFinished(win, function() {
         let [tab] = win.gBrowser.tabs;
@@ -997,17 +997,17 @@ var tests = [
            "no notification displayed in new window");
         ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
         ok(notifyObj.removedCallbackTriggered, "the removed callback was triggered");
         win.close();
         goNext();
       });
     }
   },
-  { // Test #32 - Moving a tab to a new window should preserve swappable notifications.
+  { // Test #35 - Moving a tab to a new window should preserve swappable notifications.
     run: function() {
       gBrowser.selectedTab = gBrowser.addTab("about:blank");
       let notifyObj = new basicNotification();
       let originalCallback = notifyObj.options.eventCallback;
         notifyObj.options.eventCallback = function (eventName) {
           originalCallback(eventName);
           return eventName == "swapping";
         };
@@ -1019,16 +1019,60 @@ var tests = [
         let anchor = win.document.getElementById("default-notification-icon");
         win.PopupNotifications._reshowNotifications(anchor);
         checkPopup(win.PopupNotifications.panel, notifyObj);
         ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
         win.close();
         goNext();
       });
     }
+  },
+  { // Test #36 - the hideNotNow option
+    run: function () {
+      this.notifyObj = new basicNotification();
+      this.notifyObj.options.hideNotNow = true;
+      this.notifyObj.mainAction.dismiss = true;
+      showNotification(this.notifyObj);
+    },
+    onShown: function (popup) {
+      // checkPopup verifies that the Not Now item is hidden, and that no separator is added.
+      checkPopup(popup, this.notifyObj);
+      triggerMainCommand(popup);
+    },
+    onHidden: function (popup) { }
+  },
+  { // Test #37 - the main action callback can keep the notification.
+    run: function () {
+      this.notifyObj = new basicNotification();
+      this.notifyObj.mainAction.dismiss = true;
+      showNotification(this.notifyObj);
+    },
+    onShown: function (popup) {
+      checkPopup(popup, this.notifyObj);
+      triggerMainCommand(popup);
+    },
+    onHidden: function (popup) {
+      ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback was triggered");
+      ok(!this.notifyObj.removedCallbackTriggered, "removed callback wasn't triggered");
+    }
+  },
+  { // Test #38 - a secondary action callback can keep the notification.
+    run: function () {
+      this.notifyObj = new basicNotification();
+      this.notifyObj.secondaryActions[0].dismiss = true;
+      showNotification(this.notifyObj);
+    },
+    onShown: function (popup) {
+      checkPopup(popup, this.notifyObj);
+      triggerSecondaryCommand(popup, 0);
+    },
+    onHidden: function (popup) {
+      ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback was triggered");
+      ok(!this.notifyObj.removedCallbackTriggered, "removed callback wasn't triggered");
+    }
   }
 ];
 
 function showNotification(notifyObj) {
   return PopupNotifications.show(notifyObj.browser,
                                  notifyObj.id,
                                  notifyObj.message,
                                  notifyObj.anchorID,
@@ -1058,17 +1102,22 @@ function checkPopup(popup, notificationO
   if (notificationObj.mainAction) {
     is(notification.getAttribute("buttonlabel"), notificationObj.mainAction.label, "main action label matches");
     is(notification.getAttribute("buttonaccesskey"), notificationObj.mainAction.accessKey, "main action accesskey matches");
   }
   let actualSecondaryActions = Array.filter(notification.childNodes,
                                             function (child) child.nodeName == "menuitem");
   let secondaryActions = notificationObj.secondaryActions || [];
   let actualSecondaryActionsCount = actualSecondaryActions.length;
-  if (secondaryActions.length) {
+  if (notificationObj.options.hideNotNow) {
+    is(notification.getAttribute("hidenotnow"), "true", "Not Now item hidden");
+    if (secondaryActions.length)
+      is(notification.lastChild.tagName, "menuitem", "no menuseparator");
+  }
+  else if (secondaryActions.length) {
     is(notification.lastChild.tagName, "menuseparator", "menuseparator exists");
   }
   is(actualSecondaryActionsCount, secondaryActions.length, actualSecondaryActions.length + " secondary actions");
   secondaryActions.forEach(function (a, i) {
     is(actualSecondaryActions[i].getAttribute("label"), a.label, "label for secondary action " + i + " matches");
     is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey, "accessKey for secondary action " + i + " matches");
   });
 }
--- a/browser/components/customizableui/content/aboutCustomizing.xhtml
+++ b/browser/components/customizableui/content/aboutCustomizing.xhtml
@@ -13,11 +13,13 @@
   <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
   %browserDTD;
 ]>
 
 <html xmlns="http://www.w3.org/1999/xhtml"
       disablefastfind="true">
 	<head>
 		<title>&customizeMode.tabTitle;</title>
+		<link rel="icon" type="image/x-icon"
+		      href="chrome://browser/skin/customizableui/customizeFavicon.ico"/>
 	</head>
 	<body></body>
 </html>
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -33,74 +33,84 @@
                        label="&quitApplicationCmd.label;"
                        tooltiptext="&quitApplicationCmd.label;"
 #endif
                        command="cmd_quitApplication"/>
       </footer>
     </panelview>
 
     <panelview id="PanelUI-history" flex="1">
-      <label value="&appMenuHistory.label;"/>
+      <label value="&appMenuHistory.label;" class="panel-subview-header"/>
       <toolbarbutton id="appMenuViewHistorySidebar" tabindex="0"
                      label="&appMenuHistory.viewSidebar.label;"
                      type="checkbox"
+                     class="subviewbutton"
                      oncommand="toggleSidebar('viewHistorySidebar'); PanelUI.hide();">
         <observes element="viewHistorySidebar" attribute="checked"/>
       </toolbarbutton>
       <toolbarbutton id="appMenuClearRecentHistory" tabindex="0"
                      label="&appMenuHistory.clearRecent.label;"
+                     class="subviewbutton"
                      command="Tools:Sanitize"/>
 #ifdef MOZ_SERVICES_SYNC
       <toolbarbutton id="sync-tabs-menuitem2"
-                     class="syncTabsMenuItem"
+                     class="syncTabsMenuItem subviewbutton"
                      label="&syncTabsMenu2.label;"
                      oncommand="BrowserOpenSyncTabs();"
                      disabled="true"/>
 #endif
       <toolbarbutton id="appMenuRestoreLastSession" tabindex="0"
                      label="&appMenuHistory.restoreSession.label;"
+                     class="subviewbutton"
                      command="Browser:RestoreLastSession"/>
       <menuseparator id="PanelUI-recentlyClosedTabs-separator"/>
       <vbox id="PanelUI-recentlyClosedTabs" tooltip="bhTooltip"/>
       <menuseparator id="PanelUI-recentlyClosedWindows-separator"/>
       <vbox id="PanelUI-recentlyClosedWindows" tooltip="bhTooltip"/>
       <menuseparator id="PanelUI-historyItems-separator"/>
       <vbox id="PanelUI-historyItems" tooltip="bhTooltip"/>
-      <label value="&appMenuHistory.showAll.label;"
-             id="PanelUI-historyMore"
-             class="text-link"
-             onclick="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
+      <toolbarbutton id="PanelUI-historyMore" tabindex="0"
+                     class="panel-subview-footer subviewbutton"
+                     label="&appMenuHistory.showAll.label;"
+                     oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
     </panelview>
 
-    <panelview id="PanelUI-bookmarks" flex="1">
+    <panelview id="PanelUI-bookmarks" flex="1" class="PanelUI-subView">
+      <label value="&bookmarksMenu.label;" class="panel-subview-header"/>
       <toolbarbutton id="panelMenuBookmarkThisPage"
                      label="&bookmarkThisPageCmd.label;"
+                     class="subviewbutton"
                      command="Browser:AddBookmarkAs"
                      onclick="PanelUI.hide();"/>
       <toolbarseparator/>
       <toolbarbutton id="panelMenu_showAllBookmarks"
                      label="&showAllBookmarks2.label;"
+                     class="subviewbutton"
                      command="Browser:ShowAllBookmarks"
                      onclick="PanelUI.hide();"/>
       <toolbarbutton id="panelMenu_viewBookmarksSidebar"
                      label="&viewBookmarksSidebar2.label;"
+                     class="subviewbutton"
                      oncommand="toggleSidebar('viewBookmarksSidebar'); PanelUI.hide();">
         <observes element="viewBookmarksSidebar" attribute="checked"/>
       </toolbarbutton>
       <toolbarbutton id="panelMenu_viewBookmarksToolbar"
                      label="&viewBookmarksToolbar.label;"
                      type="checkbox"
                      toolbarId="PersonalToolbar"
+                     class="subviewbutton"
                      oncommand="onViewToolbarCommand(event); PanelUI.hide();"/>
       <toolbarseparator/>
       <toolbarbutton id="panelMenu_bookmarksToolbar"
                      label="&personalbarCmd.label;"
+                     class="subviewbutton"
                      oncommand="PlacesCommandHook.showPlacesOrganizer('BookmarksToolbar'); PanelUI.hide();"/>
       <toolbarbutton id="panelMenu_unsortedBookmarks"
                      label="&unsortedBookmarksCmd.label;"
+                     class="subviewbutton"
                      oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks'); PanelUI.hide();"/>
       <toolbarseparator/>
       <toolbaritem id="panelMenu_bookmarksMenu"
                    flex="1"
                    orient="vertical"
                    smoothscroll="false"
                    onclick="if (event.button == 1) BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
                    oncommand="BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
@@ -108,30 +118,32 @@
                    tooltip="bhTooltip">
         <!-- bookmarks menu items -->
       </toolbaritem>
 
     </panelview>
 
     <panelview id="PanelUI-socialapi" flex="1"/>
 
-    <panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);"></panelview>
+    <panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);">
+      <label value="&feedsMenu.label;" class="panel-subview-header"/>
+    </panelview>
 
     <panelview id="PanelUI-helpView" flex="1">
-      <label value="&helpMenu.label;"/>
+      <label value="&helpMenu.label;" class="panel-subview-header"/>
       <vbox id="PanelUI-helpItems"/>
     </panelview>
 
     <panelview id="PanelUI-developer" flex="1">
-      <label value="&webDeveloperMenu.label;"/>
+      <label value="&webDeveloperMenu.label;" class="panel-subview-header"/>
       <vbox id="PanelUI-developerItems"/>
     </panelview>
 
     <panelview id="PanelUI-characterEncodingView" flex="1">
-      <label value="&charsetMenu.label;"/>
+      <label value="&charsetMenu.label;" class="panel-subview-header"/>
 
       <vbox id="PanelUI-characterEncodingView-customlist"
             class="PanelUI-characterEncodingView-list"/>
       <vbox>
         <label value="&charsetMenuAutodet.label;"/>
         <vbox id="PanelUI-characterEncodingView-autodetect"
               class="PanelUI-characterEncodingView-list"/>
       </vbox>
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -404,16 +404,17 @@ const PanelUI = {
         continue;
       let button = document.createElementNS(NSXUL, "toolbarbutton");
       // Copy specific attributes from a menuitem of the Help menu
       for (let attrName of attrs) {
         if (!node.hasAttribute(attrName))
           continue;
         button.setAttribute(attrName, node.getAttribute(attrName));
       }
+      button.setAttribute("class", "subviewbutton");
       fragment.appendChild(button);
     }
     items.appendChild(fragment);
 
     this.addEventListener("command", PanelUI.onCommandHandler);
   },
 
   _onHelpViewHide: function(aEvent) {
--- a/browser/components/customizableui/src/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/src/CustomizableWidgets.jsm
@@ -97,16 +97,17 @@ const CustomizableWidgets = [{
               let uri = row.getResultByIndex(1);
               let title = row.getResultByIndex(2);
               let icon = row.getResultByIndex(6);
 
               let item = doc.createElementNS(kNSXUL, "toolbarbutton");
               item.setAttribute("label", title || uri);
               item.setAttribute("tabindex", "0");
               item.setAttribute("targetURI", uri);
+              item.setAttribute("class", "subviewbutton");
               item.addEventListener("command", function (aEvent) {
                 onHistoryVisit(uri, aEvent, item);
               });
               item.addEventListener("click", function (aEvent) {
                 onHistoryVisit(uri, aEvent, item);
               });
               if (icon)
                 item.setAttribute("image", "moz-anno:favicon:" + icon);
@@ -147,22 +148,34 @@ const CustomizableWidgets = [{
         tabsFromOtherComputers.removeAttribute("disabled");
       } else {
         tabsFromOtherComputers.setAttribute("disabled", true);
       }
 #endif
 
       let tabsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getTabsFragment(doc.defaultView, "toolbarbutton");
       let separator = doc.getElementById("PanelUI-recentlyClosedTabs-separator");
-      separator.hidden = !tabsFragment.childElementCount;
+      let elementCount = tabsFragment.childElementCount;
+      separator.hidden = !elementCount;
+      while (--elementCount >= 0) {
+        if (tabsFragment.children[elementCount].localName != "toolbarbutton")
+          continue;
+        tabsFragment.children[elementCount].setAttribute("class", "subviewbutton");
+      }
       recentlyClosedTabs.appendChild(tabsFragment);
 
       let windowsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getWindowsFragment(doc.defaultView, "toolbarbutton");
       separator = doc.getElementById("PanelUI-recentlyClosedWindows-separator");
-      separator.hidden = !windowsFragment.childElementCount;
+      elementCount = windowsFragment.childElementCount;
+      separator.hidden = !elementCount;
+      while (--elementCount >= 0) {
+        if (windowsFragment.children[elementCount].localName != "toolbarbutton")
+          continue;
+        windowsFragment.children[elementCount].setAttribute("class", "subviewbutton");
+      }
       recentlyClosedWindows.appendChild(windowsFragment);
     },
     onViewHiding: function(aEvent) {
       LOG("History view is being hidden!");
     }
   }, {
     id: "privatebrowsing-button",
     shortcutId: "key_privatebrowsing",
@@ -239,16 +252,17 @@ const CustomizableWidgets = [{
           continue;
 
         let item;
         if (node.localName == "menuseparator") {
           item = doc.createElementNS(kNSXUL, "menuseparator");
         } else if (node.localName == "menuitem") {
           item = doc.createElementNS(kNSXUL, "toolbarbutton");
           item.setAttribute("tabindex", "0");
+          item.setAttribute("class", "subviewbutton");
         } else {
           continue;
         }
         for (let attr of attrs) {
           let attrVal = node.getAttribute(attr);
           if (attrVal)
             item.setAttribute(attr, attrVal);
         }
@@ -710,16 +724,17 @@ const CustomizableWidgets = [{
         let elem = aDocument.createElementNS(kNSXUL, "toolbarbutton");
         elem.setAttribute("label", item.name);
         elem.section = aSection;
         elem.value = item.value;
         if (item.current)
           elem.setAttribute("current", "true");
         if (disabled)
           elem.setAttribute("disabled", "true");
+        elem.setAttribute("class", "subviewbutton");
         containerElem.appendChild(elem);
       }
     },
     onViewShowing: function(aEvent) {
       let document = aEvent.target.ownerDocument;
 
       this.populateList(document,
                         "PanelUI-characterEncodingView-customlist",
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -1775,17 +1775,17 @@ PlacesPanelMenuView.prototype = {
 
     let type = aChild.type;
     let button;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
       button = document.createElement("toolbarseparator");
     }
     else {
       button = document.createElement("toolbarbutton");
-      button.className = "bookmark-item";
+      button.className = "bookmark-item subviewbutton";
       button.setAttribute("label", aChild.title);
       let icon = aChild.icon;
       if (icon)
         button.setAttribute("image", icon);
 
       if (PlacesUtils.containerTypes.indexOf(type) != -1) {
         button.setAttribute("container", "true");
 
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -79,40 +79,39 @@
         // Make sure we rebuild the popup in onpopupshowing
         this._needToBuildPopup = true;
 
         var os =
                Components.classes["@mozilla.org/observer-service;1"]
                          .getService(Components.interfaces.nsIObserverService);
         os.addObserver(this, "browser-search-engine-modified", false);
 
-        this._addedObserver = true;
+        this._initialized = true;
 
         this.searchService.init((function search_init_cb(aStatus) {
           // Bail out if the binding's been destroyed
-          if (this._destroyed)
+          if (!this._initialized)
             return;
 
           if (Components.isSuccessCode(aStatus)) {
             // Refresh the display (updating icon, etc)
             this.updateDisplay();
           } else {
             Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus);
           }
         }).bind(this));
       ]]></constructor>
 
       <destructor><![CDATA[
-        this._destroyed = true;
+        if (this._initialized) {
+          this._initialized = false;
 
-        if (this._addedObserver) {
           var os = Components.classes["@mozilla.org/observer-service;1"]
                              .getService(Components.interfaces.nsIObserverService);
           os.removeObserver(this, "browser-search-engine-modified");
-          this._addedObserver = false;
         }
 
         // Make sure to break the cycle from _textbox to us. Otherwise we leak
         // the world. But make sure it's actually pointing to us.
         if (this._textbox.mController.input == this)
           this._textbox.mController.input = null;
       ]]></destructor>
 
--- a/browser/components/sessionstore/src/SessionCookies.jsm
+++ b/browser/components/sessionstore/src/SessionCookies.jsm
@@ -55,17 +55,19 @@ let SessionCookiesInternal = {
     this._ensureInitialized();
 
     for (let window of windows) {
       let cookies = [];
 
       // Collect all hosts for the current window.
       let hosts = this.getHostsForWindow(window, true);
 
-      for (let [host, isPinned] in Iterator(hosts)) {
+      for (let host of Object.keys(hosts)) {
+        let isPinned = hosts[host];
+
         for (let cookie of CookieStore.getCookiesForHost(host)) {
           // _getCookiesForHost() will only return hosts with the right privacy
           // rules, so there is no need to do anything special with this call
           // to PrivacyLevel.canSave().
           if (PrivacyLevel.canSave({isHttps: cookie.secure, isPinned: isPinned})) {
             cookies.push(cookie);
           }
         }
@@ -297,17 +299,17 @@ let CookieStore = {
   getCookiesForHost: function (host) {
     if (!this._hosts.has(host)) {
       return [];
     }
 
     let cookies = [];
 
     for (let pathToNamesMap of this._hosts.get(host).values()) {
-      cookies = cookies.concat([cookie for (cookie of pathToNamesMap.values())]);
+      cookies.push(...pathToNamesMap.values());
     }
 
     return cookies;
   },
 
   /**
    * Stores a given cookie.
    *
--- a/browser/components/sessionstore/src/SessionStorage.jsm
+++ b/browser/components/sessionstore/src/SessionStorage.jsm
@@ -108,19 +108,19 @@ let SessionStorageInternal = {
       let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
 
       // There is no need to pass documentURI, it's only used to fill documentURI property of
       // domstorage event, which in this case has no consumer. Prevention of events in case
       // of missing documentURI will be solved in a followup bug to bug 600307.
       let storage = storageManager.createStorage(principal, "", aDocShell.usePrivateBrowsing);
 
-      for (let [key, value] in Iterator(data)) {
+      for (let key of Object.keys(data)) {
         try {
-          storage.setItem(key, value);
+          storage.setItem(key, data[key]);
         } catch (e) {
           // throws e.g. for URIs that can't have sessionStorage
           console.error(e);
         }
       }
     }
   },
 
--- a/browser/components/sessionstore/test/browser_615394-SSWindowState_events.js
+++ b/browser/components/sessionstore/test/browser_615394-SSWindowState_events.js
@@ -283,17 +283,18 @@ function test_setBrowserState() {
   }
 
   window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy, false);
   window.addEventListener("SSWindowStateReady", onSSWindowStateReady, false);
   Services.ww.registerNotification(windowObserver);
 
   waitForBrowserState(lameMultiWindowState, function() {
     let checkedWindows = 0;
-    for each (let [id, winEvents] in Iterator(windowEvents)) {
+    for (let id of Object.keys(windowEvents)) {
+      let winEvents = windowEvents[id];
       is(winEvents.busyEventCount, 1,
          "[test_setBrowserState] window" + id + " busy event count correct");
       is(winEvents.readyEventCount, 1,
          "[test_setBrowserState] window" + id + " ready event count correct");
       checkedWindows++;
     }
     is(checkedWindows, 2,
        "[test_setBrowserState] checked 2 windows");
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/macosx-universal/nightly-nonunified
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/macosx-universal/nightly"
+
+ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -1,17 +1,15 @@
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-accessibility
 ac_add_options --enable-signmar
 
-ac_add_options --disable-unified-compilation
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
 
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64/debug-nonunified
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/macosx64/debug"
+
+ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/whitelist
+++ b/browser/config/mozconfigs/whitelist
@@ -1,30 +1,30 @@
 # 'nightly' contains things that are in nightly mozconfigs and allowed to be missing from release builds.
 # Other keys in whitelist contain things are in that branches mozconfigs and allowed to be missing from nightly builds.
 whitelist = {
     'release': {},
     'nightly': {},
     }
 
-all_platforms = ['win32', 'linux32', 'linux64', 'macosx-universal']
+all_platforms = ['win64', 'win32', 'linux32', 'linux64', 'macosx-universal']
 
 for platform in all_platforms:
     whitelist['nightly'][platform] = [
         'ac_add_options --enable-update-channel=nightly',
         'ac_add_options --enable-profiling',
         'mk_add_options CLIENT_PY_ARGS="--hg-options=\'--verbose --time\' --hgtool=../tools/buildfarm/utils/hgtool.py --skip-chatzilla --skip-comm --skip-inspector --skip-venkman --tinderbox-print"'
     ]
 
 for platform in ['linux32', 'linux64', 'macosx-universal']:
     whitelist['nightly'][platform] += [
         'mk_add_options MOZ_MAKE_FLAGS="-j4"',
     ]
 
-for platform in ['linux32', 'linux64', 'macosx-universal', 'win32']:
+for platform in ['linux32', 'linux64', 'macosx-universal', 'win32', 'win64']:
     whitelist['nightly'][platform] += ['ac_add_options --enable-signmar']
     whitelist['nightly'][platform] += ['ac_add_options --enable-js-diagnostics']
 
 whitelist['nightly']['linux32'] += [
     'CXX=$REAL_CXX',
     'CXX="ccache $REAL_CXX"',
     'CC="ccache $REAL_CC"',
     'mk_add_options PROFILE_GEN_SCRIPT=@TOPSRCDIR@/build/profile_pageloader.pl',
@@ -57,25 +57,31 @@ whitelist['nightly']['macosx-universal']
 whitelist['nightly']['win32'] += [
     '. $topsrcdir/configs/mozilla2/win32/include/choose-make-flags',
     'mk_add_options MOZ_MAKE_FLAGS=-j1',
     'if test "$IS_NIGHTLY" != ""; then',
     'ac_add_options --disable-auto-deps',
     'fi',
     'ac_add_options --enable-metro',
 ]
+whitelist['nightly']['win64'] += [
+    '. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"',
+    'ac_add_options --enable-metro',
+]
 
 for platform in all_platforms:
     whitelist['release'][platform] = [
         'ac_add_options --enable-update-channel=release',
         'ac_add_options --enable-official-branding',
         'mk_add_options MOZ_MAKE_FLAGS="-j4"',
         'export BUILDING_RELEASE=1',
     ]
 whitelist['release']['win32'] += ['mk_add_options MOZ_PGO=1']
+whitelist['release']['win64'] += ['mk_add_options MOZ_PGO=1']
+
 whitelist['release']['linux32'] += [
     'export MOZILLA_OFFICIAL=1',
     'export MOZ_TELEMETRY_REPORTING=1',
     'mk_add_options MOZ_PGO=1',
     "mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
 ]
 whitelist['release']['linux64'] += [
     'export MOZILLA_OFFICIAL=1',
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -1,17 +1,15 @@
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
 ac_add_options --enable-metro
 
-ac_add_options --disable-unified-compilation
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2010-win64
 else
   . $topsrcdir/build/win32/mozconfig.vs2010
 fi
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/win32/debug-nonunified
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/win32/debug"
+
+ac_add_options --disable-unified-compilation
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/win32/nightly-nonunified
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/win32/nightly"
+
+ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/win64/beta
+++ b/browser/config/mozconfigs/win64/beta
@@ -1,7 +1,9 @@
+. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
 mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
+. $topsrcdir/build/win64/mozconfig.vs2010
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -3,18 +3,16 @@
 ac_add_options --target=x86_64-pc-mingw32
 ac_add_options --host=x86_64-pc-mingw32
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
 ac_add_options --enable-metro
 
-ac_add_options --disable-unified-compilation
-
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 . $topsrcdir/build/win64/mozconfig.vs2010
 
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/win64/debug-nonunified
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/win64/debug"
+
+ac_add_options --disable-unified-compilation
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/win64/nightly-nonunified
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/win64/nightly"
+
+ac_add_options --disable-unified-compilation
--- a/browser/config/mozconfigs/win64/release
+++ b/browser/config/mozconfigs/win64/release
@@ -1,13 +1,15 @@
 # This make file should be identical to the beta mozconfig, apart from the
 # safeguard below
+. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
 mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
 
 # safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
 # defines.sh during the beta cycle
 export BUILDING_RELEASE=1
+. $topsrcdir/build/win64/mozconfig.vs2010
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -230,17 +230,17 @@ let DebuggerController = {
       target.on("close", this._onTabDetached);
       target.on("navigate", this._onTabNavigated);
       target.on("will-navigate", this._onTabNavigated);
       this.client = client;
 
       if (target.chrome) {
         this._startChromeDebugging(chromeDebugger, startedDebugging.resolve);
       } else {
-        this._startDebuggingTab(threadActor, startedDebugging.resolve);
+        this._startDebuggingTab(startedDebugging.resolve);
         const startedTracing = promise.defer();
         this._startTracingTab(traceActor, startedTracing.resolve);
 
         return promise.all([startedDebugging.promise, startedTracing.promise]);
       }
 
       return startedDebugging.promise;
     }
@@ -334,38 +334,40 @@ let DebuggerController = {
     if (aResponse.error == "wrongOrder") {
       DebuggerView.Toolbar.showResumeWarning(aResponse.lastPausedUrl);
     }
   },
 
   /**
    * Sets up a debugging session.
    *
-   * @param string aThreadActor
-   *        The remote protocol grip of the tab.
    * @param function aCallback
    *        A function to invoke once the client attaches to the active thread.
    */
-  _startDebuggingTab: function(aThreadActor, aCallback) {
-    this.client.attachThread(aThreadActor, (aResponse, aThreadClient) => {
+  _startDebuggingTab: function(aCallback) {
+    this._target.activeTab.attachThread({
+      useSourceMaps: Prefs.sourceMapsEnabled
+    }, (aResponse, aThreadClient) => {
       if (!aThreadClient) {
         Cu.reportError("Couldn't attach to thread: " + aResponse.error);
         return;
       }
       this.activeThread = aThreadClient;
 
       this.ThreadState.connect();
       this.StackFrames.connect();
       this.SourceScripts.connect();
-      aThreadClient.resume(this._ensureResumptionOrder);
+      if (aThreadClient.paused) {
+        aThreadClient.resume(this._ensureResumptionOrder);
+      }
 
       if (aCallback) {
         aCallback();
       }
-    }, { useSourceMaps: Prefs.sourceMapsEnabled });
+    });
   },
 
   /**
    * Sets up a chrome debugging session.
    *
    * @param object aChromeDebugger
    *        The remote protocol grip of the chrome debugger.
    * @param function aCallback
@@ -377,17 +379,19 @@ let DebuggerController = {
         Cu.reportError("Couldn't attach to thread: " + aResponse.error);
         return;
       }
       this.activeThread = aThreadClient;
 
       this.ThreadState.connect();
       this.StackFrames.connect();
       this.SourceScripts.connect();
-      aThreadClient.resume(this._ensureResumptionOrder);
+      if (aThreadClient.paused) {
+        aThreadClient.resume(this._ensureResumptionOrder);
+      }
 
       if (aCallback) {
         aCallback();
       }
     }, { useSourceMaps: Prefs.sourceMapsEnabled });
   },
 
   /**
@@ -414,31 +418,33 @@ let DebuggerController = {
     });
   },
 
   /**
    * Detach and reattach to the thread actor with useSourceMaps true, blow
    * away old sources and get them again.
    */
   reconfigureThread: function(aUseSourceMaps) {
-    this.client.reconfigureThread({ useSourceMaps: aUseSourceMaps }, aResponse => {
+    this.activeThread.reconfigure({ useSourceMaps: aUseSourceMaps }, aResponse => {
       if (aResponse.error) {
         let msg = "Couldn't reconfigure thread: " + aResponse.message;
         Cu.reportError(msg);
         dumpn(msg);
         return;
       }
 
       // Reset the view and fetch all the sources again.
       DebuggerView.handleTabNavigation();
       this.SourceScripts.handleTabNavigation();
 
       // Update the stack frame list.
-      this.activeThread._clearFrames();
-      this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
+      if (this.activeThread.paused) {
+        this.activeThread._clearFrames();
+        this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
+      }
     });
   },
 
   /**
    * Attempts to quit the current process if allowed.
    *
    * @return object
    *         A promise that is resolved if the app will quit successfully.
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -1063,17 +1063,34 @@ FilterView.prototype = {
    * Called when a filtering key sequence was pressed.
    *
    * @param string aOperator
    *        The operator to use for filtering.
    */
   _doSearch: function(aOperator = "", aText = "") {
     this._searchbox.focus();
     this._searchbox.value = ""; // Need to clear value beforehand. Bug 779738.
-    this._searchbox.value = aOperator + (aText || DebuggerView.editor.getSelection());
+
+    if (aText) {
+      this._searchbox.value = aOperator + aText;
+    }
+    else if (DebuggerView.editor.somethingSelected()) {
+      this._searchbox.value = aOperator + DebuggerView.editor.getSelection();
+    }
+    else {
+      let cursor = DebuggerView.editor.getCursor();
+      let content = DebuggerView.editor.getText();
+      let location = DebuggerView.Sources.selectedValue;
+      let source = DebuggerController.Parser.get(content, location);
+      let identifier = source.getIdentifierAt({ line: cursor.line+1, column: cursor.ch });
+
+      if (identifier && identifier.name) {
+        this._searchbox.value = aOperator + identifier.name;
+      }
+    }
   },
 
   /**
    * Called when the source location filter key sequence was pressed.
    */
   _doFileSearch: function() {
     this._doSearch();
     this._searchboxHelpPanel.openPopup(this._searchbox);
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -242,14 +242,15 @@ support-files =
 [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-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_search-function.js]
 [browser_dbg_chrome-create.js]
 skip-if = os == "linux" # Bug 847558
 [browser_dbg_on-pause-raise.js]
 skip-if = os == "linux" # Bug 888811 & bug 891176
 [browser_dbg_break-on-dom-event.js]
 skip-if = os == "mac" # Bug 895426
--- a/browser/devtools/debugger/test/browser_dbg_break-on-dom-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_break-on-dom-01.js
@@ -27,17 +27,17 @@ function test() {
     is(gView.instrumentsPaneTab, "variables-tab",
       "The variables tab should be selected by default.");
 
     Task.spawn(function() {
       yield waitForSourceShown(aPanel, ".html");
       is(gEvents.itemCount, 0, "There should be no events before reloading.");
 
       let reloaded = waitForSourcesAfterReload();
-      gDebugger.gClient.activeTab.reload();
+      gDebugger.DebuggerController._target.activeTab.reload();
 
       is(gEvents.itemCount, 0, "There should be no events while reloading.");
       yield reloaded;
       is(gEvents.itemCount, 0, "There should be no events after reloading.");
 
       yield closeDebuggerAndFinish(aPanel);
     });
 
--- a/browser/devtools/debugger/test/browser_dbg_break-on-dom-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_break-on-dom-02.js
@@ -42,17 +42,17 @@ function test() {
     }
 
     function testFetchOnReloadWhenFocused() {
       return Task.spawn(function() {
         let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
 
         let reloading = once(gDebugger.gTarget, "will-navigate");
         let reloaded = waitForSourcesAfterReload();
-        gDebugger.gClient.activeTab.reload();
+        gDebugger.DebuggerController._target.activeTab.reload();
 
         yield reloading;
 
         is(gEvents.itemCount, 0,
           "There should be no events displayed in the view while reloading.");
         ok(true,
           "Event listeners were removed when the target started navigating.");
 
@@ -84,17 +84,17 @@ function test() {
         gView.toggleInstrumentsPane({ visible: true, animated: false }, 0);
         is(gView.instrumentsPaneHidden, false,
           "The instruments pane should still be visible.");
         is(gView.instrumentsPaneTab, "variables-tab",
           "The variables tab should be selected.");
 
         let reloading = once(gDebugger.gTarget, "will-navigate");
         let reloaded = waitForSourcesAfterReload();
-        gDebugger.gClient.activeTab.reload();
+        gDebugger.DebuggerController._target.activeTab.reload();
 
         yield reloading;
 
         is(gEvents.itemCount, 0,
           "There should be no events displayed in the view while reloading.");
         ok(true,
           "Event listeners were removed when the target started navigating.");
 
--- a/browser/devtools/debugger/test/browser_dbg_break-on-dom-event.js
+++ b/browser/devtools/debugger/test/browser_dbg_break-on-dom-event.js
@@ -63,17 +63,17 @@ function pauseDebuggee() {
 }
 
 // Test pause on all events.
 function testBreakOnAll() {
   let deferred = promise.defer();
 
   // Test calling pauseOnDOMEvents from a paused state.
   gThreadClient.pauseOnDOMEvents("*", (aPacket) => {
-    is(aPacket, undefined,
+    is(aPacket.error, undefined,
       "The pause-on-any-event request completed successfully.");
 
     gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
       is(aPacket.why.type, "pauseOnDOMEvents",
         "A hidden breakpoint was hit.");
       is(aPacket.frame.callee.name, "keyupHandler",
         "The keyupHandler is entered.");
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_search-function.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that Debugger Search uses the identifier under cursor
+ * if nothing is selected or manually passed
+ */
+
+"use strict";
+
+function test() {
+
+  const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
+
+  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    let Source = 'code_function-search-01.js';
+    let Debugger = aPanel.panelWin;
+    let Editor = Debugger.DebuggerView.editor;
+    let Filtering = Debugger.DebuggerView.Filtering;
+
+    waitForSourceShown(aPanel, Source).then(() => {
+      Editor.setCursor({ line: 7, ch: 0});
+      Filtering._doSearch("@");
+      is(Filtering._searchbox.value, "@test", "Searchbox value should be set to the identifier under cursor if no aText or selection provided");
+      closeDebuggerAndFinish(aPanel);
+    });
+  });
+};
--- a/browser/devtools/debugger/test/browser_dbg_source-maps-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_source-maps-02.js
@@ -3,35 +3,33 @@
 
 /**
  * Test that we can toggle between the original and generated sources.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_binary_search.html";
 const JS_URL = EXAMPLE_URL + "code_binary_search.js";
 
-let gTab, gDebuggee, gPanel, gDebugger;
-let gEditor, gSources, gFrames, gPrefs, gOptions;
+let gDebuggee, gPanel, gDebugger, gEditor;
+let gSources, gFrames, gPrefs, gOptions;
 
 function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
-    gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gPrefs = gDebugger.Prefs;
     gOptions = gDebugger.DebuggerView.Options;
 
     waitForSourceShown(gPanel, ".coffee")
       .then(testToggleGeneratedSource)
       .then(testSetBreakpoint)
-      .then(testHitBreakpoint)
       .then(testToggleOnPause)
       .then(testResume)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
   });
 }
@@ -63,44 +61,33 @@ function testToggleGeneratedSource() {
 
 function testSetBreakpoint() {
   let deferred = promise.defer();
 
   gDebugger.gThreadClient.setBreakpoint({ url: JS_URL, line: 7 }, aResponse => {
     ok(!aResponse.error,
       "Should be able to set a breakpoint in a js file.");
 
-    deferred.resolve();
-  });
-
-  return deferred.promise;
-}
-
-function testHitBreakpoint() {
-  let deferred = promise.defer();
-
-  gDebugger.gThreadClient.resume(aResponse => {
-    ok(!aResponse.error, "Shouldn't get an error resuming.");
-    is(aResponse.type, "resumed", "Type should be 'resumed'.");
+    gDebugger.gClient.addOneTimeListener("resumed", () => {
+      waitForCaretAndScopes(gPanel, 7).then(() => {
+        // Make sure that we have JavaScript stack frames.
+        is(gFrames.itemCount, 1,
+          "Should have only one frame.");
+        is(gFrames.getItemAtIndex(0).attachment.url.indexOf(".coffee"), -1,
+          "First frame should not be a coffee source frame.");
+        isnot(gFrames.getItemAtIndex(0).attachment.url.indexOf(".js"), -1,
+          "First frame should be a JS frame.");
 
-    waitForCaretAndScopes(gPanel, 7).then(() => {
-      // Make sure that we have JavaScript stack frames.
-      is(gFrames.itemCount, 1,
-        "Should have only one frame.");
-      is(gFrames.getItemAtIndex(0).attachment.url.indexOf(".coffee"), -1,
-        "First frame should not be a coffee source frame.");
-      isnot(gFrames.getItemAtIndex(0).attachment.url.indexOf(".js"), -1,
-        "First frame should be a JS frame.");
+        deferred.resolve();
+      });
 
-      deferred.resolve();
+      // This will cause the breakpoint to be hit, and put us back in the
+      // paused state.
+      gDebuggee.binary_search([0, 2, 3, 5, 7, 10], 5);
     });
-
-    // This will cause the breakpoint to be hit, and put us back in the
-    // paused state.
-    gDebuggee.binary_search([0, 2, 3, 5, 7, 10], 5);
   });
 
   return deferred.promise;
 }
 
 function testToggleOnPause() {
   let finished = waitForSourceAndCaretAndScopes(gPanel, ".coffee", 5).then(() => {
     is(gPrefs.sourceMapsEnabled, true,
@@ -143,17 +130,16 @@ function testResume() {
 
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
-  gTab = null;
   gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gFrames = null;
   gPrefs = null;
   gOptions = null;
--- a/browser/devtools/debugger/test/browser_dbg_source-maps-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_source-maps-03.js
@@ -3,33 +3,31 @@
 
 /**
  * Test that we can debug minified javascript with source maps.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_minified.html";
 const JS_URL = EXAMPLE_URL + "code_math.js";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gDebuggee, gPanel, gDebugger;
 let gEditor, gSources, gFrames;
 
 function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
-    gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gFrames = gDebugger.DebuggerView.StackFrames;
 
     waitForSourceShown(gPanel, JS_URL)
       .then(checkInitialSource)
       .then(testSetBreakpoint)
-      .then(testHitBreakpoint)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
   });
 }
 
 function checkInitialSource() {
@@ -40,59 +38,44 @@ function checkInitialSource() {
   is(gEditor.getText().split("\n").length, 46,
     "The debugger's editor should have the original source displayed, " +
     "not the whitespace stripped minified version.");
 }
 
 function testSetBreakpoint() {
   let deferred = promise.defer();
 
-  gDebugger.gThreadClient.interrupt(aResponse => {
-    gDebugger.gThreadClient.setBreakpoint({ url: JS_URL, line: 30, column: 21 }, aResponse => {
-      ok(!aResponse.error,
-        "Should be able to set a breakpoint in a js file.");
-      ok(!aResponse.actualLocation,
-        "Should be able to set a breakpoint on line 30 and column 10.");
+  gDebugger.gThreadClient.setBreakpoint({ url: JS_URL, line: 30, column: 21 }, aResponse => {
+    ok(!aResponse.error,
+      "Should be able to set a breakpoint in a js file.");
+    ok(!aResponse.actualLocation,
+      "Should be able to set a breakpoint on line 30 and column 10.");
 
-      deferred.resolve();
+    gDebugger.gClient.addOneTimeListener("resumed", () => {
+      waitForCaretAndScopes(gPanel, 30).then(() => {
+        // Make sure that we have the right stack frames.
+        is(gFrames.itemCount, 9,
+          "Should have nine frames.");
+        is(gFrames.getItemAtIndex(0).attachment.url.indexOf(".min.js"), -1,
+          "First frame should not be a minified JS frame.");
+        isnot(gFrames.getItemAtIndex(0).attachment.url.indexOf(".js"), -1,
+          "First frame should be a JS frame.");
+
+        deferred.resolve();
+      });
+
+      // This will cause the breakpoint to be hit, and put us back in the
+      // paused state.
+      gDebuggee.arithmetic();
     });
   });
 
   return deferred.promise;
 }
 
-function testHitBreakpoint() {
-  let deferred = promise.defer();
-
-  gDebugger.gThreadClient.resume(aResponse => {
-    ok(!aResponse.error, "Shouldn't get an error resuming.");
-    is(aResponse.type, "resumed", "Type should be 'resumed'.");
-
-    waitForCaretAndScopes(gPanel, 30).then(() => {
-      // Make sure that we have the right stack frames.
-      is(gFrames.itemCount, 9,
-        "Should have nine frames.");
-      is(gFrames.getItemAtIndex(0).attachment.url.indexOf(".min.js"), -1,
-        "First frame should not be a minified JS frame.");
-      isnot(gFrames.getItemAtIndex(0).attachment.url.indexOf(".js"), -1,
-        "First frame should be a JS frame.");
-
-      deferred.resolve();
-    });
-
-    // This will cause the breakpoint to be hit, and put us back in the
-    // paused state.
-    gDebuggee.arithmetic();
-  });
-
-  return deferred.promise;
-}
-
-
 registerCleanupFunction(function() {
-  gTab = null;
   gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gEditor = null;
   gSources = null;
   gFrames = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
@@ -96,17 +96,17 @@ function testSetBreakpoint() {
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
 function reloadPage() {
   let loaded = waitForSourceAndCaret(gPanel, ".js", 3);
-  gDebugger.gClient.activeTab.reload();
+  gDebugger.DebuggerController._target.activeTab.reload();
   return loaded.then(() => ok(true, "Page was reloaded and execution resumed."));
 }
 
 function testHitBreakpoint() {
   let deferred = promise.defer();
 
   gDebugger.gThreadClient.resume(aResponse => {
     ok(!aResponse.error, "Shouldn't get an error resuming.");
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -405,17 +405,17 @@ function ensureThreadClientState(aPanel,
     return promise.resolve(null);
   } else {
     return waitForThreadEvents(aPanel, aState);
   }
 }
 
 function navigateActiveTabTo(aPanel, aUrl, aWaitForEventName, aEventRepeat) {
   let finished = waitForDebuggerEvents(aPanel, aWaitForEventName, aEventRepeat);
-  let activeTab = aPanel.panelWin.gClient.activeTab;
+  let activeTab = aPanel.panelWin.DebuggerController._target.activeTab;
   aUrl ? activeTab.navigateTo(aUrl) : activeTab.reload();
   return finished;
 }
 
 function navigateActiveTabInHistory(aPanel, aDirection, aWaitForEventName, aEventRepeat) {
   let finished = waitForDebuggerEvents(aPanel, aWaitForEventName, aEventRepeat);
   content.history[aDirection]();
   return finished;
--- a/browser/devtools/fontinspector/font-inspector.js
+++ b/browser/devtools/fontinspector/font-inspector.js
@@ -189,27 +189,26 @@ FontInspector.prototype = {
    * Select the <body> to show all the fonts included in the document.
    */
   showAll: function FI_showAll() {
     if (!this.isActive() ||
         !this.inspector.selection.isConnected() ||
         !this.inspector.selection.isElementNode()) {
       return;
     }
-    let node = this.inspector.selection.nodeFront;
-    let contentDocument = node.ownerDocument;
-    let root = contentDocument.documentElement;
-    if (contentDocument.body) {
-      root = contentDocument.body;
-    }
-    this.inspector.selection.setNode(root, "fontinspector");
+
+    // Select the body node to show all fonts
+    let walker = this.inspector.walker;
+
+    walker.getRootNode().then(root => walker.querySelector(root, "body")).then(body => {
+      this.inspector.selection.setNodeFront(body, "fontinspector");
+    });
   },
 }
 
-
 window.setPanel = function(panel) {
   window.fontInspector = new FontInspector(panel, window);
 }
 
 window.onunload = function() {
   if (window.fontInspector) {
     window.fontInspector.destroy();
   }
--- a/browser/devtools/fontinspector/test/browser_fontinspector.js
+++ b/browser/devtools/fontinspector/test/browser_fontinspector.js
@@ -7,18 +7,18 @@ let {devtools} = Cu.import("resource://g
 let TargetFactory = devtools.TargetFactory;
 
 let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 
 function test() {
   waitForExplicitFinish();
 
   let doc;
-  let node;
   let view;
+  let viewDoc;
   let inspector;
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     doc = content.document;
     waitForFocus(setupTest, content);
   }, true);
@@ -38,65 +38,88 @@ function test() {
       let target = TargetFactory.forTab(gBrowser.selectedTab);
       gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
         openFontInspector(toolbox.getCurrentPanel());
       });
     }
   }
 
   function openFontInspector(aInspector) {
+    info("Inspector open");
     inspector = aInspector;
 
-    info("Inspector open");
-
     inspector.selection.setNode(doc.body);
     inspector.sidebar.select("fontinspector");
-    inspector.sidebar.once("fontinspector-ready", viewReady);
+    inspector.sidebar.once("fontinspector-ready", testBodyFonts);
   }
 
-  function viewReady() {
+  function testBodyFonts() {
     info("Font Inspector ready");
 
     view = inspector.sidebar.getWindowForTab("fontinspector");
+    viewDoc = view.document;
 
     ok(!!view.fontInspector, "Font inspector document is alive.");
 
-    let d = view.document;
-
-    let s = d.querySelectorAll("#all-fonts > section");
+    let s = viewDoc.querySelectorAll("#all-fonts > section");
     is(s.length, 2, "Found 2 fonts");
 
     is(s[0].querySelector(".font-name").textContent,
        "DeLarge Bold", "font 0: Right font name");
     ok(s[0].classList.contains("is-remote"),
        "font 0: is remote");
     is(s[0].querySelector(".font-url").value,
        "http://mochi.test:8888/browser/browser/devtools/fontinspector/test/browser_font.woff",
        "font 0: right url");
     is(s[0].querySelector(".font-format").textContent,
        "woff", "font 0: right font format");
     is(s[0].querySelector(".font-css-name").textContent,
        "bar", "font 0: right css name");
 
-
     let font1Name = s[1].querySelector(".font-name").textContent;
     let font1CssName = s[1].querySelector(".font-css-name").textContent;
 
     // On Linux test machines, the Arial font doesn't exist.
     // The fallback is "Liberation Sans"
 
     ok((font1Name == "Arial") || (font1Name == "Liberation Sans"),
        "font 1: Right font name");
     ok(s[1].classList.contains("is-local"), "font 1: is local");
     ok((font1CssName == "Arial") || (font1CssName == "Liberation Sans"),
        "Arial", "font 1: right css name");
 
-    executeSoon(function() {
-      gDevTools.once("toolbox-destroyed", finishUp);
-      inspector._toolbox.destroy();
+    testDivFonts();
+  }
+
+  function testDivFonts() {
+    inspector.selection.setNode(doc.querySelector("div"));
+    inspector.once("inspector-updated", () => {
+      let s = viewDoc.querySelectorAll("#all-fonts > section");
+      is(s.length, 1, "Found 1 font on DIV");
+      is(s[0].querySelector(".font-name").textContent, "DeLarge Bold",
+        "The DIV font has the right name");
+
+      testShowAllFonts();
+    });
+  }
+
+  function testShowAllFonts() {
+    viewDoc.querySelector("#showall").click();
+    inspector.once("inspector-updated", () => {
+      is(inspector.selection.node, doc.body, "Show all fonts selected the body node");
+      let s = viewDoc.querySelectorAll("#all-fonts > section");
+      is(s.length, 2, "And font-inspector still shows 2 fonts for body");
+
+      finishUp();
     });
   }
 
   function finishUp() {
-    gBrowser.removeCurrentTab();
-    finish();
+    executeSoon(function() {
+      gDevTools.once("toolbox-destroyed", () => {
+        doc = view = viewDoc = inspector = null;
+        gBrowser.removeCurrentTab();
+        finish();
+      });
+      inspector._toolbox.destroy();
+    });
   }
 }
--- a/browser/devtools/framework/target.js
+++ b/browser/devtools/framework/target.js
@@ -279,16 +279,17 @@ TabTarget.prototype = {
     this._setupRemoteListeners();
 
     let attachTab = () => {
       this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
         if (!aTabClient) {
           this._remote.reject("Unable to attach to the tab");
           return;
         }
+        this.activeTab = aTabClient;
         this.threadActor = aResponse.threadActor;
         this._remote.resolve(null);
       });
     };
 
     if (this.isLocalTab) {
       this._client.connect((aType, aTraits) => {
         this._client.listTabs(aResponse => {
@@ -439,53 +440,57 @@ TabTarget.prototype = {
     // non-remoted targets.
     this.off("thread-resumed", this._handleThreadState);
     this.off("thread-paused", this._handleThreadState);
 
     if (this._tab) {
       this._teardownListeners();
     }
 
+    let cleanupAndResolve = () => {
+      this._cleanup();
+      this._destroyer.resolve(null);
+    };
     // If this target was not remoted, the promise will be resolved before the
     // function returns.
     if (this._tab && !this._client) {
-      this._cleanup();
-      this._destroyer.resolve(null);
+      cleanupAndResolve();
     } else if (this._client) {
       // If, on the other hand, this target was remoted, the promise will be
       // resolved after the remote connection is closed.
       this._teardownRemoteListeners();
 
       if (this.isLocalTab) {
         // We started with a local tab and created the client ourselves, so we
         // should close it.
-        this._client.close(() => {
-          this._cleanup();
-          this._destroyer.resolve(null);
-        });
+        this._client.close(cleanupAndResolve);
       } else {
         // The client was handed to us, so we are not responsible for closing
-        // it.
-        this._cleanup();
-        this._destroyer.resolve(null);
+        // it. We just need to detach from the tab, if already attached.
+        if (this.activeTab) {
+          this.activeTab.detach(cleanupAndResolve);
+        } else {
+          cleanupAndResolve();
+        }
       }
     }
 
     return this._destroyer.promise;
   },
 
   /**
    * Clean up references to what this target points to.
    */
   _cleanup: function TabTarget__cleanup() {
     if (this._tab) {
       targets.delete(this._tab);
     } else {
       promiseTargets.delete(this._form);
     }
+    this.activeTab = null;
     this._client = null;
     this._tab = null;
     this._form = null;
     this._remote = null;
   },
 
   toString: function() {
     return 'TabTarget:' + (this._tab ? this._tab : (this._form && this._form.actor));
--- a/browser/devtools/framework/toolbox-options.js
+++ b/browser/devtools/framework/toolbox-options.js
@@ -207,17 +207,17 @@ OptionsPanel.prototype = {
           newValue: this.value
         };
         data.oldValue = Services.prefs.getCharPref(data.pref);
         Services.prefs.setCharPref(data.pref, data.newValue);
         gDevTools.emit("pref-changed", data);
       }.bind(menulist));
     }
 
-    this.target.client.attachTab(this.target.client.activeTab._actor, (response) => {
+    this.target.client.attachTab(this.target.activeTab._actor, (response) => {
       this._origJavascriptEnabled = response.javascriptEnabled;
       this._origCacheEnabled = response.cacheEnabled;
 
       this._populateDisableJSCheckbox();
       this._populateDisableCacheCheckbox();
     });
   },
 
@@ -243,33 +243,33 @@ OptionsPanel.prototype = {
    */
   _disableJSClicked: function(event) {
     let checked = event.target.checked;
 
     let options = {
       "javascriptEnabled": !checked
     };
 
-    this.target.client.reconfigureTab(options);
+    this.target.activeTab.reconfigure(options);
   },
 
   /**
    * Disables the cache for the currently loaded tab.
    *
    * @param {Event} event
    *        The event sent by checking / unchecking the disable cache checkbox.
    */
   _disableCacheClicked: function(event) {
     let checked = event.target.checked;
 
     let options = {
       "cacheEnabled": !checked
     };
 
-    this.target.client.reconfigureTab(options);
+    this.target.activeTab.reconfigure(options);
   },
 
   destroy: function() {
     if (this.destroyPromise) {
       return this.destroyPromise;
     }
 
     let deferred = promise.defer();
@@ -286,16 +286,16 @@ OptionsPanel.prototype = {
     this._disableJSClicked = this._disableCacheClicked = null;
 
     // If the cache or JavaScript is disabled we need to revert them to their
     // original values.
     let options = {
       "cacheEnabled": this._origCacheEnabled,
       "javascriptEnabled": this._origJavascriptEnabled
     };
-    this.target.client.reconfigureTab(options, () => {
+    this.target.activeTab.reconfigure(options, () => {
       this.toolbox = null;
       deferred.resolve();
     }, true);
 
     return deferred.promise;
   }
 };
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1212,17 +1212,17 @@ Toolbox.prototype = {
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
     let outstanding = [];
     for (let [id, panel] of this._toolPanels) {
       try {
         outstanding.push(panel.destroy());
       } catch (e) {
         // We don't want to stop here if any panel fail to close.
-        console.error(e);
+        console.error("Panel " + id + ":", e);
       }
     }
 
     // Destroying the walker and inspector fronts
     outstanding.push(this.destroyInspector());
 
     // Removing buttons
     this._pickerButton.removeEventListener("command", this.togglePicker, false);
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -977,16 +977,17 @@ var Scratchpad = {
         let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
 
         if (chrome && modeline["-sp-context"] === "browser") {
           this.setBrowserContext();
         }
 
         this.editor.setText(content);
         this.editor.clearHistory();
+        this.dirty = false;
         document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
       }
       else if (!aSilentError) {
         window.alert(this.strings.GetStringFromName("openFile.failed"));
       }
 
       if (aCallback) {
         aCallback.call(this, aStatus, content);
--- a/browser/devtools/scratchpad/test/browser_scratchpad_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_files.js
@@ -48,16 +48,19 @@ function fileImported(aStatus, aFileCont
      "the temporary file was imported successfully with Scratchpad");
 
   is(aFileContent, gFileContent,
      "received data is correct");
 
   is(gScratchpad.getText(), gFileContent,
      "the editor content is correct");
 
+  is(gScratchpad.dirty, false,
+     "the editor marks imported file as saved");
+
   // Save the file after changes.
   gFileContent += "// omg, saved!";
   gScratchpad.editor.setText(gFileContent);
 
   gScratchpad.exportToFile(gFile.QueryInterface(Ci.nsILocalFile), true, true,
                           fileExported);
 }
 
--- a/browser/devtools/shadereditor/test/head.js
+++ b/browser/devtools/shadereditor/test/head.js
@@ -224,22 +224,22 @@ function ensurePixelIs(aDebuggee, aPosit
 }
 
 function navigateInHistory(aTarget, aDirection, aWaitForTargetEvent = "navigate") {
   executeSoon(() => content.history[aDirection]());
   return once(aTarget, aWaitForTargetEvent);
 }
 
 function navigate(aTarget, aUrl, aWaitForTargetEvent = "navigate") {
-  executeSoon(() => aTarget.client.activeTab.navigateTo(aUrl));
+  executeSoon(() => aTarget.activeTab.navigateTo(aUrl));
   return once(aTarget, aWaitForTargetEvent);
 }
 
 function reload(aTarget, aWaitForTargetEvent = "navigate") {
-  executeSoon(() => aTarget.client.activeTab.reload());
+  executeSoon(() => aTarget.activeTab.reload());
   return once(aTarget, aWaitForTargetEvent);
 }
 
 function initBackend(aUrl) {
   info("Initializing a shader editor front.");
 
   if (!DebuggerServer.initialized) {
     DebuggerServer.init(() => true);
--- a/browser/devtools/shared/Parser.jsm
+++ b/browser/devtools/shared/Parser.jsm
@@ -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/. */
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 
 XPCOMUtils.defineLazyModuleGetter(this,
   "Reflect", "resource://gre/modules/reflect.jsm");
 
 this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
 
 /**
  * A JS parser using the reflection API.
@@ -60,31 +61,31 @@ Parser.prototype = {
     if (!scriptMatches.length) {
       // Reflect.parse throws when encounters a syntax error.
       try {
         let nodes = Reflect.parse(aSource);
         let length = aSource.length;
         syntaxTrees.push(new SyntaxTree(nodes, aUrl, length));
       } catch (e) {
         this.errors.push(e);
-        log(aUrl, e);
+        DevToolsUtils.reportException(aUrl, e);
       }
     }
     // Generate the AST nodes for each script.
     else {
       for (let script of scriptMatches) {
         // Reflect.parse throws when encounters a syntax error.
         try {
           let nodes = Reflect.parse(script);
           let offset = aSource.indexOf(script);
           let length = script.length;
           syntaxTrees.push(new SyntaxTree(nodes, aUrl, length, offset));
         } catch (e) {
           this.errors.push(e);
-          log(aUrl, e);
+          DevToolsUtils.reportException(aUrl, e);
         }
       }
     }
 
     let pool = new SyntaxTreesPool(syntaxTrees);
 
     // Cache the syntax trees pool by the specified url. This is entirely
     // optional, but it's strongly encouraged to cache ASTs because
@@ -220,17 +221,17 @@ SyntaxTreesPool.prototype = {
           scriptLength: syntaxTree.length,
           scriptOffset: syntaxTree.offset,
           parseResults: syntaxTree[aFunction].apply(syntaxTree, aParams)
         });
       } catch (e) {
         // Can't guarantee that the tree traversal logic is forever perfect :)
         // Language features may be added, in which case the recursive methods
         // need to be updated. If an exception is thrown here, file a bug.
-        log("syntax tree", e);
+        DevToolsUtils.reportException("syntax tree", e);
       }
     }
     this._cache.set(requestId, results);
     return results;
   },
 
   _trees: null,
   _cache: null
@@ -2336,31 +2337,9 @@ let SyntaxTreeVisitor = {
       }
     }
     if (aCallbacks.onLiteral) {
       aCallbacks.onLiteral(aNode);
     }
   }
 };
 
-/**
- * Logs a warning.
- *
- * @param string aStr
- *        The message to be displayed.
- * @param Exception aEx
- *        The thrown exception.
- */
-function log(aStr, aEx) {
-  let msg = "Warning: " + aStr + ", " + aEx.message;
-
-  if ("lineNumber" in aEx && "columnNumber" in aEx) {
-    msg += ", line: " + aEx.lineNumber + ", column: " + aEx.columnNumber;
-  }
-  if ("stack" in aEx) {
-    msg += "\n" + aEx.stack;
-  }
-
-  Cu.reportError(msg);
-  dump(msg + "\n");
-};
-
 XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", () => Reflect);
--- a/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm
+++ b/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm
@@ -162,17 +162,19 @@ BreadcrumbsWidget.prototype = {
   ensureElementIsVisible: function(aElement) {
     if (!aElement) {
       return;
     }
 
     // Repeated calls to ensureElementIsVisible would interfere with each other
     // and may sometimes result in incorrect scroll positions.
     setNamedTimeout("breadcrumb-select", ENSURE_SELECTION_VISIBLE_DELAY, () => {
-      this._list.ensureElementIsVisible(aElement);
+      if (this._list.ensureElementIsVisible) {
+        this._list.ensureElementIsVisible(aElement);
+      }
     });
   },
 
   /**
    * The underflow and overflow listener for the arrowscrollbox container.
    */
   _onUnderflow: function({ target }) {
     if (target != this._list) {
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -505,18 +505,16 @@
 @BINPATH@/components/Weave.js
 #endif
 #ifdef MOZ_CAPTIVEDETECT
 @BINPATH@/components/CaptivePortalDetectComponents.manifest
 @BINPATH@/components/captivedetect.js
 #endif
 @BINPATH@/components/servicesComponents.manifest
 @BINPATH@/components/cryptoComponents.manifest
-@BINPATH@/components/TelemetryPing.js
-@BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/TelemetryStartup.js
 @BINPATH@/components/TelemetryStartup.manifest
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/SettingsService.js
 @BINPATH@/components/SettingsService.manifest
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -124,16 +124,17 @@ These should match what Safari and other
 <!ENTITY shareLinkCmd.label "Share This Link">
 <!ENTITY shareLinkCmd.accesskey "s">
 <!ENTITY shareImageCmd.label "Share This Image">
 <!ENTITY shareImageCmd.accesskey "s">
 <!ENTITY shareSelectCmd.label "Share Selection">
 <!ENTITY shareSelectCmd.accesskey "s">
 <!ENTITY shareVideoCmd.label "Share This Video">
 <!ENTITY shareVideoCmd.accesskey "s">
+<!ENTITY feedsMenu.label "Subscribe">
 <!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
 <!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
 <!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
 <!ENTITY showAllBookmarks2.label "Show All Bookmarks">
 <!ENTITY unsortedBookmarksCmd.label "Unsorted Bookmarks">
 <!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks">
 
 <!ENTITY backCmd.label                "Back">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -472,16 +472,20 @@ getUserMedia.noVideo.label = No Video
 getUserMedia.noAudio.label = No Audio
 getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected Devices
 getUserMedia.shareSelectedDevices.accesskey = S
 getUserMedia.denyRequest.label = Don't Share
 getUserMedia.denyRequest.accesskey = D
 getUserMedia.sharingCamera.message2 = You are currently sharing your camera with this page.
 getUserMedia.sharingMicrophone.message2 = You are currently sharing your microphone with this page.
 getUserMedia.sharingCameraAndMicrophone.message2 = You are currently sharing your camera and microphone with this page.
+getUserMedia.continueSharing.label = Continue Sharing
+getUserMedia.continueSharing.accesskey = C
+getUserMedia.stopSharing.label = Stop Sharing
+getUserMedia.stopSharing.accesskey = S
 
 # Mixed Content Blocker Doorhanger Notification
 # LOCALIZATION NOTE - %S is brandShortName
 mixedContentBlocked.message = %S has blocked content that isn't secure.
 mixedContentBlocked.keepBlockingButton.label = Keep Blocking
 mixedContentBlocked.keepBlockingButton.accesskey = B
 mixedContentBlocked.unblock.label = Disable Protection on This Page
 mixedContentBlocked.unblock.accesskey = D
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -156,21 +156,16 @@ var BrowserUI = {
         FindHelperUI.init();
 #ifdef NIGHTLY_BUILD
         PdfJs.init();
 #endif
       } catch(ex) {
         Util.dumpLn("Exception in delay load module:", ex.message);
       }
 
-      if (WindowsPrefSync) {
-        // Pulls in Desktop controlled prefs and pushes out Metro controlled prefs
-        WindowsPrefSync.init();
-      }
-
       // check for left over crash reports and submit them if found.
       BrowserUI.startupCrashCheck();
 
       Util.dumpLn("* delay load complete.");
     }, false);
 
 #ifndef MOZ_OFFICIAL_BRANDING
     setTimeout(function() {
--- a/browser/metro/base/content/browser.js
+++ b/browser/metro/base/content/browser.js
@@ -180,18 +180,17 @@ var Browser = {
           let uri = commandURL || Browser.getHomePage();
           self.addTab(uri, true);
         }
       }
 
       // Should we restore the previous session (crash or some other event)
       let ss = Cc["@mozilla.org/browser/sessionstore;1"]
                .getService(Ci.nsISessionStore);
-      let shouldRestore = ss.shouldRestore()
-                       || (3 == Services.prefs.getIntPref("browser.startup.page"));
+      let shouldRestore = ss.shouldRestore();
       if (shouldRestore) {
         let bringFront = false;
         // First open any commandline URLs, except the homepage
         if (activationURI && activationURI != kStartURI) {
           this.addTab(activationURI, true, null, { flags: Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP });
         } else if (commandURL && commandURL != kStartURI) {
           this.addTab(commandURL, true);
         } else {
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -638,19 +638,16 @@ Desktop browser's sync prefs.
         <description>&sync.flyout.pairSuccess.description1;</description>
         <description>&sync.flyout.pairSuccess.description2;</description>
       </vbox>
 
     </flyoutpanel>
 #endif
 
     <flyoutpanel id="prefs-flyoutpanel" class="flyout-narrow" headertext="&optionsHeader.title;">
-      <settings id="prefs-charencoding" label="&optionsHeader.char.title;">
-        <setting pref="browser.menu.showCharacterEncoding" title="&optionsHeader.char.options.label;" type="bool"/>
-      </settings>
       <settings id="prefs-privdata" label="&clearPrivateData.title;">
         <description>&clearPrivateData.label;</description>
 
         <checkbox id="prefs-privdata-history" itemName="history" label="&clearPrivateData.history;" checked="true" />
 
         <checkbox id="prefs-privdata-other" label="&clearPrivateData.otherdata;"/>
         <hbox id="prefs-privdata-subitems" >
           <checkbox class="privdata-subitem-item" checked="true" itemName="downloads" label="&clearPrivateData.downloadHist;"/>
--- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
+++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
@@ -160,16 +160,23 @@ var ContextMenuHandler = {
   _onCopy: function _onCopy() {
     if (Util.isTextInput(this._target)) {
       let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
       if (edit) {
         edit.editor.copy();
       } else {
         Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
       }
+    } else if (Util.isEditableContent(this._target)) {
+      try {
+        this._target.ownerDocument.execCommand("copy", false);
+      } catch (ex) {
+        dump("ContextMenuHandler: exception copying from contentEditable: " +
+          ex.message + "\n");
+      }
     } else {
       let selectionText = this._previousState.string;
 
       Cc["@mozilla.org/widget/clipboardhelper;1"]
         .getService(Ci.nsIClipboardHelper).copyString(selectionText);
     }
     this.reset();
   },
--- a/browser/metro/base/content/pages/config.js
+++ b/browser/metro/base/content/pages/config.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {classes: Cc, interfaces: Ci, manager: Cm, utils: Cu} = Components;
 Cu.import("resource://gre/modules/Services.jsm");
 
-const PRIVATE_PREF_PREFIX = "capability.";   // Tag to prevent exposing private preferences
 const INITIAL_PAGE_DELAY = 500;   // Initial pause on program start for scroll alignment
 const PREFS_BUFFER_MAX = 100;   // Max prefs buffer size for getPrefsBuffer()
 const PAGE_SCROLL_TRIGGER = 200;     // Triggers additional getPrefsBuffer() on user scroll-to-bottom
 const FILTER_CHANGE_TRIGGER = 200;     // Delay between responses to filterInput changes
 const INNERHTML_VALUE_DELAY = 100;    // Delay before providing prefs innerHTML value
 
 let gStringBundle = Services.strings.createBundle("chrome://browser/locale/config.properties");
 let gClipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
@@ -81,22 +80,16 @@ var NewPrefDialog = {
   // As new pref name is initially displayed, re-focused, or modifed during user input
   _updatePositiveButton: function AC_updatePositiveButton(aPrefName) {
     this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.createButton");
     this._positiveButton.setAttribute("disabled", true);
     if (aPrefName == "") {
       return;
     }
 
-    // Prevent addition of new "private" preferences
-    if (aPrefName.startsWith(PRIVATE_PREF_PREFIX)) {
-      this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.privateButton");
-      return;
-    }
-
     // If item already in list, it's being changed, else added
     let item = document.querySelector(".pref-item[name=" + aPrefName.quote() + "]");
     if (item) {
       this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.changeButton");
     } else {
       this._positiveButton.removeAttribute("disabled");
     }
   },
@@ -201,20 +194,17 @@ var AboutConfig = {
   _list: null,
 
   // Init the main AboutConfig dialog
   init: function AC_init() {
     this.filterInput = document.getElementById("filter-input");
     this._prefsContainer = document.getElementById("prefs-container");
     this._loadingContainer = document.getElementById("loading-container");
 
-    let list = Services.prefs.getChildList("", {}).filter(function(aElement) {
-      // Prevent display of "private" preferences
-      return !aElement.startsWith(PRIVATE_PREF_PREFIX);
-    });
+    let list = Services.prefs.getChildList("");
     this._list = list.sort().map( function AC_getMapPref(aPref) {
       return new Pref(aPref);
     }, this);
 
     // Display the current prefs list (retains searchFilter value)
     this.bufferFilterInput();
 
     // Setup the prefs observers
@@ -456,18 +446,18 @@ var AboutConfig = {
 
     pref.value += aInt;
   },
 
   // Observe preference changes
   observe: function AC_observe(aSubject, aTopic, aPrefName) {
     let pref = new Pref(aPrefName);
 
-    // Ignore uninteresting preference changes, and external changes to "private" preferences
-    if ((aTopic != "nsPref:changed") || pref.name.startsWith(PRIVATE_PREF_PREFIX)) {
+    // Ignore uninteresting changes, and avoid "private" preferences
+    if (aTopic != "nsPref:changed") {
       return;
     }
 
     // If pref type invalid, refresh display as user reset/removed an item from the list
     if (pref.type == Services.prefs.PREF_INVALID) {
       document.location.reload();
       return;
     }
--- a/browser/metro/base/tests/mochitest/browser_context_menu_tests.js
+++ b/browser/metro/base/tests/mochitest/browser_context_menu_tests.js
@@ -739,55 +739,142 @@ gTests.push({
     ok(ContextUI.tabbarVisible, "Tabbar is visible on context menu action.");
 
     ContextUI.dismiss();
     yield waitForCondition(() => !ContextUI.navbarVisible);
 
     // Case #2: Document isn't in design mode and text is selected.
     tabWindow.getSelection().selectAllChildren(testSpan);
 
-    let promise = waitForEvent(tabWindow.document, "popupshown");
+    let promise = waitForEvent(document, "popupshown");
     sendContextMenuClickToSelection(tabWindow);
     yield promise;
 
     checkContextUIMenuItemVisibility(["context-copy", "context-search"]);
 
     promise = waitForEvent(document, "popuphidden");
     ContextMenuUI.hide();
     yield promise;
 
     // Case #3: Document is in design mode and nothing is selected.
     tabWindow.document.designMode = "on";
     tabWindow.getSelection().removeAllRanges();
 
-    promise = waitForEvent(tabWindow.document, "popupshown");
+    promise = waitForEvent(document, "popupshown");
     sendContextMenuClickToElement(tabWindow, testSpan);
     yield promise;
 
     checkContextUIMenuItemVisibility(["context-select-all", "context-select"]);
 
     promise = waitForEvent(document, "popuphidden");
     ContextMenuUI.hide();
     yield promise;
 
     // Case #4: Document is in design mode and text is selected.
     tabWindow.getSelection().selectAllChildren(testSpan);
 
-    promise = waitForEvent(tabWindow.document, "popupshown");
+    promise = waitForEvent(document, "popupshown");
     sendContextMenuClickToSelection(tabWindow);
     yield promise;
 
     checkContextUIMenuItemVisibility(["context-cut", "context-copy",
                                       "context-select-all", "context-select",
                                       "context-search"]);
 
     promise = waitForEvent(document, "popuphidden");
     ContextMenuUI.hide();
     yield promise;
 
     Browser.closeTab(Browser.selectedTab, { forceClose: true });
   }
 });
 
+gTests.push({
+  desc: "Bug 961702 - 'Copy' context menu action does not copy rich content " +
+        "while document in design mode (or inside container that allows to " +
+        "edit its content)",
+  run: function test() {
+    info(chromeRoot + "browser_context_menu_tests_05.html");
+    yield addTab(chromeRoot + "browser_context_menu_tests_05.html");
+
+    purgeEventQueue();
+    emptyClipboard();
+    ContextUI.dismiss();
+
+    yield waitForCondition(() => !ContextUI.navbarVisible);
+
+    let tabWindow = Browser.selectedTab.browser.contentWindow;
+    let testDiv = tabWindow.document.getElementById("div1");
+
+    // Case #1: Document is in design mode.
+    tabWindow.document.designMode = "on";
+
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(tabWindow, testDiv);
+    yield promise;
+
+    let selectAllMenuItem = document.getElementById("context-select-all");
+    promise = waitForEvent(document, "popuphidden");
+    sendNativeTap(selectAllMenuItem);
+    yield promise;
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToSelection(tabWindow);
+    yield promise;
+
+    let copyMenuItem = document.getElementById("context-copy");
+    promise = waitForEvent(document, "popuphidden");
+    sendNativeTap(copyMenuItem);
+    yield promise;
+
+    // The wait is needed to give time to populate the clipboard.
+    let clipboardContent = "";
+    let contentToCopy = tabWindow.document.body.innerHTML;
+    yield waitForCondition(function () {
+      clipboardContent = SpecialPowers.getClipboardData("text/html");
+      return clipboardContent == contentToCopy;
+    });
+    ok(clipboardContent == contentToCopy, "Rich content copied.");
+
+    // Case #2: Container with editable content.
+    emptyClipboard();
+    tabWindow.document.designMode = "off";
+    tabWindow.getSelection().removeAllRanges();
+
+    promise = waitForEvent(tabWindow.document.body, "focus");
+    sendNativeTap(testDiv);
+    yield promise;
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(tabWindow, testDiv);
+    yield promise;
+
+    selectAllMenuItem = document.getElementById("context-select-all");
+    promise = waitForEvent(document, "popuphidden");
+    sendNativeTap(selectAllMenuItem);
+    yield promise;
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToSelection(tabWindow);
+    yield promise;
+
+    copyMenuItem = document.getElementById("context-copy");
+    promise = waitForEvent(document, "popuphidden");
+    sendNativeTap(copyMenuItem);
+    yield promise;
+
+     // The wait is needed to give time to populate the clipboard.
+    clipboardContent = "";
+    contentToCopy = testDiv.innerHTML;
+    yield waitForCondition(function () {
+      clipboardContent = SpecialPowers.getClipboardData("text/html");
+      return clipboardContent == contentToCopy;
+    });
+    ok(clipboardContent == contentToCopy, "Rich content copied.");
+
+    Browser.closeTab(Browser.selectedTab, { forceClose: true });
+  }
+});
+
 function test() {
   setDevPixelEqualToPx();
   runTests();
 }
copy from browser/metro/base/tests/mochitest/browser_context_menu_tests_04.html
copy to browser/metro/base/tests/mochitest/browser_context_menu_tests_05.html
--- a/browser/metro/base/tests/mochitest/browser_context_menu_tests_04.html
+++ b/browser/metro/base/tests/mochitest/browser_context_menu_tests_05.html
@@ -1,15 +1,15 @@
 <!DOCTYPE html>
 <html>
   <head>
     <style>
     </style>
   </head>
 <body style="padding: 10px; margin: 10px;">
-  <div style="margin: 0; padding: 200px 0;">
-    <span id="text1">hello, I'm sorry but I <a id="text1-link" href="#test">must be going</a>.</span>
-  </div>
-  <div style="margin: 0; padding: 200px 0;">
-    <span id="text2"><a id="text2-link" href="#test">hello, I'm sorry but</a> I must be going.</span>
+  <span id="text1">Test text</span>
+  <div contenteditable="true" id="div1" style="border: 2px solid blue; width: 200px; height: 200px;">
+    <table>
+      <tr><td>Test content</td></tr>
+    </table>
   </div>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/browser/metro/base/tests/mochitest/metro.ini
+++ b/browser/metro/base/tests/mochitest/metro.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 support-files =
   browser_context_menu_tests_01.html
   browser_context_menu_tests_02.html
   browser_context_menu_tests_03.html
   browser_context_menu_tests_04.html
+  browser_context_menu_tests_05.html
   browser_findbar.html
   browser_form_auto_complete.html
   browser_form_selects.html
   browser_link_click.html
   browser_onscreen_keyboard.html
   browser_progress_indicator.xul
   browser_selection_basic.html
   browser_selection_caretfocus.html
--- a/browser/metro/components/HelperAppDialog.js
+++ b/browser/metro/components/HelperAppDialog.js
@@ -116,17 +116,17 @@ HelperAppLauncherDialog.prototype = {
     let fragment =  ContentUtil.populateFragmentFromString(
                       document.createDocumentFragment(),
                       msg,
                       {
                         text: aLauncher.suggestedFileName,
                         className: "download-filename-text"
                       },
                       {
-                        text: aLauncher.suggestedFileName,
+                        text: aLauncher.downloadSize,
                         className: "download-size-text"
                       },
                       {
                         text: aLauncher.source.host,
                         className: "download-host-text"
                       }
                     );
     let newBar = notificationBox.appendNotification("",
--- a/browser/metro/components/SessionStore.js
+++ b/browser/metro/components/SessionStore.js
@@ -4,16 +4,17 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
 
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
   "@mozilla.org/xre/app-info;1", "nsICrashReporter");
 #endif
 
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
   "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
@@ -194,16 +195,20 @@ SessionStore.prototype = {
         observerService.addObserver(this, "browser-lastwindow-close-granted", true);
         observerService.addObserver(this, "browser:purge-session-history", true);
         observerService.addObserver(this, "quit-application-requested", true);
         observerService.addObserver(this, "quit-application-granted", true);
         observerService.addObserver(this, "quit-application", true);
         break;
       case "final-ui-startup":
         observerService.removeObserver(this, "final-ui-startup");
+        if (WindowsPrefSync) {
+          // Pulls in Desktop controlled prefs and pushes out Metro controlled prefs
+          WindowsPrefSync.init();
+        }
         this.init();
         break;
       case "domwindowopened":
         let window = aSubject;
         window.addEventListener("load", function() {
           self.onWindowOpen(window);
           window.removeEventListener("load", arguments.callee, false);
         }, false);
@@ -335,19 +340,22 @@ SessionStore.prototype = {
     this._windows[aWindow.__SSID] = { tabs: [], selected: 0, _closedTabs: [] };
 
     // Perform additional initialization when the first window is loading
     if (this._loadState == STATE_STOPPED) {
       this._loadState = STATE_RUNNING;
       this._lastSaveTime = Date.now();
 
       // Nothing to restore, notify observers things are complete
-      if (!this._shouldRestore) {
+      if (!this.shouldRestore()) {
         this._clearCache();
         Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
+
+        // If nothing is being restored, we only have our single Metro window.
+        this._orderedWindows.push(aWindow.__SSID);
       }
     }
 
     // Add tab change listeners to all already existing tabs
     let tabs = aWindow.Browser.tabs;
     for (let i = 0; i < tabs.length; i++)
       this.onTabAdd(aWindow, tabs[i].browser, true);
 
@@ -721,17 +729,17 @@ SessionStore.prototype = {
     let browser = aTab.linkedBrowser;
     if (browser.__SS_extdata && browser.__SS_extdata[aKey])
       delete browser.__SS_extdata[aKey];
     else
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
   },
 
   shouldRestore: function ss_shouldRestore() {
-    return this._shouldRestore;
+    return this._shouldRestore || (3 == Services.prefs.getIntPref("browser.startup.page"));
   },
 
   restoreLastSession: function ss_restoreLastSession(aBringToFront) {
     let self = this;
     function notifyObservers(aMessage) {
       self._clearCache();
       Services.obs.notifyObservers(null, "sessionstore-windows-restored", aMessage || "");
     }
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -246,19 +246,36 @@ function showBrowserSpecificIndicator(aB
     Cu.reportError("showBrowserSpecificIndicator: got neither video nor audio access");
     return;
   }
 
   let chromeWin = aBrowser.ownerDocument.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
 
   let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
-  let mainAction = null;
-  let secondaryActions = null;
+
+  let windowId = aBrowser.contentWindow
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindowUtils)
+                         .currentInnerWindowID;
+  let mainAction = {
+    label: stringBundle.getString("getUserMedia.continueSharing.label"),
+    accessKey: stringBundle.getString("getUserMedia.continueSharing.accesskey"),
+    callback: function () {},
+    dismiss: true
+  };
+  let secondaryActions = [{
+    label: stringBundle.getString("getUserMedia.stopSharing.label"),
+    accessKey: stringBundle.getString("getUserMedia.stopSharing.accesskey"),
+    callback: function () {
+      Services.obs.notifyObservers(null, "getUserMedia:revoke", windowId);
+    }
+  }];
   let options = {
+    hideNotNow: true,
     dismissed: true,
     eventCallback: function(aTopic) aTopic == "swapping"
   };
   chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
                                     "webRTC-sharingDevices-notification-icon", mainAction,
                                     secondaryActions, options);
 }
 
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -22,16 +22,17 @@ browser.jar:
   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/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/identity.png
   skin/classic/browser/identity-icons-generic.png
   skin/classic/browser/identity-icons-https.png
   skin/classic/browser/identity-icons-https-ev.png
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -154,17 +154,17 @@ toolbarbutton.chevron:-moz-locale-dir(rt
 
   toolbarbutton.chevron > .toolbarbutton-icon {
     width: 13px;
   }
 }
 
 /* ----- BOOKMARK BUTTONS ----- */
 
-toolbarbutton.bookmark-item,
+toolbarbutton.bookmark-item:not(.subviewbutton),
 #personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder {
   font-weight: bold;
   color: #222;
   border: 0;
   border-radius: 10000px;
   padding: 1px 8px;
   margin: 0 0 1px;
 }
@@ -194,18 +194,18 @@ toolbarbutton.bookmark-item,
   margin: 0 !important;
 }
 
 toolbarbutton.bookmark-item:hover,
 toolbarbutton.bookmark-item[open="true"] {
   background-color: rgba(0, 0, 0, .205);
 }
 
-toolbarbutton.bookmark-item:hover,
-toolbarbutton.bookmark-item[open="true"] {
++toolbarbutton.bookmark-item:hover:not(.subviewbutton),
++toolbarbutton.bookmark-item[open="true"]:not(.subviewbutton) {
   color: #FFF !important;
   text-shadow: 0 1px rgba(0, 0, 0, .4) !important;
 }
 
 .bookmark-item:hover > .toolbarbutton-menu-dropmarker,
 .bookmark-item[open="true"] > .toolbarbutton-menu-dropmarker {
   -moz-image-region: rect(5px, 7px, 10px, 0);
 }
--- a/browser/themes/osx/customizableui/panelUIOverlay.css
+++ b/browser/themes/osx/customizableui/panelUIOverlay.css
@@ -1,18 +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/. */
 
 %include ../../shared/customizableui/panelUIOverlay.inc.css
 
-.panel-subviews {
-  background-color: #f5f5f5;
-}
-
 @media (min-resolution: 2dppx) {
   #customization-palette toolbarbutton > .toolbarbutton-icon,
   #PanelUI-contents toolbarbutton > .toolbarbutton-icon {
     width: 20px;
   }
 
   #PanelUI-customize {
     list-style-image: url(chrome://browser/skin/menuPanel-customize@2x.png);
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -22,16 +22,17 @@ browser.jar:
   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/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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c7bd843393e11b33e14e6074083497e1b9fa5799
GIT binary patch
literal 1084
zc${NkU}Ruo5D;JhgEB@21~nk9pa9`7U}9jH0~G5F@N?(olHvji@p^i=1OaJ~4gn4}
zASwU(e<F~QEOCt}3C>R|DNig)WpGT%PfAtr%uP&B4N6T+sVqF1YQw<5DB<bi7*cU-
zPOxLpAp?=xC7zL2xC_K%Pl`mWtYB)F%)crf(Ka_If%Trlxd(2sH(7n7&;ECw5i!B(
z{u%$exM%y9{9pPh@rwn6z|0p9Z1&8S=oIc=&vzz7xKr3a?w@hFO7Hc%0(HA<ul?P8
zmaV&zNi1Uh{R<+W1XfOQ3KV(K8XM<7+0y2G^R-=Z4qlzVAL@RxaTj1Y!}~suXZ;6}
z{+Z>9tC&(Fove?toGM*cxxUMmao4w_+kX61eW)OiZvS${0lT_6dqVXccz^o^pJV@f
z!KJ_W(Xx|k;v6^_Q>A9+xpGX=zU*>qb!p=+nSS-YUR}GHIcuyo2%p%*v+w<TkJsCC
z7nD9f?`>ilmhkoK&%&D@6!&>O=g)45XJlhIlbku>^<wdhpiuX8^>bP0l+c7F)Iss0
z01oxys;VzUhB}iIB-GD@o%Xx!AhOqV+M);!)zqt24r0Ar)pCj*to&b@9X70A@r(B_
z(~<2BH_DDC2MQ{buy!*fvKq3gcpOsV40`lsUSp?>B&YN-PIs$oHl^w3=2$2H{e8(l
zNo9uinaDJL*6!+OY@fxAmw$e)UED9Z@Ee2loMb=a$x`*xL|Nz8EV^!$q?hf#X|>bg
z^uM#$eK_;~#~S9lQl%2Rbb9=Kf>bWa$%}Qn-nv^UvSek$y7I00*LS@OQz);xHBq_3
zUhV_;<v%{cD+>efdA^^N8q2nx!AFX#)kX5);fEU*#BlstbkW0r>x_?opiWxQ+OWkj
z?|fLMIaHRU%n<6-Ka}uukL&E{+pk1ky#1@u<ju~{%e(8?0^eQI$%gJXnNB_Jy0J4n
zLFbu9w8PRM!A;ey$CCPl_`iJr8favBtLPa+4F7j;XC>`9#|8WOD^B%^i%LjK7qU)Y
z*&f?er08d{`EhK#i@siheX+yUtgBb&d8r=N<2@{py1nr96u<X-?=nAsF5YyaIQ`dA
zpUGn4&*smad{!>_*@N}FbPo6xo~&R!X7_w{xc&iyL%RMerau=eo}&BU+qZQy{k~Ss
z-E>?%@8(Y4S|e9O_m|NPyKPVPPxP=gORZV3U(@)OQO>o+hm|jeJ+PO1x;}C7#Ra>a
z99h^O6tI--lC70!-ncK``S!ZW%PRlto{kn<yKe1%t?#+F%d`WurbSsbG1UYW$q0)4
zIR59$hk&KCU#~8UZwU{0z|JMRqgD5rkjM{SR;#aKH6I?|Q1}qvz{pVH^(goLDb0V#
G=@S4aY{yXm
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -1,45 +1,62 @@
 /* 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/. */
 
 %filter substitution
 
 %define menuPanelWidth 22.35em
-%define panelTextSize 1rem
 %define exitSubviewGutterWidth 38px
-%define buttonStateHover :not(:-moz-any([disabled],[checked="true"],[open],:active)):hover
-%define buttonStateActive :not([disabled]):-moz-any([open],[checked="true"],:hover:active)
+%define buttonStateHover :not(:-moz-any([disabled],[open],[checked="true"],[_moz-menuactive="true"],:active)):hover
+%define buttonStateActive :not([disabled]):-moz-any([open],[checked="true"],[_moz-menuactive="true"],:hover:active)
 
 %include ../browser.inc
 
 .panel-subviews {
-  background-image: linear-gradient(to bottom, white 1px, rgba(255, 255, 255, 0) 15px);
-  background-color: -moz-dialog;
-  box-shadow: -1px 0px 0px rgba(0, 0, 0, 0.2), -1px 0px 2px rgba(0, 0, 0, 0.1), 1px 0px 0px rgba(255, 255, 255, 0.2) inset;
+  padding: 4px;
+  background-color: hsla(0,0%,100%,.97);
+  background-clip: padding-box;
+  border-right: 1px solid hsla(210,4%,10%,.2);
+  border-left: 1px solid hsla(210,4%,10%,.2);
+  box-shadow: 0 3px 5px hsla(210,4%,10%,.1),
+              0 0 7px hsla(210,4%,10%,.1);
+  color: #222426;
   -moz-margin-start: @exitSubviewGutterWidth@;
 }
 
-.panel-subviews:-moz-locale-dir(rtl) {
-  box-shadow: 1px 0px 0px rgba(0, 0, 0, 0.2), 1px 0px 2px rgba(0, 0, 0, 0.1), -1px 0px 0px rgba(255, 255, 255, 0.2) inset;
-}
-
 .panel-viewstack[viewtype="main"] > .panel-subviews {
   transform: translateX(@menuPanelWidth@);
 }
 
 .panel-viewstack[viewtype="main"] > .panel-subviews:-moz-locale-dir(rtl) {
   transform: translateX(-@menuPanelWidth@);
 }
 
 .panel-viewstack:not([viewtype="main"]) > .panel-mainview > #PanelUI-mainView {
   -moz-box-flex: 1;
 }
 
+.panel-subview-header,
+.subviewbutton.panel-subview-footer {
+  padding: 12px;
+  background-color: hsla(210,4%,10%,.04);
+}
+
+.panel-subview-header {
+  margin: -4px -4px 4px;
+  box-shadow: 0 -1px 0 hsla(210,4%,10%,.08) inset;
+  color: #797c80;
+}
+
+.subviewbutton.panel-subview-footer {
+  margin: 4px -4px -4px;
+  box-shadow: 0 1px 0 hsla(210,4%,10%,.08) inset;
+}
+
 #PanelUI-mainView {
   display: flex;
   flex-direction: column;
 }
 
 #app-extension-point-end > #PanelUI-menu-button {
   padding: 2px 5px;
 }
@@ -62,26 +79,18 @@
   padding: 0;
   box-shadow: none;
 }
 
 #PanelUI-contents {
   padding: .5em 0;
 }
 
-toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > .toolbarbutton-text,
-.panelUI-grid .panel-combined-button > .toolbarbutton-text,
-.widget-overflow-list .toolbarbutton-menubutton-button > .toolbarbutton-text,
-.widget-overflow-list .toolbarbutton-1 > .toolbarbutton-text {
-  font-size: @panelTextSize@;
-}
-
 .panelUI-grid .toolbarbutton-menubutton-button > .toolbarbutton-multiline-text,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-multiline-text {
-  font-size: @panelTextSize@;
   margin: 2px 0 0;
   text-align: center;
   -moz-hyphens: auto;
 }
 
 #wrapper-edit-controls:-moz-any([place="palette"],[place="panel"]) > #edit-controls,
 #wrapper-zoom-controls:-moz-any([place="palette"],[place="panel"]) > #zoom-controls {
   -moz-margin-start: 0;
@@ -321,58 +330,90 @@ toolbarpaletteitem[place="palette"] > to
   background-image: linear-gradient(rgb(38,115,191), rgb(38,125,191));
 }
 
 #customization-palette .toolbarbutton-multiline-text,
 #customization-palette .toolbarbutton-text {
   display: none;
 }
 
-panelview toolbarbutton,
-#widget-overflow-list > toolbarbutton,
+panelview .toolbarbutton-1,
+panelview .subviewbutton,
+.widget-overflow-list .toolbarbutton-1,
 .customizationmode-button,
 #edit-controls@inAnyPanel@ > toolbarbutton,
 #zoom-controls@inAnyPanel@ > toolbarbutton,
 #BMB_bookmarksPopup > menu,
 #BMB_bookmarksPopup > menuitem {
   -moz-appearance: none;
   padding: 2px 6px;
   background-color: hsla(210,4%,10%,0);
   border-radius: 2px;
   border: 1px solid;
   border-color: hsla(210,4%,10%,0);
   transition-property: background-color, border-color;
   transition-duration: 150ms;
 }
 
+.PanelUI-subView .subviewbutton.panel-subview-footer {
+  border-radius: 0;
+  border: none;
+}
+
+.PanelUI-subView .subviewbutton.panel-subview-footer > .toolbarbutton-text {
+  -moz-padding-start: 0;
+  text-align: center;
+}
+
+.PanelUI-subView .subviewbutton:not(.panel-subview-footer) {
+  margin: 2px 0;
+}
+
+.PanelUI-subView .subviewbutton:not(.panel-subview-footer) > .toolbarbutton-text {
+  font-size: 1.1em;
+}
+
+.PanelUI-subView .subviewbutton.bookmark-item {
+  font-weight: normal;
+  color: inherit;
+}
+
+.PanelUI-subView menuseparator,
+.PanelUI-subView toolbarseparator {
+  -moz-margin-start: -5px;
+  -moz-margin-end: -4px;
+}
+
 panelview .toolbarbutton-1,
-#widget-overflow-list > toolbarbutton {
+.widget-overflow-list .toolbarbutton-1 {
   margin-top: 6px;
 }
 
-panelview toolbarbutton@buttonStateHover@,
-#widget-overflow-list > toolbarbutton@buttonStateHover@,
+panelview .toolbarbutton-1@buttonStateHover@,
+panelview .subviewbutton@buttonStateHover@,
+.widget-overflow-list .toolbarbutton-1@buttonStateHover@,
 .customizationmode-button,
 #edit-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@,
 #zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@,
 #BMB_bookmarksPopup > menu@buttonStateHover@,
 #BMB_bookmarksPopup > menuitem@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@buttonStateActive@,
+panelview .toolbarbutton-1@buttonStateActive@,
+panelview .subviewbutton@buttonStateActive@,
 .customizationmode-button@buttonStateActive@,
-#widget-overflow-list > toolbarbutton@buttonStateActive@,
+.widget-overflow-list .toolbarbutton-1@buttonStateActive@,
 #edit-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@,
 #zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@,
 #BMB_bookmarksPopup > menu@buttonStateActive@,
 #BMB_bookmarksPopup > menuitem@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;
 }
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -24,16 +24,17 @@ browser.jar:
         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/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
--- a/build/autoconf/hooks.m4
+++ b/build/autoconf/hooks.m4
@@ -29,17 +29,17 @@ case "$host" in
     fi
     ;;
 esac
 
 if test -d "$1"; then
     (cd "$1"; $PYTHON $_topsrcdir/build/subconfigure.py dump "$_CONFIG_SHELL")
 fi
 $2
-(cd "$1"; $PYTHON $_topsrcdir/build/subconfigure.py adjust)
+(cd "$1"; $PYTHON $_topsrcdir/build/subconfigure.py adjust $ac_sub_configure)
 ])
 
 define([AC_OUTPUT_SUBDIRS],
 [trap '' EXIT
 for moz_config_dir in $1; do
   MOZ_SUBCONFIGURE_WRAP([$moz_config_dir],[
     _MOZ_AC_OUTPUT_SUBDIRS($moz_config_dir)
   ])
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -629,60 +629,18 @@ class Automation(object):
         raise
 
   def dumpScreen(self, utilityPath):
     if self.haveDumpedScreen:
       self.log.info("Not taking screenshot here: see the one that was previously logged")
       return
 
     self.haveDumpedScreen = True;
-
-    # Need to figure out what tool and whether it write to a file or stdout
-    if self.UNIXISH:
-      utility = [os.path.join(utilityPath, "screentopng")]
-      imgoutput = 'stdout'
-    elif self.IS_MAC:
-      utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
-      imgoutput = 'file'
-    elif self.IS_WIN32:
-      utility = [os.path.join(utilityPath, "screenshot.exe")]
-      imgoutput = 'file'
+    automationutils.dumpScreen(utilityPath)
 
-    # Run the capture correctly for the type of capture
-    try:
-      if imgoutput == 'file':
-        tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
-        os.close(tmpfd)
-        dumper = self.Process(utility + [imgfilename])
-      elif imgoutput == 'stdout':
-        dumper = self.Process(utility, bufsize=-1,
-                              stdout=subprocess.PIPE, close_fds=True)
-    except OSError, err:
-      self.log.info("Failed to start %s for screenshot: %s",
-                    utility[0], err.strerror)
-      return
-
-    # Check whether the capture utility ran successfully
-    dumper_out, dumper_err = dumper.communicate()
-    if dumper.returncode != 0:
-      self.log.info("%s exited with code %d", utility, dumper.returncode)
-      return
-
-    try:
-      if imgoutput == 'stdout':
-        image = dumper_out
-      elif imgoutput == 'file':
-        with open(imgfilename, 'rb') as imgfile:
-          image = imgfile.read()
-    except IOError, err:
-        self.log.info("Failed to read image from %s", imgoutput)
-
-    import base64
-    encoded = base64.b64encode(image)
-    self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
 
   def killAndGetStack(self, processPID, utilityPath, debuggerInfo):
     """Kill the process, preferrably in a way that gets us a stack trace.
        Also attempts to obtain a screenshot before killing the process."""
     if not debuggerInfo:
       self.dumpScreen(utilityPath)
     self.killAndGetStackNoScreenshot(processPID, utilityPath, debuggerInfo)
 
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -479,68 +479,50 @@ def environment(xrePath, env=None, crash
       log.info("Failed determine available memory, disabling ASan low-memory configuration: %s", err.strerror)
     except:
       log.info("Failed determine available memory, disabling ASan low-memory configuration")
     else:
       log.info(message)
 
   return env
 
+def dumpScreen(utilityPath):
+  """dumps a screenshot of the entire screen to a directory specified by
+  the MOZ_UPLOAD_DIR environment variable"""
+  import mozfile
 
-def dumpScreen(utilityPath):
-  """dumps the screen to the log file as a data URI"""
-
-  # Need to figure out what tool and whether it write to a file or stdout
+  # Need to figure out which OS-dependent tool to use
   if mozinfo.isUnix:
     utility = [os.path.join(utilityPath, "screentopng")]
-    imgoutput = 'stdout'
   elif mozinfo.isMac:
     utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
-    imgoutput = 'file'
   elif mozinfo.isWin:
     utility = [os.path.join(utilityPath, "screenshot.exe")]
-    imgoutput = 'file'
-  else:
-    log.warn("Unable to dump screen on platform '%s'", sys.platform)
+
+  # Get dir where to write the screenshot file
+  parent_dir = os.environ.get('MOZ_UPLOAD_DIR', None)
+  if not parent_dir:
+    log.info('Failed to retrieve MOZ_UPLOAD_DIR env var')
+    return
 
-  # Run the capture correctly for the type of capture
-  kwargs = {'stdout': subprocess.PIPE}
-  if imgoutput == 'file':
-    tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
-    os.close(tmpfd)
-    utility.append(imgfilename)
-  elif imgoutput == 'stdout':
-    kwargs.update(dict(bufsize=-1, close_fds=True))
+  # Run the capture
   try:
-    dumper = subprocess.Popen(utility, **kwargs)
+    with mozfile.NamedTemporaryFile(suffix='.png',
+                                    prefix='mozilla-test-fail-screenshot_',
+                                    dir=parent_dir,
+                                    delete=False) as f:
+      returncode = subprocess.call(utility + [f.name])
   except OSError, err:
     log.info("Failed to start %s for screenshot: %s",
              utility[0], err.strerror)
     return
 
   # Check whether the capture utility ran successfully
-  stdout, _ = dumper.communicate()
-  returncode = dumper.poll()
-  if returncode:
+  if returncode != 0:
     log.info("%s exited with code %d", utility, returncode)
-    return
-
-  try:
-    if imgoutput == 'stdout':
-      image = stdout
-    elif imgoutput == 'file':
-      with open(imgfilename, 'rb') as imgfile:
-        image = imgfile.read()
-  except IOError, err:
-    log.info("Failed to read image from %s", imgoutput)
-
-  encoded = base64.b64encode(image)
-  uri = "data:image/png;base64,%s" %  encoded
-  log.info("SCREENSHOT: %s", uri)
-  return uri
 
 class ShutdownLeaks(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
 
--- a/build/docs/mozinfo.rst
+++ b/build/docs/mozinfo.rst
@@ -54,16 +54,22 @@ bits
 
    Universal Mac builds do not have this key defined.
 
    Unkown processor architectures (see ``processor`` below) may not have
    this key defined.
 
    Optional.
 
+buildapp
+   The path to the XUL application being built.
+
+   For desktop Firefox, this is ``browser``. For Fennec, it's
+   ``mobile/android``. For B2G, it's ``b2g``.
+
 crashreporter
    Whether the crash reporter is enabled for this build.
 
    Values are ``true`` and ``false``.
 
    Always defined.
 
 datareporting
--- a/build/subconfigure.py
+++ b/build/subconfigure.py
@@ -13,16 +13,24 @@ import pickle
 
 class File(object):
     def __init__(self, path):
         self._path = path
         self._content = open(path, 'rb').read()
         stat = os.stat(path)
         self._times = (stat.st_atime, stat.st_mtime)
 
+    @property
+    def path(self):
+        return self._path
+
+    @property
+    def mtime(self):
+        return self._times[1]
+
     def update_time(self):
         '''If the file hasn't changed since the instance was created,
            restore its old modification time.'''
         if not os.path.exists(self._path):
             return
         if open(self._path, 'rb').read() == self._content:
             os.utime(self._path, self._times)
 
@@ -92,33 +100,38 @@ def dump(dump_file, shell):
             for f in (couple.split(':')[0] for couple in line.split()):
                 if os.path.isfile(f):
                     config_files.append(File(f))
 
     with open(dump_file, 'wb') as f:
         pickle.dump(config_files, f)
 
 
-def adjust(dump_file):
+def adjust(dump_file, configure):
     if not os.path.exists(dump_file):
         return
 
     config_files = []
 
     try:
         with open(dump_file, 'rb') as f:
             config_files = pickle.load(f)
     except Exception:
         pass
 
     for f in config_files:
+        # Still touch config.status if configure is newer than its original
+        # mtime.
+        if configure and os.path.basename(f.path) == 'config.status' and \
+                os.path.getmtime(configure) > f.mtime:
+            continue
         f.update_time()
 
     os.remove(dump_file)
 
 
 CONFIG_DUMP = 'config_files.pkl'
 
 if __name__ == '__main__':
     if sys.argv[1] == 'dump':
         dump(CONFIG_DUMP, sys.argv[2])
     elif sys.argv[1] == 'adjust':
-        adjust(CONFIG_DUMP)
+        adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None)
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -1,13 +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/.
 
-SUPPRESS_DEFAULT_RULES := 1
 STANDALONE_MAKEFILE := 1
 
 include $(topsrcdir)/config/rules.mk
 
 # Building XPIDLs effectively consists of two steps:
 #
 #   1) Staging all .idl files to a common directory.
 #   2) Doing everything with the .idl files.
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -587,27 +587,25 @@ export MOZBUILD_BACKEND_CHECKED=1
 endif
 endif
 endif
 
 # The root makefile doesn't want to do a plain export/libs, because
 # of the tiers and because of libxul. Suppress the default rules in favor
 # of something else. Makefiles which use this var *must* provide a sensible
 # default rule before including rules.mk
-ifndef SUPPRESS_DEFAULT_RULES
 default all::
 	$(MAKE) export
 ifdef MOZ_PSEUDO_DERECURSE
 ifdef COMPILE_ENVIRONMENT
 	$(MAKE) compile
 endif
 endif
 	$(MAKE) libs
 	$(MAKE) tools
-endif # SUPPRESS_DEFAULT_RULES
 
 ifeq ($(findstring s,$(filter-out --%, $(MAKEFLAGS))),)
 ECHO := echo
 QUIET :=
 else
 ECHO := true
 QUIET := -q
 endif
--- a/configure.in
+++ b/configure.in
@@ -242,16 +242,17 @@ if test -n "$gonkdir" ; then
 
         MOZ_RTSP=1
         MOZ_NFC=1
         MOZ_B2G_CAMERA=1
         MOZ_OMX_DECODER=1
         AC_SUBST(MOZ_OMX_DECODER)
         MOZ_OMX_ENCODER=1
         AC_SUBST(MOZ_OMX_ENCODER)
+        AC_DEFINE(MOZ_OMX_ENCODER)
         ;;
     19)
         GONK_INCLUDES="-I$gonkdir/frameworks/native/include"
         MOZ_B2G_BT=1
         MOZ_B2G_BT_BLUEDROID=1
         MOZ_NFC=1
 
         ;;
@@ -1152,16 +1153,20 @@ x86_64 | ia64)
 
 arm*)
     CPU_ARCH=arm
     ;;
 
 mips|mipsel)
     CPU_ARCH="mips"
     ;;
+
+aarch64*)
+    CPU_ARCH=aarch64
+    ;;
 esac
 
 if test -z "$OS_TARGET"; then
     OS_TARGET=$OS_ARCH
 fi
 OS_CONFIG="${OS_TARGET}${OS_RELEASE}"
 
 dnl Set INTEL_ARCHITECTURE if we're compiling for x86-32 or x86-64.
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -301,17 +301,17 @@ CSPRep.fromString = function(aStr, self,
 
   var dirs = aStr.split(";");
 
   directive:
   for each(var dir in dirs) {
     dir = dir.trim();
     if (dir.length < 1) continue;
 
-    var dirname = dir.split(/\s+/)[0];
+    var dirname = dir.split(/\s+/)[0].toLowerCase();
     var dirvalue = dir.substring(dirname.length).trim();
 
     if (aCSPR._directives.hasOwnProperty(dirname)) {
       // Check for (most) duplicate directives
       cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
                                                 [dirname]));
       CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
       continue directive;
@@ -559,17 +559,17 @@ CSPRep.fromStringSpecCompliant = functio
   }
 
   var dirs_list = aStr.split(";");
   var dirs = {};
   for each(var dir in dirs_list) {
     dir = dir.trim();
     if (dir.length < 1) continue;
 
-    var dirname = dir.split(/\s+/)[0];
+    var dirname = dir.split(/\s+/)[0].toLowerCase();
     var dirvalue = dir.substring(dirname.length).trim();
     dirs[dirname] = dirvalue;
   }
 
   // Spec compliant policies have different default behavior for inline
   // scripts, styles, and eval. Bug 885433
   aCSPR._allowEval = true;
   aCSPR._allowInlineScripts = true;
@@ -998,17 +998,17 @@ CSPSourceList.fromString = function(aStr
   if (self && !(self instanceof CSPSource)) {
      self = CSPSource.create(self, aCSPRep);
   }
 
   var slObj = new CSPSourceList();
   slObj._CSPRep = aCSPRep;
   aStr = aStr.trim();
   // w3 specifies case insensitive equality
-  if (aStr.toUpperCase() === "'NONE'") {
+  if (aStr.toLowerCase() === "'none'") {
     slObj._permitAllSources = false;
     return slObj;
   }
 
   var tokens = aStr.split(/\s+/);
   for (var i in tokens) {
     if (!R_SOURCEEXP.test(tokens[i])) {
       cspWarn(aCSPRep,
@@ -1066,17 +1066,17 @@ CSPSourceList.prototype = {
       return false;
     }
     if (that._sources.length != this._sources.length) {
       return false;
     }
     // sort both arrays and compare like a zipper
     // XXX (sid): I think we can make this more efficient
     var sortfn = function(a,b) {
-      return a.toString() > b.toString();
+      return a.toString.toLowerCase() > b.toString.toLowerCase();
     };
     var a_sorted = this._sources.sort(sortfn);
     var b_sorted = that._sources.sort(sortfn);
     for (var i in a_sorted) {
       if (!a_sorted[i].equals(b_sorted[i])) {
         return false;
       }
     }
@@ -1413,34 +1413,34 @@ CSPSource.fromString = function(aStr, aC
   if (R_NONCESRC.test(aStr))
     return CSPNonceSource.fromString(aStr, aCSPRep);
 
   // check for a hash-source match
   if (R_HASHSRC.test(aStr))
     return CSPHashSource.fromString(aStr, aCSPRep);
 
   // check for 'self' (case insensitive)
-  if (aStr.toUpperCase() === "'SELF'") {
+  if (aStr.toLowerCase() === "'self'") {
     if (!self) {
       cspError(aCSPRep, CSPLocalizer.getStr("selfKeywordNoSelfData"));
       return null;
     }
     sObj._self = self.clone();
     sObj._isSelf = true;
     return sObj;
   }
 
   // check for 'unsafe-inline' (case insensitive)
-  if (aStr.toUpperCase() === "'UNSAFE-INLINE'"){
+  if (aStr.toLowerCase() === "'unsafe-inline'"){
     sObj._allowUnsafeInline = true;
     return sObj;
   }
 
   // check for 'unsafe-eval' (case insensitive)
-  if (aStr.toUpperCase() === "'UNSAFE-EVAL'"){
+  if (aStr.toLowerCase() === "'unsafe-eval'"){
     sObj._allowUnsafeEval = true;
     return sObj;
   }
 
   cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource",
                                               [aStr]));
   return null;
 };
@@ -1551,17 +1551,17 @@ CSPSource.prototype = {
   permits:
   function(aSource) {
     if (!aSource) return false;
 
     if (!(aSource instanceof CSPSource))
       aSource = CSPSource.create(aSource, this._CSPRep);
 
     // verify scheme
-    if (this.scheme != aSource.scheme)
+    if (this.scheme.toLowerCase() != aSource.scheme.toLowerCase())
       return false;
 
     // port is defined in 'this' (undefined means it may not be relevant
     // to the scheme) AND this port (implicit or explicit) matches
     // aSource's port
     if (this.port && this.port !== "*" && this.port != aSource.port)
       return false;
 
@@ -1587,24 +1587,24 @@ CSPSource.prototype = {
    *        true if they have the same data
    */
   equals:
   function(that, resolveSelf) {
     // 1. schemes match
     // 2. ports match
     // 3. either both hosts are undefined, or one equals the other.
     if (resolveSelf)
-      return this.scheme === that.scheme
-          && this.port   === that.port
+      return this.scheme.toLowerCase() === that.scheme.toLowerCase()
+          && this.port === that.port
           && (!(this.host || that.host) ||
                (this.host && this.host.equals(that.host)));
 
     // otherwise, compare raw (non-self-resolved values)
-    return this._scheme === that._scheme
-        && this._port   === that._port
+    return this._scheme.toLowerCase() === that._scheme.toLowerCase()
+        && this._port === that._port
         && (!(this._host || that._host) ||
               (this._host && this._host.equals(that._host)));
   },
 
 };
 
 //////////////////////////////////////////////////////////////////////
 /**
@@ -1683,17 +1683,19 @@ CSPHost.prototype = {
    * Returns true if this host accepts the provided host (or the other way
    * around).
    * @param aHost
    *        the FQDN in question (CSPHost or String)
    * @returns
    */
   permits:
   function(aHost) {
-    if (!aHost) aHost = CSPHost.fromString("*");
+    if (!aHost) {
+      aHost = CSPHost.fromString("*");
+    }
 
     if (!(aHost instanceof CSPHost)) {
       // -- compare CSPHost to String
       aHost =  CSPHost.fromString(aHost);
     }
     var thislen = this._segments.length;
     var thatlen = aHost._segments.length;
 
@@ -1709,17 +1711,18 @@ CSPHost.prototype = {
     }
 
     // Given the wildcard condition (from above),
     // only necessary to compare elements that are present
     // in this host.  Extra tokens in aHost are ok.
     // * Compare from right to left.
     for (var i=1; i <= thislen; i++) {
       if (this._segments[thislen-i] != "*" &&
-          (this._segments[thislen-i] != aHost._segments[thatlen-i])) {
+          (this._segments[thislen-i].toLowerCase() !=
+           aHost._segments[thatlen-i].toLowerCase())) {
         return false;
       }
     }
 
     // at this point, all conditions are met, so the host is allowed
     return true;
   },
 
@@ -1732,18 +1735,20 @@ CSPHost.prototype = {
    *        true if they have the same data
    */
   equals:
   function(that) {
     if (this._segments.length != that._segments.length)
       return false;
 
     for (var i=0; i<this._segments.length; i++) {
-      if (this._segments[i] != that._segments[i])
+      if (this._segments[i].toLowerCase() !=
+          that._segments[i].toLowerCase()) {
         return false;
+      }
     }
     return true;
   }
 };
 
 this.CSPNonceSource = function CSPNonceSource() {
   this._nonce = undefined;
 }
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -1621,17 +1621,18 @@ Element::SetEventHandler(nsIAtom* aEvent
                                                                    &defer);
   if (!manager) {
     return NS_OK;
   }
 
   defer = defer && aDefer; // only defer if everyone agrees...
   manager->SetEventHandler(aEventName, aValue,
                            nsIProgrammingLanguage::JAVASCRIPT,
-                           defer, !nsContentUtils::IsChromeDoc(ownerDoc));
+                           defer, !nsContentUtils::IsChromeDoc(ownerDoc),
+                           this);
   return NS_OK;
 }
 
 
 //----------------------------------------------------------------------
 
 const nsAttrName*
 Element::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -2053,17 +2053,19 @@ nsFrameLoader::TryRemoteBrowser()
   }
 
   PROFILER_LABEL("nsFrameLoader", "CreateRemoteBrowser");
 
   MutableTabContext context;
   nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
   nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
   ScrollingBehavior scrollingBehavior = DEFAULT_SCROLLING;
-  if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
+
+  if (Preferences::GetBool("dom.browser_frames.useAsyncPanZoom", false) ||
+      mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                  nsGkAtoms::mozasyncpanzoom,
                                  nsGkAtoms::_true,
                                  eCaseMatters)) {
     scrollingBehavior = ASYNC_PAN_ZOOM;
   }
 
   bool rv = true;
   if (ownApp) {
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -994,32 +994,47 @@ nsScriptLoader::GetScriptGlobalObject()
     return nullptr;
   }
 
   return globalObject.forget();
 }
 
 void
 nsScriptLoader::FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
-                                             JS::Handle<JSObject *> scopeChain,
+                                             JS::Handle<JSObject *> aScopeChain,
                                              JS::CompileOptions *aOptions)
 {
   // It's very important to use aRequest->mURI, not the final URI of the channel
   // aRequest ended up getting script data from, as the script filename.
   nsContentUtils::GetWrapperSafeScriptFilename(mDocument, aRequest->mURI, aRequest->mURL);
 
   aOptions->setFileAndLine(aRequest->mURL.get(), aRequest->mLineNo);
   aOptions->setVersion(JSVersion(aRequest->mJSVersion));
-  aOptions->setCompileAndGo(JS_IsGlobalObject(scopeChain));
+  aOptions->setCompileAndGo(JS_IsGlobalObject(aScopeChain));
   if (aRequest->mHasSourceMapURL) {
     aOptions->setSourceMapURL(aRequest->mSourceMapURL.get());
   }
   if (aRequest->mOriginPrincipal) {
     aOptions->setOriginPrincipals(nsJSPrincipals::get(aRequest->mOriginPrincipal));
   }
+
+  AutoJSContext cx;
+  JS::Rooted<JS::Value> elementVal(cx);
+  MOZ_ASSERT(aRequest->mElement);
+  // XXXbz this is super-fragile, because the code that _uses_ the
+  // JS::CompileOptions is nowhere near us, but we have to coordinate
+  // compartments with it... and in particular, it will compile in the
+  // compartment of aScopeChain, so we want to wrap into that compartment as
+  // well.
+  if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, aScopeChain,
+                                              aRequest->mElement, &elementVal,
+                                              /* aAllowWrapping = */ true))) {
+    MOZ_ASSERT(elementVal.isObject());
+    aOptions->setElement(&elementVal.toObject());
+  }
 }
 
 nsresult
 nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
                                const nsAFlatString& aScript,
                                void** aOffThreadToken)
 {
   nsresult rv = NS_OK;
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -277,17 +277,17 @@ private:
   void FireScriptEvaluated(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   nsresult EvaluateScript(nsScriptLoadRequest* aRequest,
                           const nsAFlatString& aScript,
                           void **aOffThreadToken);
 
   already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();
   void FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
-                                    JS::Handle<JSObject *> scopeChain,
+                                    JS::Handle<JSObject *> aScopeChain,
                                     JS::CompileOptions *aOptions);
 
   nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
                                 nsIStreamLoader* aLoader,
                                 nsresult aStatus,
                                 uint32_t aStringLen,
                                 const uint8_t* aString);
 
--- a/content/base/test/unit/test_csputils.js
+++ b/content/base/test/unit/test_csputils.js
@@ -911,16 +911,76 @@ test(
                                 URI("http://self.com/foo.js"),
                                 Ci.nsIContentPolicy.TYPE_SCRIPT));
       do_check_false(testPermits(cspObjOld,
                                  URI("http://bar.com/foo.js"),
                                  Ci.nsIContentPolicy.TYPE_SCRIPT));
 
     });
 
+test(function test_equals_does_case_insensitive_comparison() {
+      // NOTE: For scheme, host and keyword-host:
+      // (1) compare the same lower-case in two distinct objects
+      // (2) compare upper-case with lower-case inputs
+      // to test case insensitivity.
+
+      // CSPSource equals ignores case
+      var upperCaseHost = "http://FOO.COM";
+      var lowerCaseHost = "http://foo.com";
+      src1 = CSPSource.fromString(lowerCaseHost);
+      src2 = CSPSource.fromString(lowerCaseHost);
+      do_check_true(src1.equals(src2))
+      src3 = CSPSource.fromString(upperCaseHost);
+      do_check_true(src1.equals(src3))
+
+      // CSPHost equals ignores case
+      var upperCaseScheme = "HTTP";
+      var lowerCaseScheme = "http";
+      src1 = CSPHost.fromString(lowerCaseScheme);
+      src2 = CSPHost.fromString(lowerCaseScheme);
+      do_check_true(src1.equals(src2));
+      src3 = CSPHost.fromString(upperCaseScheme);
+      do_check_true(src1.equals(src3));
+
+      // CSPSourceList equals (mainly for testing keywords)
+      var upperCaseKeywords = "'SELF'";
+      var lowerCaseKeywords = "'self'";
+      src1 = CSPSourceList.fromString(lowerCaseKeywords);
+      src2 = CSPSourceList.fromString(lowerCaseKeywords);
+      do_check_true(src1.equals(src2))
+      src3 = CSPSourceList.fromString(upperCaseKeywords);
+      do_check_true(src1.equals(src3))
+
+  });
+
+test(function test_csp_permits_case_insensitive() {
+      var cspr;
+      var SD = CSPRep.SRC_DIRECTIVES_NEW;
+
+      // checks directives can be case-insensitive
+      var selfHost = "http://self.com";
+      var testPolicy1 = "DEFAULT-src 'self';";
+      cspr = CSPRep.fromString(testPolicy1, URI(selfHost));
+      do_check_true(cspr.permits(URI("http://self.com"), SD.DEFAULT_SRC));
+
+      // checks hosts can be case-insensitive
+      var testPolicy2 = "default-src 'self' http://FOO.COM";
+      cspr = CSPRep.fromString(testPolicy2, URI(selfHost));
+      do_check_true(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC));
+
+      // checks schemes can be case-insensitive
+      var testPolicy3 = "default-src 'self' HTTP://foo.com";
+      cspr = CSPRep.fromString(testPolicy3, URI(selfHost));
+      do_check_true(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC));
+
+      // checks keywords can be case-insensitive
+      var testPolicy4 = "default-src 'NONE'";
+      cspr = CSPRep.fromString(testPolicy4, URI(selfHost));
+      do_check_false(cspr.permits(URI("http://foo.com"), SD.DEFAULT_SRC));
+  });
 /*
 
 test(function test_CSPRep_fromPolicyURI_failswhenmixed() {
         var cspr;
         var self = "http://localhost:" + POLICY_PORT;
         var closed_policy = CSPRep.fromString("allow 'none'");
         var my_uri_policy = "policy-uri " + POLICY_URI;
 
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2938,17 +2938,18 @@ CanvasRenderingContext2D::SetMozDashOffs
 {
   ContextState& state = CurrentState();
   if (!state.dash.IsEmpty()) {
     state.dashOffset = mozDashOffset;
   }
 }
 
 void
-CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence<double>& aSegments) {
+CanvasRenderingContext2D::SetLineDash(const Sequence<double>& aSegments)
+{
   FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
   dash.Clear();
 
   for (uint32_t x = 0; x < aSegments.Length(); x++) {
     dash.AppendElement(aSegments[x]);
   }
   if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again
     for (uint32_t x = 0; x < aSegments.Length(); x++) {
@@ -3458,17 +3459,17 @@ CanvasRenderingContext2D::DrawWindow(nsG
       return;
     }
 
     thebes = new gfxContext(drawDT);
     thebes->Scale(matrix._11, matrix._22);
   } else {
     drawSurf =
       gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(ceil(sw), ceil(sh)),
-                                                         GFX_CONTENT_COLOR_ALPHA);
+                                                         gfxContentType::COLOR_ALPHA);
     if (!drawSurf) {
       error.Throw(NS_ERROR_FAILURE);
       return;
     }
 
     thebes = new gfxContext(drawSurf);
     thebes->Scale(matrix._11, matrix._22);
   }
@@ -3895,17 +3896,17 @@ CanvasRenderingContext2D::PutImageData_e
   }
 
   uint32_t len = w * h * 4;
   if (aDataLen != len) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
-                                                          gfxImageFormatARGB32,
+                                                          gfxImageFormat::ARGB32,
                                                           false);
   if (!imgsurf || imgsurf->CairoStatus()) {
     return NS_ERROR_FAILURE;
   }
 
   uint8_t *src = aData;
   uint8_t *dst = imgsurf->Data();
 
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -333,17 +333,17 @@ public:
                                      JS::Handle<JSObject*> currentTransform,
                                      mozilla::ErrorResult& error);
   void GetFillRule(nsAString& fillRule);
   void SetFillRule(const nsAString& fillRule);
   JS::Value GetMozDash(JSContext* cx, mozilla::ErrorResult& error);
   void SetMozDash(JSContext* cx, const JS::Value& mozDash,
                   mozilla::ErrorResult& error);
 
-  void SetLineDash(const mozilla::dom::AutoSequence<double>& mSegments);
+  void SetLineDash(const Sequence<double>& mSegments);
   void GetLineDash(nsTArray<double>& mSegments) const;
 
   void SetLineDashOffset(double mOffset);
   double LineDashOffset() const;
 
   double MozDashOffset()
   {
     return CurrentState().dashOffset;
--- a/content/canvas/src/DocumentRendererChild.cpp
+++ b/content/canvas/src/DocumentRendererChild.cpp
@@ -71,17 +71,17 @@ DocumentRendererChild::RenderDocument(ns
 
     // Draw directly into the output array.
     data.SetLength(renderSize.width * renderSize.height * 4);
 
     nsRefPtr<gfxImageSurface> surf =
         new gfxImageSurface(reinterpret_cast<uint8_t*>(data.BeginWriting()),
                             gfxIntSize(renderSize.width, renderSize.height),
                             4 * renderSize.width,
-                            gfxImageFormatARGB32);
+                            gfxImageFormat::ARGB32);
     nsRefPtr<gfxContext> ctx = new gfxContext(surf);
     ctx->SetMatrix(mozilla::gfx::ThebesMatrix(transform));
 
     nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
     shell->RenderDocument(documentRect, renderFlags, bgColor, ctx);
 
     return true;
 }
--- a/content/canvas/src/DocumentRendererParent.cpp
+++ b/content/canvas/src/DocumentRendererParent.cpp
@@ -27,17 +27,17 @@ void DocumentRendererParent::DrawToCanva
 {
     if (!mCanvas || !mCanvasContext)
         return;
 
     nsRefPtr<gfxImageSurface> surf =
         new gfxImageSurface(reinterpret_cast<uint8_t*>(const_cast<nsCString&>(aData).BeginWriting()),
                             gfxIntSize(aSize.width, aSize.height),
                             aSize.width * 4,
-                            gfxImageFormatARGB32);
+                            gfxImageFormat::ARGB32);
     nsRefPtr<gfxPattern> pat = new gfxPattern(surf);
 
     gfxRect rect(gfxPoint(0, 0), gfxSize(aSize.width, aSize.height));
     mCanvasContext->NewPath();
     mCanvasContext->PixelSnappedRectangleAndSetPattern(rect, pat);
     mCanvasContext->Fill();
 
     // get rid of the pattern surface ref, because aData is very
--- a/content/canvas/src/ImageEncoder.cpp
+++ b/content/canvas/src/ImageEncoder.cpp
@@ -279,17 +279,17 @@ ImageEncoder::ExtractDataInternal(const 
                                   getter_AddRefs(imgStream));
   } else {
     // no context, so we have to encode an empty image
     // note that if we didn't have a current context, the spec says we're
     // supposed to just return transparent black pixels of the canvas
     // dimensions.
     nsRefPtr<gfxImageSurface> emptyCanvas =
       new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
-                          gfxImageFormatARGB32);
+                          gfxImageFormat::ARGB32);
     if (emptyCanvas->CairoStatus()) {
       return NS_ERROR_INVALID_ARG;
     }
     rv = aEncoder->InitFromData(emptyCanvas->Data(),
                                 aSize.width * aSize.height * 4,
                                 aSize.width,
                                 aSize.height,
                                 aSize.width * 4,
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -616,17 +616,17 @@ WebGLContext::SetDimensions(int32_t widt
 
 NS_IMETHODIMP
 WebGLContext::Render(gfxContext *ctx, GraphicsFilter f, uint32_t aFlags)
 {
     if (!gl)
         return NS_OK;
 
     nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                                                         gfxImageFormatARGB32);
+                                                         gfxImageFormat::ARGB32);
     if (surf->CairoStatus() != 0)
         return NS_ERROR_FAILURE;
 
     gl->MakeCurrent();
     ReadScreenIntoImageSurface(gl, surf);
 
     bool srcPremultAlpha = mOptions.premultipliedAlpha;
     bool dstPremultAlpha = aFlags & RenderFlagPremultAlpha;
@@ -744,17 +744,17 @@ void WebGLContext::LoseOldestWebGLContex
 void
 WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
 {
     *aImageBuffer = nullptr;
     *aFormat = 0;
 
     nsRefPtr<gfxImageSurface> imgsurf =
         new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                            gfxImageFormatARGB32);
+                            gfxImageFormat::ARGB32);
 
     if (!imgsurf || imgsurf->CairoStatus()) {
         return;
     }
 
     nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
     if (!ctx || ctx->HasError()) {
         return;
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -34,20 +34,20 @@
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/ErrorResult.h"
 
 class nsIDocShell;
 
-/* 
+/*
  * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
  *   https://bugzilla.mozilla.org/show_bug.cgi?id=686732
- * 
+ *
  * Exceptions: some of the following values are set to higher values than in the spec because
  * the values in the spec are ridiculously low. They are explicitly marked below
 */
 #define MINVALUE_GL_MAX_TEXTURE_SIZE                  1024  // Different from the spec, which sets it to 64 on page 162
 #define MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE         512   // Different from the spec, which sets it to 16 on page 162
 #define MINVALUE_GL_MAX_VERTEX_ATTRIBS                8     // Page 164
 #define MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS      16    // Page 164
 #define MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS        128   // Page 164
@@ -225,17 +225,18 @@ public:
     bool IsPremultAlpha() const { return mOptions.premultipliedAlpha; }
 
     bool PresentScreenBuffer();
 
     // a number that increments every time we have an event that causes
     // all context resources to be lost.
     uint32_t Generation() { return mGeneration.value(); }
 
-    const WebGLRectangleObject *FramebufferRectangleObject() const;
+    // Returns null if the current bound FB is not likely complete.
+    const WebGLRectangleObject* CurValidFBRectObject() const;
 
     static const size_t sMaxColorAttachments = 16;
 
     // This is similar to GLContext::ClearSafely, but tries to minimize the
     // amount of work it does.
     // It only clears the buffers we specify, and can reset its state without
     // first having to query anything, as WebGL knows its state at all times.
     void ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[sMaxColorAttachments]);
@@ -437,17 +438,17 @@ public:
                                -1, srcFormat, mPixelStorePremultiplyAlpha);
     }
     void TexParameterf(GLenum target, GLenum pname, GLfloat param) {
         TexParameter_base(target, pname, nullptr, &param);
     }
     void TexParameteri(GLenum target, GLenum pname, GLint param) {
         TexParameter_base(target, pname, &param, nullptr);
     }
-    
+
     void TexSubImage2D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset,
                        GLsizei width, GLsizei height, GLenum format,
                        GLenum type,
                        const Nullable<dom::ArrayBufferView> &pixels,
                        ErrorResult& rv);
     void TexSubImage2D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLenum format,
@@ -471,33 +472,33 @@ public:
 
         gfx::IntSize size = data->GetSize();
         uint32_t byteLength = data->Stride() * size.height;
         return TexSubImage2D_base(target, level, xoffset, yoffset,
                                   size.width, size.height,
                                   data->Stride(), format, type,
                                   data->GetData(), byteLength,
                                   -1, srcFormat, mPixelStorePremultiplyAlpha);
-        
+
     }
 
     void Uniform1i(WebGLUniformLocation* location, GLint x);
     void Uniform2i(WebGLUniformLocation* location, GLint x, GLint y);
     void Uniform3i(WebGLUniformLocation* location, GLint x, GLint y,
                    GLint z);
     void Uniform4i(WebGLUniformLocation* location, GLint x, GLint y,
                    GLint z, GLint w);
 
     void Uniform1f(WebGLUniformLocation* location, GLfloat x);
     void Uniform2f(WebGLUniformLocation* location, GLfloat x, GLfloat y);
     void Uniform3f(WebGLUniformLocation* location, GLfloat x, GLfloat y,
                    GLfloat z);
     void Uniform4f(WebGLUniformLocation* location, GLfloat x, GLfloat y,
                    GLfloat z, GLfloat w);
-    
+
     void Uniform1iv(WebGLUniformLocation* location,
                     const dom::Int32Array& arr) {
         Uniform1iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform1iv(WebGLUniformLocation* location,
                     const dom::Sequence<GLint>& arr) {
         Uniform1iv_base(location, arr.Length(), arr.Elements());
     }
@@ -520,17 +521,17 @@ public:
         Uniform3iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3iv(WebGLUniformLocation* location,
                     const dom::Sequence<GLint>& arr) {
         Uniform3iv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform3iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLint* data);
-    
+
     void Uniform4iv(WebGLUniformLocation* location,
                     const dom::Int32Array& arr) {
         Uniform4iv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4iv(WebGLUniformLocation* location,
                     const dom::Sequence<GLint>& arr) {
         Uniform4iv_base(location, arr.Length(), arr.Elements());
     }
@@ -564,17 +565,17 @@ public:
         Uniform3fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform3fv(WebGLUniformLocation* location,
                     const dom::Sequence<GLfloat>& arr) {
         Uniform3fv_base(location, arr.Length(), arr.Elements());
     }
     void Uniform3fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
                          const GLfloat* data);
-    
+
     void Uniform4fv(WebGLUniformLocation* location,
                     const dom::Float32Array& arr) {
         Uniform4fv_base(location, arr.Length(), arr.Data());
     }
     void Uniform4fv(WebGLUniformLocation* location,
                     const dom::Sequence<GLfloat>& arr) {
         Uniform4fv_base(location, arr.Length(), arr.Elements());
     }
@@ -812,18 +813,18 @@ protected:
     void BindFakeBlackTextures();
     void UnbindFakeBlackTextures();
 
     WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need();
     bool DoFakeVertexAttrib0(GLuint vertexCount);
     void UndoFakeVertexAttrib0();
     void InvalidateFakeVertexAttrib0();
 
-    static CheckedUint32 GetImageSize(GLsizei height, 
-                                      GLsizei width, 
+    static CheckedUint32 GetImageSize(GLsizei height,
+                                      GLsizei width,
                                       uint32_t pixelSize,
                                       uint32_t alignment);
 
     // Returns x rounded to the next highest multiple of y.
     static CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y) {
         return ((x + y - 1) / y) * y;
     }
 
@@ -947,17 +948,17 @@ protected:
     bool ValidateComparisonEnum(GLenum target, const char *info);
     bool ValidateStencilOpEnum(GLenum action, const char *info);
     bool ValidateFaceEnum(GLenum face, const char *info);
     bool ValidateTexFormatAndType(GLenum format, GLenum type, int jsArrayType,
                                       uint32_t *texelSize, const char *info);
     bool ValidateDrawModeEnum(GLenum mode, const char *info);
     bool ValidateAttribIndex(GLuint index, const char *info);
     bool ValidateStencilParamsForDrawCall();
-    
+
     bool ValidateGLSLVariableName(const nsAString& name, const char *info);
     bool ValidateGLSLCharacter(char16_t c);
     bool ValidateGLSLString(const nsAString& string, const char *info);
     bool ValidateTexImage2DFormat(GLenum format, const char* info);
     bool ValidateTexImage2DTarget(GLenum target, GLsizei width, GLsizei height, const char* info);
     bool ValidateCompressedTextureSize(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, uint32_t byteLength, const char* info);
     bool ValidateLevelWidthHeightForTarget(GLenum target, GLint level, GLsizei width, GLsizei height, const char* info);
 
@@ -1043,17 +1044,17 @@ private:
     // to not be null already.
     template<class ObjectType>
     bool ValidateObjectAssumeNonNull(const char* info, ObjectType *aObject);
 
 protected:
     int32_t MaxTextureSizeForTarget(GLenum target) const {
         return target == LOCAL_GL_TEXTURE_2D ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize;
     }
-    
+
     /** like glBufferData but if the call may change the buffer size, checks any GL error generated
      * by this glBufferData call and returns it */
     GLenum CheckedBufferData(GLenum target,
                              GLsizeiptr size,
                              const GLvoid *data,
                              GLenum usage);
     /** like glTexImage2D but if the call may change the texture size, checks any GL error generated
      * by this glTexImage2D call and returns it */
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -48,19 +48,32 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize);
 static GLenum InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2);
 
-const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {
-    return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject()
-                             : static_cast<const WebGLRectangleObject*>(this);
+const WebGLRectangleObject*
+WebGLContext::CurValidFBRectObject() const
+{
+    const WebGLRectangleObject* rect = nullptr;
+
+    if (mBoundFramebuffer) {
+        // We don't really need to ask the driver.
+        // Use 'precheck' to just check that our internal state looks good.
+        GLenum precheckStatus = mBoundFramebuffer->PrecheckFramebufferStatus();
+        if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
+            rect = &mBoundFramebuffer->RectangleObject();
+    } else {
+        rect = static_cast<const WebGLRectangleObject*>(this);
+    }
+
+    return rect;
 }
 
 WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format)
     : mGL(gl)
     , mGLName(0)
 {
   MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D || target == LOCAL_GL_TEXTURE_CUBE_MAP);
   MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
@@ -103,17 +116,17 @@ WebGLContext::FakeBlackTexture::~FakeBla
 
 //
 //  WebGL API
 //
 
 void
 WebGLContext::ActiveTexture(GLenum texture)
 {
-    if (IsContextLost()) 
+    if (IsContextLost())
         return;
 
     if (texture < LOCAL_GL_TEXTURE0 ||
         texture >= LOCAL_GL_TEXTURE0 + uint32_t(mGLMaxTextureUnits))
     {
         return ErrorInvalidEnum(
             "ActiveTexture: texture unit %d out of range. "
             "Accepted values range from TEXTURE0 to TEXTURE0 + %d. "
@@ -163,17 +176,17 @@ WebGLContext::BindAttribLocation(WebGLPr
         return;
 
     if (!ValidateAttribIndex(location, "bindAttribLocation"))
         return;
 
     NS_LossyConvertUTF16toASCII cname(name);
     nsCString mappedName;
     prog->MapIdentifier(cname, &mappedName);
-    
+
     MakeContextCurrent();
     gl->fBindAttribLocation(progname, location, mappedName.get());
 }
 
 void
 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer *wfb)
 {
     if (IsContextLost())
@@ -342,79 +355,42 @@ WebGLContext::BlendFuncSeparate(GLenum s
     MakeContextCurrent();
     gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
 }
 
 GLenum
 WebGLContext::CheckFramebufferStatus(GLenum target)
 {
     if (IsContextLost())
-    {
         return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
-    }
-
-    MakeContextCurrent();
+
     if (target != LOCAL_GL_FRAMEBUFFER) {
         ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER");
         return 0;
     }
 
     if (!mBoundFramebuffer)
         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
-    if(mBoundFramebuffer->HasDepthStencilConflict())
-        return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
-
-    bool hasImages = false;
-    hasImages |= mBoundFramebuffer->DepthAttachment().IsDefined();
-    hasImages |= mBoundFramebuffer->StencilAttachment().IsDefined();
-    hasImages |= mBoundFramebuffer->DepthStencilAttachment().IsDefined();
-
-    if (!hasImages) {
-        int32_t colorAttachmentCount = mBoundFramebuffer->mColorAttachments.Length();
-
-        for(int32_t i = 0; i < colorAttachmentCount; i++) {
-            if (mBoundFramebuffer->ColorAttachment(i).IsDefined()) {
-                hasImages = true;
-                break;
-            }
-        }
-
-        /* http://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf section 4.4.5 (page 118)
-         GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
-         No images are attached to the framebuffer.
-         */
-        if (!hasImages) {
-            return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
-        }
-    }
-
-    if(mBoundFramebuffer->HasIncompleteAttachment())
-        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
-    if(mBoundFramebuffer->HasAttachmentsOfMismatchedDimensions())
-        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
-
-    // Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}.
-    mBoundFramebuffer->FinalizeAttachments();
-
-    return gl->fCheckFramebufferStatus(target);
+
+    return mBoundFramebuffer->CheckFramebufferStatus();
 }
 
 void
 WebGLContext::CopyTexSubImage2D_base(GLenum target,
                                      GLint level,
                                      GLenum internalformat,
                                      GLint xoffset,
                                      GLint yoffset,
                                      GLint x,
                                      GLint y,
                                      GLsizei width,
                                      GLsizei height,
                                      bool sub)
 {
-    const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject();
+    const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
     GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
     GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
 
     const char *info = sub ? "copyTexSubImage2D" : "copyTexImage2D";
 
     if (!ValidateLevelWidthHeightForTarget(target, level, width, height, info)) {
         return;
     }
@@ -438,17 +414,17 @@ WebGLContext::CopyTexSubImage2D_base(GLe
         /*** first, we initialize the texture as black ***/
 
         // first, compute the size of the buffer we should allocate to initialize the texture as black
 
         uint32_t texelSize = 0;
         if (!ValidateTexFormatAndType(internalformat, LOCAL_GL_UNSIGNED_BYTE, -1, &texelSize, info))
             return;
 
-        CheckedUint32 checked_neededByteLength = 
+        CheckedUint32 checked_neededByteLength =
             GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
 
         if (!checked_neededByteLength.isValid())
             return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info);
 
         uint32_t bytesNeeded = checked_neededByteLength.value();
 
         // now that the size is known, create the buffer
@@ -909,17 +885,17 @@ WebGLContext::DoFakeVertexAttrib0(GLuint
 
     CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat);
 
     if (!checked_dataSize.isValid()) {
         ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
                          "with %d vertices. Try reducing the number of vertices.", vertexCount);
         return false;
     }
-    
+
     GLuint dataSize = checked_dataSize.value();
 
     if (!mFakeVertexAttrib0BufferObject) {
         gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
     }
 
     // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and
     // we don't need it to be, then consider it OK
@@ -955,30 +931,30 @@ WebGLContext::DoFakeVertexAttrib0(GLuint
                 array[4 * i + 2] = mVertexAttrib0Vector[2];
                 array[4 * i + 3] = mVertexAttrib0Vector[3];
             }
             gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array, LOCAL_GL_DYNAMIC_DRAW);
         } else {
             gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
         }
         UpdateWebGLErrorAndClearGLError(&error);
-        
+
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
 
         // note that we do this error checking and early return AFTER having restored the buffer binding above
         if (error) {
             ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
                              "with %d vertices. Try reducing the number of vertices.", vertexCount);
             return false;
         }
     }
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
     gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
-    
+
     return true;
 }
 
 void
 WebGLContext::UndoFakeVertexAttrib0()
 {
     WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
 
@@ -1190,24 +1166,24 @@ WebGLContext::GenerateMipmap(GLenum targ
 
     if (!ValidateTextureTargetEnum(target, "generateMipmap"))
         return;
 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
 
     if (!tex)
         return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
-    
+
     GLenum imageTarget = (target == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_2D
                                                          : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
     if (!tex->HasImageInfoAt(imageTarget, 0))
     {
         return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
     }
-    
+
     if (!tex->IsFirstImagePowerOfTwo())
         return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
 
     GLenum format = tex->ImageInfoAt(imageTarget, 0).InternalFormat();
     if (IsTextureFormatCompressed(format))
         return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
 
     if (IsExtensionEnabled(WEBGL_depth_texture) &&
@@ -1316,17 +1292,17 @@ WebGLContext::GetAttribLocation(WebGLPro
 {
     if (IsContextLost())
         return -1;
 
     if (!ValidateObject("getAttribLocation: program", prog))
         return -1;
 
     if (!ValidateGLSLVariableName(name, "getAttribLocation"))
-        return -1; 
+        return -1;
 
     NS_LossyConvertUTF16toASCII cname(name);
     nsCString mappedName;
     prog->MapIdentifier(cname, &mappedName);
 
     GLuint progname = prog->GLName();
 
     MakeContextCurrent();
@@ -1625,17 +1601,17 @@ WebGLContext::GetProgramInfoLog(WebGLPro
         retval.SetIsVoid(true);
         return;
     }
 
     if (!ValidateObject("getProgramInfoLog: program", prog)) {
         retval.Truncate();
         return;
     }
-        
+
     GLuint progname = prog->GLName();
 
     MakeContextCurrent();
 
     GLint k = -1;
     gl->fGetProgramiv(progname, LOCAL_GL_INFO_LOG_LENGTH, &k);
     if (k == -1) {
         // If GetProgramiv doesn't modify |k|,
@@ -2170,17 +2146,17 @@ WebGLContext::LinkProgram(WebGLProgram *
 
             nsAutoCString log;
 
             bool alreadyReportedShaderInfoLog = false;
 
             for (size_t i = 0; i < program->AttachedShaders().Length(); i++) {
 
                 WebGLShader* shader = program->AttachedShaders()[i];
-                
+
                 if (shader->CompileStatus())
                     continue;
 
                 const char *shaderTypeName = nullptr;
                 if (shader->ShaderType() == LOCAL_GL_VERTEX_SHADER) {
                     shaderTypeName = "vertex";
                 } else if (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) {
                     shaderTypeName = "fragment";
@@ -2264,17 +2240,17 @@ WebGLContext::ReadPixels(GLint x, GLint 
     }
 
     if (width < 0 || height < 0)
         return ErrorInvalidValue("readPixels: negative size passed");
 
     if (pixels.IsNull())
         return ErrorInvalidValue("readPixels: null destination buffer");
 
-    const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject();
+    const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
     GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
     GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
 
     uint32_t channels = 0;
 
     // Check the format param
     switch (format) {
         case LOCAL_GL_ALPHA:
@@ -2402,17 +2378,17 @@ WebGLContext::ReadPixels(GLint x, GLint 
             return ErrorInvalidOperation("readPixels: integer overflow computing clipped rect size");
 
         // now we know that subrect_width is in the [0..width] interval, and same for heights.
 
         // now, same computation as above to find the size of the intermediate buffer to allocate for the subrect
         // no need to check again for integer overflow here, since we already know the sizes aren't greater than before
         uint32_t subrect_plainRowSize = subrect_width * bytesPerPixel;
     // There are checks above to ensure that this doesn't overflow.
-        uint32_t subrect_alignedRowSize = 
+        uint32_t subrect_alignedRowSize =
             RoundedToNextMultipleOf(subrect_plainRowSize, mPixelStorePackAlignment).value();
         uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize;
 
         // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer
         GLubyte *subrect_data = new GLubyte[subrect_byteLength];
         gl->fReadPixels(subrect_x, subrect_y, subrect_width, subrect_height, format, type, subrect_data);
 
         // notice that this for loop terminates because we already checked that subrect_height is at most height
@@ -2465,17 +2441,17 @@ WebGLContext::ReadPixels(GLint x, GLint 
                     }
 
                     row += checked_alignedRowSize.value();
                 }
             } else {
                 NS_WARNING("Unhandled case, how'd we get here?");
                 return rv.Throw(NS_ERROR_FAILURE);
             }
-        }            
+        }
     }
 }
 
 void
 WebGLContext::RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
 {
     if (IsContextLost())
         return;
@@ -3121,32 +3097,32 @@ WebGLContext::CompileShader(WebGLShader 
 #ifdef XP_MACOSX
             if (gl->Vendor() == gl::GLVendor::NVIDIA) {
                 // Work around bug 890432
                 resources.MaxExpressionComplexity = 1000;
             }
 #endif
         }
 
-        // We're storing an actual instance of StripComments because, if we don't, the 
+        // We're storing an actual instance of StripComments because, if we don't, the
         // cleanSource nsAString instance will be destroyed before the reference is
         // actually used.
         StripComments stripComments(shader->Source());
         const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
         if (!ValidateGLSLString(cleanSource, "compileShader"))
             return;
 
         // shaderSource() already checks that the source stripped of comments is in the
         // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
         NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
 
         if (gl->WorkAroundDriverBugs()) {
             const uint32_t maxSourceLength = 0x3ffff;
             if (sourceCString.Length() > maxSourceLength)
-                return ErrorInvalidValue("compileShader: source has more than %d characters", 
+                return ErrorInvalidValue("compileShader: source has more than %d characters",
                                          maxSourceLength);
         }
 
         const char *s = sourceCString.get();
 
 #define WEBGL2_BYPASS_ANGLE
 #ifdef WEBGL2_BYPASS_ANGLE
         /*
@@ -3653,17 +3629,17 @@ void
 WebGLContext::ShaderSource(WebGLShader *shader, const nsAString& source)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("shaderSource: shader", shader))
         return;
 
-    // We're storing an actual instance of StripComments because, if we don't, the 
+    // We're storing an actual instance of StripComments because, if we don't, the
     // cleanSource nsAString instance will be destroyed before the reference is
     // actually used.
     StripComments stripComments(source);
     const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
     if (!ValidateGLSLString(cleanSource, "compileShader"))
         return;
 
     shader->SetSource(source);
@@ -3680,25 +3656,25 @@ GLenum WebGLContext::CheckedTexImage2D(G
                                        GLenum format,
                                        GLenum type,
                                        const GLvoid *data)
 {
     WebGLTexture *tex = activeBoundTextureForTarget(target);
     MOZ_ASSERT(tex != nullptr, "no texture bound");
 
     bool sizeMayChange = true;
-    
+
     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();
     }
-    
+
     if (sizeMayChange) {
         UpdateWebGLErrorAndClearGLError();
         gl->fTexImage2D(target, level, internalFormat, width, height, border, format, type, data);
         GLenum error = LOCAL_GL_NO_ERROR;
         UpdateWebGLErrorAndClearGLError(&error);
         return error;
     } else {
         gl->fTexImage2D(target, level, internalFormat, width, height, border, format, type, data);
@@ -3759,17 +3735,17 @@ WebGLContext::TexImage2D_base(GLenum tar
     if (!ValidateTexFormatAndType(format, type, jsArrayType, &dstTexelSize, "texImage2D"))
         return;
 
     WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
-    CheckedUint32 checked_neededByteLength = 
+    CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
 
     CheckedUint32 checked_alignedRowSize =
         RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
 
     if (!checked_neededByteLength.isValid())
@@ -3885,17 +3861,17 @@ WebGLContext::TexImage2D(GLenum target, 
 {
     if (IsContextLost())
         return;
 
     if (!pixels) {
         // Spec says to generate an INVALID_VALUE error
         return ErrorInvalidValue("texImage2D: null ImageData");
     }
-    
+
     Uint8ClampedArray arr(pixels->GetDataObject());
     return TexImage2D_base(target, level, internalformat, pixels->Width(),
                            pixels->Height(), 4*pixels->Width(), 0,
                            format, type, arr.Data(), arr.Length(), -1,
                            WebGLTexelFormat::RGBA8, false);
 }
 
 
@@ -3943,44 +3919,44 @@ WebGLContext::TexSubImage2D_base(GLenum 
     WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     if (width == 0 || height == 0)
         return; // ES 2.0 says it has no effect, we better return right now
 
-    CheckedUint32 checked_neededByteLength = 
+    CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
 
-    CheckedUint32 checked_alignedRowSize = 
+    CheckedUint32 checked_alignedRowSize =
         RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
 
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
 
     uint32_t bytesNeeded = checked_neededByteLength.value();
- 
+
     if (byteLength < bytesNeeded)
         return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
 
     if (!tex)
         return ErrorInvalidOperation("texSubImage2D: no texture is bound to this target");
 
     if (!tex->HasImageInfoAt(target, level))
         return ErrorInvalidOperation("texSubImage2D: no texture image previously defined for this level and face");
-    
+
     const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
     if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, imageInfo.Width(), imageInfo.Height()))
         return ErrorInvalidValue("texSubImage2D: subtexture rectangle out of bounds");
-    
+
     // Require the format and type in texSubImage2D to match that of the existing texture as created by texImage2D
     if (imageInfo.InternalFormat() != format || imageInfo.Type() != type)
         return ErrorInvalidOperation("texSubImage2D: format or type doesn't match the existing texture");
 
     if (imageInfo.HasUninitializedImageData()) {
         tex->DoDeferredImageInitialization(target, level);
     }
 
@@ -4231,18 +4207,18 @@ InternalFormatForFormatAndType(GLenum fo
     if (isGLES2)
         return format;
 
     if (format == LOCAL_GL_DEPTH_COMPONENT) {
         if (type == LOCAL_GL_UNSIGNED_SHORT)
             return LOCAL_GL_DEPTH_COMPONENT16;
         else if (type == LOCAL_GL_UNSIGNED_INT)
             return LOCAL_GL_DEPTH_COMPONENT32;
-    } 
-    
+    }
+
     if (format == LOCAL_GL_DEPTH_STENCIL) {
         if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
             return LOCAL_GL_DEPTH24_STENCIL8;
     }
 
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
--- a/content/canvas/src/WebGLContextVertices.cpp
+++ b/content/canvas/src/WebGLContextVertices.cpp
@@ -730,17 +730,17 @@ void WebGLContext::Draw_cleanup()
             if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
                 gl->fFlush();
                 mDrawCallsSinceLastFlush = 0;
             }
         }
     }
 
     // Let's check the viewport
-    const WebGLRectangleObject* rect = FramebufferRectangleObject();
+    const WebGLRectangleObject* rect = CurValidFBRectObject();
     if (rect) {
         if (mViewportWidth > rect->Width() ||
             mViewportHeight > rect->Height())
         {
             if (!mAlreadyWarnedAboutViewportLargerThanDest) {
                 GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
                                 "(This warning will only be given once)");
                 mAlreadyWarnedAboutViewportLargerThanDest = true;
--- a/content/canvas/src/WebGLFramebuffer.cpp
+++ b/content/canvas/src/WebGLFramebuffer.cpp
@@ -7,111 +7,134 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLExtensions.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLTexture.h"
 #include "WebGLRenderbuffer.h"
 #include "GLContext.h"
+#include "WebGLContextUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gl;
 
 JSObject*
-WebGLFramebuffer::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) {
+WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> scope)
+{
     return dom::WebGLFramebufferBinding::Wrap(cx, scope, this);
 }
 
-WebGLFramebuffer::WebGLFramebuffer(WebGLContext *context)
+WebGLFramebuffer::WebGLFramebuffer(WebGLContext* context)
     : WebGLContextBoundObject(context)
     , mHasEverBeenBound(false)
     , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT)
     , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT)
     , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
 {
     SetIsDOMBinding();
     mContext->MakeContextCurrent();
     mContext->gl->fGenFramebuffers(1, &mGLName);
     mContext->mFramebuffers.insertBack(this);
 
     mColorAttachments.SetLength(1);
     mColorAttachments[0].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0;
 }
 
 bool
-WebGLFramebuffer::Attachment::IsDeleteRequested() const {
+WebGLFramebuffer::Attachment::IsDeleteRequested() const
+{
     return Texture() ? Texture()->IsDeleteRequested()
          : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
          : false;
 }
 
 bool
-WebGLFramebuffer::Attachment::HasAlpha() const {
+WebGLFramebuffer::Attachment::HasAlpha() const
+{
     GLenum format = 0;
     if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
         format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).InternalFormat();
     else if (Renderbuffer())
         format = Renderbuffer()->InternalFormat();
     return FormatHasAlpha(format);
 }
 
 void
-WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture *tex, GLenum target, GLint level) {
+WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, GLenum target, GLint level)
+{
     mTexturePtr = tex;
     mRenderbufferPtr = nullptr;
     mTexImageTarget = target;
     mTexImageLevel = level;
 }
 
 bool
-WebGLFramebuffer::Attachment::HasUninitializedImageData() const {
-    if (mRenderbufferPtr) {
-        return mRenderbufferPtr->HasUninitializedImageData();
-    } else if (mTexturePtr) {
-        if (!mTexturePtr->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
-            return false;
-        return mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).HasUninitializedImageData();
-    } else {
+WebGLFramebuffer::Attachment::HasUninitializedImageData() const
+{
+    if (!HasImage())
         return false;
+
+    if (Renderbuffer()) {
+        return Renderbuffer()->HasUninitializedImageData();
+    } else if (Texture()) {
+        MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
+        return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).HasUninitializedImageData();
     }
+
+    MOZ_ASSERT(false, "Should not get here.");
+    return false;
 }
 
 void
-WebGLFramebuffer::Attachment::SetImageDataStatus(WebGLImageDataStatus newStatus) {
+WebGLFramebuffer::Attachment::SetImageDataStatus(WebGLImageDataStatus newStatus)
+{
+    if (!HasImage())
+        return;
+
     if (mRenderbufferPtr) {
         mRenderbufferPtr->SetImageDataStatus(newStatus);
+        return;
     } else if (mTexturePtr) {
         mTexturePtr->SetImageDataStatus(mTexImageTarget, mTexImageLevel, newStatus);
-    } else {
-        MOZ_ASSERT(false); // should not get here, worth crashing a debug build.
+        return;
     }
-}
 
-const WebGLRectangleObject*
-WebGLFramebuffer::Attachment::RectangleObject() const {
-    if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
-        return &Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
-    else if (Renderbuffer())
-        return Renderbuffer();
-    else
-        return nullptr;
+    MOZ_ASSERT(false, "Should not get here.");
 }
 
 bool
-WebGLFramebuffer::Attachment::HasSameDimensionsAs(const Attachment& other) const {
-    const WebGLRectangleObject *thisRect = RectangleObject();
-    const WebGLRectangleObject *otherRect = other.RectangleObject();
-    return thisRect &&
-           otherRect &&
-           thisRect->HasSameDimensionsAs(*otherRect);
+WebGLFramebuffer::Attachment::HasImage() const
+{
+    if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
+        return true;
+    else if (Renderbuffer())
+        return true;
+
+    return false;
+}
+
+const WebGLRectangleObject&
+WebGLFramebuffer::Attachment::RectangleObject() const
+{
+    MOZ_ASSERT(HasImage(), "Make sure it has an image before requesting the rectangle.");
+
+    if (Texture()) {
+        MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
+        return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
+    } else if (Renderbuffer()) {
+        return *Renderbuffer();
+    }
+
+    MOZ_CRASH("Should not get here.");
 }
 
 static inline bool
-IsValidAttachedTextureColorFormat(GLenum format) {
+IsValidAttachedTextureColorFormat(GLenum format)
+{
     return (
         /* linear 8-bit formats */
         format == LOCAL_GL_ALPHA ||
         format == LOCAL_GL_LUMINANCE ||
         format == LOCAL_GL_LUMINANCE_ALPHA ||
         format == LOCAL_GL_RGB ||
         format == LOCAL_GL_RGBA ||
         /* sRGB 8-bit formats */
@@ -121,71 +144,76 @@ IsValidAttachedTextureColorFormat(GLenum
         format ==  LOCAL_GL_ALPHA32F_ARB ||
         format ==  LOCAL_GL_LUMINANCE32F_ARB ||
         format ==  LOCAL_GL_LUMINANCE_ALPHA32F_ARB ||
         format ==  LOCAL_GL_RGB32F_ARB ||
         format ==  LOCAL_GL_RGBA32F_ARB);
 }
 
 bool
-WebGLFramebuffer::Attachment::IsComplete() const {
-    const WebGLRectangleObject *thisRect = RectangleObject();
-
-    if (!thisRect ||
-        !thisRect->Width() ||
-        !thisRect->Height())
+WebGLFramebuffer::Attachment::IsComplete() const
+{
+    if (!HasImage())
         return false;
 
+    const WebGLRectangleObject& rect = RectangleObject();
+
+    if (!rect.Width() ||
+        !rect.Height())
+    {
+        return false;
+    }
+
     if (mTexturePtr) {
-        if (!mTexturePtr->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
-            return false;
-
+        MOZ_ASSERT(mTexturePtr->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
         GLenum format = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).InternalFormat();
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) {
             return format == LOCAL_GL_DEPTH_COMPONENT;
-        }
-        else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+        } else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
             return format == LOCAL_GL_DEPTH_STENCIL;
-        }
-        else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-                 mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments)) {
+        } else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
+                   mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments))
+        {
             return IsValidAttachedTextureColorFormat(format);
         }
-        MOZ_CRASH("Invalid WebGL attachment poin?");
+        MOZ_ASSERT(false, "Invalid WebGL attachment point?");
+        return false;
     }
 
     if (mRenderbufferPtr) {
         GLenum format = mRenderbufferPtr->InternalFormat();
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) {
             return format == LOCAL_GL_DEPTH_COMPONENT16;
-        }
-        else if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) {
+        } else if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) {
             return format == LOCAL_GL_STENCIL_INDEX8;
-        }
-        else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+        } else if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
             return format == LOCAL_GL_DEPTH_STENCIL;
+        } else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
+                   mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments))
+        {
+            return format == LOCAL_GL_RGB565 ||
+                   format == LOCAL_GL_RGB5_A1 ||
+                   format == LOCAL_GL_RGBA4 ||
+                   format == LOCAL_GL_SRGB8_ALPHA8_EXT;
         }
-        else if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-                 mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + WebGLContext::sMaxColorAttachments)) {
-            return (format == LOCAL_GL_RGB565 ||
-                    format == LOCAL_GL_RGB5_A1 ||
-                    format == LOCAL_GL_RGBA4 ||
-                    format == LOCAL_GL_SRGB8_ALPHA8_EXT);
-        }
-        MOZ_CRASH("Invalid WebGL attachment poin?");
+        MOZ_ASSERT(false, "Invalid WebGL attachment point?");
+        return false;
     }
 
-    MOZ_ASSERT(false); // should never get there
+    MOZ_ASSERT(false, "Should not get here.");
     return false;
 }
 
 void
-WebGLFramebuffer::Attachment::FinalizeAttachment(GLenum attachmentLoc) const {
+WebGLFramebuffer::Attachment::FinalizeAttachment(GLenum attachmentLoc) const
+{
+    MOZ_ASSERT(HasImage());
+
     if (Texture()) {
         GLContext* gl = Texture()->Context()->gl;
         if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                       TexImageTarget(), Texture()->GLName(), TexImageLevel());
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
                                       TexImageTarget(), Texture()->GLName(), TexImageLevel());
         } else {
@@ -195,42 +223,41 @@ WebGLFramebuffer::Attachment::FinalizeAt
         return;
     }
 
     if (Renderbuffer()) {
         Renderbuffer()->FramebufferRenderbuffer(attachmentLoc);
         return;
     }
 
-    // Neither?
-    MOZ_ASSERT(false, "FB attachment without a tex or RB.");
+    MOZ_ASSERT(false, "Should not get here.");
 }
 
 void
-WebGLFramebuffer::Delete() {
+WebGLFramebuffer::Delete()
+{
     mColorAttachments.Clear();
     mDepthAttachment.Reset();
     mStencilAttachment.Reset();
     mDepthStencilAttachment.Reset();
+
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteFramebuffers(1, &mGLName);
     LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
 }
 
 void
 WebGLFramebuffer::FramebufferRenderbuffer(GLenum target,
-                             GLenum attachment,
-                             GLenum rbtarget,
-                             WebGLRenderbuffer *wrb)
+                                          GLenum attachment,
+                                          GLenum rbtarget,
+                                          WebGLRenderbuffer* wrb)
 {
     MOZ_ASSERT(mContext->mBoundFramebuffer == this);
     if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb))
-    {
         return;
-    }
 
     if (target != LOCAL_GL_FRAMEBUFFER)
         return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target);
 
     if (rbtarget != LOCAL_GL_RENDERBUFFER)
         return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget);
 
     switch (attachment) {
@@ -253,35 +280,37 @@ WebGLFramebuffer::FramebufferRenderbuffe
         EnsureColorAttachments(colorAttachmentId);
         mColorAttachments[colorAttachmentId].SetRenderbuffer(wrb);
         break;
     }
 }
 
 void
 WebGLFramebuffer::FramebufferTexture2D(GLenum target,
-                          GLenum attachment,
-                          GLenum textarget,
-                          WebGLTexture *wtex,
-                          GLint level)
+                                       GLenum attachment,
+                                       GLenum textarget,
+                                       WebGLTexture* wtex,
+                                       GLint level)
 {
     MOZ_ASSERT(mContext->mBoundFramebuffer == this);
     if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture",
                                            wtex))
     {
         return;
     }
 
     if (target != LOCAL_GL_FRAMEBUFFER)
         return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target);
 
     if (textarget != LOCAL_GL_TEXTURE_2D &&
         (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
          textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
+    {
         return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget);
+    }
 
     if (wtex) {
         bool isTexture2D = wtex->Target() == LOCAL_GL_TEXTURE_2D;
         bool isTexTarget2D = textarget == LOCAL_GL_TEXTURE_2D;
         if (isTexture2D != isTexTarget2D) {
             return mContext->ErrorInvalidOperation("framebufferTexture2D: mismatched texture and texture target");
         }
     }
@@ -295,263 +324,351 @@ WebGLFramebuffer::FramebufferTexture2D(G
         break;
     case LOCAL_GL_STENCIL_ATTACHMENT:
         mStencilAttachment.SetTexImage(wtex, textarget, level);
         break;
     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
         mDepthStencilAttachment.SetTexImage(wtex, textarget, level);
         break;
     default:
-        if (!CheckColorAttachementNumber(attachment, "framebufferTexture2D")){
+        if (!CheckColorAttachementNumber(attachment, "framebufferTexture2D"))
             return;
-        }
 
         size_t colorAttachmentId = size_t(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
         EnsureColorAttachments(colorAttachmentId);
         mColorAttachments[colorAttachmentId].SetTexImage(wtex, textarget, level);
         break;
     }
 }
 
-bool
-WebGLFramebuffer::HasIncompleteAttachment() const {
-    int32_t colorAttachmentCount = mColorAttachments.Length();
-
-    for (int32_t i = 0; i < colorAttachmentCount; i++)
-    {
-        if (mColorAttachments[i].IsDefined() && !mColorAttachments[i].IsComplete())
-        {
-            return true;
-        }
-    }
-
-    return ((mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) ||
-            (mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) ||
-            (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete()));
-}
-
-bool
-WebGLFramebuffer::HasAttachmentsOfMismatchedDimensions() const {
-    int32_t colorAttachmentCount = mColorAttachments.Length();
-
-    for (int32_t i = 1; i < colorAttachmentCount; i++)
-    {
-        if (mColorAttachments[i].IsDefined() && !mColorAttachments[i].HasSameDimensionsAs(mColorAttachments[0]))
-        {
-            return true;
-        }
-    }
-
-    return ((mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachments[0])) ||
-            (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachments[0])) ||
-            (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachments[0])));
-}
-
 const WebGLFramebuffer::Attachment&
-WebGLFramebuffer::GetAttachment(GLenum attachment) const {
+WebGLFramebuffer::GetAttachment(GLenum attachment) const
+{
     if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
         return mDepthStencilAttachment;
     if (attachment == LOCAL_GL_DEPTH_ATTACHMENT)
         return mDepthAttachment;
     if (attachment == LOCAL_GL_STENCIL_ATTACHMENT)
         return mStencilAttachment;
 
     if (!CheckColorAttachementNumber(attachment, "getAttachment")) {
         MOZ_ASSERT(false);
         return mColorAttachments[0];
     }
 
-    uint32_t colorAttachmentId = uint32_t(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
-
+    size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0;
     if (colorAttachmentId >= mColorAttachments.Length()) {
         MOZ_ASSERT(false);
         return mColorAttachments[0];
     }
 
     return mColorAttachments[colorAttachmentId];
 }
 
 void
-WebGLFramebuffer::DetachTexture(const WebGLTexture *tex) {
-    int32_t colorAttachmentCount = mColorAttachments.Length();
-
-    for (int32_t i = 0; i < colorAttachmentCount; i++) {
+WebGLFramebuffer::DetachTexture(const WebGLTexture* tex)
+{
+    for (size_t i = 0; i < mColorAttachments.Length(); i++) {
         if (mColorAttachments[i].Texture() == tex) {
             FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, nullptr, 0);
             // a texture might be attached more that once while editing the framebuffer
         }
     }
 
     if (mDepthAttachment.Texture() == tex)
         FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
     if (mStencilAttachment.Texture() == tex)
         FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
     if (mDepthStencilAttachment.Texture() == tex)
         FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
 }
 
 void
-WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer *rb) {
-    int32_t colorAttachmentCount = mColorAttachments.Length();
-
-    for (int32_t i = 0; i < colorAttachmentCount; i++) {
+WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb)
+{
+    for (size_t i = 0; i < mColorAttachments.Length(); i++) {
         if (mColorAttachments[0].Renderbuffer() == rb) {
             FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, nullptr);
             // a renderbuffer might be attached more that once while editing the framebuffer
         }
     }
 
     if (mDepthAttachment.Renderbuffer() == rb)
         FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
     if (mStencilAttachment.Renderbuffer() == rb)
         FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
     if (mDepthStencilAttachment.Renderbuffer() == rb)
         FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
 }
 
 bool
-WebGLFramebuffer::CheckAndInitializeAttachments()
+WebGLFramebuffer::HasDefinedAttachments() const
+{
+    bool hasAttachments = false;
+
+    for (size_t i = 0; i < mColorAttachments.Length(); i++) {
+        hasAttachments |= mColorAttachments[i].IsDefined();
+    }
+
+    hasAttachments |= mDepthAttachment.IsDefined();
+    hasAttachments |= mStencilAttachment.IsDefined();
+    hasAttachments |= mDepthStencilAttachment.IsDefined();
+
+    return hasAttachments;
+}
+
+
+static bool
+IsIncomplete(const WebGLFramebuffer::Attachment& cur)
+{
+  return cur.IsDefined() && !cur.IsComplete();
+}
+
+bool
+WebGLFramebuffer::HasIncompleteAttachments() const
+{
+    bool hasIncomplete = false;
+
+    for (size_t i = 0; i < mColorAttachments.Length(); i++) {
+        hasIncomplete |= IsIncomplete(mColorAttachments[i]);
+    }
+
+    hasIncomplete |= IsIncomplete(mDepthAttachment);
+    hasIncomplete |= IsIncomplete(mStencilAttachment);
+    hasIncomplete |= IsIncomplete(mDepthStencilAttachment);
+
+    return hasIncomplete;
+}
+
+
+const WebGLRectangleObject&
+WebGLFramebuffer::GetAnyRectObject() const
+{
+    MOZ_ASSERT(HasDefinedAttachments());
+
+    for (size_t i = 0; i < mColorAttachments.Length(); i++) {
+        if (mColorAttachments[i].HasImage())
+            return mColorAttachments[i].RectangleObject();
+    }
+
+    if (mDepthAttachment.HasImage())
+        return mDepthAttachment.RectangleObject();
+
+    if (mStencilAttachment.HasImage())
+        return mStencilAttachment.RectangleObject();
+
+    if (mDepthStencilAttachment.HasImage())
+        return mDepthStencilAttachment.RectangleObject();
+
+    MOZ_CRASH("Should not get here.");
+}
+
+
+static bool
+RectsMatch(const WebGLFramebuffer::Attachment& attachment,
+           const WebGLRectangleObject& rect)
+{
+    return attachment.RectangleObject().HasSameDimensionsAs(rect);
+}
+
+bool
+WebGLFramebuffer::AllImageRectsMatch() const
+{
+    MOZ_ASSERT(HasDefinedAttachments());
+    MOZ_ASSERT(!HasIncompleteAttachments());
+
+    const WebGLRectangleObject& rect = GetAnyRectObject();
+
+    // Alright, we have *a* rect, let's check all the others.
+    bool imageRectsMatch = true;
+
+    for (size_t i = 0; i < mColorAttachments.Length(); i++) {
+        if (mColorAttachments[i].HasImage())
+            imageRectsMatch &= RectsMatch(mColorAttachments[i], rect);
+    }
+
+    if (mDepthAttachment.HasImage())
+        imageRectsMatch &= RectsMatch(mDepthAttachment, rect);
+
+    if (mStencilAttachment.HasImage())
+        imageRectsMatch &= RectsMatch(mStencilAttachment, rect);
+
+    if (mDepthStencilAttachment.HasImage())
+        imageRectsMatch &= RectsMatch(mDepthStencilAttachment, rect);
+
+    return imageRectsMatch;
+}
+
+
+const WebGLRectangleObject&
+WebGLFramebuffer::RectangleObject() const
+{
+    // If we're using this as the RectObj of an FB, we need to be sure the FB
+    // has a consistent rect.
+    MOZ_ASSERT(AllImageRectsMatch(), "Did you mean `GetAnyRectObject`?");
+    return GetAnyRectObject();
+}
+
+GLenum
+WebGLFramebuffer::PrecheckFramebufferStatus() const
 {
     MOZ_ASSERT(mContext->mBoundFramebuffer == this);
-    // enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not
-    // generate the INVALID_FRAMEBUFFER_OPERATION that we need here
+
+    if (!HasDefinedAttachments())
+        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments
+
+    if (HasIncompleteAttachments())
+        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+
+    if (!AllImageRectsMatch())
+        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // No consistent size
+
     if (HasDepthStencilConflict())
-        return false;
+        return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
+
+    return LOCAL_GL_FRAMEBUFFER_COMPLETE;
+}
 
-    if (HasIncompleteAttachment())
-        return false;
+GLenum
+WebGLFramebuffer::CheckFramebufferStatus() const
+{
+    GLenum precheckStatus = PrecheckFramebufferStatus();
+    if (precheckStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+        return precheckStatus;
 
+    // Looks good on our end. Let's ask the driver.
     mContext->MakeContextCurrent();
 
     // Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}.
     FinalizeAttachments();
 
+    return mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+}
+
+
+
+bool
+WebGLFramebuffer::CheckAndInitializeAttachments()
+{
+    MOZ_ASSERT(mContext->mBoundFramebuffer == this);
+
+    if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+        return false;
+
+    // Cool! We've checked out ok. Just need to initialize.
     size_t colorAttachmentCount = size_t(mColorAttachments.Length());
 
+    // Check if we need to initialize anything
     {
-        bool hasUnitializedAttachments = false;
+        bool hasUninitializedAttachments = false;
 
         for (size_t i = 0; i < colorAttachmentCount; i++) {
-            hasUnitializedAttachments |= mColorAttachments[i].HasUninitializedImageData();
+            if (mColorAttachments[i].HasImage())
+                hasUninitializedAttachments |= mColorAttachments[i].HasUninitializedImageData();
         }
 
-        if (!hasUnitializedAttachments &&
-            !mDepthAttachment.HasUninitializedImageData() &&
-            !mStencilAttachment.HasUninitializedImageData() &&
-            !mDepthStencilAttachment.HasUninitializedImageData())
-        {
+        if (mDepthAttachment.HasImage())
+            hasUninitializedAttachments |= mDepthAttachment.HasUninitializedImageData();
+        if (mStencilAttachment.HasImage())
+            hasUninitializedAttachments |= mStencilAttachment.HasUninitializedImageData();
+        if (mDepthStencilAttachment.HasImage())
+            hasUninitializedAttachments |= mDepthStencilAttachment.HasUninitializedImageData();
+
+        if (!hasUninitializedAttachments)
             return true;
-        }
     }
 
-    // ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case
-    const WebGLRectangleObject *rect = mColorAttachments[0].RectangleObject();
-    if (!rect ||
-        !rect->Width() ||
-        !rect->Height())
-        return false;
-
-    GLenum status = mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
-        return false;
-
+    // Get buffer-bit-mask and color-attachment-mask-list
     uint32_t mask = 0;
     bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = { false };
-
-    MOZ_ASSERT( colorAttachmentCount <= WebGLContext::sMaxColorAttachments );
+    MOZ_ASSERT(colorAttachmentCount <= WebGLContext::sMaxColorAttachments);
 
-    for (size_t i = 0; i < colorAttachmentCount; i++)
-    {
-        colorAttachmentsMask[i] = mColorAttachments[i].HasUninitializedImageData();
-
-        if (colorAttachmentsMask[i]) {
-            mask |= LOCAL_GL_COLOR_BUFFER_BIT;
+    for (size_t i = 0; i < colorAttachmentCount; i++) {
+        if (mColorAttachments[i].HasUninitializedImageData()) {
+          colorAttachmentsMask[i] = true;
+          mask |= LOCAL_GL_COLOR_BUFFER_BIT;
         }
     }
 
     if (mDepthAttachment.HasUninitializedImageData() ||
         mDepthStencilAttachment.HasUninitializedImageData())
     {
         mask |= LOCAL_GL_DEPTH_BUFFER_BIT;
     }
 
     if (mStencilAttachment.HasUninitializedImageData() ||
         mDepthStencilAttachment.HasUninitializedImageData())
     {
         mask |= LOCAL_GL_STENCIL_BUFFER_BIT;
     }
 
+    // Clear!
     mContext->ForceClearFramebufferWithDefaultValues(mask, colorAttachmentsMask);
 
-    for (size_t i = 0; i < colorAttachmentCount; i++)
-    {
+    // Mark all the uninitialized images as initialized.
+    for (size_t i = 0; i < colorAttachmentCount; i++) {
         if (mColorAttachments[i].HasUninitializedImageData())
             mColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
     }
 
     if (mDepthAttachment.HasUninitializedImageData())
         mDepthAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
     if (mStencilAttachment.HasUninitializedImageData())
         mStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
     if (mDepthStencilAttachment.HasUninitializedImageData())
         mDepthStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
 
     return true;
 }
 
-bool WebGLFramebuffer::CheckColorAttachementNumber(GLenum attachment, const char * functionName) const
+bool WebGLFramebuffer::CheckColorAttachementNumber(GLenum attachment, const char* functionName) const
 {
     const char* const errorFormating = "%s: attachment: invalid enum value 0x%x";
 
-    if (mContext->IsExtensionEnabled(WebGLContext::WEBGL_draw_buffers))
-    {
+    if (mContext->IsExtensionEnabled(WebGLContext::WEBGL_draw_buffers)) {
         if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
             attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mGLMaxColorAttachments))
         {
             mContext->ErrorInvalidEnum(errorFormating, functionName, attachment);
             return false;
         }
-    }
-    else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
-    {
+    } else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) {
         if (attachment > LOCAL_GL_COLOR_ATTACHMENT0 &&
             attachment <= LOCAL_GL_COLOR_ATTACHMENT15)
         {
             mContext->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x. "
                                        "Try the WEBGL_draw_buffers extension if supported.", functionName, attachment);
             return false;
-        }
-        else
-        {
+        } else {
             mContext->ErrorInvalidEnum(errorFormating, functionName, attachment);
             return false;
         }
     }
 
     return true;
 }
 
-void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId) {
-    size_t currentAttachmentCount = mColorAttachments.Length();
+void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId)
+{
+    MOZ_ASSERT(colorAttachmentId < WebGLContext::sMaxColorAttachments);
 
-    if (mColorAttachments.Length() > colorAttachmentId) {
+    size_t currentAttachmentCount = mColorAttachments.Length();
+    if (colorAttachmentId < currentAttachmentCount)
         return;
-    }
-
-    MOZ_ASSERT( colorAttachmentId < WebGLContext::sMaxColorAttachments );
 
     mColorAttachments.SetLength(colorAttachmentId + 1);
 
     for (size_t i = colorAttachmentId; i >= currentAttachmentCount; i--) {
         mColorAttachments[i].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0 + i;
     }
 }
 
 void
-WebGLFramebuffer::FinalizeAttachments() const {
+WebGLFramebuffer::FinalizeAttachments() const
+{
     for (size_t i = 0; i < ColorAttachmentCount(); i++) {
         if (ColorAttachment(i).IsDefined())
             ColorAttachment(i).FinalizeAttachment(LOCAL_GL_COLOR_ATTACHMENT0 + i);
     }
 
     if (DepthAttachment().IsDefined())
         DepthAttachment().FinalizeAttachment(LOCAL_GL_DEPTH_ATTACHMENT);
 
--- a/content/canvas/src/WebGLFramebuffer.h
+++ b/content/canvas/src/WebGLFramebuffer.h
@@ -22,17 +22,17 @@ namespace gl {
 
 class WebGLFramebuffer MOZ_FINAL
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
     , public WebGLContextBoundObject
 {
 public:
-    WebGLFramebuffer(WebGLContext *context);
+    WebGLFramebuffer(WebGLContext* context);
 
     ~WebGLFramebuffer() {
         DeleteOnce();
     }
 
     struct Attachment
     {
         // deleting a texture or renderbuffer immediately detaches it
@@ -49,31 +49,31 @@ public:
         bool IsDefined() const {
             return Texture() || Renderbuffer();
         }
 
         bool IsDeleteRequested() const;
 
         bool HasAlpha() const;
 
-        void SetTexImage(WebGLTexture *tex, GLenum target, GLint level);
-        void SetRenderbuffer(WebGLRenderbuffer *rb) {
+        void SetTexImage(WebGLTexture* tex, GLenum target, GLint level);
+        void SetRenderbuffer(WebGLRenderbuffer* rb) {
             mTexturePtr = nullptr;
             mRenderbufferPtr = rb;
         }
-        const WebGLTexture *Texture() const {
+        const WebGLTexture* Texture() const {
             return mTexturePtr;
         }
-        WebGLTexture *Texture() {
+        WebGLTexture* Texture() {
             return mTexturePtr;
         }
-        const WebGLRenderbuffer *Renderbuffer() const {
+        const WebGLRenderbuffer* Renderbuffer() const {
             return mRenderbufferPtr;
         }
-        WebGLRenderbuffer *Renderbuffer() {
+        WebGLRenderbuffer* Renderbuffer() {
             return mRenderbufferPtr;
         }
         GLenum TexImageTarget() const {
             return mTexImageTarget;
         }
         GLint TexImageLevel() const {
             return mTexImageLevel;
         }
@@ -81,55 +81,61 @@ public:
         bool HasUninitializedImageData() const;
         void SetImageDataStatus(WebGLImageDataStatus x);
 
         void Reset() {
             mTexturePtr = nullptr;
             mRenderbufferPtr = nullptr;
         }
 
-        const WebGLRectangleObject* RectangleObject() const;
-        bool HasSameDimensionsAs(const Attachment& other) const;
+        const WebGLRectangleObject& RectangleObject() const;
 
+        bool HasImage() const;
         bool IsComplete() const;
 
         void FinalizeAttachment(GLenum attachmentLoc) const;
     };
 
     void Delete();
 
     bool HasEverBeenBound() { return mHasEverBeenBound; }
     void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
     GLuint GLName() { return mGLName; }
 
     void FramebufferRenderbuffer(GLenum target,
                                  GLenum attachment,
                                  GLenum rbtarget,
-                                 WebGLRenderbuffer *wrb);
+                                 WebGLRenderbuffer* wrb);
 
     void FramebufferTexture2D(GLenum target,
                               GLenum attachment,
                               GLenum textarget,
-                              WebGLTexture *wtex,
+                              WebGLTexture* wtex,
                               GLint level);
 
-    bool HasIncompleteAttachment() const;
+private:
+    const WebGLRectangleObject& GetAnyRectObject() const;
+
+public:
+    bool HasDefinedAttachments() const;
+    bool HasIncompleteAttachments() const;
+    bool AllImageRectsMatch() const;
+    GLenum PrecheckFramebufferStatus() const;
+    GLenum CheckFramebufferStatus() const;
 
     bool HasDepthStencilConflict() const {
         return int(mDepthAttachment.IsDefined()) +
                int(mStencilAttachment.IsDefined()) +
                int(mDepthStencilAttachment.IsDefined()) >= 2;
     }
 
-    bool HasAttachmentsOfMismatchedDimensions() const;
-
-    const size_t ColorAttachmentCount() const {
+    size_t ColorAttachmentCount() const {
         return mColorAttachments.Length();
     }
-    const Attachment& ColorAttachment(uint32_t colorAttachmentId) const {
+    const Attachment& ColorAttachment(size_t colorAttachmentId) const {
         return mColorAttachments[colorAttachmentId];
     }
 
     const Attachment& DepthAttachment() const {
         return mDepthAttachment;
     }
 
     const Attachment& StencilAttachment() const {
@@ -137,39 +143,37 @@ public:
     }
 
     const Attachment& DepthStencilAttachment() const {
         return mDepthStencilAttachment;
     }
 
     const Attachment& GetAttachment(GLenum attachment) const;
 
-    void DetachTexture(const WebGLTexture *tex);
+    void DetachTexture(const WebGLTexture* tex);
 
-    void DetachRenderbuffer(const WebGLRenderbuffer *rb);
+    void DetachRenderbuffer(const WebGLRenderbuffer* rb);
 
-    const WebGLRectangleObject *RectangleObject() {
-        return mColorAttachments[0].RectangleObject();
-    }
+    const WebGLRectangleObject& RectangleObject() const;
 
-    WebGLContext *GetParentObject() const {
+    WebGLContext* GetParentObject() const {
         return Context();
     }
 
     void FinalizeAttachments() const;
 
-    virtual JSObject* WrapObject(JSContext *cx,
+    virtual JSObject* WrapObject(JSContext* cx,
                                  JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLFramebuffer)
 
     bool CheckAndInitializeAttachments();
 
-    bool CheckColorAttachementNumber(GLenum attachment, const char * functionName) const;
+    bool CheckColorAttachementNumber(GLenum attachment, const char* functionName) const;
 
     GLuint mGLName;
     bool mHasEverBeenBound;
 
     void EnsureColorAttachments(size_t colorAttachmentId);
 
     // we only store pointers to attached renderbuffers, not to attached textures, because
     // we will only need to initialize renderbuffers. Textures are already initialized.
--- a/content/html/content/src/HTMLTrackElement.cpp
+++ b/content/html/content/src/HTMLTrackElement.cpp
@@ -303,16 +303,16 @@ HTMLTrackElement::UnbindFromTree(bool aD
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 uint16_t
 HTMLTrackElement::ReadyState() const
 {
   if (!mTrack) {
-    return NONE;
+    return READY_STATE_NONE;
   }
 
   return mTrack->ReadyState();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/HTMLTrackElement.h
+++ b/content/html/content/src/HTMLTrackElement.h
@@ -92,20 +92,20 @@ public:
   }
   void SetDefault(bool aDefault, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::_default, aDefault, aError);
   }
 
   // Constants for numeric readyState property values.
   enum {
-    NONE = 0U,
-    LOADING = 1U,
-    LOADED = 2U,
-    ERROR = 3U
+    READY_STATE_NONE = 0U,
+    READY_STATE_LOADING = 1U,
+    READY_STATE_LOADED = 2U,
+    READY_STATE_ERROR = 3U
   };
   uint16_t ReadyState() const;
 
   TextTrack* Track();
 
   virtual nsresult Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const MOZ_OVERRIDE;
 
   // For Track, ItemValue reflects the src attribute
--- a/content/html/content/src/TextTrackManager.cpp
+++ b/content/html/content/src/TextTrackManager.cpp
@@ -56,17 +56,17 @@ TextTrackManager::TextTracks() const
 }
 
 already_AddRefed<TextTrack>
 TextTrackManager::AddTextTrack(TextTrackKind aKind, const nsAString& aLabel,
                                const nsAString& aLanguage)
 {
   nsRefPtr<TextTrack> ttrack =
     mTextTracks->AddTextTrack(mMediaElement, aKind, aLabel, aLanguage);
-  ttrack->SetReadyState(HTMLTrackElement::LOADED);
+  ttrack->SetReadyState(HTMLTrackElement::READY_STATE_LOADED);
   AddCues(ttrack);
   return ttrack.forget();
 }
 
 void
 TextTrackManager::AddTextTrack(TextTrack* aTextTrack)
 {
   mTextTracks->AddTextTrack(aTextTrack);
@@ -150,16 +150,16 @@ TextTrackManager::AddCue(TextTrackCue& a
 void
 TextTrackManager::PopulatePendingList()
 {
   uint32_t len = mTextTracks->Length();
   bool dummy;
   for (uint32_t index = 0; index < len; ++index) {
     TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy);
     if (ttrack && ttrack->Mode() != TextTrackMode::Disabled &&
-        ttrack->ReadyState() == HTMLTrackElement::LOADING) {
+        ttrack->ReadyState() == HTMLTrackElement::READY_STATE_LOADING) {
       mPendingTextTracks->AddTextTrack(ttrack);
     }
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -3149,17 +3149,17 @@ nsGenericHTMLElement::SetItemValue(JSCon
                                    ErrorResult& aError)
 {
   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::itemprop) ||
       HasAttr(kNameSpaceID_None, nsGkAtoms::itemscope)) {
     aError.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
 
-  FakeDependentString string;
+  binding_detail::FakeDependentString string;
   JS::Rooted<JS::Value> value(aCx, aValue);
   if (!ConvertJSValueToString(aCx, value, &value, eStringify, eStringify, string)) {
     aError.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
   SetItemValueText(string);
 }
 
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1564,19 +1564,19 @@ nsresult MediaDecoder::GetBuffered(dom::
 
 int64_t MediaDecoder::VideoQueueMemoryInUse() {
   if (mDecoderStateMachine) {
     return mDecoderStateMachine->VideoQueueMemoryInUse();
   }
   return 0;
 }
 
-int64_t MediaDecoder::AudioQueueMemoryInUse() {
+size_t MediaDecoder::SizeOfAudioQueue() {
   if (mDecoderStateMachine) {
-    return mDecoderStateMachine->AudioQueueMemoryInUse();
+    return mDecoderStateMachine->SizeOfAudioQueue();
   }
   return 0;
 }
 
 void MediaDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {
   if (mDecoderStateMachine) {
     mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
   }
@@ -1821,17 +1821,17 @@ MediaDecoder::IsAppleMP3Enabled()
 NS_IMETHODIMP
 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
                                    nsISupports* aData)
 {
   int64_t video = 0, audio = 0;
   DecodersArray& decoders = Decoders();
   for (size_t i = 0; i < decoders.Length(); ++i) {
     video += decoders[i]->VideoQueueMemoryInUse();
-    audio += decoders[i]->AudioQueueMemoryInUse();
+    audio += decoders[i]->SizeOfAudioQueue();
   }
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
       nsresult rv;                                                            \
       rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
                                    KIND_HEAP, UNITS_BYTES, _amount,           \
                                    NS_LITERAL_CSTRING(_desc), aData);         \
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -637,17 +637,17 @@ public:
 
   // Constructs the time ranges representing what segments of the media
   // are buffered and playable.
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered);
 
   // Returns the size, in bytes, of the heap memory used by the currently
   // queued decoded video and audio data.
   virtual int64_t VideoQueueMemoryInUse();
-  virtual int64_t AudioQueueMemoryInUse();
+  size_t SizeOfAudioQueue();
 
   VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE
   {
     return mVideoFrameContainer;
   }
   layers::ImageContainer* GetImageContainer() MOZ_OVERRIDE;
 
   // Sets the length of the framebuffer used in MozAudioAvailable events.
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -8,16 +8,17 @@
 
 #include <nsDeque.h>
 #include "nsSize.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "SharedBuffer.h"
 #include "AudioSampleFormat.h"
 #include "AbstractMediaDecoder.h"
 #include "ImageTypes.h"
+#include "nsIMemoryReporter.h"
 
 struct nsIntRect;
 
 namespace mozilla {
 
 namespace layers {
 class Image;
 class ImageContainer;
@@ -149,16 +150,24 @@ public:
     MOZ_COUNT_CTOR(AudioData);
   }
 
   ~AudioData()
   {
     MOZ_COUNT_DTOR(AudioData);
   }
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+    size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
+    if (mAudioBuffer) {
+      size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
+    }
+    return size;
+  }
+
   // If mAudioBuffer is null, creates it from mAudioData.
   void EnsureAudioBuffer();
 
   const uint32_t mFrames;
   const uint32_t mChannels;
   // At least one of mAudioBuffer/mAudioData must be non-null.
   // mChannels channels, each with mFrames frames
   nsRefPtr<SharedBuffer> mAudioBuffer;
@@ -577,31 +586,33 @@ public:
   virtual int64_t VideoQueueMemoryInUse() {
     VideoQueueMemoryFunctor functor;
     mVideoQueue.LockedForEach(functor);
     return functor.mResult;
   }
 
   class AudioQueueMemoryFunctor : public nsDequeFunctor {
   public:
-    AudioQueueMemoryFunctor() : mResult(0) {}
+    AudioQueueMemoryFunctor() : mSize(0) {}
+
+    MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
 
     virtual void* operator()(void* anObject) {
       const AudioData* audioData = static_cast<const AudioData*>(anObject);
-      mResult += audioData->mFrames * audioData->mChannels * sizeof(AudioDataValue);
+      mSize += audioData->SizeOfIncludingThis(MallocSizeOf);
       return nullptr;
     }
 
-    int64_t mResult;
+    size_t mSize;
   };
 
-  virtual int64_t AudioQueueMemoryInUse() {
+  size_t SizeOfAudioQueue() {
     AudioQueueMemoryFunctor functor;
     mAudioQueue.LockedForEach(functor);
-    return functor.mResult;
+    return functor.mSize;
   }
 
   // Only used by WebMReader and MediaOmxReader for now, so stub here rather
   // than in every reader than inherits from MediaDecoderReader.
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
 
   virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
   virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -262,19 +262,19 @@ public:
 
   int64_t VideoQueueMemoryInUse() {
     if (mReader) {
       return mReader->VideoQueueMemoryInUse();
     }
     return 0;
   }
 
-  int64_t AudioQueueMemoryInUse() {
+  size_t SizeOfAudioQueue() {
     if (mReader) {
-      return mReader->AudioQueueMemoryInUse();
+      return mReader->SizeOfAudioQueue();
     }
     return 0;
   }
 
   void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
 
   int64_t GetEndMediaTime() const {
     AssertCurrentThreadInMonitor();
--- a/content/media/SharedBuffer.h
+++ b/content/media/SharedBuffer.h
@@ -38,15 +38,19 @@ public:
   {
     void* m = moz_xmalloc(sizeof(SharedBuffer) + aSize);
     nsRefPtr<SharedBuffer> p = new (m) SharedBuffer();
     NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
                  "SharedBuffers should be at least 4-byte aligned");
     return p.forget();
   }
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+    return aMallocSizeOf(this);
+  }
+
 private:
   SharedBuffer() {}
 };
 
 }
 
 #endif /* MOZILLA_SHAREDBUFFER_H_ */
new file mode 100644
--- /dev/null
+++ b/content/media/SharedThreadPool.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "SharedThreadPool.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/StaticPtr.h"
+#include "nsDataHashtable.h"
+#include "VideoUtils.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/Preferences.h"
+
+#ifdef XP_WIN
+// Required to init MSCOM by MSCOMInitThreadPoolListener.
+#include <Objbase.h>
+#endif
+
+namespace mozilla {
+
+// Created and destroyed on the main thread.
+static StaticAutoPtr<ReentrantMonitor> sMonitor;
+
+// Hashtable, maps thread pool name to SharedThreadPool instance.
+// Modified only on the main thread.
+static StaticAutoPtr<nsDataHashtable<nsCStringHashKey, SharedThreadPool*>> sPools;
+
+static already_AddRefed<nsIThreadPool>
+CreateThreadPool(const nsCString& aName);
+
+static void
+DestroySharedThreadPoolHashTable();
+
+void
+SharedThreadPool::EnsureInitialized()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (sMonitor || sPools) {
+    // Already initalized.
+    return;
+  }
+  sMonitor = new ReentrantMonitor("SharedThreadPool");
+  sPools = new nsDataHashtable<nsCStringHashKey, SharedThreadPool*>();
+}
+
+class ShutdownPoolsEvent : public nsRunnable {
+public:
+  NS_IMETHODIMP Run() {
+    MOZ_ASSERT(NS_IsMainThread());
+    DestroySharedThreadPoolHashTable();
+    return NS_OK;
+  }
+};
+
+static void
+DestroySharedThreadPoolHashTable()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(sMonitor && sPools);
+  if (!sPools->Count()) {
+    // No more SharedThreadPool singletons. Delete the hash table.
+    // Note we don't need to lock sMonitor, since we only modify the
+    // hash table on the main thread, and if the hash table is empty
+    // there are no external references into its contents.
+    sPools = nullptr;
+    sMonitor = nullptr;
+  }
+}
+
+TemporaryRef<SharedThreadPool>
+SharedThreadPool::Get(const nsCString& aName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  EnsureInitialized();
+  MOZ_ASSERT(sMonitor);
+  ReentrantMonitorAutoEnter mon(*sMonitor);
+  SharedThreadPool* pool = nullptr;
+  if (!sPools->Get(aName, &pool)) {
+    nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName));
+    NS_ENSURE_TRUE(threadPool, nullptr);
+    pool = new SharedThreadPool(aName, threadPool);
+    sPools->Put(aName, pool);
+  }
+  MOZ_ASSERT(pool);
+  RefPtr<SharedThreadPool> instance(pool);
+  return instance.forget();
+}
+
+NS_IMETHODIMP_(nsrefcnt) SharedThreadPool::AddRef(void)
+{
+  MOZ_ASSERT(sMonitor);
+  ReentrantMonitorAutoEnter mon(*sMonitor);
+  MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
+  nsrefcnt count = ++mRefCnt;
+  NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
+  return count;
+}
+
+NS_IMETHODIMP_(nsrefcnt) SharedThreadPool::Release(void)
+{
+  MOZ_ASSERT(sMonitor);
+  bool dispatchShutdownEvent;
+  {
+    ReentrantMonitorAutoEnter mon(*sMonitor);
+    nsrefcnt count = --mRefCnt;
+    NS_LOG_RELEASE(this, count, "SharedThreadPool");
+    if (count) {
+      return count;
+    }
+
+    // Zero refcount. Must shutdown and then delete the thread pool.
+
+    // First, dispatch an event to the main thread to call Shutdown() on
+    // the nsIThreadPool. The Runnable here  will add a refcount to the pool,
+    // and when the Runnable releases the nsIThreadPool it will be deleted.
+    RefPtr<nsIRunnable> r = NS_NewRunnableMethod(mPool, &nsIThreadPool::Shutdown);
+    NS_DispatchToMainThread(r);
+
+    // Remove SharedThreadPool from table of pools.
+    sPools->Remove(mName);
+    MOZ_ASSERT(!sPools->Get(mName));
+
+    // Stabilize refcount, so that if something in the dtor QIs,
+    // it won't explode.
+    mRefCnt = 1;
+
+    delete this;
+
+    dispatchShutdownEvent = sPools->Count() == 0;
+  }
+  if (dispatchShutdownEvent) {
+    // No more SharedThreadPools alive. Destroy the hash table.
+    // Ensure that we only run on the main thread.
+    // Do this in an event so that if something holds the monitor we won't
+    // be deleting the monitor while it's held.
+    NS_DispatchToMainThread(new ShutdownPoolsEvent(), NS_DISPATCH_NORMAL);
+  }
+  return 0;
+}
+
+NS_IMPL_QUERY_INTERFACE1(SharedThreadPool, nsIThreadPool)
+
+SharedThreadPool::SharedThreadPool(const nsCString& aName,
+                                   nsIThreadPool* aPool)
+  : mName(aName)
+  , mPool(aPool)
+  , mRefCnt(0)
+{
+  mEventTarget = do_QueryInterface(aPool);
+}
+
+SharedThreadPool::~SharedThreadPool()
+{
+}
+
+#ifdef XP_WIN
+
+// Thread pool listener which ensures that MSCOM is initialized and
+// deinitialized on the thread pool thread. We may call into WMF or
+// DirectShow on this thread, so we need MSCOM working.
+class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITHREADPOOLLISTENER
+};
+
+NS_IMPL_ISUPPORTS1(MSCOMInitThreadPoolListener, nsIThreadPoolListener)
+
+NS_IMETHODIMP
+MSCOMInitThreadPoolListener::OnThreadCreated()
+{
+  HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MSCOMInitThreadPoolListener::OnThreadShuttingDown()
+{
+  CoUninitialize();
+  return NS_OK;
+}
+
+#endif // XP_WIN
+
+static already_AddRefed<nsIThreadPool>
+CreateThreadPool(const nsCString& aName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv;
+  nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  rv = pool->SetName(aName);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  // We limit the number of threads that we use for media. Note that the
+  // default thread limit is the same as the idle limit so that we're not
+  // constantly creating and destroying threads (see Bug 881954). When the
+  // thread pool threads shutdown they dispatch an event to the main thread
+  // to call nsIThread::Shutdown(), and if we're very busy that can take a
+  // while to run, and we end up with dozens of extra threads. Note that
+  // threads that are idle for 60 seconds are shutdown naturally.
+  rv = pool->SetThreadLimit(
+    Preferences::GetUint("media.thread-pool.thread-limit", 4));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  rv = pool->SetIdleThreadLimit(
+    Preferences::GetUint("media.thread-pool.idle-thread-limit", 4));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+#ifdef XP_WIN
+  // Ensure MSCOM is initialized on the thread pools threads.
+  nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
+  rv = pool->SetListener(listener);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+#endif
+
+  return pool.forget();
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/SharedThreadPool.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SharedThreadPool_h_
+#define SharedThreadPool_h_
+
+#include <queue>
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+#include "nsIThreadPool.h"
+#include "nsISupports.h"
+#include "nsISupportsImpl.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+
+// Wrapper that makes an nsIThreadPool a singleton, and provides a
+// consistent threadsafe interface to get instances. Callers simply get a
+// SharedThreadPool by the name of its nsIThreadPool. All get requests of
+// the same name get the same SharedThreadPool. Users must store a reference
+// to the pool, and when the last reference to a SharedThreadPool is dropped
+// the pool is shutdown and deleted. Users aren't required to manually
+// shutdown the pool, and can release references on any thread. On Windows
+// all threads in the pool have MSCOM initialized with COINIT_MULTITHREADED.
+class SharedThreadPool : public nsIThreadPool {
+public:
+
+  // Gets (possibly creating) the shared thread pool singleton instance with
+  // thread pool named aName.
+  // *Must* be called on the main thread.
+  static TemporaryRef<SharedThreadPool> Get(const nsCString& aName);
+
+  // We implement custom threadsafe AddRef/Release pair, that destroys the
+  // the shared pool singleton when the refcount drops to 0. The addref/release
+  // are implemented using locking, so it's not recommended that you use them
+  // in a tight loop.
+  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
+  NS_IMETHOD_(nsrefcnt) AddRef(void);
+  NS_IMETHOD_(nsrefcnt) Release(void);
+
+  // Forward behaviour to wrapped thread pool implementation.
+  NS_FORWARD_SAFE_NSITHREADPOOL(mPool);
+  NS_FORWARD_SAFE_NSIEVENTTARGET(mEventTarget);
+
+private:
+
+  // Creates necessary statics.
+  // Main thread only.
+  static void EnsureInitialized();
+
+  // Creates a singleton SharedThreadPool wrapper around aPool.
+  // aName is the name of the aPool, and is used to lookup the
+  // SharedThreadPool in the hash table of all created pools.
+  SharedThreadPool(const nsCString& aName, nsIThreadPool* aPool);
+  virtual ~SharedThreadPool();
+
+  // Name of mPool.
+  const nsCString mName;
+
+  // Thread pool being wrapped.
+  nsCOMPtr<nsIThreadPool> mPool;
+
+  // Refcount. We implement custom ref counting so that the thread pool is
+  // shutdown in a threadsafe manner and singletonness is preserved.
+  nsrefcnt mRefCnt;
+
+  // mPool QI'd to nsIEventTarget. We cache this, so that we can use
+  // NS_FORWARD_SAFE_NSIEVENTTARGET above.
+  nsCOMPtr<nsIEventTarget> mEventTarget;
+};
+
+} // namespace mozilla
+
+#endif // SharedThreadPool_h_
\ No newline at end of file
--- a/content/media/TextTrack.cpp
+++ b/content/media/TextTrack.cpp
@@ -64,17 +64,17 @@ TextTrack::SetDefaultSettings()
 {
   mKind = TextTrackKind::Subtitles;
   mMode = TextTrackMode::Hidden;
   mCueList = new TextTrackCueList(mParent);
   mActiveCueList = new TextTrackCueList(mParent);
   mRegionList = new TextTrackRegionList(mParent);
   mCuePos = 0;
   mDirty = false;
-  mReadyState = HTMLTrackElement::NONE;
+  mReadyState = HTMLTrackElement::READY_STATE_NONE;
 }
 
 JSObject*
 TextTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return TextTrackBinding::Wrap(aCx, aScope, this);
 }
 
@@ -190,16 +190,16 @@ TextTrack::ReadyState() const
 {
   return mReadyState;
 }
 
 void
 TextTrack::SetReadyState(uint16_t aState)
 {
   mReadyState = aState;
-  if (mMediaElement && (mReadyState == HTMLTrackElement::LOADED ||
-      mReadyState == HTMLTrackElement::ERROR)) {
+  if (mMediaElement && (mReadyState == HTMLTrackElement::READY_STATE_LOADED ||
+      mReadyState == HTMLTrackElement::READY_STATE_ERROR)) {
     mMediaElement->RemoveTextTrack(this, true);
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/WebVTTListener.cpp
+++ b/content/media/WebVTTListener.cpp
@@ -73,17 +73,17 @@ WebVTTListener::LoadResource()
 
   nsPIDOMWindow* window = mElement->OwnerDoc()->GetWindow();
   rv = mParserWrapper->LoadParser(window);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mParserWrapper->Watch(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mElement->mTrack->SetReadyState(HTMLTrackElement::LOADING);
+  mElement->mTrack->SetReadyState(HTMLTrackElement::READY_STATE_LOADING);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebVTTListener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
                                        nsIChannel* aNewChannel,
                                        uint32_t aFlags,
                                        nsIAsyncVerifyRedirectCallback* cb)
@@ -101,19 +101,19 @@ WebVTTListener::OnStartRequest(nsIReques
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebVTTListener::OnStopRequest(nsIRequest* aRequest,
                               nsISupports* aContext,
                               nsresult aStatus)
 {
-  if (mElement->ReadyState() != HTMLTrackElement::ERROR) {
+  if (mElement->ReadyState() != HTMLTrackElement::READY_STATE_ERROR) {
     TextTrack* track = mElement->Track();
-    track->SetReadyState(HTMLTrackElement::LOADED);
+    track->SetReadyState(HTMLTrackElement::READY_STATE_LOADED);
   }
   // Attempt to parse any final data the parser might still have.
   mParserWrapper->Flush();
   return NS_OK;
 }
 
 NS_METHOD
 WebVTTListener::ParseChunk(nsIInputStream* aInStream, void* aClosure,
--- a/content/media/encoder/moz.build
+++ b/content/media/encoder/moz.build
@@ -20,17 +20,16 @@ UNIFIED_SOURCES += [
     'TrackEncoder.cpp',
 ]
 
 if CONFIG['MOZ_OPUS']:
     EXPORTS += ['OpusTrackEncoder.h']
     UNIFIED_SOURCES += ['OpusTrackEncoder.cpp']
 
 if CONFIG['MOZ_OMX_ENCODER']:
-    DEFINES['MOZ_OMX_ENCODER'] = True
     EXPORTS += ['OmxTrackEncoder.h']
     UNIFIED_SOURCES += ['OmxTrackEncoder.cpp']
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'gklayout'
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -76,17 +76,17 @@ public:
   }
 
 private:
   RefPtr<MediaResource> mResource;
 };
 
 MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder),
-    mLayersBackendType(layers::LAYERS_NONE),
+    mLayersBackendType(layers::LayersBackend::LAYERS_NONE),
     mHasAudio(false),
     mHasVideo(false)
 {
   MOZ_COUNT_CTOR(MP4Reader);
 }
 
 MP4Reader::~MP4Reader()
 {
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -80,16 +80,17 @@ EXPORTS += [
     'MediaMetadataManager.h',
     'MediaRecorder.h',
     'MediaResource.h',
     'MediaSegment.h',
     'MediaStreamGraph.h',
     'MP3FrameParser.h',
     'RtspMediaResource.h',
     'SharedBuffer.h',
+    'SharedThreadPool.h',
     'StreamBuffer.h',
     'TimeVarying.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
 ]
@@ -126,16 +127,17 @@ UNIFIED_SOURCES += [
     'MediaDecoderStateMachine.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaShutdownManager.cpp',
     'MediaStreamGraph.cpp',
     'MediaStreamTrack.cpp',
     'MP3FrameParser.cpp',
     'RtspMediaResource.cpp',
+    'SharedThreadPool.cpp',
     'StreamBuffer.cpp',
     'TextTrack.cpp',
     'TextTrackCue.cpp',
     'TextTrackCueList.cpp',
     'TextTrackList.cpp',
     'TextTrackRegion.cpp',
     'TextTrackRegionList.cpp',
     'VideoFrameContainer.cpp',
--- a/content/media/plugins/MediaPluginReader.cpp
+++ b/content/media/plugins/MediaPluginReader.cpp
@@ -348,17 +348,17 @@ MediaPluginReader::ImageBufferCallback::
     return nullptr;
   }
 
   nsRefPtr<Image> image;
   switch(aColorFormat) {
     case MPAPI::RGB565:
       image = mozilla::layers::CreateSharedRGBImage(mImageContainer,
                                                     nsIntSize(aWidth, aHeight),
-                                                    gfxImageFormatRGB16_565);
+                                                    gfxImageFormat::RGB16_565);
       if (!image) {
         NS_WARNING("Could not create rgb image");
         return nullptr;
       }
 
       mImage = image;
       return image->AsSharedImage()->GetBuffer();
     case MPAPI::I420:
--- a/content/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/content/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -233,17 +233,17 @@ MediaEngineTabVideoSource::Draw() {
   nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
   uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
                              nsIPresShell::RENDER_DOCUMENT_RELATIVE);
   nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale),
            nsPresContext::CSSPixelsToAppUnits(srcY / scale),
            nsPresContext::CSSPixelsToAppUnits(srcW / scale),
            nsPresContext::CSSPixelsToAppUnits(srcH / scale));
 
-  gfxImageFormat format = gfxImageFormatRGB24;
+  gfxImageFormat format = gfxImageFormat::RGB24;
   uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width);
 
   nsRefPtr<layers::ImageContainer> container = layers::LayerManager::CreateImageContainer();
   nsRefPtr<gfxASurface> surf;
   surf = new gfxImageSurface(static_cast<unsigned char*>(mData),
                              ThebesIntSize(size), stride, format);
   if (surf->CairoStatus() != 0) {
     return;
--- a/content/media/wmf/WMFByteStream.cpp
+++ b/content/media/wmf/WMFByteStream.cpp
@@ -14,97 +14,29 @@
 #include "WMFUtils.h"
 #include "MediaResource.h"
 #include "nsISeekableStream.h"
 #include "mozilla/RefPtr.h"
 #include "nsIThreadPool.h"
 #include "nsXPCOMCIDInternal.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/DebugOnly.h"
+#include "SharedThreadPool.h"
 #include <algorithm>
 #include <cassert>
 
 namespace mozilla {
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gWMFByteStreamLog = nullptr;
 #define WMF_BS_LOG(...) PR_LOG(gWMFByteStreamLog, PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define WMF_BS_LOG(...)
 #endif
 
-// Limit the number of threads that we use for IO.
-static const uint32_t NumWMFIoThreads = 4;
-
-// Thread pool listener which ensures that MSCOM is initialized and
-// deinitialized on the thread pool thread. We can call back into WMF
-// on this thread, so we need MSCOM working.
-class ThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSITHREADPOOLLISTENER
-};
-
-NS_IMPL_ISUPPORTS1(ThreadPoolListener, nsIThreadPoolListener)
-
-NS_IMETHODIMP
-ThreadPoolListener::OnThreadCreated()
-{
-  HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
-  if (FAILED(hr)) {
-    NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ThreadPoolListener::OnThreadShuttingDown()
-{
-  CoUninitialize();
-  return NS_OK;
-}
-
-// Thread pool on which read requests are processed.
-// This is created and destroyed on the main thread only.
-static nsIThreadPool* sThreadPool = nullptr;
-
-// Counter of the number of WMFByteStreams that are instantiated and that need
-// the thread pool. This is read/write on the main thread only.
-static int32_t sThreadPoolRefCnt = 0;
-
-class ReleaseWMFByteStreamResourcesEvent MOZ_FINAL : public nsRunnable {
-public:
-  ReleaseWMFByteStreamResourcesEvent(already_AddRefed<MediaResource> aResource)
-    : mResource(aResource) {}
-  virtual ~ReleaseWMFByteStreamResourcesEvent() {}
-  NS_IMETHOD Run() {
-    NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
-    // Explicitly release the MediaResource reference. We *must* do this on
-    // the main thread, so we must explicitly release it here, we can't rely
-    // on the destructor to release it, since if this event runs before its
-    // dispatch call returns the destructor may run on the non-main thread.
-    mResource = nullptr;
-    NS_ASSERTION(sThreadPoolRefCnt > 0, "sThreadPoolRefCnt Should be non-negative");
-    sThreadPoolRefCnt--;
-    if (sThreadPoolRefCnt == 0) {
-      NS_ASSERTION(sThreadPool != nullptr, "Should have thread pool ref if sThreadPoolRefCnt==0.");
-      // Note: store ref to thread pool, then clear global ref, then
-      // Shutdown() using the stored ref. Events can run during the Shutdown()
-      // call, so if we release after calling Shutdown(), another event may
-      // have incremented the refcnt in the meantime, and have a dangling
-      // pointer to the now destroyed threadpool!
-      nsCOMPtr<nsIThreadPool> pool = sThreadPool;
-      NS_IF_RELEASE(sThreadPool);
-      pool->Shutdown();
-    }
-    return NS_OK;
-  }
-  nsRefPtr<MediaResource> mResource;
-};
-
 WMFByteStream::WMFByteStream(MediaResource* aResource,
                              WMFSourceReaderCallback* aSourceReaderCallback)
   : mSourceReaderCallback(aSourceReaderCallback),
     mResource(aResource),
     mReentrantMonitor("WMFByteStream.Data"),
     mOffset(0),
     mIsShutdown(false)
 {
@@ -118,63 +50,26 @@ WMFByteStream::WMFByteStream(MediaResour
 #endif
   WMF_BS_LOG("[%p] WMFByteStream CTOR", this);
   MOZ_COUNT_CTOR(WMFByteStream);
 }
 
 WMFByteStream::~WMFByteStream()
 {
   MOZ_COUNT_DTOR(WMFByteStream);
-  // The WMFByteStream can be deleted from a thread pool thread, so we
-  // dispatch an event to the main thread to deref the thread pool and
-  // deref the MediaResource.
-  nsCOMPtr<nsIRunnable> event =
-    new ReleaseWMFByteStreamResourcesEvent(mResource.forget());
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   WMF_BS_LOG("[%p] WMFByteStream DTOR", this);
 }
 
 nsresult
 WMFByteStream::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
 
-  if (!sThreadPool) {
-    nsresult rv;
-    nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    sThreadPool = pool;
-    NS_ADDREF(sThreadPool);
-
-    rv = sThreadPool->SetName(NS_LITERAL_CSTRING("WMFByteStream Async Read Pool"));
-    NS_ENSURE_SUCCESS(rv, rv);
-    
-    // We limit the number of threads that we use for IO. Note that the thread
-    // limit is the same as the idle limit so that we're not constantly creating
-    // and destroying threads. When the thread pool threads shutdown they
-    // dispatch an event to the main thread to call nsIThread::Shutdown(),
-    // and if we're very busy that can take a while to run, and we end up with
-    // dozens of extra threads. Note that threads that are idle for 60 seconds
-    // are shutdown naturally.
-    rv = sThreadPool->SetThreadLimit(NumWMFIoThreads);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = sThreadPool->SetIdleThreadLimit(NumWMFIoThreads);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIThreadPoolListener> listener = new ThreadPoolListener();
-    rv = sThreadPool->SetListener(listener);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  sThreadPoolRefCnt++;
-
-  // Store a ref to the thread pool, so that we keep the pool alive as long as
-  // we're alive.
-  mThreadPool = sThreadPool;
+  mThreadPool = SharedThreadPool::Get(NS_LITERAL_CSTRING("WMFByteStream IO"));
+  NS_ENSURE_TRUE(mThreadPool, NS_ERROR_FAILURE);
 
   NS_ConvertUTF8toUTF16 contentTypeUTF16(mResource->GetContentType());
   if (!contentTypeUTF16.IsEmpty()) {
     HRESULT hr = wmf::MFCreateAttributes(byRef(mAttributes), 1);
     NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
     hr = mAttributes->SetString(MF_BYTESTREAM_CONTENT_TYPE,
                                 contentTypeUTF16.get());
--- a/content/media/wmf/WMFByteStream.h
+++ b/content/media/wmf/WMFByteStream.h
@@ -10,23 +10,22 @@
 
 #include "nsISupportsImpl.h"
 #include "nsCOMPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/Attributes.h"
 #include "nsAutoPtr.h"
 #include "mozilla/RefPtr.h"
 
-class nsIThreadPool;
-
 namespace mozilla {
 
 class MediaResource;
 class ReadRequest;
 class WMFSourceReaderCallback;
+class SharedThreadPool;
 
 // Wraps a MediaResource around an IMFByteStream interface, so that it can
 // be used by the IMFSourceReader. Each WMFByteStream creates a WMF Work Queue
 // on which blocking I/O is performed. The SourceReader requests reads
 // asynchronously using {Begin,End}Read(), and more rarely synchronously
 // using Read().
 //
 // Note: This implementation attempts to be bug-compatible with Windows Media
@@ -118,17 +117,17 @@ private:
   // call this function.
   nsresult Read(ReadRequest* aRequestState);
 
   // Returns true if the current position of the stream is at end of stream.
   bool IsEOS();
 
   // Reference to the thread pool in which we perform the reads asynchronously.
   // Note this is pool is shared amongst all active WMFByteStreams.
-  nsCOMPtr<nsIThreadPool> mThreadPool;
+  RefPtr<SharedThreadPool> mThreadPool;
 
   // Reference to the source reader's callback. We use this reference to
   // notify threads waiting on a ReadSample() callback to stop waiting
   // if the stream is closed, which happens when the media element is
   // shutdown.
   RefPtr<WMFSourceReaderCallback> mSourceReaderCallback;
 
   // Resource we're wrapping.
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -66,17 +66,17 @@ this.AppsUtils = {
   // Clones a app, without the manifest.
   cloneAppObject: function cloneAppObject(aApp) {
     return {
       name: aApp.name,
       csp: aApp.csp,
       installOrigin: aApp.installOrigin,
       origin: aApp.origin,
 #ifdef MOZ_ANDROID_SYNTHAPKS
-      packageName: aApp.packageName,
+      apkPackageName: aApp.apkPackageName,
 #endif
       receipts: aApp.receipts ? JSON.parse(JSON.stringify(aApp.receipts)) : null,
       installTime: aApp.installTime,
       manifestURL: aApp.manifestURL,
       appStatus: aApp.appStatus,
       removable: aApp.removable,
       id: aApp.id,
       localId: aApp.localId,
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -236,26 +236,38 @@ this.DOMApplicationRegistry = {
         // will be empty so we can't just apply a staged update.
         app.downloading = false;
         app.readyToApplyDownload = false;
       }
     });
   },
 
   // Notify we are starting with registering apps.
+  _registryStarted: Promise.defer(),
   notifyAppsRegistryStart: function notifyAppsRegistryStart() {
     Services.obs.notifyObservers(this, "webapps-registry-start", null);
+    this._registryStarted.resolve();
+  },
+
+  get registryStarted() {
+    return this._registryStarted.promise;
   },
 
   // Notify we are done with registering apps and save a copy of the registry.
+  _registryReady: Promise.defer(),
   notifyAppsRegistryReady: function notifyAppsRegistryReady() {
+    this._registryReady.resolve();
     Services.obs.notifyObservers(this, "webapps-registry-ready", null);
     this._saveApps();
   },
 
+  get registryReady() {
+    return this._registryReady.promise;
+  },
+
   // Ensure that the .to property in redirects is a relative URL.
   sanitizeRedirects: function sanitizeRedirects(aSource) {
     if (!aSource) {
       return null;
     }
 
     let res = [];
     for (let i = 0; i < aSource.length; i++) {
@@ -962,19 +974,27 @@ this.DOMApplicationRegistry = {
 
     try {
       let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
       file.initWithPath(aPath);
       let channel = NetUtil.newChannel(file);
       channel.contentType = "application/json";
       NetUtil.asyncFetch(channel, function(aStream, aResult) {
         if (!Components.isSuccessCode(aResult)) {
+          deferred.resolve(null);
+
+          if (aResult == Cr.NS_ERROR_FILE_NOT_FOUND) {
+            // We expect this under certain circumstances, like for webapps.json
+            // on firstrun, so we return early without reporting an error.
+            return;
+          }
+
           Cu.reportError("DOMApplicationRegistry: Could not read from json file "
                          + aPath);
-          deferred.resolve(null);
+          return;
         }
 
         try {
           // Obtain a converter to read from a UTF-8 encoded input stream.
           let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                           .createInstance(Ci.nsIScriptableUnicodeConverter);
           converter.charset = "UTF-8";
 
@@ -1993,16 +2013,26 @@ this.DOMApplicationRegistry = {
 
     let sendError = function sendError(aError) {
       aData.error = aError;
       aMm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
       Cu.reportError("Error installing app from: " + app.installOrigin +
                      ": " + aError);
     }.bind(this);
 
+    if (app.receipts.length > 0) {
+      for (let receipt of app.receipts) {
+        let error = this.isReceipt(receipt);
+        if (error) {
+          sendError(error);
+          return;
+        }
+      }
+    }
+
     // Hosted apps can't be trusted or certified, so just check that the
     // manifest doesn't ask for those.
     function checkAppStatus(aManifest) {
       let manifestStatus = aManifest.type || "web";
       return manifestStatus === "web";
     }
 
     let checkManifest = (function() {
@@ -2102,16 +2132,26 @@ this.DOMApplicationRegistry = {
 
     let sendError = function sendError(aError) {
       aData.error = aError;
       aMm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
       Cu.reportError("Error installing packaged app from: " +
                      app.installOrigin + ": " + aError);
     }.bind(this);
 
+    if (app.receipts.length > 0) {
+      for (let receipt of app.receipts) {
+        let error = this.isReceipt(receipt);
+        if (error) {
+          sendError(error);
+          return;
+        }
+      }
+    }
+
     let checkUpdateManifest = (function() {
       let manifest = app.updateManifest;
 
       // Disallow reinstalls from the same manifest URL for now.
       let id = this._appIdForManifestURL(app.manifestURL);
       if (id !== null && this._isLaunchable(this.webapps[id])) {
         sendError("REINSTALL_FORBIDDEN");
         return false;
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -11,8 +11,9 @@ support-files =
 
 [test_app_update.html]
 [test_bug_795164.html]
 [test_packaged_app_common.js]
 [test_packaged_app_install.html]
 [test_packaged_app_update.html]
 [test_uninstall_errors.html]
 [test_receipt_operations.html]
+[test_install_receipts.html]
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/test_install_receipts.html
@@ -0,0 +1,215 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id={960837}
+-->
+<head>
+  <title>Test for Bug {960837}</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={960837}">Mozilla Bug {960837}</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+var gManifestURL = "http://test/tests/dom/apps/tests/file_app.sjs?apptype=hosted&getmanifest=true";
+var gGenerator = runTest();
+
+function debug(aMsg) {
+  //dump("== Tests debug == " + aMsg + "\n");
+}
+
+function go() {
+  SpecialPowers.pushPermissions(
+    [{ "type": "webapps-manage", "allow": 1, "context": document }],
+    function() { gGenerator.next() });
+}
+
+function continueTest() {
+  try {
+    gGenerator.next();
+  } catch (e) {
+    dump("Got exception: " + e + "\n");
+  }
+}
+
+function finish() {
+  SimpleTest.finish();
+}
+
+function cbError(aError) {
+  ok(false, "Error callback invoked " + aError);
+  finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+  launchableValue = SpecialPowers.setAllAppsLaunchable(true);
+
+  SpecialPowers.autoConfirmAppInstall(continueTest);
+  yield undefined;
+
+  // Test install with three valid receipts
+  let valid_receipt1 = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTYifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.eZpTEnCLUR3iP3rm9WyJOqx1k66mQaAxqcrvX11r5E0';
+
+  let valid_receipt2 = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTcifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.k7tI0PTaMJf0w0keAHJR6couypGY-EtA38q2xOtSv6k';
+
+  let valid_receipt3 = 'eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCIsICJqa3UiOiAiaHR0cHM6Ly9tYXJrZXRwbGFjZS5jZG4ubW96aWxsYS5uZXQvcHVibGljX2tleXMvbWFya2V0cGxhY2Utcm9vdC1wdWIta2V5Lmp3ayJ9.eyJpc3MiOiAiaHR0cHM6Ly9tYXJrZXRwbGFjZS5jZG4ubW96aWxsYS5uZXQvcHVibGljX2tleXMvbWFya2V0cGxhY2Utcm9vdC1wdWIta2V5Lmp3ayIsICJwcmljZV9saW1pdCI6IDEwMCwgImp3ayI6IFt7ImFsZyI6ICJSU0EiLCAibW9kIjogIkFMYkszek5VQ0lFTEJRZ1QycGUzTEkwdC1sR0w5OElFTnBWOUtuX0F4VGxjLXZzX0ZFMlVyNzU2Z012bHA3a3BWVmFEWVNCdnVCQjgtZEZpU3VJbHdCUFB2bWFIaTFhd0xJMjRRY2JOMVJrN3pZS01SclVfSzdkVEN6MEh6VHoza01YVXp1ci1ySTIxS3BKb0NSZFNxeUl4bHpnUWFna1dUUWxIYUI2VzkzUjBacUxlQk9lUzhjbzNOUlczdjFfY0h4VTE1d0k4T0JHY0tRSXB3VHpONUVfRFdNZ0F1MGFQMHlWY3EzT0FwXy1fa1pjYXBtQnpSTmVMOHBxMjZXN01jMUpJZVBnZVZ5SXExcFBLMU9ldGhmdF9KeTk5R19EWWxQNW15YjFEY1VpbHE3RVNKc1UyeUZPUjJhWmkyYU1lTkRZekwyUmdZSGt2RWxyNDRMM2NZM0UiLCAiZXhwIjogIkFRQUIiLCAia2lkIjogImFwcHN0b3JlLm1vemlsbGEuY29tLTIwMTMtMTEtMjcifV0sICJleHAiOiAxMzg2Nzg4NDAxLCAiaWF0IjogMTM4NTU3ODgwMSwgInR5cCI6ICJjZXJ0aWZpZWQta2V5IiwgIm5iZiI6IDEzODU1Nzg4MDF9.Ne5AffwNIjbQmwY_dSKVXR0R0wdB92sW_BWQWbN2WKa_Ep6V0Fwr2pfcv0KenZcYKdxhhSPBrs5R38EcIqTYYrgIeeJyM_gGzv-ESsUsqbFejAbVH2xfwATZ1lXNPh0VSt33Drf2RY5jeU5PD3usXgOPr8RYAGkMxz_0SUay5WCBVRLkrgtrCUNyIKBwuHlxKK1JkncVXsN0mr_gwbm0EpBgIOEZQj75TE0KcviMUvYn8uhVYEwYMLzMQmUbI5quxH2z5mcK2DDNQGgT6ABJljKWCY-PPuMo9tsgXe6L7MTafulBuSIjs1ztAl4ZnwZjKmxWmhdeiaT41tCFlr4K8Q~eyJqa3UiOiAiaHR0cHM6Ly9tYXJrZXRwbGFjZS5jZG4ubW96aWxsYS5uZXQvcHVibGljX2tleXMvbWFya2V0cGxhY2Utcm9vdC1wdWIta2V5Lmp3ayIsICJ0eXAiOiAiSldUIiwgImFsZyI6ICJSUzI1NiJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly9tYXJrZXRwbGFjZS5maXJlZm94LmNvbSIsICJzdG9yZWRhdGEiOiAiaWQ9NDM4OTc4In0sICJpc3MiOiAiaHR0cHM6Ly9tYXJrZXRwbGFjZS5maXJlZm94LmNvbSIsICJ2ZXJpZnkiOiAiaHR0cHM6Ly9yZWNlaXB0Y2hlY2subWFya2V0cGxhY2UuZmlyZWZveC5jb20vdmVyaWZ5LyIsICJkZXRhaWwiOiAiaHR0cHM6Ly9tYXJrZXRwbGFjZS5maXJlZm94LmNvbS9hcGkvdjEvcmVjZWlwdHMvcmVpc3N1ZS8iLCAicmVpc3N1ZSI6ICJodHRwczovL21hcmtldHBsYWNlLmZpcmVmb3guY29tL2FwaS92MS9yZWNlaXB0cy9yZWlzc3VlLyIsICJ1c2VyIjogeyJ0eXBlIjogImRpcmVjdGVkLWlkZW50aWZpZXIiLCAidmFsdWUiOiAiMTkzMzI2LTVjMTUzNmQ1LWUxMDQtNDAzYy04NDBlLTQ5YjMyMmQ5Yjg4NSJ9LCAiZXhwIjogMTQwMTgyNTEyOCwgImlhdCI6IDEzODYxMDAzMjgsICJ0eXAiOiAicHVyY2hhc2UtcmVjZWlwdCIsICJuYmYiOiAxMzg2MTAwMzI4fQ.r2DVUpouRDJYqZe61LJBcIwmeF2mI8FmbGMRlfNFcinKAIs8nMVVNX8xSWJ6jXXgZ62VfHJCLHapADX8rCg6NgxFV_FdP7j2H_2Ufo0E0TREifTN6V4v1dCnzDulNhZmO8G-nQJUVOAtNfNC95PY7tVa8WC7dYXnKZsD6NhIxxVEtBGuiiySpWArI-g3pcl41rXNHHpJbRfrOD4QgVNrsV83TWILYRr6PWr3aqOM2XT_x2SzEfhBNvdG8AJmR0MKQytvfcgz3Vt1hMak88nFrzTLiKkuuPAXpwB5q83LZIl4EYG3UAnte4-XWlLb-NJ78vgXa64myy-3fPr7EO6LaQ';
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ valid_receipt1, valid_receipt2, valid_receipt3 ]});
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+
+  var app = request.result;
+  ok(app, "App is non-null");
+  ok(app.receipts.length == 3, "Three receipts");
+
+  request = navigator.mozApps.mgmt.uninstall(app);
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+
+  // Test install with a receipt with less than 3 segments
+  let receipt_few_segments = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTcifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0';
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_few_segments ]});
+  request.onsuccess = function() {
+    ok(false, "Less than 3 segments");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "INVALID_SEGMENTS_NUMBER",
+       "Less than 3 segments");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a receipt without the typ field
+  let receipt_without_typ = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTgifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.6NY9DCEwhv9gTo1d7G4SJubZp_wSkaAJWA9iIp0PAQg";
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_without_typ ]});
+  request.onsuccess = function() {
+    ok(false, "No type field");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "RECEIPT_TYPE_REQUIRED",
+       "No type field");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a receipt without the product field
+  let receipt_without_product = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJyZWlzc3VlIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVpc3N1ZS81MTY5MzE0MzU2IiwgInVzZXIiOiB7InR5cGUiOiAiZGlyZWN0ZWQtaWRlbnRpZmllciIsICJ2YWx1ZSI6ICI0ZmIzNTE1MS0yYjliLTRiYTItODI4My1jNDlkMzgxNjQwYmQifSwgInZlcmlmeSI6ICJodHRwOi8vbW9jaGkudGVzdDo4ODg4L3ZlcmlmeS81MTY5MzE0MzU2IiwgImlzcyI6ICJodHRwOi8vbW9jaGkudGVzdDo4ODg4IiwgImlhdCI6IDEzMTM2MDE4OCwgInR5cCI6ICJwdXJjaGFzZS1yZWNlaXB0IiwgIm5iZiI6IDEzMTM2MDE4NSwgImRldGFpbCI6ICJodHRwOi8vbW9jaGkudGVzdDo4ODg4L3JlY2VpcHQvNTE2OTMxNDM1NiJ9.TWB8VXM2MhsyHLX3kWdjpSyn5Mz3SfG0oRIn-5p_0ko";
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_without_product ]});
+  request.onsuccess = function() {
+    ok(false, "No product field");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "RECEIPT_PRODUCT_REQUIRED",
+       "No product field");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a receipt without the user field
+  let receipt_without_user = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTgifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.eYm4ncnKvYzn-OYam5L55RQ8ShK6UlMrjFQOwByHSew";
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_without_user ]});
+  request.onsuccess = function() {
+    ok(false, "No user field");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "RECEIPT_USER_REQUIRED",
+       "No user field");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a receipt without the iss field
+  let receipt_without_iss = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTgifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.kvumGJAu8HkJ_utucwHwiOA_jJcqRMXBRQVDZd4KfUw";
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_without_iss ]});
+  request.onsuccess = function() {
+    ok(false, "No iss field");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "RECEIPT_ISS_REQUIRED",
+       "No iss field");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a receipt without the nbf field
+  let receipt_without_nbf = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTgifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.TYp6RQDFmyxLrmwAbhwkWLWgqhILR5yGA9CWDKNRU9Q";
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_without_nbf ]});
+  request.onsuccess = function() {
+    ok(false, "No nbf field");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "RECEIPT_NBF_REQUIRED",
+       "No nbf field");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a receipt without the iat field
+  let receipt_without_iat = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTgifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.EWxAzgfIM1VtwAscajDjqsRt_lysk_Wtj4_d4NbMg3U";
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_without_iat ]});
+  request.onsuccess = function() {
+    ok(false, "No iat field");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "RECEIPT_IAT_REQUIRED",
+       "No iat field");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a receipt with a not expected type
+  let receipt_with_wrong_typ = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTgifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogImZha2UiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.SGF5lCYvGQEoILyq_tVBD_gPYp00dOTvRlgyOs4twmQ";
+
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_with_wrong_typ ]});
+  request.onsuccess = function() {
+    ok(false, "Wrong type");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "RECEIPT_TYPE_UNSUPPORTED",
+       "Wrong type");
+    continueTest();
+  }
+  yield undefined;
+
+  // Test install with a valid receipt and a not valid one
+  var request = navigator.mozApps.install(gManifestURL, { receipts: [ receipt_few_segments, valid_receipt1 ]});
+  request.onsuccess = function() {
+    ok(false, "Less than 3 segments");
+  }
+  request.onerror = function() {
+    ok(this.error.name == "INVALID_SEGMENTS_NUMBER",
+       "Less than 3 segments");
+    continueTest();
+  }
+  yield undefined;
+
+  // All done.
+  ok(true, "All done");
+  finish();
+}
+
+addLoadEvent(go);
+
+</script>
+</pre>
+</body>
+</html>
\ No newline at end of file
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -155,35 +155,16 @@ nsJSUtils::CompileFunction(JSContext* aC
     ReportPendingException(aCx);
     return NS_ERROR_FAILURE;
   }
 
   *aFunctionObject = JS_GetFunctionObject(fun);
   return NS_OK;
 }
 
-class MOZ_STACK_CLASS AutoDontReportUncaught {
-  JSContext* mContext;
-  bool mWasSet;
-
-public:
-  AutoDontReportUncaught(JSContext* aContext) : mContext(aContext) {
-    MOZ_ASSERT(aContext);
-    mWasSet = JS::ContextOptionsRef(mContext).dontReportUncaught();
-    if (!mWasSet) {
-      JS::ContextOptionsRef(mContext).setDontReportUncaught(true);
-    }
-  }
-  ~AutoDontReportUncaught() {
-    if (!mWasSet) {
-      JS::ContextOptionsRef(mContext).setDontReportUncaught(false);
-    }
-  }
-};
-
 nsresult
 nsJSUtils::EvaluateString(JSContext* aCx,
                           const nsAString& aScript,
                           JS::Handle<JSObject*> aScopeObject,
                           JS::CompileOptions& aCompileOptions,
                           EvaluateOptions& aEvaluateOptions,
                           JS::Value* aRetValue,
                           void **aOffThreadToken)
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -85,16 +85,35 @@ public:
                                  JS::Handle<JSObject*> aScopeObject,
                                  JS::CompileOptions &aCompileOptions,
                                  EvaluateOptions& aEvaluateOptions,
                                  JS::Value* aRetValue,
                                  void **aOffThreadToken = nullptr);
 
 };
 
+class MOZ_STACK_CLASS AutoDontReportUncaught {
+  JSContext* mContext;
+  bool mWasSet;
+
+public:
+  AutoDontReportUncaught(JSContext* aContext) : mContext(aContext) {
+    MOZ_ASSERT(aContext);
+    mWasSet = JS::ContextOptionsRef(mContext).dontReportUncaught();
+    if (!mWasSet) {
+      JS::ContextOptionsRef(mContext).setDontReportUncaught(true);
+    }
+  }
+  ~AutoDontReportUncaught() {
+    if (!mWasSet) {
+      JS::ContextOptionsRef(mContext).setDontReportUncaught(false);
+    }
+  }
+};
+
 
 class nsDependentJSString : public nsDependentString
 {
 public:
   /**
    * In the case of string ids, getting the string's chars is infallible, so
    * the dependent string can be constructed directly.
    */
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -317,17 +317,19 @@ public:
   }
 };
 
 // Specialization for strings.
 // XXXbz we can't pull in FakeDependentString here, because it depends on
 // internal strings.  So we just have to forward-declare it and reimplement its
 // ToAStringPtr.
 
+namespace binding_detail {
 struct FakeDependentString;
+} // namespace binding_detail
 
 template<>
 class Optional<nsAString>
 {
 public:
   Optional() : mPassed(false) {}
 
   bool WasPassed() const
@@ -339,17 +341,17 @@ public:
   {
     MOZ_ASSERT(str);
     mStr = str;
     mPassed = true;
   }
 
   // If this code ever goes away, remove the comment pointing to it in the
   // FakeDependentString class in BindingUtils.h.
-  void operator=(const FakeDependentString* str)
+  void operator=(const binding_detail::FakeDependentString* str)
   {
     MOZ_ASSERT(str);
     mStr = reinterpret_cast<const nsDependentString*>(str);
     mPassed = true;
   }
 
   const nsAString& Value() const
   {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1311,16 +1311,35 @@ WrapCallThisObject(JSContext* cx, JS::Ha
   // But all that won't necessarily put things in the compartment of cx.
   if (!JS_WrapObject(cx, &obj)) {
     return nullptr;
   }
 
   return obj;
 }
 
+/*
+ * This specialized function simply wraps a JS::Rooted<> since
+ * WrapNativeParent() is not applicable for JS objects.
+ */
+template<>
+inline JSObject*
+WrapCallThisObject<JS::Rooted<JSObject*>>(JSContext* cx,
+                                          JS::Handle<JSObject*> scope,
+                                          const JS::Rooted<JSObject*>& p)
+{
+  JS::Rooted<JSObject*> obj(cx, p);
+
+  if (!JS_WrapObject(cx, &obj)) {
+    return nullptr;
+  }
+
+  return obj;
+}
+
 // Helper for calling WrapNewBindingObject with smart pointers
 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
 template <class T, bool isSmartPtr=HasgetMember<T>::Value>
 struct WrapNewBindingObjectHelper
 {
   static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope,
                           const T& value, JS::MutableHandle<JS::Value> rval)
   {
@@ -1451,16 +1470,18 @@ HasPropertyOnPrototype(JSContext* cx, JS
 // shadowPrototypeProperties is false then skip properties that are also
 // present on the proto chain of proxy.  If shadowPrototypeProperties is true,
 // then the "proxy" argument is ignored.
 bool
 AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
                        nsTArray<nsString>& names,
                        bool shadowPrototypeProperties, JS::AutoIdVector& props);
 
+namespace binding_detail {
+
 // A struct that has the same layout as an nsDependentString but much
 // faster constructor and destructor behavior
 struct FakeDependentString {
   FakeDependentString() :
     mFlags(nsDependentString::F_TERMINATED)
   {
   }
 
@@ -1528,29 +1549,31 @@ private:
                     "Offset of mLength should match");
       static_assert(offsetof(FakeDependentString, mFlags) ==
                       offsetof(DepedentStringAsserter, mFlags),
                     "Offset of mFlags should match");
     }
   };
 };
 
+} // namespace binding_detail
+
 enum StringificationBehavior {
   eStringify,
   eEmpty,
   eNull
 };
 
 // pval must not be null and must point to a rooted JS::Value
 static inline bool
 ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
                        JS::MutableHandle<JS::Value> pval,
                        StringificationBehavior nullBehavior,
                        StringificationBehavior undefinedBehavior,
-                       FakeDependentString& result)
+                       binding_detail::FakeDependentString& result)
 {
   JSString *s;
   if (v.isString()) {
     s = v.toString();
   } else {
     StringificationBehavior behavior;
     if (v.isNull()) {
       behavior = nullBehavior;
@@ -1592,29 +1615,33 @@ ConvertJSValueToByteString(JSContext* cx
                            nsACString& result);
 
 template<typename T>
 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
 template<typename T>
 void DoTraceSequence(JSTracer* trc, InfallibleTArray<T>& seq);
 
 // Class for simple sequence arguments, only used internally by codegen.
+namespace binding_detail {
+
 template<typename T>
 class AutoSequence : public AutoFallibleTArray<T, 16>
 {
 public:
   AutoSequence() : AutoFallibleTArray<T, 16>()
   {}
 
   // Allow converting to const sequences as needed
   operator const Sequence<T>&() const {
     return *reinterpret_cast<const Sequence<T>*>(this);
   }
 };
 
+} // namespace binding_detail
+
 // Class used to trace sequences, with specializations for various
 // sequence types.
 template<typename T,
          bool isDictionary=IsBaseOf<DictionaryBase, T>::value,
          bool isTypedArray=IsBaseOf<AllTypedArraysBase, T>::value>
 class SequenceTracer
 {
   explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated
@@ -2039,16 +2066,49 @@ T& NonNullHelper(OwningNonNull<T>& aArg)
 }
 
 template<typename T>
 const T& NonNullHelper(const OwningNonNull<T>& aArg)
 {
   return aArg;
 }
 
+inline
+void NonNullHelper(NonNull<binding_detail::FakeDependentString>& aArg)
+{
+  // This overload is here to make sure that we never end up applying
+  // NonNullHelper to a NonNull<binding_detail::FakeDependentString>. If we
+  // try to, it should fail to compile, since presumably the caller will try to
+  // use our nonexistent return value.
+}
+
+inline
+void NonNullHelper(const NonNull<binding_detail::FakeDependentString>& aArg)
+{
+  // This overload is here to make sure that we never end up applying
+  // NonNullHelper to a NonNull<binding_detail::FakeDependentString>. If we
+  // try to, it should fail to compile, since presumably the caller will try to
+  // use our nonexistent return value.
+}
+
+inline
+void NonNullHelper(binding_detail::FakeDependentString& aArg)
+{
+  // This overload is here to make sure that we never end up applying
+  // NonNullHelper to a FakeDependentString before we've constified it.  If we
+  // try to, it should fail to compile, since presumably the caller will try to
+  // use our nonexistent return value.
+}
+
+MOZ_ALWAYS_INLINE
+const nsAString& NonNullHelper(const binding_detail::FakeDependentString& aArg)
+{
+  return aArg;
+}
+
 // Reparent the wrapper of aObj to whatever its native now thinks its
 // parent should be.
 nsresult
 ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
 
 /**
  * Used to implement the hasInstance hook of an interface object.
  *
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -146,17 +146,17 @@ CallbackObject::CallSetup::CallSetup(Cal
   // compartment that we ended up in with mAutoEntryScript above, because the
   // entry point is based off of the unwrapped callback (realCallback).
   mAc.construct(cx, mRootedCallable.ref());
 
   // And now we're ready to go.
   mCx = cx;
 
   // Make sure the JS engine doesn't report exceptions we want to re-throw
-  if (mExceptionHandling == eRethrowContentExceptions ||
+  if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
       mExceptionHandling == eRethrowExceptions) {
     mSavedJSContextOptions = JS::ContextOptionsRef(cx);
     JS::ContextOptionsRef(cx).setDontReportUncaught(true);
   }
 }
 
 bool
 CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3025,17 +3025,17 @@ def getJSToNativeConversionInfo(type, de
         # optional or nullable complexity going on.  In that situation, we can
         # use an AutoSequence instead.  We have to keep using Sequence in the
         # nullable and optional cases because we don't want to leak the
         # AutoSequence type to consumers, which would be unavoidable with
         # Nullable<AutoSequence> or Optional<AutoSequence>.
         if isMember or isOptional or nullable or isCallbackReturnValue:
             sequenceClass = "Sequence"
         else:
-            sequenceClass = "AutoSequence"
+            sequenceClass = "binding_detail::AutoSequence"
 
         # XXXbz we can't include the index in the the sourceDescription, because
         # we don't really have a way to pass one in dynamically at runtime...
         elementInfo = getJSToNativeConversionInfo(
             elementType, descriptorProvider, isMember="Sequence",
             exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
             isCallbackReturnValue=isCallbackReturnValue,
             sourceDescription="element of %s" % sourceDescription)
@@ -3631,36 +3631,40 @@ for (uint32_t i = 0; i < length; ++i) {
             if isMember == "Variadic":
                 # The string is kept alive by the argument, so we can just
                 # depend on it.
                 assignString = "${declName}.Rebind(str.Data(), str.Length())"
             else:
                 assignString = "${declName} = str"
             return JSToNativeConversionInfo(
                 "{\n"
-                "  FakeDependentString str;\n"
+                "  binding_detail::FakeDependentString str;\n"
                 "%s\n"
                 "  %s;\n"
                 "}\n" % (
                     CGIndenter(CGGeneric(getConversionCode("str"))).define(),
                     assignString),
                 declType=declType, dealWithOptional=isOptional)
 
         if isOptional:
             declType = "Optional<nsAString>"
-        else:
-            declType = "NonNull<nsAString>"
+            holderType = CGGeneric("binding_detail::FakeDependentString")
+            conversionCode = ("%s\n"
+                              "${declName} = &${holderName};" %
+                              getConversionCode("${holderName}"))
+        else:
+            declType = "binding_detail::FakeDependentString"
+            holderType = None
+            conversionCode = getConversionCode("${declName}")
 
         # No need to deal with optional here; we handled it already
         return JSToNativeConversionInfo(
-            ("%s\n"
-             "${declName} = &${holderName};" %
-             getConversionCode("${holderName}")),
+            conversionCode,
             declType=CGGeneric(declType),
-            holderType=CGGeneric("FakeDependentString"))
+            holderType=holderType)
 
     if type.isByteString():
         assert not isEnforceRange and not isClamp
 
         nullable = toStringBool(type.nullable())
 
         conversionCode = (
             "if (!ConvertJSValueToByteString(cx, ${val}, ${mutableVal},"
@@ -3827,17 +3831,17 @@ for (uint32_t i = 0; i < length; ++i) {
         # should be able to assume not isOptional here.
         assert not isOptional
 
         typeName = CGDictionary.makeDictionaryName(type.inner)
         if not isMember and not isCallbackReturnValue:
             # Since we're not a member and not nullable or optional, no one will
             # see our real type, so we can do the fast version of the dictionary
             # that doesn't pre-initialize members.
-            typeName = "dictionary_detail::Fast" + typeName
+            typeName = "binding_detail::Fast" + typeName
 
         declType = CGGeneric(typeName)
 
         # We do manual default value handling here, because we
         # actually do want a jsval, and we only handle null anyway
         # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
         # we know we have a value, so we don't have to worry about the
         # default value.
@@ -4165,17 +4169,17 @@ class CGArgumentConverter(CGThing):
 
         # Variadic arguments get turned into a sequence.
         if typeConversion.dealWithOptional:
             raise TypeError("Shouldn't have optional things in variadics")
         if typeConversion.holderType is not None:
             raise TypeError("Shouldn't need holders for variadics")
 
         replacer = dict(self.argcAndIndex, **self.replacementVariables)
-        replacer["seqType"] = CGTemplatedType("AutoSequence",
+        replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence",
                                               typeConversion.declType).define()
         if typeNeedsRooting(self.argument.type):
             rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" %
                           typeConversion.declType.define())
         else:
             rooterDecl = ""
         replacer["elemType"] = typeConversion.declType.define()
 
@@ -6937,17 +6941,17 @@ class CGUnionStruct(CGThing):
                 if self.ownsMembers:
                     methods.append(vars["setter"])
                     if t.isString():
                         methods.append(
                             ClassMethod("SetStringData", "void",
                                 [Argument("const nsString::char_type*", "aData"),
                                  Argument("nsString::size_type", "aLength")],
                                 inline=True, bodyInHeader=True,
-                                body="mValue.mString.Value().Assign(aData, aLength);"))
+                                body="SetAsString().Assign(aData, aLength);"))
 
             body = string.Template('MOZ_ASSERT(Is${name}(), "Wrong type!");\n'
                                    'mValue.m${name}.Destroy();\n'
                                    'mType = eUninitialized;').substitute(vars)
             methods.append(ClassMethod("Destroy" + vars["name"],
                                        "void",
                                        [],
                                        visibility="private",
@@ -7136,17 +7140,17 @@ class CGUnionConversionStruct(CGThing):
                                            bodyInHeader=True,
                                            body=body,
                                            visibility="private"))
                 if t.isString():
                     methods.append(ClassMethod("SetStringData", "void",
                                      [Argument("const nsDependentString::char_type*", "aData"),
                                       Argument("nsDependentString::size_type", "aLength")],
                                      inline=True, bodyInHeader=True,
-                                     body="mStringHolder.SetData(aData, aLength);"))
+                                     body="SetAsString().SetData(aData, aLength);"))
 
             if vars["holderType"] is not None:
                 members.append(ClassMember("m%sHolder" % vars["name"],
                                            vars["holderType"]))
 
         return CGClass(structName + "Argument",
                        members=members,
                        constructors=[ctor],
@@ -7960,17 +7964,17 @@ class CGProxyNamedOperation(CGProxySpeci
             unwrapString = ("nameVal = %s;\n" % self.value) + unwrapString
 
         # Sadly, we have to set up nameVal even if we have an atom id,
         # because we don't know for sure, and we can end up needing it
         # so it needs to be higher up the stack.  Using a Maybe here
         # seems like probable overkill.
         return ("JS::Rooted<JS::Value> nameVal(cx);\n" +
                 idDecl +
-                ("FakeDependentString %s;\n" % argName) +
+                ("binding_detail::FakeDependentString %s;\n" % argName) +
                 unwrapString +
                 ("\n"
                  "\n"
                  "%s* self = UnwrapProxy(proxy);\n" %
                  self.descriptor.nativeType) +
                 CGProxySpecialOperation.define(self))
 
 class CGProxyNamedGetter(CGProxyNamedOperation):
@@ -9253,17 +9257,17 @@ if (""",
             )
 
         fastStruct = CGClass("Fast" + selfName,
             bases=[ClassBase(selfName)],
             constructors=[fastDictionaryCtor],
             isStruct=True)
 
         return CGList([struct,
-                       CGNamespace.build(['dictionary_detail'],
+                       CGNamespace.build(['binding_detail'],
                                          fastStruct)],
                       "\n")
 
     def deps(self):
         return self.dictionary.getDeps()
 
     @staticmethod
     def makeDictionaryName(dictionary):
@@ -11596,16 +11600,18 @@ struct PrototypeTraits;
         (includes, implincludes,
          declarations, unions) = UnionTypes(config.getDescriptors(),
                                             config.getDictionaries(),
                                             config.getCallbacks(),
                                             config)
         includes.add("mozilla/dom/OwningNonNull.h")
         includes.add("mozilla/dom/UnionMember.h")
         includes.add("mozilla/dom/BindingDeclarations.h")
+        # Need BindingUtils.h for FakeDependentString
+        includes.add("mozilla/dom/BindingUtils.h")
         implincludes.add("mozilla/dom/PrimitiveConversions.h")
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'], unions)
 
         curr = CGWrapper(curr, post='\n')
 
         namespaces = []
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
@@ -671,16 +671,24 @@ BluetoothA2dpManager::OnDisconnect(const
 void
 BluetoothA2dpManager::HandleSinkPropertyChanged(const BluetoothSignal& aSignal)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSignal.value().type() ==
              BluetoothValue::TArrayOfBluetoothNamedValue);
 
   const nsString& address = aSignal.path();
+  /**
+   * Update sink property only if
+   * - mDeviceAddress is empty (A2dp is disconnected), or
+   * - this property change is from the connected sink.
+   */
+  NS_ENSURE_TRUE_VOID(mDeviceAddress.IsEmpty() ||
+                      mDeviceAddress.Equals(address));
+
   const InfallibleTArray<BluetoothNamedValue>& arr =
     aSignal.value().get_ArrayOfBluetoothNamedValue();
   MOZ_ASSERT(arr.Length() == 1);
 
   /**
    * There are three properties:
    * - "State": a string
    * - "Connected": a boolean value
--- a/dom/bluetooth/bluez/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothA2dpManager.cpp
@@ -252,16 +252,24 @@ BluetoothA2dpManager::OnDisconnect(const
  */
 void
 BluetoothA2dpManager::HandleSinkPropertyChanged(const BluetoothSignal& aSignal)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
 
   const nsString& address = aSignal.path();
+  /**
+   * Update sink property only if
+   * - mDeviceAddress is empty (A2dp is disconnected), or
+   * - this property change is from the connected sink.
+   */
+  NS_ENSURE_TRUE_VOID(mDeviceAddress.IsEmpty() ||
+                      mDeviceAddress.Equals(address));
+
   const InfallibleTArray<BluetoothNamedValue>& arr =
     aSignal.value().get_ArrayOfBluetoothNamedValue();
   MOZ_ASSERT(arr.Length() == 1);
 
   /**
    * There are three properties:
    * - "State": a string
    * - "Connected": a boolean value
--- a/dom/browser-element/BrowserElementPanning.js
+++ b/dom/browser-element/BrowserElementPanning.js
@@ -9,16 +9,22 @@
 dump("############################### browserElementPanning.js loaded\n");
 
 let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu }  = Components;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Geometry.jsm");
 
 var global = this;
 
+const kObservedEvents = [
+  "BEC:ShownModalPrompt",
+  "Activity:Success",
+  "Activity:Error"
+];
+
 const ContentPanning = {
   // Are we listening to touch or mouse events?
   watchedEventsType: '',
 
   // Are mouse events being delivered to this content along with touch
   // events, in violation of spec?
   hybridEvents: false,
 
@@ -50,22 +56,27 @@ const ContentPanning = {
     events.forEach(function(type) {
       // Using the system group for mouse/touch events to avoid
       // missing events if .stopPropagation() has been called.
       els.addSystemEventListener(global, type,
                                  this.handleEvent.bind(this),
                                  /* useCapture = */ false);
     }.bind(this));
 
+    addEventListener("unload",
+		     this._unloadHandler.bind(this),
+		     /* useCapture = */ false,
+		     /* wantsUntrusted = */ false);
+
     addMessageListener("Viewport:Change", this._recvViewportChange.bind(this));
     addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
     addEventListener("visibilitychange", this._handleVisibilityChange.bind(this));
-    Services.obs.addObserver(this, "BEC:ShownModalPrompt", false);
-    Services.obs.addObserver(this, "Activity:Success", false);
-    Services.obs.addObserver(this, "Activity:Error", false);
+    kObservedEvents.forEach((topic) => {
+      Services.obs.addObserver(this, topic, false);
+    });
   },
 
   handleEvent: function cp_handleEvent(evt) {
     this._tryDelayMouseEvents();
 
     if (evt.defaultPrevented || evt.multipleActionsPrevented) {
       // clean up panning state even if touchend/mouseup has been preventDefault.
       if(evt.type === 'touchend' || evt.type === 'mouseup') {
@@ -591,16 +602,22 @@ const ContentPanning = {
     delete this.primaryPointerId;
     this._activationTimer.cancel();
 
     // If there is a scroll action but the application is not managed by
     // the AsyncPanZoom controller, let's do a manual kinetic panning action.
     if (this.panning && docShell.asyncPanZoomEnabled === false) {
       KineticPanning.start(this);
     }
+  },
+
+  _unloadHandler: function() {
+    kObservedEvents.forEach((topic) => {
+      Services.obs.removeObserver(this, topic);
+    });
   }
 };
 
 // Min/max velocity of kinetic panning. This is in pixels/millisecond.
 const kMinVelocity = 0.2;
 const kMaxVelocity = 6;
 
 // Constants that affect the "friction" of the scroll pane.
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -963,16 +963,40 @@ nsGonkCameraControl::SetPictureSize(uint
     RwAutoLockWrite write(mRwLock);
     mParams.setPictureSize(static_cast<int>(w), static_cast<int>(h));
   }
 
   // Finally, update the thumbnail size
   UpdateThumbnailSize();
 }
 
+int32_t
+nsGonkCameraControl::RationalizeRotation(int32_t aRotation)
+{
+  int32_t r = aRotation;
+
+  // The result of this operation is an angle from 0..270 degrees,
+  // in steps of 90 degrees. Angles are rounded to the nearest
+  // magnitude, so 45 will be rounded to 90, and -45 will be rounded
+  // to -90 (not 0).
+  if (r >= 0) {
+    r += 45;
+  } else {
+    r -= 45;
+  }
+  r /= 90;
+  r %= 4;
+  r *= 90;
+  if (r < 0) {
+    r += 360;
+  }
+
+  return r;
+}
+
 nsresult
 nsGonkCameraControl::TakePictureImpl(TakePictureTask* aTakePicture)
 {
   if (aTakePicture->mCancel) {
     if (mCameraHw.get()) {
       mCameraHw->CancelTakePicture();
     }
   }
@@ -986,23 +1010,20 @@ nsGonkCameraControl::TakePictureImpl(Tak
   mDeferConfigUpdate = true;
 
   SetPictureSize(aTakePicture->mSize.width, aTakePicture->mSize.height);
 
   // Picture format -- need to keep it for the callback.
   mFileFormat = aTakePicture->mFileFormat;
   SetParameter(CameraParameters::KEY_PICTURE_FORMAT, NS_ConvertUTF16toUTF8(mFileFormat).get());
 
-  // Convert 'rotation' to a positive value from 0..270 degrees, in steps of 90.
-  uint32_t r = static_cast<uint32_t>(aTakePicture->mRotation);
+  // Round 'rotation' up to a positive value from 0..270 degrees, in steps of 90.
+  int32_t r = static_cast<uint32_t>(aTakePicture->mRotation);
   r += mCameraHw->GetSensorOrientation(GonkCameraHardware::OFFSET_SENSOR_ORIENTATION);
-  r %= 360;
-  r += 45;
-  r /= 90;
-  r *= 90;
+  r = RationalizeRotation(r);
   DOM_CAMERA_LOGI("setting picture rotation to %d degrees (mapped from %d)\n", r, aTakePicture->mRotation);
   SetParameter(CameraParameters::KEY_ROTATION, nsPrintfCString("%u", r).get());
 
   // Add any specified positional information -- don't care if these fail.
   if (!isnan(aTakePicture->mPosition.latitude)) {
     DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aTakePicture->mPosition.latitude);
     SetParameter(CameraParameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.latitude).get());
   }
@@ -1512,24 +1533,17 @@ nsGonkCameraControl::SetupRecording(int 
     aMaxFileSizeBytes = -1;
   }
   snprintf(buffer, SIZE, "max-filesize=%lld", aMaxFileSizeBytes);
   CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
 
   // adjust rotation by camera sensor offset
   int r = aRotation;
   r += mCameraHw->GetSensorOrientation();
-  r %= 360;
-  r += 45;
-  r /= 90;
-  r *= 90;
-  if (r < 0) {
-    // the video recorder only supports positive rotations
-    r += 360;
-  }
+  r = RationalizeRotation(r);
   DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r, aRotation);
   snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", r);
   CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
 
   CHECK_SETARG(mRecorder->setListener(new GonkRecorderListener(this)));
 
   // recording API needs file descriptor of output file
   CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0));
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -91,16 +91,18 @@ protected:
 
   nsresult SetupRecording(int aFd, int aRotation, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs);
   nsresult SetupVideoMode(const nsAString& aProfile);
   void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
   void SetThumbnailSize(uint32_t aWidth, uint32_t aHeight);
   void UpdateThumbnailSize();
   void SetPictureSize(uint32_t aWidth, uint32_t aHeight);
 
+  int32_t RationalizeRotation(int32_t aRotation);
+
   android::sp<android::GonkCameraHardware> mCameraHw;
   double                    mExposureCompensationMin;
   double                    mExposureCompensationStep;
   bool                      mDeferConfigUpdate;
   PRRWLock*                 mRwLock;
   android::CameraParameters mParams;
   uint32_t                  mWidth;
   uint32_t                  mHeight;
--- a/dom/datastore/DataStore.jsm
+++ b/dom/datastore/DataStore.jsm
@@ -31,17 +31,17 @@ Cu.import('resource://gre/modules/XPCOMU
 Cu.importGlobalProperties(["indexedDB"]);
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 /* Helper functions */
 function createDOMError(aWindow, aEvent) {
-  return new aWindow.DOMError(aEvent.target.error.name);
+  return new aWindow.DOMError(aEvent);
 }
 
 function throwInvalidArg(aWindow) {
   return aWindow.Promise.reject(
     new aWindow.DOMError("SyntaxError", "Non-numeric or invalid id"));
 }
 
 function throwReadOnly(aWindow) {
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_duplicate.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for DataStore - duplicate keys</title>
+</head>
+<body>
+<div id="container"></div>
+  <script type="application/javascript;version=1.7">
+
+  var gStore;
+  var gEvent;
+  var gChangeId;
+
+  function ok(a, msg) {
+    alert((a ? 'OK' : 'KO')+ ' ' + msg)
+  }
+
+  function is(a, b, msg) {
+    ok(a === b, msg);
+  }
+
+  function cbError() {
+    alert('KO error');
+  }
+
+  function finish() {
+    alert('DONE');
+  }
+
+  function testGetDataStores() {
+    navigator.getDataStores('foo').then(function(stores) {
+      gStore = stores[0];
+      runTest();
+    }, cbError);
+  }
+
+  function testAdd(success) {
+    gStore.add({ a: 42 }, 'test').then(function() {
+      is(success, true, "Record added");
+      runTest();
+    }, function(e) {
+      is(success, false, "Record failed");
+      ok(e instanceof DOMError, "DOMError received");
+      is(e.name, 'ConstraintError', 'e.name: ConstraintError');
+      is(e.message, '', 'e.message');
+      runTest();
+    });
+  }
+
+  var tests = [
+    // Test for GetDataStore
+    testGetDataStores,
+
+    // add
+    function() { testAdd(true); },
+
+    // add duplicate
+    function() { testAdd(false); }
+  ];
+
+  function runTest() {
+    if (!tests.length) {
+      finish();
+      return;
+    }
+
+    var test = tests.shift();
+    test();
+  }
+
+  runTest();
+  </script>
+</body>
+</html>
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -8,19 +8,21 @@ support-files =
   file_app.sjs
   file_app.template.webapp
   file_app2.template.webapp
   file_arrays.html
   file_sync.html
   file_bug924104.html
   file_certifiedApp.html
   file_keys.html
+  file_duplicate.html
 
 [test_app_install.html]
 [test_readonly.html]
 [test_basic.html]
 [test_changes.html]
 [test_arrays.html]
 [test_oop.html]
 [test_sync.html]
 [test_bug924104.html]
 [test_certifiedApp.html]
 [test_keys.html]
+[test_duplicate.html]
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/test_duplicate.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for DataStore - duplicate keys</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="container"></div>
+  <script type="application/javascript;version=1.7">
+
+  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_duplicate.html';
+  var gApp;
+
+  function cbError() {
+    ok(false, "Error callback invoked");
+    finish();
+  }
+
+  function installApp() {
+    var request = navigator.mozApps.install(gHostedManifestURL);
+    request.onerror = cbError;
+    request.onsuccess = function() {
+      gApp = request.result;
+      runTest();
+    }
+  }
+
+  function uninstallApp() {
+    // Uninstall the app.
+    var request = navigator.mozApps.mgmt.uninstall(gApp);
+    request.onerror = cbError;
+    request.onsuccess = function() {
+      // All done.
+      info("All done");
+      runTest();
+    }
+  }
+
+  function testApp() {
+    var ifr = document.createElement('iframe');
+    ifr.setAttribute('mozbrowser', 'true');
+    ifr.setAttribute('mozapp', gApp.manifestURL);
+    ifr.setAttribute('src', gApp.manifest.launch_path);
+    var domParent = document.getElementById('container');
+
+    // Set us up to listen for messages from the app.
+    var listener = function(e) {
+      var message = e.detail.message;
+      if (/^OK/.exec(message)) {
+        ok(true, "Message from app: " + message);
+      } else if (/KO/.exec(message)) {
+        ok(false, "Message from app: " + message);
+      } else if (/DONE/.exec(message)) {
+        ok(true, "Messaging from app complete");
+        ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
+        domParent.removeChild(ifr);
+        runTest();
+      }
+    }
+
+    // This event is triggered when the app calls "alert".
+    ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
+    domParent.appendChild(ifr);
+  }
+
+  var tests = [
+    // Permissions
+    function() {
+      SpecialPowers.pushPermissions(
+        [{ "type": "browser", "allow": 1, "context": document },
+         { "type": "embed-apps", "allow": 1, "context": document },
+         { "type": "webapps-manage", "allow": 1, "context": document }], runTest);
+    },
+
+    // Preferences
+    function() {
+      SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true],
+                                         ["dom.datastore.enabled", true],
+                                         ["dom.testing.ignore_ipc_principal", true],
+                                         ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
+    },
+
+    function() {
+      SpecialPowers.setAllAppsLaunchable(true);
+      SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
+      runTest();
+    },
+
+    // No confirmation needed when an app is installed
+    function() {
+      SpecialPowers.autoConfirmAppInstall(runTest);
+    },
+
+    // Installing the app
+    installApp,
+
+    // Run tests in app
+    testApp,
+
+    // Uninstall the app
+    uninstallApp
+  ];
+
+  function runTest() {
+    if (!tests.length) {
+      finish();
+      return;
+    }
+
+    var test = tests.shift();
+    test();
+  }
+
+  function finish() {
+    SimpleTest.finish();
+  }
+
+  if (SpecialPowers.isMainProcess()) {
+    SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  runTest();
+  </script>
+</body>
+</html>
--- a/dom/events/nsEventListenerManager.cpp
+++ b/dom/events/nsEventListenerManager.cpp
@@ -32,16 +32,18 @@
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsCOMArray.h"
 #include "nsEventListenerService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "xpcpublic.h"
 #include "nsSandboxFlags.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BindingUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 #define EVENT_TYPE_EQUALS(ls, type, userType, typeString, allEvents) \
   ((ls->mEventType == type &&                                        \
     (ls->mEventType != NS_USER_DEFINED_EVENT ||                      \
@@ -622,17 +624,18 @@ nsEventListenerManager::SetEventHandlerI
   return ls;
 }
 
 nsresult
 nsEventListenerManager::SetEventHandler(nsIAtom *aName,
                                         const nsAString& aBody,
                                         uint32_t aLanguage,
                                         bool aDeferCompilation,
-                                        bool aPermitUntrustedEvents)
+                                        bool aPermitUntrustedEvents,
+                                        Element* aElement)
 {
   NS_PRECONDITION(aLanguage != nsIProgrammingLanguage::UNKNOWN,
                   "Must know the language for the script event listener");
 
   // |aPermitUntrustedEvents| is set to False for chrome - events
   // *generated* from an unknown source are not allowed.
   // However, for script languages with no 'sandbox', we want to reject
   // such scripts based on the source of their code, not just the source
@@ -728,17 +731,17 @@ nsEventListenerManager::SetEventHandler(
                               global->GetGlobalJSObject());
 
   nsListenerStruct* ls = SetEventHandlerInternal(scope, aName,
                                                  EmptyString(),
                                                  nsEventHandler(),
                                                  aPermitUntrustedEvents);
 
   if (!aDeferCompilation) {
-    return CompileEventHandlerInternal(ls, &aBody);
+    return CompileEventHandlerInternal(ls, &aBody, aElement);
   }
 
   return NS_OK;
 }
 
 void
 nsEventListenerManager::RemoveEventHandler(nsIAtom* aName,
                                            const nsAString& aTypeString)
@@ -757,17 +760,18 @@ nsEventListenerManager::RemoveEventHandl
     if (mTarget && aName) {
       mTarget->EventListenerRemoved(aName);
     }
   }
 }
 
 nsresult
 nsEventListenerManager::CompileEventHandlerInternal(nsListenerStruct *aListenerStruct,
-                                                    const nsAString* aBody)
+                                                    const nsAString* aBody,
+                                                    Element* aElement)
 {
   NS_PRECONDITION(aListenerStruct->GetJSListener(),
                   "Why do we not have a JS listener?");
   NS_PRECONDITION(aListenerStruct->mHandlerIsString,
                   "Why are we compiling a non-string JS listener?");
 
   nsresult result = NS_OK;
 
@@ -782,34 +786,38 @@ nsEventListenerManager::CompileEventHand
 
   nsIScriptContext* context = global->GetScriptContext();
   NS_ENSURE_STATE(context);
 
   // Push a context to make sure exceptions are reported in the right place.
   AutoPushJSContext cx(context->GetNativeContext());
   JS::Rooted<JSObject*> handler(cx);
 
+  JS::Rooted<JSObject*> scope(cx, listener->GetEventScope());
+
+  nsIAtom* attrName = aListenerStruct->mTypeAtom;
+
   if (aListenerStruct->mHandlerIsString) {
     // OK, we didn't find an existing compiled event handler.  Flag us
     // as not a string so we don't keep trying to compile strings
     // which can't be compiled
     aListenerStruct->mHandlerIsString = false;
 
-    // mTarget may not be an nsIContent if it's a window and we're
+    // mTarget may not be an Element if it's a window and we're
     // getting an inline event listener forwarded from <html:body> or
     // <html:frameset> or <xul:window> or the like.
     // XXX I don't like that we have to reference content from
     // here. The alternative is to store the event handler string on
     // the nsIJSEventListener itself, and that still doesn't address
     // the arg names issue.
-    nsCOMPtr<nsIContent> content = do_QueryInterface(mTarget);
+    nsCOMPtr<Element> element = do_QueryInterface(mTarget);
+    MOZ_ASSERT(element || aBody, "Where will we get our body?");
     nsAutoString handlerBody;
     const nsAString* body = aBody;
-    if (content && !aBody) {
-      nsIAtom* attrName = aListenerStruct->mTypeAtom;
+    if (!aBody) {
       if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGLoad)
         attrName = nsGkAtoms::onload;
       else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGUnload)
         attrName = nsGkAtoms::onunload;
       else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGAbort)
         attrName = nsGkAtoms::onabort;
       else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGError)
         attrName = nsGkAtoms::onerror;
@@ -821,61 +829,74 @@ nsEventListenerManager::CompileEventHand
         attrName = nsGkAtoms::onzoom;
       else if (aListenerStruct->mTypeAtom == nsGkAtoms::onbeginEvent)
         attrName = nsGkAtoms::onbegin;
       else if (aListenerStruct->mTypeAtom == nsGkAtoms::onrepeatEvent)
         attrName = nsGkAtoms::onrepeat;
       else if (aListenerStruct->mTypeAtom == nsGkAtoms::onendEvent)
         attrName = nsGkAtoms::onend;
 
-      content->GetAttr(kNameSpaceID_None, attrName, handlerBody);
+      element->GetAttr(kNameSpaceID_None, attrName, handlerBody);
       body = &handlerBody;
+      aElement = element;
     }
 
     uint32_t lineNo = 0;
     nsAutoCString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
-    if (doc) {
-      nsIURI *uri = doc->GetDocumentURI();
-      if (uri) {
-        uri->GetSpec(url);
-        lineNo = 1;
-      }
+    MOZ_ASSERT(body);
+    MOZ_ASSERT(aElement);
+    nsIURI *uri = aElement->OwnerDoc()->GetDocumentURI();
+    if (uri) {
+      uri->GetSpec(url);
+      lineNo = 1;
     }
 
     uint32_t argCount;
     const char **argNames;
-    // If no content, then just use kNameSpaceID_None for the
-    // namespace ID.  In practice, it doesn't matter since SVG is
-    // the only thing with weird arg names and SVG doesn't map event
-    // listeners to the window.
-    nsContentUtils::GetEventArgNames(content ?
-                                       content->GetNameSpaceID() :
-                                       kNameSpaceID_None,
+    nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(),
                                      aListenerStruct->mTypeAtom,
                                      &argCount, &argNames);
 
     JSAutoCompartment ac(cx, context->GetWindowProxy());
     JS::CompileOptions options(cx);
     options.setFileAndLine(url.get(), lineNo)
            .setVersion(SCRIPTVERSION_DEFAULT);
 
+    JS::Rooted<JS::Value> targetVal(cx);
+    // Go ahead and wrap into the current compartment of cx directly.
+    JS::Rooted<JSObject*> wrapScope(cx, JS::CurrentGlobalOrNull(cx));
+    if (WrapNewBindingObject(cx, wrapScope, aElement, &targetVal)) {
+      MOZ_ASSERT(targetVal.isObject());
+
+      nsDependentAtomString str(attrName);
+      // Most of our names are short enough that we don't even have to malloc
+      // the JS string stuff, so don't worry about playing games with
+      // refcounting XPCOM stringbuffers.
+      JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx,
+                                                          str.BeginReading(),
+                                                          str.Length()));
+      NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
+
+      options.setElement(&targetVal.toObject())
+             .setElementAttributeName(jsStr);
+    }
+
     JS::Rooted<JSObject*> handlerFun(cx);
     result = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options,
                                         nsAtomCString(aListenerStruct->mTypeAtom),
                                         argCount, argNames, *body, handlerFun.address());
     NS_ENSURE_SUCCESS(result, result);
     handler = handlerFun;
     NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
   }
 
   if (handler) {
     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget);
     // Bind it
     JS::Rooted<JSObject*> boundHandler(cx);
-    JS::Rooted<JSObject*> scope(cx, listener->GetEventScope());
     context->BindCompiledEventHandler(mTarget, scope, handler, &boundHandler);
     aListenerStruct = nullptr;
     // Note - We pass null for aIncumbentGlobal below. We could also pass the
     // compilation global, but since the handler is guaranteed to be scripted,
     // there's no need to use an override, since the JS engine will always give
     // us the right answer.
     if (!boundHandler) {
       listener->ForgetHandler();
@@ -904,17 +925,17 @@ nsEventListenerManager::HandleEventSubTy
 {
   nsresult result = NS_OK;
   EventListenerHolder listener(aListenerStruct->mListener);  // strong ref
 
   // If this is a script handler and we haven't yet
   // compiled the event handler itself
   if ((aListenerStruct->mListenerType == eJSEventListener) &&
       aListenerStruct->mHandlerIsString) {
-    result = CompileEventHandlerInternal(aListenerStruct, nullptr);
+    result = CompileEventHandlerInternal(aListenerStruct, nullptr, nullptr);
     aListenerStruct = nullptr;
   }
 
   if (NS_SUCCEEDED(result)) {
     if (mIsMainThreadELM) {
       nsContentUtils::EnterMicroTask();
     }
     // nsIDOMEvent::currentTarget is set in nsEventDispatcher.
@@ -1137,17 +1158,18 @@ nsEventListenerManager::GetListenerInfo(
   NS_ENSURE_STATE(target);
   aList->Clear();
   uint32_t count = mListeners.Length();
   for (uint32_t i = 0; i < count; ++i) {
     const nsListenerStruct& ls = mListeners.ElementAt(i);
     // If this is a script handler and we haven't yet
     // compiled the event handler itself go ahead and compile it
     if ((ls.mListenerType == eJSEventListener) && ls.mHandlerIsString) {
-      CompileEventHandlerInternal(const_cast<nsListenerStruct*>(&ls), nullptr);
+      CompileEventHandlerInternal(const_cast<nsListenerStruct*>(&ls), nullptr,
+                                  nullptr);
     }
     nsAutoString eventType;
     if (ls.mAllEvents) {
       eventType.SetIsVoid(true);
     } else {
       eventType.Assign(Substring(nsDependentAtomString(ls.mTypeAtom), 2));
     }
     // EventListenerInfo is defined in XPCOM, so we have to go ahead
@@ -1246,17 +1268,17 @@ nsEventListenerManager::GetEventHandlerI
 
   if (!ls) {
     return nullptr;
   }
 
   nsIJSEventListener *listener = ls->GetJSListener();
     
   if (ls->mHandlerIsString) {
-    CompileEventHandlerInternal(ls, nullptr);
+    CompileEventHandlerInternal(ls, nullptr, nullptr);
   }
 
   const nsEventHandler& handler = listener->GetHandler();
   if (handler.HasEventHandler()) {
     return &handler;
   }
 
   return nullptr;
--- a/dom/events/nsEventListenerManager.h
+++ b/dom/events/nsEventListenerManager.h
@@ -27,16 +27,17 @@ struct nsListenerStruct;
 class nsEventListenerManager;
 
 template<class T> class nsCOMArray;
 
 namespace mozilla {
 namespace dom {
 
 class EventTarget;
+class Element;
 
 typedef CallbackObjectHolder<EventListener, nsIDOMEventListener>
   EventListenerHolder;
 
 struct EventListenerFlags
 {
   friend struct ::nsListenerStruct;
   friend class  ::nsEventListenerManager;
@@ -278,24 +279,27 @@ public:
                                  const nsAString& type,
                                  const mozilla::dom::EventListenerFlags& aFlags);
 
   /**
    * Sets the current "inline" event listener for aName to be a
    * function compiled from aFunc if !aDeferCompilation.  If
    * aDeferCompilation, then we assume that we can get the string from
    * mTarget later and compile lazily.
+   *
+   * aElement, if not null, is the element the string is associated with.
    */
   // XXXbz does that play correctly with nodes being adopted across
   // documents?  Need to double-check the spec here.
   nsresult SetEventHandler(nsIAtom *aName,
                            const nsAString& aFunc,
                            uint32_t aLanguage,
                            bool aDeferCompilation,
-                           bool aPermitUntrustedEvents);
+                           bool aPermitUntrustedEvents,
+                           mozilla::dom::Element* aElement);
   /**
    * Remove the current "inline" event listener for aName.
    */
   void RemoveEventHandler(nsIAtom *aName, const nsAString& aTypeString);
 
   void HandleEvent(nsPresContext* aPresContext,
                    mozilla::WidgetEvent* aEvent, 
                    nsIDOMEvent** aDOMEvent,
@@ -415,20 +419,22 @@ protected:
 
   nsresult HandleEventSubType(nsListenerStruct* aListenerStruct,
                               nsIDOMEvent* aDOMEvent,
                               mozilla::dom::EventTarget* aCurrentTarget);
 
   /**
    * Compile the "inline" event listener for aListenerStruct.  The
    * body of the listener can be provided in aBody; if this is null we
-   * will look for it on mTarget.
+   * will look for it on mTarget.  If aBody is provided, aElement should be
+   * as well; otherwise it will also be inferred from mTarget.
    */
   nsresult CompileEventHandlerInternal(nsListenerStruct *aListenerStruct,
-                                       const nsAString* aBody);
+                                       const nsAString* aBody,
+                                       mozilla::dom::Element* aElement);
 
   /**
    * Find the nsListenerStruct for the "inline" event listener for aTypeAtom.
    */
   nsListenerStruct* FindEventHandler(uint32_t aEventType, nsIAtom* aTypeAtom,
                                      const nsAString& aTypeString);
 
   /**
--- a/dom/events/nsEventListenerService.cpp
+++ b/dom/events/nsEventListenerService.cpp
@@ -64,61 +64,60 @@ nsEventListenerInfo::GetInSystemEventGro
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEventListenerInfo::GetListenerObject(JSContext* aCx,
                                        JS::MutableHandle<JS::Value> aObject)
 {
   mozilla::Maybe<JSAutoCompartment> ac;
-  GetJSVal(aCx, ac, aObject.address());
+  GetJSVal(aCx, ac, aObject);
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsEventListenerService, nsIEventListenerService)
 
-// Caller must root *aJSVal!
 bool
 nsEventListenerInfo::GetJSVal(JSContext* aCx,
                               mozilla::Maybe<JSAutoCompartment>& aAc,
-                              JS::Value* aJSVal)
+                              JS::MutableHandle<JS::Value> aJSVal)
 {
-  *aJSVal = JSVAL_NULL;
+  aJSVal.setNull();
   nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(mListener);
   if (wrappedJS) {
     JS::Rooted<JSObject*> object(aCx, wrappedJS->GetJSObject());
     if (!object) {
       return false;
     }
     aAc.construct(aCx, object);
-    *aJSVal = OBJECT_TO_JSVAL(object);
+    aJSVal.setObject(*object);
     return true;
   }
 
   nsCOMPtr<nsIJSEventListener> jsl = do_QueryInterface(mListener);
   if (jsl && jsl->GetHandler().HasEventHandler()) {
     JS::Handle<JSObject*> handler(jsl->GetHandler().Ptr()->Callable());
     if (handler) {
       aAc.construct(aCx, handler);
-      *aJSVal = OBJECT_TO_JSVAL(handler);
+      aJSVal.setObject(*handler);
       return true;
     }
   }
   return false;
 }
 
 NS_IMETHODIMP
 nsEventListenerInfo::ToSource(nsAString& aResult)
 {
   aResult.SetIsVoid(true);
 
   AutoSafeJSContext cx;
   mozilla::Maybe<JSAutoCompartment> ac;
-  JS::Rooted<JS::Value> v(cx, JSVAL_NULL);
-  if (GetJSVal(cx, ac, v.address())) {
+  JS::Rooted<JS::Value> v(cx);
+  if (GetJSVal(cx, ac, &v)) {
     JSString* str = JS_ValueToSource(cx, v);
     if (str) {
       nsDependentJSString depStr;
       if (depStr.init(cx, str)) {
         aResult.Assign(depStr);
       }
     }
   }
@@ -137,18 +136,18 @@ nsEventListenerInfo::GetDebugObject(nsIS
   NS_ENSURE_SUCCESS(rv, NS_OK);
 
   bool isOn = false;
   jsd->GetIsOn(&isOn);
   NS_ENSURE_TRUE(isOn, NS_OK);
 
   AutoSafeJSContext cx;
   mozilla::Maybe<JSAutoCompartment> ac;
-  JS::Rooted<JS::Value> v(cx, JSVAL_NULL);
-  if (GetJSVal(cx, ac, v.address())) {
+  JS::Rooted<JS::Value> v(cx);
+  if (GetJSVal(cx, ac, &v)) {
     nsCOMPtr<jsdIValue> jsdValue;
     rv = jsd->WrapValue(v, getter_AddRefs(jsdValue));
     NS_ENSURE_SUCCESS(rv, rv);
     jsdValue.forget(aRetVal);
   }
 #endif
 
   return NS_OK;
--- a/dom/events/nsEventListenerService.h
+++ b/dom/events/nsEventListenerService.h
@@ -29,17 +29,17 @@ public:
     mAllowsUntrusted(aAllowsUntrusted),
     mInSystemEventGroup(aInSystemEventGroup) {}
   virtual ~nsEventListenerInfo() {}
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsEventListenerInfo)
   NS_DECL_NSIEVENTLISTENERINFO
 protected:
   bool GetJSVal(JSContext* aCx, mozilla::Maybe<JSAutoCompartment>& aAc,
-                JS::Value* aJSVal);
+                JS::MutableHandle<JS::Value> aJSVal);
 
   nsString                      mType;
   // nsReftPtr because that is what nsListenerStruct uses too.
   nsRefPtr<nsIDOMEventListener> mListener;
   bool                          mCapturing;
   bool                          mAllowsUntrusted;
   bool                          mInSystemEventGroup;
 };
--- a/dom/events/nsEventStateManager.cpp
+++ b/dom/events/nsEventStateManager.cpp
@@ -1696,20 +1696,18 @@ nsEventStateManager::HandleCrossProcessE
   // event to.
   //
   // NB: the elements of |targets| must be unique, for correctness.
   nsAutoTArray<nsCOMPtr<nsIContent>, 1> targets;
   if (aEvent->eventStructType != NS_TOUCH_EVENT ||
       aEvent->message == NS_TOUCH_START) {
     // If this event only has one target, and it's remote, add it to
     // the array.
-    nsIContent* target = mCurrentTargetContent;
-    if (!target && aTargetFrame) {
-      target = aTargetFrame->GetContent();
-    }
+    nsIFrame* frame = GetEventTarget();
+    nsIContent* target = frame ? frame->GetContent() : nullptr;
     if (IsRemoteTarget(target)) {
       targets.AppendElement(target);
     }
   } else {
     // This is a touch event with possibly multiple touch points.
     // Each touch point may have its own target.  So iterate through
     // all of them and collect the unique set of targets for event
     // forwarding.
--- a/dom/events/nsJSEventListener.cpp
+++ b/dom/events/nsJSEventListener.cpp
@@ -168,17 +168,17 @@ nsJSEventListener::HandleEvent(nsIDOMEve
 
     NS_ENSURE_TRUE(aEvent, NS_ERROR_UNEXPECTED);
     InternalScriptErrorEvent* scriptEvent =
       aEvent->GetInternalNSEvent()->AsScriptErrorEvent();
     if (scriptEvent &&
         (scriptEvent->message == NS_LOAD_ERROR ||
          scriptEvent->typeString.EqualsLiteral("error"))) {
       errorMsg = scriptEvent->errorMsg;
-      msgOrEvent.SetAsString() = static_cast<nsAString*>(&errorMsg);
+      msgOrEvent.SetAsString().SetData(errorMsg.Data(), errorMsg.Length());
 
       file = scriptEvent->fileName;
       fileName = &file;
 
       lineNumber.Construct();
       lineNumber.Value() = scriptEvent->lineNr;
     } else {
       msgOrEvent.SetAsEvent() = aEvent->InternalDOMEvent();
--- a/dom/file/FileHandle.cpp
+++ b/dom/file/FileHandle.cpp
@@ -31,17 +31,18 @@ public:
                 FileRequest* aFileRequest,
                 MetadataParameters* aParams,
                 FileHandle* aFileHandle)
   : MetadataHelper(aLockedFile, aFileRequest, aParams),
     mFileHandle(aFileHandle)
   { }
 
   nsresult
-  GetSuccessResult(JSContext* aCx, JS::Value* aVal);
+  GetSuccessResult(JSContext* aCx,
+                   JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
 
   void
   ReleaseObjects()
   {
     mFileHandle = nullptr;
 
     MetadataHelper::ReleaseObjects();
   }
@@ -173,29 +174,27 @@ FileHandle::GetFileId()
 
 NS_IMETHODIMP_(mozilla::dom::indexedDB::FileInfo*)
 FileHandle::GetFileInfo()
 {
   return nullptr;
 }
 
 nsresult
-GetFileHelper::GetSuccessResult(JSContext* aCx, JS::Value* aVal)
+GetFileHelper::GetSuccessResult(JSContext* aCx,
+                                JS::MutableHandle<JS::Value> aVal)
 {
   nsCOMPtr<nsIDOMFile> domFile =
     mFileHandle->CreateFileObject(mLockedFile, mParams->Size());
 
   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
-  JS::Rooted<JS::Value> rval(aCx);
   nsresult rv =
     nsContentUtils::WrapNative(aCx, global, domFile,
-                               &NS_GET_IID(nsIDOMFile), &rval);
+                               &NS_GET_IID(nsIDOMFile), aVal);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-  *aVal = rval;
-
   return NS_OK;
 }
 
 /* virtual */
 JSObject*
 FileHandle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return FileHandleBinding::Wrap(aCx, aScope, this);
--- a/dom/file/FileHelper.cpp
+++ b/dom/file/FileHelper.cpp
@@ -145,21 +145,21 @@ FileHelper::GetCurrentLockedFile()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   return gCurrentLockedFile;
 }
 
 nsresult
 FileHelper::GetSuccessResult(JSContext* aCx,
-                             JS::Value* aVal)
+                             JS::MutableHandle<JS::Value> aVal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  *aVal = JSVAL_VOID;
+  aVal.setUndefined();
   return NS_OK;
 }
 
 void
 FileHelper::ReleaseObjects()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
--- a/dom/file/FileHelper.h
+++ b/dom/file/FileHelper.h
@@ -69,17 +69,17 @@ protected:
   FileHelper(LockedFile* aLockedFile, FileRequest* aRequest);
 
   virtual ~FileHelper();
 
   virtual nsresult
   DoAsyncRun(nsISupports* aStream) = 0;
 
   virtual nsresult
-  GetSuccessResult(JSContext* aCx, JS::Value* aVal);
+  GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal);
 
   virtual void
   ReleaseObjects();
 
   void
   Finish();
 
   nsCOMPtr<nsIFileStorage> mFileStorage;
--- a/dom/file/FileRequest.cpp
+++ b/dom/file/FileRequest.cpp
@@ -79,17 +79,17 @@ FileRequest::NotifyHelperCompleted(FileH
 
   JS::Rooted<JS::Value> result(cx);
 
   JS::Rooted<JSObject*> global(cx, sc->GetWindowProxy());
   NS_ASSERTION(global, "Failed to get global object!");
 
   JSAutoCompartment ac(cx, global);
 
-  rv = aFileHelper->GetSuccessResult(cx, result.address());
+  rv = aFileHelper->GetSuccessResult(cx, &result);
   if (NS_FAILED(rv)) {
     NS_WARNING("GetSuccessResult failed!");
   }
 
   if (NS_SUCCEEDED(rv)) {
     FireSuccess(result);
   }
   else {
--- a/dom/file/LockedFile.cpp
+++ b/dom/file/LockedFile.cpp
@@ -57,20 +57,21 @@ public:
   {
     NS_ASSERTION(mSize, "Passed zero size!");
   }
 
   nsresult
   Init();
 
   nsresult
-  DoAsyncRun(nsISupports* aStream);
+  DoAsyncRun(nsISupports* aStream) MOZ_OVERRIDE;
 
   nsresult
-  GetSuccessResult(JSContext* aCx, JS::Value* aVal);
+  GetSuccessResult(JSContext* aCx,
+                   JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
 
 protected:
   uint64_t mLocation;
   uint64_t mSize;
 
   nsRefPtr<MemoryOutputStream> mStream;
 };
 
@@ -82,17 +83,18 @@ public:
                  uint64_t aLocation,
                  uint64_t aSize,
                  const nsAString& aEncoding)
   : ReadHelper(aLockedFile, aFileRequest, aLocation, aSize),
     mEncoding(aEncoding)
   { }
 
   nsresult
-  GetSuccessResult(JSContext* aCx, JS::Value* aVal);
+  GetSuccessResult(JSContext* aCx,
+                   JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
 
 private:
   nsString mEncoding;
 };
 
 class WriteHelper : public FileHelper
 {
 public:
@@ -822,23 +824,23 @@ LockedFile::OpenInputStream(bool aWholeF
   nsRefPtr<OpenStreamHelper> helper =
     new OpenStreamHelper(this, aWholeFile, aStart, aLength);
 
   nsresult rv = helper->Enqueue();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
 
   nsCOMPtr<nsIInputStream>& result = helper->Result();
   NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-  
+
   result.forget(aResult);
   return NS_OK;
 }
 
 nsresult
-LockedFile::WriteOrAppend(const JS::Value& aValue,
+LockedFile::WriteOrAppend(JS::Handle<JS::Value> aValue,
                           JSContext* aCx,
                           nsISupports** _retval,
                           bool aAppend)
 {
   if (!IsOpen()) {
     return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
   }
 
@@ -850,21 +852,20 @@ LockedFile::WriteOrAppend(const JS::Valu
     return NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR;
   }
 
   // Do nothing if the window is closed
   if (!GetOwner()) {
     return NS_OK;
   }
 
-  JS::Rooted<JS::Value> val(aCx, aValue);
   nsCOMPtr<nsIInputStream> inputStream;
   uint64_t inputLength;
   nsresult rv =
-    GetInputStreamForJSVal(val, aCx, getter_AddRefs(inputStream),
+    GetInputStreamForJSVal(aValue, aCx, getter_AddRefs(inputStream),
                            &inputLength);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!inputLength) {
     return NS_OK;
   }
 
   nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
@@ -1014,34 +1015,31 @@ ReadHelper::DoAsyncRun(nsISupports* aStr
 
   mRequest = do_QueryInterface(copier);
 
   return NS_OK;
 }
 
 nsresult
 ReadHelper::GetSuccessResult(JSContext* aCx,
-                             JS::Value* aVal)
+                             JS::MutableHandle<JS::Value> aVal)
 {
   JS::Rooted<JSObject*> arrayBuffer(aCx);
   nsresult rv =
     nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  *aVal = OBJECT_TO_JSVAL(arrayBuffer);
-
+  aVal.setObject(*arrayBuffer);
   return NS_OK;
 }
 
 nsresult
 ReadTextHelper::GetSuccessResult(JSContext* aCx,
-                                 JS::Value* aVal)
+                                 JS::MutableHandle<JS::Value> aVal)
 {
-  nsresult rv;
-
   nsAutoCString encoding;
   const nsCString& data = mStream->Data();
   // The BOM sniffing is baked into the "decode" part of the Encoding
   // Standard, which the File API references.
   if (!nsContentUtils::CheckForBOM(
         reinterpret_cast<const unsigned char *>(data.get()),
         data.Length(),
         encoding)) {
@@ -1050,27 +1048,24 @@ ReadTextHelper::GetSuccessResult(JSConte
       // API argument failed. Since we are dealing with a file system file,
       // we don't have a meaningful type attribute for the blob available,
       // so proceeding to the next step, which is defaulting to UTF-8.
       encoding.AssignLiteral("UTF-8");
     }
   }
 
   nsString tmpString;
-  rv = nsContentUtils::ConvertStringFromEncoding(encoding, data,
-                                                 tmpString);
+  nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data,
+                                                          tmpString);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  JS::Rooted<JS::Value> rval(aCx);
-  if (!xpc::StringToJsval(aCx, tmpString, &rval)) {
+  if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
     NS_WARNING("Failed to convert string!");
     return NS_ERROR_FAILURE;
   }
-
-  *aVal = rval;
   return NS_OK;
 }
 
 nsresult
 WriteHelper::DoAsyncRun(nsISupports* aStream)
 {
   NS_ASSERTION(aStream, "Passed a null stream!");
 
@@ -1102,17 +1097,17 @@ nsresult
 TruncateHelper::DoAsyncRun(nsISupports* aStream)
 {
   NS_ASSERTION(aStream, "Passed a null stream!");
 
   nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset);
 
   nsresult rv = truncator->AsyncWork(this, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
-  
+
   return NS_OK;
 }
 
 nsresult
 TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream)
 {
   nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream);
 
--- a/dom/file/LockedFile.h
+++ b/dom/file/LockedFile.h
@@ -96,17 +96,17 @@ private:
 
   void
   OnRequestFinished();
 
   inline already_AddRefed<FileRequest>
   GenerateFileRequest();
 
   nsresult
-  WriteOrAppend(const jsval& aValue, JSContext* aCx,
+  WriteOrAppend(JS::Handle<JS::Value> aValue, JSContext* aCx,
                 nsISupports** _retval, bool aAppend);
 
   nsresult
   Finish();
 
   nsRefPtr<FileHandle> mFileHandle;
   ReadyState mReadyState;
   FileMode mMode;
--- a/dom/file/MetadataHelper.cpp
+++ b/dom/file/MetadataHelper.cpp
@@ -21,17 +21,17 @@ MetadataHelper::DoAsyncRun(nsISupports* 
   nsresult rv = getter->AsyncWork(this, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 MetadataHelper::GetSuccessResult(JSContext* aCx,
-                                 JS::Value* aVal)
+                                 JS::MutableHandle<JS::Value> aVal)
 {
   JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(),
                                               JS::NullPtr()));
   NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
 
   if (mParams->SizeRequested()) {
     JS::Value val = JS_NumberValue(mParams->Size());
 
@@ -47,18 +47,17 @@ MetadataHelper::GetSuccessResult(JSConte
     NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
 
     if (!JS_DefineProperty(aCx, obj, "lastModified", OBJECT_TO_JSVAL(date),
                            nullptr, nullptr, JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
   }
 
-  *aVal = OBJECT_TO_JSVAL(obj);
-
+  aVal.setObject(*obj);
   return NS_OK;
 }
 
 nsresult
 MetadataHelper::AsyncMetadataGetter::DoStreamWork(nsISupports* aStream)
 {
   nsresult rv;
 
--- a/dom/file/MetadataHelper.h
+++ b/dom/file/MetadataHelper.h
@@ -80,17 +80,18 @@ public:
   : FileHelper(aLockedFile, aFileRequest),
     mParams(aParams)
   { }
 
   nsresult
   DoAsyncRun(nsISupports* aStream) MOZ_OVERRIDE;
 
   nsresult
-  GetSuccessResult(JSContext* aCx, JS::Value* aVal) MOZ_OVERRIDE;
+  GetSuccessResult(JSContext* aCx,
+                   JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
 
 protected:
   class AsyncMetadataGetter : public AsyncHelper
   {
   public:
     AsyncMetadataGetter(nsISupports* aStream, MetadataParameters* aParams,
                         bool aReadWrite)
     : AsyncHelper(aStream),
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -24,17 +24,17 @@ using namespace mozilla;
 using namespace mozilla::dom;
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom::indexedDB::ipc;
 
 namespace {
 
 inline nsresult
 GetKeyFromJSVal(JSContext* aCx,
-                jsval aVal,
+                JS::Handle<JS::Value> aVal,
                 Key& aKey,
                 bool aAllowUnset = false)
 {
   nsresult rv = aKey.SetFromJSVal(aCx, aVal);
   if (NS_FAILED(rv)) {
     NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB,
                  "Bad error code!");
     return rv;
@@ -47,17 +47,17 @@ GetKeyFromJSVal(JSContext* aCx,
   return NS_OK;
 }
 
 } // anonymous namespace
 
 // static
 nsresult
 IDBKeyRange::FromJSVal(JSContext* aCx,
-                       const jsval& aVal,
+                       JS::Handle<JS::Value> aVal,
                        IDBKeyRange** aKeyRange)
 {
   nsRefPtr<IDBKeyRange> keyRange;
 
   if (aVal.isNullOrUndefined()) {
     // undefined and null returns no IDBKeyRange.
     keyRange.forget(aKeyRange);
     return NS_OK;
@@ -140,18 +140,18 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRa
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange)
 
 void
 IDBKeyRange::DropJSObjects()
 {
   if (!mRooted) {
     return;
   }
-  mCachedLowerVal = JSVAL_VOID;
-  mCachedUpperVal = JSVAL_VOID;
+  mCachedLowerVal = JS::UndefinedValue();
+  mCachedUpperVal = JS::UndefinedValue();
   mHaveCachedLowerVal = false;
   mHaveCachedUpperVal = false;
   mRooted = false;
   mozilla::DropJSObjects(this);
 }
 
 IDBKeyRange::~IDBKeyRange()
 {
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -31,17 +31,17 @@ class KeyRange;
 
 class IDBKeyRange MOZ_FINAL : public nsISupports
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBKeyRange)
 
   static nsresult FromJSVal(JSContext* aCx,
-                            const jsval& aVal,
+                            JS::Handle<JS::Value> aVal,
                             IDBKeyRange** aKeyRange);
 
   template <class T>
   static already_AddRefed<IDBKeyRange>
   FromSerializedKeyRange(const T& aKeyRange);
 
   const Key& Lower() const
   {
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -96,49 +96,44 @@ USING_INDEXEDDB_NAMESPACE
  [[]]          // 8
 */
 
 const int MaxArrayCollapse = 3;
 
 const int MaxRecursionDepth = 256;
 
 nsresult
-Key::EncodeJSValInternal(JSContext* aCx, const jsval aVal,
+Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal,
                          uint8_t aTypeOffset, uint16_t aRecursionDepth)
 {
   NS_ENSURE_TRUE(aRecursionDepth < MaxRecursionDepth, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
 
   static_assert(eMaxType * MaxArrayCollapse < 256,
                 "Unable to encode jsvals.");
 
-  if (JSVAL_IS_STRING(aVal)) {
+  if (aVal.isString()) {
     nsDependentJSString str;
     if (!str.init(aCx, aVal)) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
     EncodeString(str, aTypeOffset);
     return NS_OK;
   }
 
-  if (JSVAL_IS_INT(aVal)) {
-    EncodeNumber((double)JSVAL_TO_INT(aVal), eFloat + aTypeOffset);
-    return NS_OK;
-  }
-
-  if (JSVAL_IS_DOUBLE(aVal)) {
-    double d = JSVAL_TO_DOUBLE(aVal);
+  if (aVal.isNumber()) {
+    double d = aVal.toNumber();
     if (mozilla::IsNaN(d)) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
     EncodeNumber(d, eFloat + aTypeOffset);
     return NS_OK;
   }
 
-  if (!JSVAL_IS_PRIMITIVE(aVal)) {
-    JS::Rooted<JSObject*> obj(aCx, JSVAL_TO_OBJECT(aVal));
+  if (aVal.isObject()) {
+    JS::Rooted<JSObject*> obj(aCx, &aVal.toObject());
     if (JS_IsArrayObject(aCx, obj)) {
       aTypeOffset += eMaxType;
 
       if (aTypeOffset == eMaxType * MaxArrayCollapse) {
         mBuffer.Append(aTypeOffset);
         aTypeOffset = 0;
       }
       NS_ASSERTION((aTypeOffset % eMaxType) == 0 &&
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -156,21 +156,21 @@ public:
   void SetFromInteger(int64_t aInt)
   {
     mBuffer.Truncate();
     EncodeNumber(double(aInt), eFloat);
     TrimBuffer();
   }
 
   nsresult SetFromJSVal(JSContext* aCx,
-                        const JS::Value aVal)
+                        JS::Handle<JS::Value> aVal)
   {
     mBuffer.Truncate();
 
-    if (JSVAL_IS_NULL(aVal) || JSVAL_IS_VOID(aVal)) {
+    if (aVal.isNull() || aVal.isUndefined()) {
       Unset();
       return NS_OK;
     }
 
     nsresult rv = EncodeJSVal(aCx, aVal, 0);
     if (NS_FAILED(rv)) {
       Unset();
       return rv;
@@ -179,17 +179,17 @@ public:
 
     return NS_OK;
   }
 
   nsresult ToJSVal(JSContext* aCx,
                    JS::MutableHandle<JS::Value> aVal) const
   {
     if (IsUnset()) {
-      aVal.set(JSVAL_VOID);
+      aVal.setUndefined();
       return NS_OK;
     }
 
     const unsigned char* pos = BufferStart();
     nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(pos >= BufferEnd(),
@@ -206,17 +206,17 @@ public:
     if (NS_SUCCEEDED(rv)) {
       aVal = value;
     }
     return rv;
   }
 
   nsresult AppendItem(JSContext* aCx,
                       bool aFirstOfArray,
-                      const JS::Value aVal)
+                      JS::Handle<JS::Value> aVal)
   {
     nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0);
     if (NS_FAILED(rv)) {
       Unset();
       return rv;
     }
 
     return NS_OK;
@@ -300,17 +300,17 @@ private:
     while (!*end) {
       --end;
     }
 
     mBuffer.Truncate(end + 1 - mBuffer.BeginReading());
   }
 
   // Encoding functions. These append the encoded value to the end of mBuffer
-  inline nsresult EncodeJSVal(JSContext* aCx, const JS::Value aVal,
+  inline nsresult EncodeJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal,
                               uint8_t aTypeOffset)
   {
     return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0);
   }
   void EncodeString(const nsAString& aString, uint8_t aTypeOffset);
   void EncodeNumber(double aFloat, uint8_t aType);
 
   // Decoding functions. aPos points into mBuffer and is adjusted to point
@@ -326,17 +326,17 @@ private:
                            const unsigned char* aEnd,
                            nsString& aString);
   static double DecodeNumber(const unsigned char*& aPos,
                              const unsigned char* aEnd);
 
   nsCString mBuffer;
 
 private:
-  nsresult EncodeJSValInternal(JSContext* aCx, const JS::Value aVal,
+  nsresult EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal,
                                uint8_t aTypeOffset, uint16_t aRecursionDepth);
 
   static nsresult DecodeJSValInternal(const unsigned char*& aPos,
                                       const unsigned char* aEnd,
                                       JSContext* aCx, uint8_t aTypeOffset,
                                       JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth);
 };
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -208,16 +208,23 @@ parent:
 
     /**
      * Used to set the current text of the status tooltip.
      * Nowadays this is mainly used for link locations on hover.
      */
     SetStatus(uint32_t type, nsString status);
 
     /**
+     * Show/hide a tooltip when the mouse hovers over an element in the content
+     * document.
+     */
+    ShowTooltip(uint32_t x, uint32_t y, nsString tooltip);
+    HideTooltip();
+
+    /**
      * Initiates an asynchronous request for permission for the
      * provided principal.
      *
      * @param aType
      *   The type of permission to request.
      * @param aAccess
      *   Access type. "read" for example.
      * @param aPrincipal
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -730,16 +730,17 @@ NS_INTERFACE_MAP_BEGIN(TabChild)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
   NS_INTERFACE_MAP_ENTRY(nsITabChild)
   NS_INTERFACE_MAP_ENTRY(nsIDialogCreator)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsSupportsWeakReference)
+  NS_INTERFACE_MAP_ENTRY(nsITooltipListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(TabChild)
 NS_IMPL_RELEASE(TabChild)
 
 NS_IMETHODIMP
 TabChild::SetStatus(uint32_t aStatusType, const char16_t* aStatus)
 {
@@ -2360,17 +2361,17 @@ TabChild::GetDefaultScale(double* aScale
 
 void
 TabChild::NotifyPainted()
 {
     // Normally we only need to notify the content process once, but with BasicCompositor
     // we need to notify content every change so that it can compute an invalidation
     // region and send that to the widget.
     if (UseDirectCompositor() &&
-        (!mNotified || mTextureFactoryIdentifier.mParentBackend == LAYERS_BASIC)) {
+        (!mNotified || mTextureFactoryIdentifier.mParentBackend == LayersBackend::LAYERS_BASIC)) {
         mRemoteFrame->SendNotifyCompositorTransaction();
         mNotified = true;
     }
 }
 
 bool
 TabChild::IsAsyncPanZoomEnabled()
 {
@@ -2507,16 +2508,30 @@ TabChild::GetFrom(nsIPresShell* aPresShe
   nsIDocument* doc = aPresShell->GetDocument();
   if (!doc) {
       return nullptr;
   }
   nsCOMPtr<nsIDocShell> docShell(doc->GetDocShell());
   return GetFrom(docShell);
 }
 
+NS_IMETHODIMP
+TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText)
+{