Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 28 Aug 2014 16:15:55 +0200
changeset 202170 61c5ca15dd597d9738422554f08595e662f67acd
parent 202169 dbf535e324b6c5d8c9118389cf2fa84039839b9c (current diff)
parent 202127 47c9418fbc28608e6b9b2c0b1f1664b183bd5b30 (diff)
child 202171 5b1e40f666cc46795043a152ca71b0f0002e81e6
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone34.0a1
Merge mozilla-central to b2g-inbound
content/media/test/can_play_type_mpeg.js
security/nss/lib/certhigh/certvfypkixprint.c
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1435,18 +1435,25 @@ HyperTextAccessible::SelectionBoundsAt(i
     nsINode* tempNode = startNode;
     startNode = endNode;
     endNode = tempNode;
     int32_t tempOffset = startOffset;
     startOffset = endOffset;
     endOffset = tempOffset;
   }
 
-  *aStartOffset = DOMPointToOffset(startNode, startOffset);
-  *aEndOffset = DOMPointToOffset(endNode, endOffset, true);
+  if (!nsContentUtils::ContentIsDescendantOf(startNode, mContent))
+    *aStartOffset = 0;
+  else
+    *aStartOffset = DOMPointToOffset(startNode, startOffset);
+
+  if (!nsContentUtils::ContentIsDescendantOf(endNode, mContent))
+    *aEndOffset = CharacterCount();
+  else
+    *aEndOffset = DOMPointToOffset(endNode, endOffset, true);
   return true;
 }
 
 bool
 HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
                                           int32_t aStartOffset,
                                           int32_t aEndOffset)
 {
--- a/accessible/jsat/ContentControl.jsm
+++ b/accessible/jsat/ContentControl.jsm
@@ -115,32 +115,34 @@ this.ContentControl.prototype = {
         if (action === 'moveNext') {
           childAction = 'moveFirst';
         } else if (action === 'movePrevious') {
           childAction = 'moveLast';
         }
 
         // Attempt to forward move to a potential child cursor in our
         // new position.
-        this.sendToChild(vc, aMessage, { action: childAction});
+        this.sendToChild(vc, aMessage, { action: childAction }, true);
       }
     } else if (!this._childMessageSenders.has(aMessage.target)) {
       // We failed to move, and the message is not from a child, so forward
       // to parent.
       this.sendToParent(aMessage);
     }
   },
 
   handleEvent: function cc_handleEvent(aEvent) {
     if (aEvent.type === 'mousemove') {
       this.handleMoveToPoint(
         { json: { x: aEvent.screenX, y: aEvent.screenY, rule: 'Simple' } });
     }
     if (!Utils.getMessageManager(aEvent.target)) {
       aEvent.preventDefault();
+    } else {
+      aEvent.target.focus();
     }
   },
 
   handleMoveToPoint: function cc_handleMoveToPoint(aMessage) {
     let [x, y] = [aMessage.json.x, aMessage.json.y];
     let rule = TraversalRules[aMessage.json.rule];
 
     let dpr = this.window.devicePixelRatio;
@@ -148,16 +150,17 @@ this.ContentControl.prototype = {
   },
 
   handleClearCursor: function cc_handleClearCursor(aMessage) {
     let forwarded = this.sendToChild(this.vc, aMessage);
     this.vc.position = null;
     if (!forwarded) {
       this._contentScope.get().sendAsyncMessage('AccessFu:CursorCleared');
     }
+    this.document.activeElement.blur();
   },
 
   handleAutoMove: function cc_handleAutoMove(aMessage) {
     this.autoMove(null, aMessage.json);
   },
 
   handleActivate: function cc_handleActivate(aMessage) {
     let activateAccessible = (aAccessible) => {
@@ -243,17 +246,17 @@ this.ContentControl.prototype = {
           return activatable;
         }
       }
 
       return null;
     };
 
     let vc = this.vc;
-    if (!this.sendToChild(vc, aMessage)) {
+    if (!this.sendToChild(vc, aMessage, null, true)) {
       let position = vc.position;
       activateAccessible(getActivatableDescendant(position) || position);
     }
   },
 
   handleMoveByGranularity: function cc_handleMoveByGranularity(aMessage) {
     // XXX: Add sendToChild. Right now this is only used in Android, so no need.
     let direction = aMessage.json.direction;
@@ -342,22 +345,28 @@ this.ContentControl.prototype = {
       }
 
       return mm;
     }
 
     return null;
   },
 
-  sendToChild: function cc_sendToChild(aVirtualCursor, aMessage, aReplacer) {
-    let mm = this.getChildCursor(aVirtualCursor.position);
+  sendToChild: function cc_sendToChild(aVirtualCursor, aMessage, aReplacer,
+                                       aFocus) {
+    let position = aVirtualCursor.position;
+    let mm = this.getChildCursor(position);
     if (!mm) {
       return false;
     }
 
+    if (aFocus) {
+      position.takeFocus();
+    }
+
     // XXX: This is a silly way to make a deep copy
     let newJSON = JSON.parse(JSON.stringify(aMessage.json));
     newJSON.origin = 'parent';
     for (let attr in aReplacer) {
       newJSON[attr] = aReplacer[attr];
     }
 
     mm.sendAsyncMessage(aMessage.name, newJSON);
@@ -427,17 +436,17 @@ this.ContentControl.prototype = {
       let sentToChild = this.sendToChild(vc, {
         name: 'AccessFu:AutoMove',
         json: {
           moveMethod: aOptions.moveMethod,
           moveToFocused: aOptions.moveToFocused,
           noOpIfOnScreen: true,
           forcePresent: true
         }
-      });
+      }, null, true);
 
       if (!moved && !sentToChild) {
         forcePresentFunc();
       }
     };
 
     if (aOptions.delay) {
       this._autoMove = this.window.setTimeout(moveFunc, aOptions.delay);
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -151,17 +151,18 @@ this.EventManager.prototype = {
         let position = pivot.position;
         if (position && position.role == Roles.INTERNAL_FRAME)
           break;
         let event = aEvent.
           QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
         let reason = event.reason;
         let oldAccessible = event.oldAccessible;
 
-        if (this.editState.editing) {
+        if (this.editState.editing &&
+            !Utils.getState(position).contains(States.FOCUSED)) {
           aEvent.accessibleDocument.takeFocus();
         }
         this.present(
           Presentation.pivotChanged(position, oldAccessible, reason,
                                     pivot.startOffset, pivot.endOffset,
                                     aEvent.isFromUserInput));
 
         break;
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -298,16 +298,22 @@ AccessFuContentTest.prototype = {
       }
 
       if (expected.android) {
         var checkFunc = SimpleTest[expected.android_checkFunc] || ok;
         checkFunc.apply(SimpleTest,
           this.lazyCompare(android, expected.android));
       }
 
+      if (expected.focused) {
+        var doc = currentTabDocument();
+        is(doc.activeElement, doc.querySelector(expected.focused),
+          'Correct element is focused');
+      }
+
       this.pump();
     }
 
   },
 
   lazyCompare: function lazyCompare(aReceived, aExpected) {
     var matches = true;
     var delta = [];
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -25,20 +25,22 @@
       var doc = currentTabDocument();
       var iframe = doc.createElement('iframe');
       iframe.mozbrowser = true;
       iframe.addEventListener('mozbrowserloadend', function () {
       var contentTest = new AccessFuContentTest(
         [
           // Simple traversal forward
           [ContentMessages.simpleMoveNext, {
-            speak: ['Phone status bar', 'Traversal Rule test document']
+            speak: ['Phone status bar', 'Traversal Rule test document'],
+            focused: 'body'
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: ['wow', {'string': 'headingLevel', 'args': [1]} ,'such app']
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]} ,'such app'],
+            focused: 'iframe'
           }],
           [ContentMessages.simpleMoveNext, {
             speak: ['many option', {'string': 'stateNotChecked'},
               {'string': 'checkbutton'}, {'string': 'listStart'},
               {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}]
           }],
           // check checkbox
           [ContentMessages.activateCurrent(), {
--- a/accessible/tests/mochitest/textselection/test_general.html
+++ b/accessible/tests/mochitest/textselection/test_general.html
@@ -106,17 +106,17 @@
       this.getID = function removeSelection_getID()
       {
         return "nsIAccessibleText::removeSelection test for " + aID;
       }
     }
 
     function changeDOMSelection(aID, aNodeID1, aNodeOffset1,
                                 aNodeID2, aNodeOffset2,
-                                aStartOffset, aEndOffset)
+                                aTests)
     {
       this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
 
       this.eventSeq = [
         new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
       ];
 
       this.invoke = function changeDOMSelection_invoke()
@@ -125,25 +125,28 @@
         var range = document.createRange();
         range.setStart(getNode(aNodeID1), aNodeOffset1);
         range.setEnd(getNode(aNodeID2), aNodeOffset2);
         sel.addRange(range);
       }
 
       this.finalCheck = function changeDOMSelection_finalCheck()
       {
-        is(this.hyperText.selectionCount, 1,
-           "setSelectionBounds: Wrong selection count for " + aID);
-        var startOffset = {}, endOffset = {};
-        this.hyperText.getSelectionBounds(0, startOffset, endOffset);
+        for (var i = 0; i < aTests.length; i++) {
+          var text = getAccessible(aTests[i][0], nsIAccessibleText);
+          is(text.selectionCount, 1,
+             "setSelectionBounds: Wrong selection count for " + aID);
+          var startOffset = {}, endOffset = {};
+          text.getSelectionBounds(0, startOffset, endOffset);
 
-        is(startOffset.value, aStartOffset,
-           "setSelectionBounds: Wrong start offset for " + aID);
-        is(endOffset.value, aEndOffset,
-           "setSelectionBounds: Wrong end offset for " + aID);
+          is(startOffset.value, aTests[i][1],
+             "setSelectionBounds: Wrong start offset for " + aID);
+          is(endOffset.value, aTests[i][2],
+             "setSelectionBounds: Wrong end offset for " + aID);
+        }
       }
 
       this.getID = function changeDOMSelection_getID()
       {
         return "DOM selection change for " + aID;
       }
     }
 
@@ -174,17 +177,20 @@
       gQueue.push(new removeSelection("paragraph"));
 
       gQueue.push(new synthFocus("textbox", onfocusEventSeq("textbox")));
       gQueue.push(new changeSelection("textbox", 1, 3));
 
       gQueue.push(new synthFocus("textarea", onfocusEventSeq("textarea")));
       gQueue.push(new changeSelection("textarea", 1, 3));
 
-      gQueue.push(new changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0, 2, 2));
+      gQueue.push(new changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0,
+                                         [["c1", 2, 2]]));
+      gQueue.push(new changeDOMSelection("c2", "c2", 0, "c2_div2", 1,
+                                         [["c2", 0, 3], ["c2_div2", 0, 2]]));
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
@@ -204,11 +210,12 @@
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <p id="paragraph">hello</p>
   <input id="textbox" value="hello"/>
   <textarea id="textarea">hello</textarea>
   <div id="c1">hi<span id="c1_span1"></span><span id="c1_span2"></span>hi</div>
+  <div id="c2">hi<div id="c2_div2">hi</div></div>
 
 </body>
 </html>
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -20,16 +20,17 @@
 #include <string.h>
 
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 #ifdef XP_WIN
 // we want a wmain entry point
+#define XRE_DONT_SUPPORT_XPSP2 // See https://bugzil.la/1023941#c32
 #include "nsWindowsWMain.cpp"
 #define snprintf _snprintf
 #define strcasecmp _stricmp
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "GonkDisplay.h"
 #endif
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -25,16 +25,21 @@ GENERATED_INCLUDES += [
 ]
 
 LOCAL_INCLUDES += [
     '/toolkit/xre',
     '/xpcom/base',
     '/xpcom/build',
 ]
 
+DELAYLOAD_DLLS += [
+    'mozglue.dll',
+]
+USE_STATIC_LIBS = True
+
 if CONFIG['_MSC_VER']:
     # Always enter a Windows program through wmain, whether or not we're
     # a console application.
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     RCINCLUDE = 'splash.rc'
     DEFINES['MOZ_PHOENIX'] = True
@@ -45,19 +50,25 @@ if CONFIG['OS_ARCH'] == 'WINNT':
 #
 # The default heap size is 1MB on Win32.
 # The heap will grow if need be.
 #
 # Set it to 256k.  See bug 127069.
 if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
     LDFLAGS += ['/HEAP:0x40000']
 
-USE_LIBS += [
-    'xpcomglue',
-]
+if CONFIG['OS_ARCH'] == 'WINNT':
+    USE_LIBS += [
+        'mozglue',
+        'xpcomglue_staticruntime',
+    ]
+else:
+    USE_LIBS += [
+        'xpcomglue',
+    ]
 
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['MOZ_LINKER']:
     OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
 
 if CONFIG['HAVE_CLOCK_MONOTONIC']:
     OS_LIBS += CONFIG['REALTIME_LIBS']
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -244,25 +244,47 @@ var tests = {
       Social.installProvider(doc, manifest2, function(addonManifest) {
         SocialService.enableProvider(addonManifest.origin, function(provider) {
           is(provider.manifest.version, 1, "manifest version is 1");
 
           // watch for the provider-update and test the new version
           SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
             if (topic != "provider-update")
               return;
-            is(origin, addonManifest.origin, "provider updated")
+            // The worker will have reloaded and the current provider instance
+            // disabled, removed from the provider list. We have a reference
+            // here, check it is is disabled.
+            is(provider.enabled, false, "old provider instance is disabled")
+            is(origin, addonManifest.origin, "provider manifest updated")
             SocialService.unregisterProviderListener(providerListener);
-            Services.prefs.clearUserPref("social.whitelist");
-            let provider = Social._getProviderFromOrigin(origin);
-            is(provider.manifest.version, 2, "manifest version is 2");
-            Social.uninstallProvider(origin, function() {
-              gBrowser.removeTab(tab);
-              next();
-            });
+
+            // Get the new provider instance, fetch the manifest via workerapi
+            // and validate that data as well.
+            let p = Social._getProviderFromOrigin(origin);
+            is(p.manifest.version, 2, "manifest version is 2");
+            let port = p.getWorkerPort();
+            ok(port, "got a new port");
+            port.onmessage = function (e) {
+              let topic = e.data.topic;
+              switch (topic) {
+                case "social.manifest":
+                  let manifest = e.data.data;
+                  is(manifest.version, 2, "manifest version is 2");
+                  port.close();
+                  Social.uninstallProvider(origin, function() {
+                    Services.prefs.clearUserPref("social.whitelist");
+                    gBrowser.removeTab(tab);
+                    next();
+                  });
+                  break;
+              }
+            }
+            port.postMessage({topic: "test-init"});
+            port.postMessage({topic: "manifest-get"});
+
           });
 
           let port = provider.getWorkerPort();
           port.postMessage({topic: "worker.update", data: true});
 
         });
       });
     });
--- a/browser/base/content/test/social/social_worker.js
+++ b/browser/base/content/test/social/social_worker.js
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-let testPort, sidebarPort, apiPort;
+let testPort, sidebarPort, apiPort, updatingManifest=false;
 
 onconnect = function(e) {
   let port = e.ports[0];
   port.onmessage = function onMessage(event) {
     let topic = event.data.topic;
     switch (topic) {
       case "test-init":
         testPort = port;
@@ -111,18 +111,27 @@ onconnect = function(e) {
         break;
       case "test-isVisible-response":
         testPort.postMessage({topic: "got-isVisible-response", result: event.data.result});
         break;
       case "share-data-message":
         if (testPort)
           testPort.postMessage({topic:"got-share-data-message", result: event.data.result});
         break;
+      case "manifest-get":
+        apiPort.postMessage({topic: 'social.manifest-get'});
+        break;
       case "worker.update":
+        updatingManifest = true;
         apiPort.postMessage({topic: 'social.manifest-get'});
         break;
       case "social.manifest":
-        event.data.data.version = 2;
-        apiPort.postMessage({topic: 'social.manifest-set', data: event.data.data});
+        if (updatingManifest) {
+          updatingManifest = false;
+          event.data.data.version = 2;
+          apiPort.postMessage({topic: 'social.manifest-set', data: event.data.data});
+        } else if (testPort) {
+          testPort.postMessage({topic:"social.manifest", data: event.data.data});
+        }
         break;
     }
   }
 }
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1663,16 +1663,22 @@
             <xul:hbox anonid="trackingContentProtectionDisabled" hidden="true"
                       class="popup-notification-footer" xbl:inherits="popupid">
               <xul:description class="popup-notification-item-message popup-notification-item-message-critical" xbl:inherits="popupid">
                 &trackingContentBlocked.disabled.message;
                 </xul:description>
             </xul:hbox>
           </xul:vbox>
         </xul:vbox>
+        <xul:vbox pack="start">
+          <xul:toolbarbutton anonid="closebutton"
+                             class="messageCloseButton popup-notification-closebutton tabbable close-icon"
+                             xbl:inherits="oncommand=closebuttoncommand"
+                             tooltiptext="&closeNotification.tooltip;"/>
+        </xul:vbox>
       </xul:hbox>
     </content>
     <resources>
       <stylesheet src="chrome://global/skin/notification.css"/>
     </resources>
     <implementation>
       <field name="_brandShortName">
         document.getElementById("bundle_brand").getString("brandShortName")
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -1166,16 +1166,17 @@ SourceScripts.prototype = {
         }
       });
     }
 
     // If there are any stored breakpoints for this source, display them again,
     // both in the editor and the breakpoints pane.
     DebuggerController.Breakpoints.updatePaneBreakpoints();
     DebuggerController.Breakpoints.updateEditorBreakpoints();
+    DebuggerController.HitCounts.updateEditorHitCounts();
 
     // Make sure the events listeners are up to date.
     if (DebuggerView.instrumentsPaneTab == "events-tab") {
       DebuggerController.Breakpoints.DOM.scheduleEventListenersFetch();
     }
 
     // Signal that a new source has been added.
     window.emit(EVENTS.NEW_SOURCE);
@@ -1218,16 +1219,17 @@ SourceScripts.prototype = {
     else if (!DebuggerView.Sources.selectedValue) {
       DebuggerView.Sources.selectedIndex = 0;
     }
 
     // If there are any stored breakpoints for the sources, display them again,
     // both in the editor and the breakpoints pane.
     DebuggerController.Breakpoints.updatePaneBreakpoints();
     DebuggerController.Breakpoints.updateEditorBreakpoints();
+    DebuggerController.HitCounts.updateEditorHitCounts();
 
     // Signal that sources have been added.
     window.emit(EVENTS.SOURCES_ADDED);
   },
 
   /**
    * Handler for the debugger client's 'blackboxchange' notification.
    */
@@ -1483,16 +1485,17 @@ Tracer.prototype = {
     }
 
     DebuggerView.Tracer.selectTab();
 
     let id = this._trace = "dbg.trace" + Math.random();
     let fields = [
       "name",
       "location",
+      "hitCount",
       "parameterNames",
       "depth",
       "arguments",
       "return",
       "throw",
       "yield"
     ];
 
@@ -1516,40 +1519,50 @@ Tracer.prototype = {
     }
     this.traceClient.stopTrace(this._trace, aResponse => {
       const { error } = aResponse;
       if (error) {
         DevToolsUtils.reportException("Tracer.prototype.stopTracing", error);
       }
 
       this._trace = null;
+      DebuggerController.HitCounts.clear();
       aCallback(aResponse);
     });
   },
 
   onTraces: function (aEvent, { traces }) {
     const tracesLength = traces.length;
     let tracesToShow;
 
+    // Update hit counts.
+    for (let t of traces) {
+      if (t.type == "enteredFrame") {
+        DebuggerController.HitCounts.set(t.location, t.hitCount);
+      }
+    }
+    DebuggerController.HitCounts.updateEditorHitCounts();
+
+    // Limit number of traces to be shown in the log.
     if (tracesLength > TracerView.MAX_TRACES) {
       tracesToShow = traces.slice(tracesLength - TracerView.MAX_TRACES, tracesLength);
       this._stack.splice(0, this._stack.length);
       DebuggerView.Tracer.empty();
     } else {
       tracesToShow = traces;
     }
 
+    // Show traces in the log.
     for (let t of tracesToShow) {
       if (t.type == "enteredFrame") {
         this._onCall(t);
       } else {
         this._onReturn(t);
       }
     }
-
     DebuggerView.Tracer.commit();
   },
 
   /**
    * Callback for handling a new call frame.
    */
   _onCall: function({ name, location, blackBoxed, parameterNames, depth, arguments: args }) {
     const item = {
@@ -2220,16 +2233,94 @@ Breakpoints.prototype = {
 Object.defineProperty(Breakpoints.prototype, "_addedOrDisabled", {
   get: function* () {
     yield* this._added.values();
     yield* this._disabled.values();
   }
 });
 
 /**
+ * Handles Tracer's hit counts.
+ */
+function HitCounts() {
+  /**
+   * Storage of hit counts for every location
+   * hitCount = _locations[url][line][column]
+   */
+  this._hitCounts = Object.create(null);
+}
+
+HitCounts.prototype = {
+  set: function({url, line, column}, aHitCount) {
+    if (!this._hitCounts[url]) {
+      this._hitCounts[url] = Object.create(null);
+    }
+    if (!this._hitCounts[url][line]) {
+      this._hitCounts[url][line] = Object.create(null);
+    }
+    this._hitCounts[url][line][column] = aHitCount;
+  },
+
+  /**
+   * Update all the hit counts in the editor view. This is invoked when the
+   * selected script is changed, or when new sources are received via the
+   * _onNewSource and _onSourcesAdded event listeners.
+   */
+  updateEditorHitCounts: function() {
+    // First, remove all hit counters.
+    DebuggerView.editor.removeAllMarkers("hit-counts");
+
+    // Then, add new hit counts, just for the current source.
+    for (let url in this._hitCounts) {
+      for (let line in this._hitCounts[url]) {
+        for (let column in this._hitCounts[url][line]) {
+          this._updateEditorHitCount({url, line, column});
+        }
+      }
+    }
+  },
+
+  /**
+   * Update a hit counter on a certain line.
+   */
+  _updateEditorHitCount: function({url, line, column}) {
+    // Editor must be initialized.
+    if (!DebuggerView.editor) {
+      return;
+    }
+
+    // No need to do anything if the counter's source is not being shown in the
+    // editor.
+    if (DebuggerView.Sources.selectedValue != url) {
+      return;
+    }
+
+    // There might be more counters on the same line. We need to combine them
+    // into one.
+    let content = Object.keys(this._hitCounts[url][line])
+                    .sort() // Sort by key (column).
+                    .map(a => this._hitCounts[url][line][a]) // Extract values.
+                    .map(a => a + "\u00D7") // Format hit count (e.g. 146×).
+                    .join("|");
+
+    // CodeMirror's lines are indexed from 0, while traces start from 1
+    DebuggerView.editor.addContentMarker(line - 1, "hit-counts", "hit-count",
+                                         content);
+  },
+
+  /**
+   * Remove all hit couters and clear the storage
+   */
+  clear: function() {
+    DebuggerView.editor.removeAllMarkers("hit-counts");
+    this._hitCounts = Object.create(null);
+  }
+}
+
+/**
  * Localization convenience methods.
  */
 let L10N = new ViewHelpers.L10N(DBG_STRINGS_URI);
 
 /**
  * Shortcuts for accessing various debugger preferences.
  */
 let Prefs = new ViewHelpers.Prefs("devtools", {
@@ -2260,16 +2351,17 @@ EventEmitter.decorate(this);
 DebuggerController.initialize();
 DebuggerController.Parser = new Parser();
 DebuggerController.ThreadState = new ThreadState();
 DebuggerController.StackFrames = new StackFrames();
 DebuggerController.SourceScripts = new SourceScripts();
 DebuggerController.Breakpoints = new Breakpoints();
 DebuggerController.Breakpoints.DOM = new EventListeners();
 DebuggerController.Tracer = new Tracer();
+DebuggerController.HitCounts = new HitCounts();
 
 /**
  * Export some properties to the global scope for easier access.
  */
 Object.defineProperties(window, {
   "gTarget": {
     get: function() DebuggerController._target
   },
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -216,22 +216,27 @@ let DebuggerView = {
     extraKeys["Esc"] = false;
 
     function bindKey(func, key, modifiers = {}) {
       let key = document.getElementById(key).getAttribute("key");
       let shortcut = Editor.accel(key, modifiers);
       extraKeys[shortcut] = () => DebuggerView.Filtering[func]();
     }
 
+    let gutters = ["breakpoints"];
+    if (Services.prefs.getBoolPref("devtools.debugger.tracer")) {
+      gutters.unshift("hit-counts");
+    }
+
     this.editor = new Editor({
       mode: Editor.modes.text,
       readOnly: true,
       lineNumbers: true,
       showAnnotationRuler: true,
-      gutters: [ "breakpoints" ],
+      gutters: gutters,
       extraKeys: extraKeys,
       contextMenu: "sourceEditorContextMenu"
     });
 
     this.editor.appendTo(document.getElementById("editor")).then(() => {
       this.editor.extend(DebuggerEditor);
       this._loadingText = L10N.getStr("loadingText");
       this._onEditorLoad(aCallback);
@@ -405,16 +410,17 @@ let DebuggerView = {
       }
 
       this._setEditorText(aText);
       this._setEditorMode(aSource.url, aContentType, aText);
 
       // Synchronize any other components with the currently displayed source.
       DebuggerView.Sources.selectedValue = aSource.url;
       DebuggerController.Breakpoints.updateEditorBreakpoints();
+      DebuggerController.HitCounts.updateEditorHitCounts();
 
       histogram.add(Date.now() - startTime);
 
       // Resolve and notify that a source file was shown.
       window.emit(EVENTS.SOURCE_SHOWN, aSource);
       deferred.resolve([aSource, aText, aContentType]);
     },
     ([, aError]) => {
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -18,16 +18,17 @@ support-files =
   code_function-search-01.js
   code_function-search-02.js
   code_function-search-03.js
   code_location-changes.js
   code_math.js
   code_math.map
   code_math.min.js
   code_math_bogus_map.js
+  code_same-line-functions.js
   code_script-switching-01.js
   code_script-switching-02.js
   code_test-editor-mode
   code_tracing-01.js
   code_ugly.js
   code_ugly-2.js
   code_ugly-3.js
   code_ugly-4.js
@@ -69,16 +70,17 @@ support-files =
   doc_no-page-sources.html
   doc_pause-exceptions.html
   doc_pretty-print.html
   doc_pretty-print-2.html
   doc_pretty-print-3.html
   doc_pretty-print-on-paused.html
   doc_random-javascript.html
   doc_recursion-stack.html
+  doc_same-line-functions.html
   doc_scope-variable.html
   doc_scope-variable-2.html
   doc_scope-variable-3.html
   doc_scope-variable-4.html
   doc_script-switching-01.html
   doc_script-switching-02.html
   doc_split-console-paused-reload.html
   doc_step-out.html
@@ -156,16 +158,18 @@ skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_editor-mode.js]
 [browser_dbg_event-listeners-01.js]
 [browser_dbg_event-listeners-02.js]
 [browser_dbg_event-listeners-03.js]
 [browser_dbg_file-reload.js]
 [browser_dbg_function-display-name.js]
 [browser_dbg_global-method-override.js]
 [browser_dbg_globalactor.js]
+[browser_dbg_hit-counts-01.js]
+[browser_dbg_hit-counts-02.js]
 [browser_dbg_host-layout.js]
 [browser_dbg_iframes.js]
 [browser_dbg_instruments-pane-collapse.js]
 [browser_dbg_interrupts.js]
 [browser_dbg_listaddons.js]
 [browser_dbg_listtabs-01.js]
 [browser_dbg_listtabs-02.js]
 [browser_dbg_listtabs-03.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_hit-counts-01.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Evaluating two functions on the same line and checking for correct hit count
+ * for both of them in CodeMirror's gutter.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
+const CODE_URL = "code_same-line-functions.js";
+
+let gTab, gDebuggee, gPanel, gDebugger;
+let gEditor;
+
+function test() {
+  Task.async(function* () {
+    yield pushPrefs(["devtools.debugger.tracer", true]);
+
+    initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+      gTab = aTab;
+      gDebuggee = aDebuggee;
+      gPanel = aPanel;
+      gDebugger = gPanel.panelWin;
+      gEditor = gDebugger.DebuggerView.editor;
+
+      Task.async(function* () {
+        yield waitForSourceShown(gPanel, CODE_URL);
+        yield startTracing(gPanel);
+
+        clickButton();
+
+        yield waitForClientEvents(aPanel, "traces");
+
+        testHitCounts();
+
+        yield stopTracing(gPanel);
+        yield popPrefs();
+        yield closeDebuggerAndFinish(gPanel);
+      })();
+    });
+  })().catch(e => {
+    ok(false, "Got an error: " + e.message + "\n" + e.stack);
+  });
+}
+
+function clickButton() {
+  EventUtils.sendMouseEvent({ type: "click" },
+                            gDebuggee.document.querySelector("button"),
+                            gDebuggee);
+}
+
+function testHitCounts() {
+  let marker = gEditor.getMarker(0, 'hit-counts');
+
+  is(marker.innerHTML, "1\u00D7|1\u00D7",
+    "Both functions should be hit only once.");
+}
+
+registerCleanupFunction(function() {
+  gTab = null;
+  gDebuggee = null;
+  gPanel = null;
+  gDebugger = null;
+  gEditor = null;
+});
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_hit-counts-02.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * When tracing is stopped all hit counters should be cleared.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
+const CODE_URL = "code_same-line-functions.js";
+
+let gTab, gDebuggee, gPanel, gDebugger;
+let gEditor;
+
+function test() {
+  Task.async(function* () {
+    yield pushPrefs(["devtools.debugger.tracer", true]);
+
+    initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+      gTab = aTab;
+      gDebuggee = aDebuggee;
+      gPanel = aPanel;
+      gDebugger = gPanel.panelWin;
+      gEditor = gDebugger.DebuggerView.editor;
+
+      Task.async(function* () {
+        yield waitForSourceShown(gPanel, CODE_URL);
+        yield startTracing(gPanel);
+
+        clickButton();
+
+        yield waitForClientEvents(aPanel, "traces");
+
+        testHitCountsBeforeStopping();
+
+        yield stopTracing(gPanel);
+
+        testHitCountsAfterStopping();
+
+        yield popPrefs();
+        yield closeDebuggerAndFinish(gPanel);
+      })();
+    });
+  })().catch(e => {
+    ok(false, "Got an error: " + e.message + "\n" + e.stack);
+  });
+}
+
+function clickButton() {
+  EventUtils.sendMouseEvent({ type: "click" },
+                            gDebuggee.document.querySelector("button"),
+                            gDebuggee);
+}
+
+function testHitCountsBeforeStopping() {
+  let marker = gEditor.getMarker(0, 'hit-counts');
+  ok(marker, "A counter should exists.");
+}
+
+function testHitCountsAfterStopping() {
+  let marker = gEditor.getMarker(0, 'hit-counts');
+  is(marker, undefined, "A counter should be cleared.");
+}
+
+registerCleanupFunction(function() {
+  gTab = null;
+  gDebuggee = null;
+  gPanel = null;
+  gDebugger = null;
+  gEditor = null;
+});
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/code_same-line-functions.js
@@ -0,0 +1,1 @@
+function first() { var a = "first"; second(); function second() { var a = "second"; } }
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_same-line-functions.html
@@ -0,0 +1,15 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Debugger Tracer test page</title>
+  </head>
+
+  <body>
+    <script src="code_same-line-functions.js"></script>
+    <button onclick="first()">Click me!</button>
+  </body>
+</html>
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -921,8 +921,19 @@ function doResume(aPanel) {
   return rdpInvoke(threadClient, threadClient.resume);
 }
 
 function doInterrupt(aPanel) {
   const threadClient = aPanel.panelWin.gThreadClient;
   return rdpInvoke(threadClient, threadClient.interrupt);
 }
 
+function pushPrefs(...aPrefs) {
+  let deferred = promise.defer();
+  SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
+  return deferred.promise;
+}
+
+function popPrefs() {
+  let deferred = promise.defer();
+  SpecialPowers.popPrefEnv(deferred.resolve);
+  return deferred.promise;
+}
\ No newline at end of file
--- a/browser/devtools/shared/test/browser_tableWidget_basic.js
+++ b/browser/devtools/shared/test/browser_tableWidget_basic.js
@@ -31,16 +31,17 @@ function test() {
           col2: "Column 2",
           col3: "Column 3",
           col4: "Column 4"
         },
         uniqueId: "col1",
         emptyText: "This is dummy empty text",
         highlightUpdated: true,
         removableColumns: true,
+        firstColumn: "col4"
       });
       startTests();
     });
   });
 }
 
 function endTests() {
   table.destroy();
@@ -121,23 +122,53 @@ function populateTable() {
 }
 
 /**
  * Test if the nodes are inserted correctly in the table.
  */
 function testTreeItemInsertedCorrectly() {
   is(table.tbody.children.length, 4*2 /* double because splitters */,
      "4 columns exist");
-  for (let i = 0; i < 4; i++) {
-    is(table.tbody.children[i*2].firstChild.children.length, 9 + 1 /* header */,
+
+  // Test firstColumn option and check if the nodes are inserted correctly
+  is(table.tbody.children[0].firstChild.children.length, 9 + 1 /* header */,
+     "Correct rows in column 4");
+  is(table.tbody.children[0].firstChild.firstChild.value, "Column 4",
+     "Correct column header value");
+
+  for (let i = 1; i < 4; i++) {
+    is(table.tbody.children[i * 2].firstChild.children.length, 9 + 1 /* header */,
        "Correct rows in column " + i);
-    is(table.tbody.children[i*2].firstChild.firstChild.value, "Column " + (i + 1),
+    is(table.tbody.children[i * 2].firstChild.firstChild.value, "Column " + i,
        "Correct column header value");
   }
   for (let i = 1; i < 10; i++) {
+    is(table.tbody.children[2].firstChild.children[i].value, "id" + i,
+     "Correct value in row " + i);
+  }
+
+  // Remove firstColumn option and reset the table
+  table.clear();
+  table.firstColumn = "";
+  table.setColumns({
+    col1: "Column 1",
+    col2: "Column 2",
+    col3: "Column 3",
+    col4: "Column 4"
+  });
+  populateTable();
+
+  // Check if the nodes are inserted correctly without firstColumn option
+  for (let i = 0; i < 4; i++) {
+  is(table.tbody.children[i * 2].firstChild.children.length, 9 + 1 /* header */,
+     "Correct rows in column " + i);
+  is(table.tbody.children[i * 2].firstChild.firstChild.value, "Column " + (i + 1),
+     "Correct column header value");
+  }
+  for (let i = 1; i < 10; i++) {
     is(table.tbody.firstChild.firstChild.children[i].value, "id" + i,
      "Correct value in row " + i);
   }
 }
 
 /**
  * Tests if the API exposed by TableWidget works properly
  */
--- a/browser/devtools/shared/widgets/TableWidget.js
+++ b/browser/devtools/shared/widgets/TableWidget.js
@@ -35,28 +35,30 @@ const MAX_VISIBLE_STRING_SIZE = 100;
  *        - initialColumns: map of key vs display name for initial columns of
  *                          the table. See @setupColumns for more info.
  *        - uniqueId: the column which will be the unique identifier of each
  *                    entry in the table. Default: name.
  *        - emptyText: text to display when no entries in the table to display.
  *        - highlightUpdated: true to highlight the changed/added row.
  *        - removableColumns: Whether columns are removeable. If set to true,
  *                            the context menu in the headers will not appear.
+ *        - firstColumn: key of the first column that should appear.
  */
 function TableWidget(node, options={}) {
   EventEmitter.decorate(this);
 
   this.document = node.ownerDocument;
   this.window = this.document.defaultView;
   this._parent = node;
 
-  let {initialColumns, emptyText, uniqueId, highlightUpdated, removableColumns} =
-      options;
+  let {initialColumns, emptyText, uniqueId, highlightUpdated, removableColumns,
+       firstColumn} = options;
   this.emptyText = emptyText || "";
   this.uniqueId = uniqueId || "name";
+  this.firstColumn = firstColumn || "";
   this.highlightUpdated = highlightUpdated || false;
   this.removableColumns = removableColumns || false;
 
   this.tbody = this.document.createElementNS(XUL_NS, "hbox");
   this.tbody.className = "table-widget-body theme-body";
   this.tbody.setAttribute("flex", "1");
   this.tbody.setAttribute("tabindex", "0");
   this._parent.appendChild(this.tbody);
@@ -232,20 +234,34 @@ TableWidget.prototype = {
     }
 
     this.columns.clear();
 
     if (!(sortOn in columns)) {
       sortOn = null;
     }
 
+    if (!(this.firstColumn in columns)) {
+      this.firstColumn = null;
+    }
+
+    if (this.firstColumn) {
+      this.columns.set(this.firstColumn,
+        new Column(this, this.firstColumn, columns[this.firstColumn]));
+    }
+
     for (let id in columns) {
       if (!sortOn) {
         sortOn = id;
       }
+
+      if (this.firstColumn && id == this.firstColumn) {
+        continue;
+      }
+
       this.columns.set(id, new Column(this, id, columns[id]));
       if (hiddenColumns.indexOf(id) > -1) {
         this.columns.get(id).toggleColumn();
       }
     }
     this.sortedOn = sortOn;
     this.sortBy(this.sortedOn);
     this.populateMenuPopup();
--- a/browser/devtools/sourceeditor/codemirror/mozilla.css
+++ b/browser/devtools/sourceeditor/codemirror/mozilla.css
@@ -2,26 +2,41 @@
  * 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/. */
 
 .errors,
 .breakpoints {
   width: 16px;
 }
 
+.hit-counts {
+  width: 6px;
+}
+
 .error, .breakpoint, .debugLocation, .breakpoint-debugLocation {
   display: inline-block;
   margin-left: 5px;
   width: 12px;
   height: 12px;
   background-repeat: no-repeat;
   background-position: center;
   background-size: contain;
 }
 
+.hit-count {
+  display: inline-block;
+  height: 12px;
+  border: solid rgba(0,0,0,0.2);
+  border-width: 1px 1px 1px 0;
+  border-radius: 0 3px 3px 0;
+  padding: 0 3px;
+  font-size: 10px;
+  pointer-events: none;
+}
+
 .error {
   background-image: url("chrome://browser/skin/devtools/editor-error.png");
   opacity: 0.75;
 }
 
 .breakpoint {
   background-image: url("chrome://browser/skin/devtools/editor-breakpoint.png");
   position: relative;
--- a/browser/devtools/sourceeditor/debugger.js
+++ b/browser/devtools/sourceeditor/debugger.js
@@ -110,16 +110,17 @@ function hasBreakpoint(ctx, line) {
   // In some rare occasions CodeMirror might not be properly initialized yet, so
   // return an exceptional value in that case.
   if (cm.lineInfo(line) === null) {
     return null;
   }
   let markers = cm.lineInfo(line).gutterMarkers;
 
   return markers != null &&
+    markers.breakpoints &&
     markers.breakpoints.classList.contains("breakpoint");
 }
 
 /**
  * Adds a visual breakpoint for a specified line. Third
  * parameter 'cond' can hold any object.
  *
  * After adding a breakpoint, this function makes Editor to
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -613,16 +613,42 @@ Editor.prototype = {
   removeMarker: function (line, gutterName, markerClass) {
     if (!this.hasMarker(line, gutterName, markerClass))
       return;
 
     let cm = editors.get(this);
     cm.lineInfo(line).gutterMarkers[gutterName].classList.remove(markerClass);
   },
 
+  /**
+   * Adds a marker with a specified class and an HTML content to a line's
+   * gutter. If another marker exists on that line, it is overwritten by a new
+   * marker.
+   */
+  addContentMarker: function (line, gutterName, markerClass, content) {
+    let cm = editors.get(this);
+    let info = cm.lineInfo(line);
+    if (!info)
+      return;
+
+    let marker = cm.getWrapperElement().ownerDocument.createElement("div");
+    marker.className = markerClass;
+    marker.innerHTML = content;
+    cm.setGutterMarker(info.line, gutterName, marker);
+  },
+
+  /**
+   * The reverse of addContentMarker. Removes any line's markers in the
+   * specified gutter.
+   */
+  removeContentMarker: function (line, gutterName) {
+    let cm = editors.get(this);
+    cm.setGutterMarker(info.line, gutterName, null);
+  },
+
   getMarker: function(line, gutterName) {
     let cm = editors.get(this);
     let info = cm.lineInfo(line);
     if (!info)
       return null;
 
     let gutterMarkers = info.gutterMarkers;
     if (!gutterMarkers)
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -5,18 +5,22 @@
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
 loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
 loader.lazyImporter(this, "escapeHTML", "resource:///modules/devtools/VariablesView.jsm");
 loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
-loader.lazyImporter(this, "Task","resource://gre/modules/Task.jsm");
+loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
 loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
+loader.lazyImporter(this, "ObjectClient", "resource://gre/modules/devtools/dbg-client.jsm");
+
+loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "TableWidget", "devtools/shared/widgets/TableWidget", true);
 
 const Heritage = require("sdk/core/heritage");
 const URI = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
 
 const WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
@@ -76,16 +80,17 @@ const COMPAT = {
 const CONSOLE_API_LEVELS_TO_SEVERITIES = {
   error: "error",
   exception: "error",
   assert: "error",
   warn: "warning",
   info: "info",
   log: "log",
   trace: "log",
+  table: "log",
   debug: "log",
   dir: "log",
   group: "log",
   groupCollapsed: "log",
   groupEnd: "log",
   time: "log",
   timeEnd: "log",
   count: "log"
@@ -106,16 +111,22 @@ const RE_ALLOWED_STYLES = /^(?:-moz-)?(?
 const RE_CLEANUP_STYLES = [
   // url(), -moz-element()
   /\b(?:url|(?:-moz-)?element)[\s('"]+/gi,
 
   // various URL protocols
   /['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
 ];
 
+// Maximum number of rows to display in console.table().
+const TABLE_ROW_MAX_ITEMS = 1000;
+
+// Maximum number of columns to display in console.table().
+const TABLE_COLUMN_MAX_ITEMS = 10;
+
 /**
  * The ConsoleOutput object is used to manage output of messages in the Web
  * Console.
  *
  * @constructor
  * @param object owner
  *        The console output owner. This usually the WebConsoleFrame instance.
  *        Any other object can be used, as long as it has the following
@@ -1611,16 +1622,354 @@ Messages.ConsoleTrace.prototype = Herita
   },
 
   // no-op for the message location and .repeats elements.
   // |this._renderStack| handles customized message output.
   _renderLocation: function() { },
   _renderRepeatNode: function() { },
 }); // Messages.ConsoleTrace.prototype
 
+/**
+ * The ConsoleTable message is used for console.table() calls.
+ *
+ * @constructor
+ * @extends Messages.Extended
+ * @param object packet
+ *        The Console API call packet received from the server.
+ */
+Messages.ConsoleTable = function(packet)
+{
+  let options = {
+    className: "cm-s-mozilla",
+    timestamp: packet.timeStamp,
+    category: "webdev",
+    severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
+    private: packet.private,
+    filterDuplicates: false,
+    location: {
+      url: packet.filename,
+      line: packet.lineNumber,
+    },
+  };
+
+  this._populateTableData = this._populateTableData.bind(this);
+  this._renderTable = this._renderTable.bind(this);
+  Messages.Extended.call(this, [this._renderTable], options);
+
+  this._repeatID.consoleApiLevel = packet.level;
+  this._arguments = packet.arguments;
+};
+
+Messages.ConsoleTable.prototype = Heritage.extend(Messages.Extended.prototype,
+{
+  /**
+   * Holds the arguments the content script passed to the console.table()
+   * method.
+   *
+   * @private
+   * @type array
+   */
+  _arguments: null,
+
+  /**
+   * Array of objects that holds the data to log in the table.
+   *
+   * @private
+   * @type array
+   */
+  _data: null,
+
+  /**
+   * Key value pair of the id and display name for the columns in the table.
+   * Refer to the TableWidget API.
+   *
+   * @private
+   * @type object
+   */
+  _columns: null,
+
+  /**
+   * A promise that resolves when the table data is ready or null if invalid
+   * arguments are provided.
+   *
+   * @private
+   * @type promise|null
+   */
+  _populatePromise: null,
+
+  init: function()
+  {
+    let result = Messages.Extended.prototype.init.apply(this, arguments);
+    this._data = [];
+    this._columns = {};
+
+    this._populatePromise = this._populateTableData();
+
+    return result;
+  },
+
+  /**
+   * Sets the key value pair of the id and display name for the columns in the
+   * table.
+   *
+   * @private
+   * @param array|string columns
+   *        Either a string or array containing the names for the columns in
+   *        the output table.
+   */
+  _setColumns: function(columns)
+  {
+    if (columns.class == "Array") {
+      let items = columns.preview.items;
+
+      for (let item of items) {
+        if (typeof item == "string") {
+          this._columns[item] = item;
+        }
+      }
+    } else if (typeof columns == "string" && columns) {
+      this._columns[columns] = columns;
+    }
+  },
+
+  /**
+   * Retrieves the table data and columns from the arguments received from the
+   * server.
+   *
+   * @return Promise|null
+   *         Returns a promise that resolves when the table data is ready or
+   *         null if the arguments are invalid.
+   */
+  _populateTableData: function()
+  {
+    let deferred = promise.defer();
+
+    if (this._arguments.length <= 0) {
+      return;
+    }
+
+    let data = this._arguments[0];
+    if (data.class != "Array" && data.class != "Object" &&
+        data.class != "Map" && data.class != "Set") {
+      return;
+    }
+
+    let hasColumnsArg = false;
+    if (this._arguments.length > 1) {
+      if (data.class == "Object" || data.class == "Array") {
+        this._columns["_index"] = l10n.getStr("table.index");
+      } else {
+        this._columns["_index"] = l10n.getStr("table.iterationIndex");
+      }
+
+      this._setColumns(this._arguments[1]);
+      hasColumnsArg = true;
+    }
+
+    if (data.class == "Object" || data.class == "Array") {
+      // Get the object properties, and parse the key and value properties into
+      // the table data and columns.
+      this.client = new ObjectClient(this.output.owner.jsterm.hud.proxy.client,
+          data);
+      this.client.getPrototypeAndProperties(aResponse => {
+        let {ownProperties} = aResponse;
+        let rowCount = 0;
+        let columnCount = 0;
+
+        for (let index of Object.keys(ownProperties || {})) {
+          // Avoid outputting the length property if the data argument provided
+          // is an array
+          if (data.class == "Array" && index == "length") {
+            continue;
+          }
+
+          if (!hasColumnsArg) {
+            this._columns["_index"] = l10n.getStr("table.index");
+          }
+
+          let property = ownProperties[index].value;
+          let item = { _index: index };
+
+          if (property.class == "Object" || property.class == "Array") {
+            let {preview} = property;
+            let entries = property.class == "Object" ?
+                preview.ownProperties : preview.items;
+
+            for (let key of Object.keys(entries)) {
+              let value = property.class == "Object" ?
+                  preview.ownProperties[key].value : preview.items[key];
+
+              item[key] = this._renderValueGrip(value, { concise: true });
+
+              if (!hasColumnsArg && !(key in this._columns) &&
+                  (++columnCount <= TABLE_COLUMN_MAX_ITEMS)) {
+                this._columns[key] = key;
+              }
+            }
+          } else {
+            // Display the value for any non-object data input.
+            item["_value"] = this._renderValueGrip(property, { concise: true });
+
+            if (!hasColumnsArg && !("_value" in this._columns)) {
+              this._columns["_value"] = l10n.getStr("table.value");
+            }
+          }
+
+          this._data.push(item);
+
+          if (++rowCount == TABLE_ROW_MAX_ITEMS) {
+            break;
+          }
+        }
+
+        deferred.resolve();
+      });
+    } else if (data.class == "Map") {
+      let entries = data.preview.entries;
+
+      if (!hasColumnsArg) {
+        this._columns["_index"] = l10n.getStr("table.iterationIndex");
+        this._columns["_key"] = l10n.getStr("table.key");
+        this._columns["_value"] = l10n.getStr("table.value");
+      }
+
+      let rowCount = 0;
+      for (let index of Object.keys(entries || {})) {
+        let [key, value] = entries[index];
+        let item = {
+          _index: index,
+          _key: this._renderValueGrip(key, { concise: true }),
+          _value: this._renderValueGrip(value, { concise: true })
+        };
+
+        this._data.push(item);
+
+        if (++rowCount == TABLE_ROW_MAX_ITEMS) {
+          break;
+        }
+      }
+
+      deferred.resolve();
+    } else if (data.class == "Set") {
+      let entries = data.preview.items;
+
+      if (!hasColumnsArg) {
+        this._columns["_index"] = l10n.getStr("table.iterationIndex");
+        this._columns["_value"] = l10n.getStr("table.value");
+      }
+
+      let rowCount = 0;
+      for (let index of Object.keys(entries || {})) {
+        let value = entries[index];
+        let item = {
+          _index : index,
+          _value: this._renderValueGrip(value, { concise: true })
+        };
+
+        this._data.push(item);
+
+        if (++rowCount == TABLE_ROW_MAX_ITEMS) {
+          break;
+        }
+      }
+
+      deferred.resolve();
+    }
+
+    return deferred.promise;
+  },
+
+  render: function()
+  {
+    Messages.Extended.prototype.render.apply(this, arguments);
+    this.element.setAttribute("open", true);
+    return this;
+  },
+
+  /**
+   * Render the table.
+   *
+   * @private
+   * @return DOMElement
+   */
+  _renderTable: function()
+  {
+    let cmvar = this.document.createElementNS(XHTML_NS, "span");
+    cmvar.className = "cm-variable";
+    cmvar.textContent = "console";
+
+    let cmprop = this.document.createElementNS(XHTML_NS, "span");
+    cmprop.className = "cm-property";
+    cmprop.textContent = "table";
+
+    let title = this.document.createElementNS(XHTML_NS, "span");
+    title.className = "message-body devtools-monospace";
+    title.appendChild(cmvar);
+    title.appendChild(this.document.createTextNode("."));
+    title.appendChild(cmprop);
+    title.appendChild(this.document.createTextNode("():"));
+
+    let repeatNode = Messages.Simple.prototype._renderRepeatNode.call(this);
+    let location = Messages.Simple.prototype._renderLocation.call(this);
+    if (location) {
+      location.target = "jsdebugger";
+    }
+
+    let body = this.document.createElementNS(XHTML_NS, "span");
+    body.className = "message-flex-body";
+    body.appendChild(title);
+    if (repeatNode) {
+      body.appendChild(repeatNode);
+    }
+    if (location) {
+      body.appendChild(location);
+    }
+    body.appendChild(this.document.createTextNode("\n"));
+
+    let result = this.document.createElementNS(XHTML_NS, "div");
+    result.appendChild(body);
+
+    if (this._populatePromise) {
+      this._populatePromise.then(() => {
+        if (this._data.length > 0) {
+          let widget = new Widgets.Table(this, this._data, this._columns).render();
+          result.appendChild(widget.element);
+        }
+
+        result.scrollIntoView();
+        this.output.owner.emit("messages-table-rendered");
+
+        // Release object actors
+        if (Array.isArray(this._arguments)) {
+          for (let arg of this._arguments) {
+            if (WebConsoleUtils.isActorGrip(arg)) {
+              this.output._releaseObject(arg.actor);
+            }
+          }
+        }
+        this._arguments = null;
+      });
+    }
+
+    return result;
+  },
+
+  _renderBody: function()
+  {
+    let body = Messages.Simple.prototype._renderBody.apply(this, arguments);
+    body.classList.remove("devtools-monospace", "message-body");
+    return body;
+  },
+
+  // no-op for the message location and .repeats elements.
+  // |this._renderTable| handles customized message output.
+  _renderLocation: function() { },
+  _renderRepeatNode: function() { },
+}); // Messages.ConsoleTable.prototype
+
 let Widgets = {};
 
 /**
  * The base widget class.
  *
  * @constructor
  * @param object message
  *        The owning message.
@@ -3007,16 +3356,73 @@ Widgets.Stacktrace.prototype = Heritage.
     elem.appendChild(location);
     elem.appendChild(this.document.createTextNode("\n"));
 
     return elem;
   },
 }); // Widgets.Stacktrace.prototype
 
 
+/**
+ * The table widget.
+ *
+ * @constructor
+ * @extends Widgets.BaseWidget
+ * @param object message
+ *        The owning message.
+ * @param array data
+ *        Array of objects that holds the data to log in the table.
+ * @param object columns
+ *        Object containing the key value pair of the id and display name for
+ *        the columns in the table.
+ */
+Widgets.Table = function(message, data, columns)
+{
+  Widgets.BaseWidget.call(this, message);
+  this.data = data;
+  this.columns = columns;
+};
+
+Widgets.Table.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
+{
+  /**
+   * Array of objects that holds the data to output in the table.
+   * @type array
+   */
+  data: null,
+
+  /**
+   * Object containing the key value pair of the id and display name for
+   * the columns in the table.
+   * @type object
+   */
+  columns: null,
+
+  render: function() {
+    if (this.element) {
+      return this;
+    }
+
+    let result = this.element = this.document.createElementNS(XHTML_NS, "div");
+    result.className = "consoletable devtools-monospace";
+
+    this.table = new TableWidget(result, {
+      initialColumns: this.columns,
+      uniqueId: "_index",
+      firstColumn: "_index"
+    });
+
+    for (let row of this.data) {
+      this.table.push(row);
+    }
+
+    return this;
+  }
+}); // Widgets.Table.prototype
+
 function gSequenceId()
 {
   return gSequenceId.n++;
 }
 gSequenceId.n = 0;
 
 exports.ConsoleOutput = ConsoleOutput;
 exports.Messages = Messages;
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -62,16 +62,17 @@ support-files =
   test-closure-optimized-out.html
   test-closures.html
   test-console-assert.html
   test-console-count.html
   test-console-count-external-file.js
   test-console-extras.html
   test-console-replaced-api.html
   test-console.html
+  test-console-table.html
   test-console-output-02.html
   test-console-output-03.html
   test-console-output-04.html
   test-console-output-dom-elements.html
   test-console-output-events.html
   test-consoleiframes.html
   test-data.json
   test-data.json^headers^
@@ -300,15 +301,16 @@ skip-if = buildapp == 'mulet'
 [browser_webconsole_output_04.js]
 [browser_webconsole_output_05.js]
 [browser_webconsole_output_06.js]
 [browser_webconsole_output_dom_elements_01.js]
 [browser_webconsole_output_dom_elements_02.js]
 [browser_webconsole_output_dom_elements_03.js]
 [browser_webconsole_output_dom_elements_04.js]
 [browser_webconsole_output_events.js]
+[browser_webconsole_output_table.js]
 [browser_console_variables_view_highlighter.js]
 [browser_webconsole_start_netmon_first.js]
 [browser_webconsole_console_trace_duplicates.js]
 [browser_webconsole_cd_iframe.js]
 [browser_webconsole_autocomplete_crossdomain_iframe.js]
 [browser_webconsole_console_custom_styles.js]
 [browser_webconsole_console_api_stackframe.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_table.js
@@ -0,0 +1,158 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that console.table() works as intended.
+
+ "use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-table.html";
+
+const TEST_DATA = [
+  {
+    command: "console.table(languages1)",
+    data: [
+        { _index: "0", name: "\"JavaScript\"", fileExtension: "Array[1]" },
+        { _index: "1", name: "Object", fileExtension: "\".ts\"" },
+        { _index: "2", name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+    ],
+    columns: { _index: "(index)", name: "name", fileExtension: "fileExtension" }
+  },
+  {
+    command: "console.table(languages1, 'name')",
+    data: [
+        { _index: "0", name: "\"JavaScript\"", fileExtension: "Array[1]" },
+        { _index: "1", name: "Object", fileExtension: "\".ts\"" },
+        { _index: "2", name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+    ],
+    columns: { _index: "(index)", name: "name" }
+  },
+  {
+    command: "console.table(languages1, ['name'])",
+    data: [
+        { _index: "0", name: "\"JavaScript\"", fileExtension: "Array[1]" },
+        { _index: "1", name: "Object", fileExtension: "\".ts\"" },
+        { _index: "2", name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+    ],
+    columns: { _index: "(index)", name: "name" }
+  },
+  {
+    command: "console.table(languages2)",
+    data: [
+      { _index: "csharp", name: "\"C#\"", paradigm: "\"object-oriented\"" },
+      { _index: "fsharp", name: "\"F#\"", paradigm: "\"functional\"" }
+    ],
+    columns: { _index: "(index)", name: "name", paradigm: "paradigm" }
+  },
+  {
+    command: "console.table([[1, 2], [3, 4]])",
+    data: [
+      { _index: "0", 0: "1", 1: "2" },
+      { _index: "1", 0: "3", 1: "4" }
+    ],
+    columns: { _index: "(index)", 0: "0", 1: "1" }
+  },
+  {
+    command: "console.table({a: [1, 2], b: [3, 4]})",
+    data: [
+      { _index: "a", 0: "1", 1: "2" },
+      { _index: "b", 0: "3", 1: "4" }
+    ],
+    columns: { _index: "(index)", 0: "0", 1: "1" }
+  },
+  {
+    command: "console.table(family)",
+    data: [
+      { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", age: "32" },
+      { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", age: "33" },
+      { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", age: "5" },
+      { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+    ],
+    columns: { _index: "(index)", firstName: "firstName", lastName: "lastName", age: "age" }
+  },
+  {
+    command: "console.table(family, [])",
+    data: [
+      { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", age: "32" },
+      { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", age: "33" },
+      { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", age: "5" },
+      { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+    ],
+    columns: { _index: "(index)" }
+  },
+  {
+    command: "console.table(family, ['firstName', 'lastName'])",
+    data: [
+      { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", age: "32" },
+      { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", age: "33" },
+      { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", age: "5" },
+      { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+    ],
+    columns: { _index: "(index)", firstName: "firstName", lastName: "lastName" }
+  },
+  {
+    command: "console.table(mySet)",
+    data: [
+      { _index: "0", _value: "1" },
+      { _index: "1", _value: "5" },
+      { _index: "2", _value: "\"some text\"" },
+      { _index: "3", _value: "null" },
+      { _index: "4", _value: "undefined" }
+    ],
+    columns: { _index: "(iteration index)", _value: "Values" }
+  },
+  {
+    command: "console.table(myMap)",
+    data: [
+      { _index: "0", _key: "\"a string\"", _value: "\"value associated with 'a string'\"" },
+      { _index: "1", _key: "5", _value: "\"value associated with 5\"" },
+    ],
+    columns: { _index: "(iteration index)", _key: "Key", _value: "Values" }
+  }
+];
+
+let test = asyncTest(function*() {
+  const {tab} = yield loadTab(TEST_URI);
+  let hud = yield openConsole(tab);
+
+  for (let testdata of TEST_DATA) {
+    hud.jsterm.clearOutput();
+
+    info("Executing " + testdata.command);
+
+    let onTableRender = once(hud.ui, "messages-table-rendered");
+    hud.jsterm.execute(testdata.command);
+    yield onTableRender;
+
+    let [result] = yield waitForMessages({
+      webconsole: hud,
+      messages: [{
+        name: testdata.command + " output",
+        consoleTable: true
+      }],
+    });
+
+    let node = [...result.matched][0];
+    ok(node, "found trace log node");
+
+    let obj = node._messageObject;
+    ok(obj, "console.trace message object");
+
+    ok(obj._data, "found table data object");
+
+    let data = obj._data.map(entries => {
+      let result = {};
+
+      for (let key of Object.keys(entries)) {
+        result[key] = entries[key] instanceof HTMLElement ?
+          entries[key].textContent : entries[key];
+      }
+
+      return result;
+    });
+
+    is(data.toSource(), testdata.data.toSource(), "table data is correct");
+    ok(obj._columns, "found table column object");
+    is(obj._columns.toSource(), testdata.columns.toSource(), "table column is correct");
+  }
+});
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -907,16 +907,18 @@ function openDebugger(aOptions = {})
  *            and/or line number in the trace message.
  *            - consoleTime: string that matches a console.time() timer name.
  *            Provide this if you want to match a console.time() message.
  *            - consoleTimeEnd: same as above, but for console.timeEnd().
  *            - consoleDir: boolean, set to |true| to match a console.dir()
  *            message.
  *            - consoleGroup: boolean, set to |true| to match a console.group()
  *            message.
+ *            - consoleTable: boolean, set to |true| to match a console.table()
+ *            message.
  *            - longString: boolean, set to |true} to match long strings in the
  *            message.
  *            - collapsible: boolean, set to |true| to match messages that can
  *            be collapsed/expanded.
  *            - type: match messages that are instances of the given object. For
  *            example, you can point to Messages.NavigationMarker to match any
  *            such message.
  *            - objects: boolean, set to |true| if you expect inspectable
@@ -965,16 +967,32 @@ function waitForMessages(aOptions)
       result = aRule.test(aText);
     }
     else {
       result = aRule == aText;
     }
     return result;
   }
 
+  function checkConsoleTable(aRule, aElement)
+  {
+    let elemText = aElement.textContent;
+    let table = aRule.consoleTable;
+
+    if (!checkText("console.table():", elemText)) {
+      return false;
+    }
+
+    aRule.category = CATEGORY_WEBDEV;
+    aRule.severity = SEVERITY_LOG;
+    aRule.type = Messages.ConsoleTable;
+
+    return true;
+  }
+
   function checkConsoleTrace(aRule, aElement)
   {
     let elemText = aElement.textContent;
     let trace = aRule.consoleTrace;
 
     if (!checkText("console.trace():", elemText)) {
       return false;
     }
@@ -1141,16 +1159,20 @@ function waitForMessages(aOptions)
     if (aRule.text && !checkText(aRule.text, elemText)) {
       return false;
     }
 
     if (aRule.noText && checkText(aRule.noText, elemText)) {
       return false;
     }
 
+    if (aRule.consoleTable && !checkConsoleTable(aRule, aElement)) {
+      return false;
+    }
+
     if (aRule.consoleTrace && !checkConsoleTrace(aRule, aElement)) {
       return false;
     }
 
     if (aRule.consoleTime && !checkConsoleTime(aRule, aElement)) {
       return false;
     }
 
@@ -1588,8 +1610,39 @@ function checkOutputForInputs(hud, input
       ok(entry.expectedTab && entry.expectedTab == uri,
         "opened tab '" + uri +  "', expected tab '" + entry.expectedTab + "'");
       return closeTab(tab);
     }).then(resolve, reject);
   }
 
   return Task.spawn(runner);
 }
+
+/**
+ * Wait for eventName on target.
+ * @param {Object} target An observable object that either supports on/off or
+ * addEventListener/removeEventListener
+ * @param {String} eventName
+ * @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
+ * @return A promise that resolves when the event has been handled
+ */
+function once(target, eventName, useCapture=false) {
+  info("Waiting for event: '" + eventName + "' on " + target + ".");
+
+  let deferred = promise.defer();
+
+  for (let [add, remove] of [
+    ["addEventListener", "removeEventListener"],
+    ["addListener", "removeListener"],
+    ["on", "off"]
+  ]) {
+    if ((add in target) && (remove in target)) {
+      target[add](eventName, function onEvent(...aArgs) {
+        target[remove](eventName, onEvent, useCapture);
+        deferred.resolve.apply(deferred, aArgs);
+      }, useCapture);
+      break;
+    }
+  }
+
+  return deferred.promise;
+}
+
--- a/browser/devtools/webconsole/test/test-console-extras.html
+++ b/browser/devtools/webconsole/test/test-console-extras.html
@@ -2,19 +2,16 @@
 <html dir="ltr" xml:lang="en-US" lang="en-US"><head>
     <meta charset="utf-8">
     <title>Console extended API test</title>
     <script type="text/javascript">
       function test() {
         console.log("start");
         console.clear()
         console.dirxml()
-        console.profile()
-        console.profileEnd()
-        console.table()
         console.log("end");
       }
     </script>
   </head>
   <body>
     <h1 id="header">Heads Up Display Demo</h1>
     <button onclick="test();">Test Extended API</button>
     <div id="myDiv"></div>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-console-table.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+  <head>
+    <meta charset="utf8">
+    <!--
+    - Any copyright is dedicated to the Public Domain.
+    - http://creativecommons.org/publicdomain/zero/1.0/
+    -->
+    <title>Test for Bug 899753 - console.table support</title>
+    <script>
+      var languages1 = [
+        { name: "JavaScript", fileExtension: [".js"] },
+        { name: { a: "TypeScript" }, fileExtension: ".ts" },
+        { name: "CoffeeScript", fileExtension: ".coffee" }
+      ];
+
+      var languages2 = {
+        csharp: { name: "C#", paradigm: "object-oriented" },
+        fsharp: { name: "F#", paradigm: "functional" }
+      };
+
+      function Person(firstName, lastName, age)
+      {
+          this.firstName = firstName;
+          this.lastName = lastName;
+          this.age = age;
+      }
+
+      var family = {};
+      family.mother = new Person("Susan", "Doyle", 32);
+      family.father = new Person("John", "Doyle", 33);
+      family.daughter = new Person("Lily", "Doyle", 5);
+      family.son = new Person("Mike", "Doyle", 8);
+
+      var myMap = new Map();
+
+      myMap.set("a string", "value associated with 'a string'");
+      myMap.set(5, "value associated with 5");
+
+      var mySet = new Set();
+
+      mySet.add(1);
+      mySet.add(5);
+      mySet.add("some text");
+      mySet.add(null);
+      mySet.add(undefined);
+    </script>
+  </head>
+  <body>
+    <p>Hello world!</p>
+  </body>
+</html>
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -118,16 +118,17 @@ const MESSAGE_PREFERENCE_KEYS = [
 const LEVELS = {
   error: SEVERITY_ERROR,
   exception: SEVERITY_ERROR,
   assert: SEVERITY_ERROR,
   warn: SEVERITY_WARNING,
   info: SEVERITY_INFO,
   log: SEVERITY_LOG,
   trace: SEVERITY_LOG,
+  table: SEVERITY_LOG,
   debug: SEVERITY_LOG,
   dir: SEVERITY_LOG,
   group: SEVERITY_LOG,
   groupCollapsed: SEVERITY_LOG,
   groupEnd: SEVERITY_LOG,
   time: SEVERITY_LOG,
   timeEnd: SEVERITY_LOG,
   count: SEVERITY_LOG
@@ -1207,16 +1208,21 @@ WebConsoleFrame.prototype = {
       case "error":
       case "exception":
       case "assert":
       case "debug": {
         let msg = new Messages.ConsoleGeneric(aMessage);
         node = msg.init(this.output).render().element;
         break;
       }
+      case "table": {
+        let msg = new Messages.ConsoleTable(aMessage);
+        node = msg.init(this.output).render().element;
+        break;
+      }
       case "trace": {
         let msg = new Messages.ConsoleTrace(aMessage);
         node = msg.init(this.output).render().element;
         break;
       }
       case "dir": {
         body = { arguments: args };
         let clipboardArray = [];
--- a/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
@@ -245,8 +245,15 @@ messageToggleDetails=Show/hide message d
 # LOCALIZATION NOTE (emptySlotLabel): the text is displayed when an Array
 # with empty slots is printed to the console.
 # This is a semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 number of empty slots
 # example: 1 empty slot
 # example: 5 empty slots
 emptySlotLabel=#1 empty slot;#1 empty slots
+
+# LOCALIZATION NOTE (table.index, table.iterationIndex, table.key, table.value):
+# the column header displayed in the console table widget.
+table.index=(index)
+table.iterationIndex=(iteration index)
+table.key=Key
+table.value=Values
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2659,16 +2659,17 @@ richlistitem[type~="action"][actiontype=
 #historySwipeAnimationContainer {
   background: url("chrome://browser/skin/subtle-pattern.png") #B3B9C1;
 }
 
 /* ----- SIDEBAR ELEMENTS ----- */
 
 #sidebar,
 sidebarheader {
+  -moz-appearance: -moz-mac-vibrancy-light;
   background-color: #e2e7ed;
 }
 
 #sidebar:-moz-window-inactive,
 sidebarheader:-moz-window-inactive {
   background-color: #e8e8e8;
 }
 
--- a/browser/themes/shared/devtools/webconsole.inc.css
+++ b/browser/themes/shared/devtools/webconsole.inc.css
@@ -58,16 +58,20 @@ a {
   }
 }
 
 .message > .message-body-wrapper {
   flex: 1 1 100%;
   margin: 3px;
 }
 
+.message-body-wrapper .table-widget-body {
+  overflow: visible;
+}
+
 /* The red bubble that shows the number of times a message is repeated */
 .message-repeats {
   -moz-user-select: none;
   flex: none;
   margin: 2px 6px;
   padding: 0 6px;
   height: 1.25em;
   color: white;
@@ -218,16 +222,23 @@ a {
 .theme-dark .console-string {
   color: #d99b28;
 }
 
 .theme-light .console-string {
   color: hsl(24,85%,39%);
 }
 
+.theme-selected .console-string,
+.theme-selected .cm-number,
+.theme-selected .cm-variable,
+.theme-selected .kind-ArrayLike {
+  color: #f5f7fa !important; /* Selection Text Color */
+}
+
 .message[category=network] > .indent {
   -moz-border-end: solid #000 6px;
 }
 
 .message[category=network][severity=error] > .icon::before {
   background-position: -8px 0;
 }
 
@@ -424,16 +435,20 @@ a {
   padding: 0 1em 0 1.5em;
   margin: 5px 0 0 0;
   max-height: 10em;
   overflow-y: auto;
   border: 1px solid rgb(200,200,200);
   border-radius: 3px;
 }
 
+.consoletable {
+  margin: 5px 0 0 0;
+}
+
 .theme-light .message[severity=error] .stacktrace {
   background-color: rgba(255, 255, 255, 0.5);
 }
 
 .theme-dark .message[severity=error] .stacktrace {
   background-color: rgba(0, 0, 0, 0.5);
 }
 
--- a/content/base/src/ChildIterator.h
+++ b/content/base/src/ChildIterator.h
@@ -39,16 +39,30 @@ public:
     : mParent(aParent),
       mChild(nullptr),
       mDefaultChild(nullptr),
       mIndexInInserted(0),
       mIsFirst(aStartAtBeginning)
   {
   }
 
+  ExplicitChildIterator(const ExplicitChildIterator& aOther)
+    : mParent(aOther.mParent), mChild(aOther.mChild),
+      mDefaultChild(aOther.mDefaultChild),
+      mShadowIterator(aOther.mShadowIterator ?
+                      new ExplicitChildIterator(*aOther.mShadowIterator) :
+                      nullptr),
+      mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
+
+  ExplicitChildIterator(ExplicitChildIterator&& aOther)
+    : mParent(aOther.mParent), mChild(aOther.mChild),
+      mDefaultChild(aOther.mDefaultChild),
+      mShadowIterator(Move(aOther.mShadowIterator)),
+      mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
+
   nsIContent* GetNextChild();
 
   // Looks for aChildToFind respecting insertion points until aChildToFind
   // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns
   // whether aChildToFind was found as an explicit child prior to encountering
   // aBound.
   bool Seek(nsIContent* aChildToFind, nsIContent* aBound = nullptr)
   {
@@ -113,25 +127,31 @@ class FlattenedChildIterator : public Ex
 {
 public:
   FlattenedChildIterator(nsIContent* aParent)
     : ExplicitChildIterator(aParent), mXBLInvolved(false)
   {
     Init(false);
   }
 
+  FlattenedChildIterator(FlattenedChildIterator&& aOther)
+    : ExplicitChildIterator(Move(aOther)), mXBLInvolved(aOther.mXBLInvolved) {}
+
+  FlattenedChildIterator(const FlattenedChildIterator& aOther)
+    : ExplicitChildIterator(aOther), mXBLInvolved(aOther.mXBLInvolved) {}
+
   bool XBLInvolved() { return mXBLInvolved; }
 
 protected:
   /**
    * This constructor is a hack to help AllChildrenIterator which sometimes
    * doesn't want to consider XBL.
    */
   FlattenedChildIterator(nsIContent* aParent, bool aIgnoreXBL)
-  : ExplicitChildIterator(aParent), mXBLInvolved(false)
+    : ExplicitChildIterator(aParent), mXBLInvolved(false)
   {
     Init(aIgnoreXBL);
   }
 
   void Init(bool aIgnoreXBL);
 
   // For certain optimizations, nsCSSFrameConstructor needs to know if the
   // child list of the element that we're iterating matches its .childNodes.
@@ -147,16 +167,26 @@ protected:
 class AllChildrenIterator : private FlattenedChildIterator
 {
 public:
   AllChildrenIterator(nsIContent* aNode, uint32_t aFlags) :
     FlattenedChildIterator(aNode, (aFlags & nsIContent::eAllButXBL)),
     mOriginalContent(aNode), mFlags(aFlags),
     mPhase(eNeedBeforeKid) {}
 
+  AllChildrenIterator(AllChildrenIterator&& aOther)
+    : FlattenedChildIterator(Move(aOther)),
+      mOriginalContent(aOther.mOriginalContent),
+      mAnonKids(Move(aOther.mAnonKids)), mFlags(aOther.mFlags),
+      mPhase(aOther.mPhase)
+#ifdef DEBUG
+      , mMutationGuard(aOther.mMutationGuard)
+#endif
+      {}
+
 #ifdef DEBUG
   ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
 #endif
 
   nsIContent* GetNextChild();
 
 private:
   enum IteratorPhase
--- a/content/base/src/nsDOMAttributeMap.cpp
+++ b/content/base/src/nsDOMAttributeMap.cpp
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Implementation of the |attributes| property of DOM Core's Element object.
  */
 
@@ -45,23 +46,27 @@ RemoveMapRef(nsAttrHashKey::KeyType aKey
 {
   aData->SetMap(nullptr);
 
   return PL_DHASH_REMOVE;
 }
 
 nsDOMAttributeMap::~nsDOMAttributeMap()
 {
-  mAttributeCache.Enumerate(RemoveMapRef, nullptr);
+  if (mAttributeCache) {
+    mAttributeCache->Enumerate(RemoveMapRef, nullptr);
+  }
 }
 
 void
 nsDOMAttributeMap::DropReference()
 {
-  mAttributeCache.Enumerate(RemoveMapRef, nullptr);
+  if (mAttributeCache) {
+    mAttributeCache->Enumerate(RemoveMapRef, nullptr);
+  }
   mContent = nullptr;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap)
   tmp->DropReference();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -77,17 +82,19 @@ TraverseMapEntry(nsAttrHashKey::KeyType 
     static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
 
   cb->NoteXPCOMChild(static_cast<nsINode*>(aData.get()));
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap)
-  tmp->mAttributeCache.Enumerate(TraverseMapEntry, &cb);
+  if (tmp->mAttributeCache) {
+    tmp->mAttributeCache->Enumerate(TraverseMapEntry, &cb);
+  }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMAttributeMap)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMAttributeMap)
   if (tmp->IsBlack()) {
@@ -131,76 +138,79 @@ SetOwnerDocumentFunc(nsAttrHashKey::KeyT
   nsresult rv = aData->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
 
   return NS_FAILED(rv) ? PL_DHASH_STOP : PL_DHASH_NEXT;
 }
 
 nsresult
 nsDOMAttributeMap::SetOwnerDocument(nsIDocument* aDocument)
 {
-  uint32_t n = mAttributeCache.Enumerate(SetOwnerDocumentFunc, aDocument);
-  NS_ENSURE_TRUE(n == mAttributeCache.Count(), NS_ERROR_FAILURE);
-
+  if (mAttributeCache) {
+    uint32_t n = mAttributeCache->Enumerate(SetOwnerDocumentFunc, aDocument);
+    NS_ENSURE_TRUE(n == mAttributeCache->Count(), NS_ERROR_FAILURE);
+  }
   return NS_OK;
 }
 
 void
 nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID, nsIAtom* aLocalName)
 {
   nsAttrKey attr(aNamespaceID, aLocalName);
-  Attr *node = mAttributeCache.GetWeak(attr);
-  if (node) {
-    // Break link to map
-    node->SetMap(nullptr);
+  if (mAttributeCache) {
+    Attr *node = mAttributeCache->GetWeak(attr);
+    if (node) {
+      // Break link to map
+      node->SetMap(nullptr);
 
-    // Remove from cache
-    mAttributeCache.Remove(attr);
+      // Remove from cache
+      mAttributeCache->Remove(attr);
+    }
   }
 }
 
 already_AddRefed<Attr>
 nsDOMAttributeMap::RemoveAttribute(mozilla::dom::NodeInfo* aNodeInfo)
 {
   NS_ASSERTION(aNodeInfo, "RemoveAttribute() called with aNodeInfo == nullptr!");
 
   nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
 
   nsRefPtr<Attr> node;
-  if (!mAttributeCache.Get(attr, getter_AddRefs(node))) {
+  if (mAttributeCache && mAttributeCache->Get(attr, getter_AddRefs(node))) {
+    // Break link to map
+    node->SetMap(nullptr);
+
+    // Remove from cache
+    mAttributeCache->Remove(attr);
+  } else {
     nsAutoString value;
     // As we are removing the attribute we need to set the current value in
     // the attribute node.
     mContent->GetAttr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom(), value);
     nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
     node = new Attr(nullptr, ni.forget(), value, true);
   }
-  else {
-    // Break link to map
-    node->SetMap(nullptr);
-
-    // Remove from cache
-    mAttributeCache.Remove(attr);
-  }
 
   return node.forget();
 }
 
 Attr*
 nsDOMAttributeMap::GetAttribute(mozilla::dom::NodeInfo* aNodeInfo, bool aNsAware)
 {
   NS_ASSERTION(aNodeInfo, "GetAttribute() called with aNodeInfo == nullptr!");
 
   nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
 
-  Attr* node = mAttributeCache.GetWeak(attr);
+  EnsureAttributeCache();
+  Attr* node = mAttributeCache->GetWeak(attr);
   if (!node) {
     nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
     nsRefPtr<Attr> newAttr =
       new Attr(this, ni.forget(), EmptyString(), aNsAware);
-    mAttributeCache.Put(attr, newAttr);
+    mAttributeCache->Put(attr, newAttr);
     node = newAttr;
   }
 
   return node;
 }
 
 Attr*
 nsDOMAttributeMap::NamedGetter(const nsAString& aAttrName, bool& aFound)
@@ -236,16 +246,24 @@ nsDOMAttributeMap::GetNamedItem(const ns
 {
   NS_ENSURE_ARG_POINTER(aAttribute);
 
   NS_IF_ADDREF(*aAttribute = GetNamedItem(aAttrName));
 
   return NS_OK;
 }
 
+void
+nsDOMAttributeMap::EnsureAttributeCache()
+{
+  if (!mAttributeCache) {
+    mAttributeCache = MakeUnique<AttrCache>();
+  }
+}
+
 NS_IMETHODIMP
 nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
 {
   Attr* attribute = static_cast<Attr*>(aAttr);
   NS_ENSURE_ARG(attribute);
 
   ErrorResult rv;
   *aReturn = SetNamedItem(*attribute, rv).take();
@@ -337,17 +355,18 @@ nsDOMAttributeMap::SetNamedItemInternal(
   }
 
   nsAutoString value;
   aAttr.GetValue(value);
 
   // Add the new attribute to the attribute map before updating
   // its value in the element. @see bug 364413.
   nsAttrKey attrkey(ni->NamespaceID(), ni->NameAtom());
-  mAttributeCache.Put(attrkey, &aAttr);
+  EnsureAttributeCache();
+  mAttributeCache->Put(attrkey, &aAttr);
   aAttr.SetMap(this);
 
   rv = mContent->SetAttr(ni->NamespaceID(), ni->NameAtom(),
                          ni->GetPrefixAtom(), value, true);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     DropAttribute(ni->NamespaceID(), ni->NameAtom());
   }
@@ -523,41 +542,43 @@ nsDOMAttributeMap::RemoveNamedItemNS(con
   mContent->UnsetAttr(attrNi->NamespaceID(), attrNi->NameAtom(), true);
 
   return attr.forget();
 }
 
 uint32_t
 nsDOMAttributeMap::Count() const
 {
-  return mAttributeCache.Count();
+  return mAttributeCache ? mAttributeCache->Count() : 0;
 }
 
 uint32_t
 nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
                              void *aUserArg) const
 {
-  return mAttributeCache.EnumerateRead(aFunc, aUserArg);
+  return mAttributeCache ? mAttributeCache->EnumerateRead(aFunc, aUserArg) : 0;
 }
 
 size_t
 AttrCacheSizeEnumerator(const nsAttrKey& aKey,
                         const nsRefPtr<Attr>& aValue,
                         MallocSizeOf aMallocSizeOf,
                         void* aUserArg)
 {
   return aMallocSizeOf(aValue.get());
 }
 
 size_t
 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
-  n += mAttributeCache.SizeOfExcludingThis(AttrCacheSizeEnumerator,
-                                           aMallocSizeOf);
+  n += mAttributeCache
+     ? mAttributeCache->SizeOfExcludingThis(AttrCacheSizeEnumerator,
+                                            aMallocSizeOf)
+     : 0;
 
   // NB: mContent is non-owning and thus not counted.
   return n;
 }
 
 /* virtual */ JSObject*
 nsDOMAttributeMap::WrapObject(JSContext* aCx)
 {
--- a/content/base/src/nsDOMAttributeMap.h
+++ b/content/base/src/nsDOMAttributeMap.h
@@ -6,16 +6,17 @@
 /*
  * Implementation of the |attributes| property of DOM Core's Element object.
  */
 
 #ifndef nsDOMAttributeMap_h
 #define nsDOMAttributeMap_h
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/Attr.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMMozNamedAttrMap.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
@@ -178,19 +179,21 @@ public:
 
 protected:
   virtual ~nsDOMAttributeMap();
 
 private:
   nsCOMPtr<Element> mContent;
 
   /**
-   * Cache of Attrs.
+   * Cache of Attrs. It's usually empty, and thus initialized lazily.
    */
-  AttrCache mAttributeCache;
+  mozilla::UniquePtr<AttrCache> mAttributeCache;
+
+  void EnsureAttributeCache();
 
   /**
    * SetNamedItem() (aWithNS = false) and SetNamedItemNS() (aWithNS =
    * true) implementation.
    */
   already_AddRefed<Attr>
   SetNamedItemInternal(Attr& aNode, bool aWithNS, ErrorResult& aError);
 
--- a/content/media/DecoderTraits.cpp
+++ b/content/media/DecoderTraits.cpp
@@ -318,17 +318,17 @@ IsDirectShowSupportedType(const nsACStri
 }
 #endif
 
 #ifdef MOZ_FMP4
 static bool
 IsMP4SupportedType(const nsACString& aType)
 {
   return Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
-         MP4Decoder::GetSupportedCodecs(aType, nullptr);
+         MP4Decoder::CanHandleMediaType(aType);
 }
 #endif
 
 #ifdef MOZ_APPLEMEDIA
 static const char * const gAppleMP3Types[] = {
   "audio/mp3",
   "audio/mpeg",
   nullptr,
@@ -401,18 +401,19 @@ DecoderTraits::CanHandleMediaType(const 
 #endif
 #if defined(MOZ_WEBM) && !defined(MOZ_OMX_WEBM_DECODER)
   if (IsWebMType(nsDependentCString(aMIMEType))) {
     codecList = gWebMCodecs;
     result = CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_FMP4
-  if (IsMP4SupportedType(nsDependentCString(aMIMEType))) {
-    result = aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
+  if (MP4Decoder::CanHandleMediaType(nsDependentCString(aMIMEType),
+                                     aRequestedCodecs)) {
+    return aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_GSTREAMER
   if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType),
                                            aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) {
     if (aHaveRequestedCodecs)
       return CANPLAY_YES;
     return CANPLAY_MAYBE;
@@ -678,16 +679,24 @@ MediaDecoderReader* DecoderTraits::Creat
   if (false) {} // dummy if to take care of the dangling else
 
   return decoderReader;
 }
 
 /* static */
 bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
 {
+  // Forbid playing media in video documents if the user has opted
+  // not to, using either the legacy WMF specific pref, or the newer
+  // catch-all pref.
+  if (!Preferences::GetBool("media.windows-media-foundation.play-stand-alone", true) ||
+      !Preferences::GetBool("media.play-stand-alone", true)) {
+    return false;
+  }
+
   return
     IsOggType(aType) ||
 #ifdef MOZ_OMX_DECODER
     // We support amr inside WebApps on firefoxOS but not in general web content.
     // Ensure we dont create a VideoDocument when accessing amr URLs directly.
     (IsOmxSupportedType(aType) && !aType.EqualsASCII("audio/amr")) ||
 #endif
 #ifdef MOZ_WEBM
@@ -698,18 +707,17 @@ bool DecoderTraits::IsSupportedInVideoDo
 #endif
 #ifdef MOZ_ANDROID_OMX
     (MediaDecoder::IsAndroidMediaEnabled() && IsAndroidMediaType(aType)) ||
 #endif
 #ifdef MOZ_FMP4
     IsMP4SupportedType(aType) ||
 #endif
 #ifdef MOZ_WMF
-    (IsWMFSupportedType(aType) &&
-     Preferences::GetBool("media.windows-media-foundation.play-stand-alone", true)) ||
+    IsWMFSupportedType(aType) ||
 #endif
 #ifdef MOZ_DIRECTSHOW
     IsDirectShowSupportedType(aType) ||
 #endif
 #ifdef MOZ_APPLEMEDIA
     IsAppleMediaSupportedType(aType) ||
 #endif
 #ifdef NECKO_PROTOCOL_rtsp
--- a/content/media/VideoUtils.cpp
+++ b/content/media/VideoUtils.cpp
@@ -193,9 +193,41 @@ IsValidVideoRegion(const nsIntSize& aFra
 }
 
 TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool()
 {
   return SharedThreadPool::Get(NS_LITERAL_CSTRING("Media Decode"),
                                Preferences::GetUint("media.num-decode-threads", 25));
 }
 
+bool
+ExtractH264CodecDetails(const nsAString& aCodec,
+                        int16_t& aProfile,
+                        int16_t& aLevel)
+{
+  // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
+  // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
+  // We ignore the constraint_set flags, as it's not clear from any
+  // documentation what constraints the platform decoders support.
+  // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
+  // for more details.
+  if (aCodec.Length() != strlen("avc1.PPCCLL")) {
+    return false;
+  }
+
+  // Verify the codec starts with "avc1.".
+  const nsAString& sample = Substring(aCodec, 0, 5);
+  if (!sample.EqualsASCII("avc1.")) {
+    return false;
+  }
+
+  // Extract the profile_idc, constrains, and level_idc.
+  nsresult rv = NS_OK;
+  aProfile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  aLevel = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return true;
+}
+
 } // end namespace mozilla
--- a/content/media/VideoUtils.h
+++ b/content/media/VideoUtils.h
@@ -210,11 +210,50 @@ private:
 };
 
 class SharedThreadPool;
 
 // Returns the thread pool that is shared amongst all decoder state machines
 // for decoding streams.
 TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool();
 
+enum H264_PROFILE {
+  H264_PROFILE_UNKNOWN                     = 0,
+  H264_PROFILE_BASE                        = 0x42,
+  H264_PROFILE_MAIN                        = 0x4D,
+  H264_PROFILE_EXTENDED                    = 0x58,
+  H264_PROFILE_HIGH                        = 0x64,
+};
+
+enum H264_LEVEL {
+    H264_LEVEL_1         = 10,
+    H264_LEVEL_1_b       = 11,
+    H264_LEVEL_1_1       = 11,
+    H264_LEVEL_1_2       = 12,
+    H264_LEVEL_1_3       = 13,
+    H264_LEVEL_2         = 20,
+    H264_LEVEL_2_1       = 21,
+    H264_LEVEL_2_2       = 22,
+    H264_LEVEL_3         = 30,
+    H264_LEVEL_3_1       = 31,
+    H264_LEVEL_3_2       = 32,
+    H264_LEVEL_4         = 40,
+    H264_LEVEL_4_1       = 41,
+    H264_LEVEL_4_2       = 42,
+    H264_LEVEL_5         = 50,
+    H264_LEVEL_5_1       = 51,
+    H264_LEVEL_5_2       = 52
+};
+
+// Extracts the H.264/AVC profile and level from an H.264 codecs string.
+// H.264 codecs parameters have a type defined as avc1.PPCCLL, where
+// PP = profile_idc, CC = constraint_set flags, LL = level_idc.
+// See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
+// for more details.
+// Returns false on failure.
+bool
+ExtractH264CodecDetails(const nsAString& aCodecs,
+                        int16_t& aProfile,
+                        int16_t& aLevel);
+
 } // end namespace mozilla
 
 #endif
--- a/content/media/fmp4/MP4Decoder.cpp
+++ b/content/media/fmp4/MP4Decoder.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MP4Decoder.h"
 #include "MP4Reader.h"
 #include "MediaDecoderStateMachine.h"
 #include "mozilla/Preferences.h"
+#include "nsCharSeparatedTokenizer.h"
 #ifdef MOZ_EME
 #include "mozilla/CDMProxy.h"
 #endif
 #include "prlog.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
@@ -45,58 +46,87 @@ MP4Decoder::SetCDMProxy(CDMProxy* aProxy
     nsRefPtr<nsIRunnable> task(
       NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
     caps.CallOnMainThreadWhenCapsAvailable(task);
   }
   return NS_OK;
 }
 #endif
 
+static bool
+IsSupportedAudioCodec(const nsAString& aCodec)
+{
+  // AAC-LC, HE-AAC or MP3 in M4A.
+  return aCodec.EqualsASCII("mp4a.40.2") ||
+#ifndef MOZ_GONK_MEDIACODEC // B2G doesn't support MP3 in MP4 yet.
+         aCodec.EqualsASCII("mp3") ||
+#endif
+         aCodec.EqualsASCII("mp4a.40.5");
+}
+
+static bool
+IsSupportedH264Codec(const nsAString& aCodec)
+{
+  int16_t profile = 0, level = 0;
+
+  if (!ExtractH264CodecDetails(aCodec, profile, level)) {
+    return false;
+  }
+
+  // Just assume what we can play on all platforms the codecs/formats that
+  // WMF can play, since we don't have documentation about what other
+  // platforms can play... According to the WMF documentation:
+  // http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx
+  // "The Media Foundation H.264 video decoder is a Media Foundation Transform
+  // that supports decoding of Baseline, Main, and High profiles, up to level
+  // 5.1.". We also report that we can play Extended profile, as there are
+  // bitstreams that are Extended compliant that are also Baseline compliant.
+  return level >= H264_LEVEL_1 &&
+         level <= H264_LEVEL_5_1 &&
+         (profile == H264_PROFILE_BASE ||
+          profile == H264_PROFILE_MAIN ||
+          profile == H264_PROFILE_EXTENDED ||
+          profile == H264_PROFILE_HIGH);
+}
+
+/* static */
 bool
-MP4Decoder::GetSupportedCodecs(const nsACString& aType,
-                               char const *const ** aCodecList)
+MP4Decoder::CanHandleMediaType(const nsACString& aType,
+                               const nsAString& aCodecs)
 {
   if (!IsEnabled()) {
     return false;
   }
 
-  // AAC in M4A.
-  static char const *const aacAudioCodecs[] = {
-    "mp4a.40.2",    // AAC-LC
-    // TODO: AAC-HE ?
-    nullptr
-  };
-  if (aType.EqualsASCII("audio/mp4") ||
-      aType.EqualsASCII("audio/x-m4a")) {
-    if (aCodecList) {
-      *aCodecList = aacAudioCodecs;
-    }
-    return true;
+  if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
+    return aCodecs.IsEmpty() || IsSupportedAudioCodec(aCodecs);
+  }
+
+  if (!aType.EqualsASCII("video/mp4")) {
+    return false;
   }
 
-  // H.264 + AAC in MP4.
-  static char const *const h264Codecs[] = {
-    "avc1.42E01E",  // H.264 Constrained Baseline Profile Level 3.0
-    "avc1.42001E",  // H.264 Baseline Profile Level 3.0
-    "avc1.58A01E",  // H.264 Extended Profile Level 3.0
-    "avc1.4D401E",  // H.264 Main Profile Level 3.0
-    "avc1.64001E",  // H.264 High Profile Level 3.0
-    "avc1.64001F",  // H.264 High Profile Level 3.1
-    "mp4a.40.2",    // AAC-LC
-    // TODO: There must be more profiles here?
-    nullptr
-  };
-  if (aType.EqualsASCII("video/mp4")) {
-    if (aCodecList) {
-      *aCodecList = h264Codecs;
+  // Verify that all the codecs specifed are ones that we expect that
+  // we can play.
+  nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
+  bool expectMoreTokens = false;
+  while (tokenizer.hasMoreTokens()) {
+    const nsSubstring& token = tokenizer.nextToken();
+    expectMoreTokens = tokenizer.separatorAfterCurrentToken();
+    if (IsSupportedAudioCodec(token) || IsSupportedH264Codec(token)) {
+      continue;
     }
-    return true;
+    return false;
   }
+  if (expectMoreTokens) {
+    // Last codec name was empty
+    return false;
+  }
+  return true;
 
-  return false;
 }
 
 static bool
 IsFFmpegAvailable()
 {
 #ifndef MOZ_FFMPEG
   return false;
 #else
@@ -146,23 +176,23 @@ HavePlatformMPEGDecoders()
 {
   return Preferences::GetBool("media.fragmented-mp4.use-blank-decoder") ||
 #ifdef XP_WIN
          // We have H.264/AAC platform decoders on Windows Vista and up.
          IsVistaOrLater() ||
 #endif
          IsFFmpegAvailable() ||
          IsAppleAvailable() ||
-	 IsGonkMP4DecoderAvailable() ||
+         IsGonkMP4DecoderAvailable() ||
          // TODO: Other platforms...
          false;
 }
 
 /* static */
 bool
 MP4Decoder::IsEnabled()
 {
-  return HavePlatformMPEGDecoders() &&
-         Preferences::GetBool("media.fragmented-mp4.enabled");
+  return Preferences::GetBool("media.fragmented-mp4.enabled") &&
+         HavePlatformMPEGDecoders();
 }
 
 } // namespace mozilla
 
--- a/content/media/fmp4/MP4Decoder.h
+++ b/content/media/fmp4/MP4Decoder.h
@@ -23,22 +23,21 @@ public:
   }
 
   virtual MediaDecoderStateMachine* CreateStateMachine();
 
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
 #endif
 
-  // Returns true if aType is a MIME type that we can render with the
-  // a MP4 platform decoder backend. If aCodecList is non null,
-  // it is filled with a (static const) null-terminated list of strings
-  // denoting the codecs we'll playback.
-  static bool GetSupportedCodecs(const nsACString& aType,
-                                 char const *const ** aCodecList);
+  // Returns true if aMIMEType is a type that we think we can render with the
+  // a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
+  // with a comma-delimited list of codecs to check support for.
+  static bool CanHandleMediaType(const nsACString& aMIMEType,
+                                 const nsAString& aCodecs = EmptyString());
 
   // Returns true if the MP4 backend is preffed on, and we're running on a
   // platform that is likely to have decoders for the contained formats.
   static bool IsEnabled();
 };
 
 } // namespace mozilla
 
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -769,17 +769,20 @@ MP4Reader::Seek(int64_t aTime,
   return NS_OK;
 }
 
 void
 MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength,
                              int64_t aOffset)
 {
   if (NS_IsMainThread()) {
-    GetTaskQueue()->Dispatch(NS_NewRunnableMethod(this, &MP4Reader::UpdateIndex));
+    if (GetTaskQueue()) {
+      GetTaskQueue()->Dispatch(
+        NS_NewRunnableMethod(this, &MP4Reader::UpdateIndex));
+    }
   } else {
     UpdateIndex();
   }
 }
 
 void
 MP4Reader::UpdateIndex()
 {
--- a/content/media/fmp4/apple/AppleVTDecoder.cpp
+++ b/content/media/fmp4/apple/AppleVTDecoder.cpp
@@ -140,25 +140,27 @@ AppleVTDecoder::Drain()
 
 //
 // Implementation details.
 //
 
 // Context object to hold a copy of sample metadata.
 class FrameRef {
 public:
-  Microseconds timestamp;
+  Microseconds decode_timestamp;
+  Microseconds composition_timestamp;
   Microseconds duration;
   int64_t byte_offset;
   bool is_sync_point;
 
   explicit FrameRef(mp4_demuxer::MP4Sample* aSample)
   {
     MOZ_ASSERT(aSample);
-    timestamp = aSample->composition_timestamp;
+    decode_timestamp = aSample->decode_timestamp;
+    composition_timestamp = aSample->composition_timestamp;
     duration = aSample->duration;
     byte_offset = aSample->byte_offset;
     is_sync_point = aSample->is_sync_point;
   }
 };
 
 // Callback passed to the VideoToolbox decoder for returning data.
 // This needs to be static because the API takes a C-style pair of
@@ -175,19 +177,20 @@ PlatformCallback(void* decompressionOutp
 {
   LOG("AppleVideoDecoder %s status %d flags %d", __func__, status, flags);
 
   AppleVTDecoder* decoder =
     static_cast<AppleVTDecoder*>(decompressionOutputRefCon);
   nsAutoPtr<FrameRef> frameRef =
     nsAutoPtr<FrameRef>(static_cast<FrameRef*>(sourceFrameRefCon));
 
-  LOG("mp4 output frame %lld pts %lld duration %lld us%s",
+  LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
     frameRef->byte_offset,
-    frameRef->timestamp,
+    frameRef->decode_timestamp,
+    frameRef->composition_timestamp,
     frameRef->duration,
     frameRef->is_sync_point ? " keyframe" : ""
   );
 
   // Validate our arguments.
   if (status != noErr || !image) {
     NS_WARNING("VideoToolbox decoder returned no data");
     return;
@@ -297,53 +300,64 @@ AppleVTDecoder::OutputFrame(CVPixelBuffe
 
   // Copy the image data into our own format.
   nsAutoPtr<VideoData> data;
   data =
     VideoData::Create(info,
                       mImageContainer,
                       nullptr,
                       aFrameRef->byte_offset,
-                      aFrameRef->timestamp,
+                      aFrameRef->composition_timestamp,
                       aFrameRef->duration,
                       buffer,
                       aFrameRef->is_sync_point,
-                      aFrameRef->timestamp,
+                      aFrameRef->decode_timestamp,
                       visible);
   // Unlock the returned image data.
   CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
 
   if (!data) {
     NS_ERROR("Couldn't create VideoData for frame");
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
   // Frames come out in DTS order but we need to output them
   // in composition order.
   mReorderQueue.Push(data.forget());
-  if (mReorderQueue.Length() > 2) {
+  // Assume a frame with a PTS <= current DTS is ready.
+  while (mReorderQueue.Length() > 0) {
     VideoData* readyData = mReorderQueue.Pop();
-    mCallback->Output(readyData);
+    if (readyData->mTime <= aFrameRef->decode_timestamp) {
+      LOG("returning queued frame with pts %lld", readyData->mTime);
+      mCallback->Output(readyData);
+    } else {
+      LOG("requeued frame with pts %lld > %lld",
+          readyData->mTime, aFrameRef->decode_timestamp);
+      mReorderQueue.Push(readyData);
+      break;
+    }
   }
+  LOG("%llu decoded frames queued",
+      static_cast<unsigned long long>(mReorderQueue.Length()));
 
   return NS_OK;
 }
 
 // Helper to fill in a timestamp structure.
 static CMSampleTimingInfo
 TimingInfoFromSample(mp4_demuxer::MP4Sample* aSample)
 {
   CMSampleTimingInfo timestamp;
 
   timestamp.duration = CMTimeMake(aSample->duration, USECS_PER_S);
   timestamp.presentationTimeStamp =
     CMTimeMake(aSample->composition_timestamp, USECS_PER_S);
-  // No DTS value available from libstagefright.
-  timestamp.decodeTimeStamp = CMTimeMake(0, USECS_PER_S);
+  timestamp.decodeTimeStamp =
+    CMTimeMake(aSample->decode_timestamp, USECS_PER_S);
 
   return timestamp;
 }
 
 nsresult
 AppleVTDecoder::SubmitFrame(mp4_demuxer::MP4Sample* aSample)
 {
   // For some reason this gives me a double-free error with stagefright.
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
+++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
@@ -51,16 +51,17 @@ void
 FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(mp4_demuxer::MP4Sample* aSample)
 {
   AVPacket packet;
   av_init_packet(&packet);
 
   aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE);
   packet.data = aSample->data;
   packet.size = aSample->size;
+  packet.dts = aSample->decode_timestamp;
   packet.pts = aSample->composition_timestamp;
   packet.flags = aSample->is_sync_point ? AV_PKT_FLAG_KEY : 0;
   packet.pos = aSample->byte_offset;
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
     mCallback->Error();
     return;
deleted file mode 100644
--- a/content/media/test/can_play_type_mpeg.js
+++ /dev/null
@@ -1,51 +0,0 @@
-function check_mp4(v, enabled) {
-  function check(type, expected) {
-    var ex = enabled ? expected : "";
-    is(v.canPlayType(type), ex, type + "='" + ex + "'");
-  }
-
-  check("video/mp4", "maybe");
-  check("audio/mp4", "maybe");
-  check("audio/x-m4a", "maybe");
-
-  // Not the MIME type that other browsers respond to, so we won't either.
-  check("audio/m4a", "");
-  // Only Safari responds affirmatively to "audio/aac",
-  // so we'll let x-m4a cover aac support.
-  check("audio/aac", "");
-
-  check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably");
-  check("video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "probably");
-  check("video/mp4; codecs=\"avc1.58A01E, mp4a.40.2\"", "probably");
-  check("video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"", "probably");
-  check("video/mp4; codecs=\"avc1.64001E, mp4a.40.2\"", "probably");
-  check("video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"", "probably");
-
-  check("video/mp4; codecs=\"avc1.42E01E\"", "probably");
-  check("video/mp4; codecs=\"avc1.42001E\"", "probably");
-  check("video/mp4; codecs=\"avc1.58A01E\"", "probably");
-  check("video/mp4; codecs=\"avc1.4D401E\"", "probably");
-  check("video/mp4; codecs=\"avc1.64001E\"", "probably");
-  check("video/mp4; codecs=\"avc1.64001F\"", "probably");
-
-  check("audio/mp4; codecs=\"mp4a.40.2\"", "probably");
-  check("audio/mp4; codecs=mp4a.40.2", "probably");
-  check("audio/x-m4a; codecs=\"mp4a.40.2\"", "probably");
-  check("audio/x-m4a; codecs=mp4a.40.2", "probably");
-}
-
-function check_mp3(v, enabled) {
-  function check(type, expected) {
-    var ex = enabled ? expected : "";
-    is(v.canPlayType(type), ex, type + "='" + ex + "'");
-  }
-
-  check("audio/mpeg", "maybe");
-  check("audio/mp3", "maybe");
-
-  check("audio/mpeg; codecs=\"mp3\"", "probably");
-  check("audio/mpeg; codecs=mp3", "probably");
-
-  check("audio/mp3; codecs=\"mp3\"", "probably");
-  check("audio/mp3; codecs=mp3", "probably");
-}
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -103,17 +103,16 @@ support-files =
   bug580982.webm
   bug580982.webm^headers^
   bug603918.webm
   bug603918.webm^headers^
   bug604067.webm
   bug604067.webm^headers^
   bug883173.vtt
   can_play_type_dash.js
-  can_play_type_mpeg.js
   can_play_type_ogg.js
   can_play_type_wave.js
   can_play_type_webm.js
   cancellable_request.sjs
   chain.ogg
   chain.ogg^headers^
   chain.ogv
   chain.ogv^headers^
--- a/content/media/test/test_can_play_type_mpeg.html
+++ b/content/media/test/test_can_play_type_mpeg.html
@@ -12,19 +12,92 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <video id="v"></video>
 
 <pre id="test">
-<script src="can_play_type_mpeg.js"></script>
 <script>
 
+function check_mp4(v, enabled) {
+  function check(type, expected) {
+    var ex = enabled ? expected : "";
+    is(v.canPlayType(type), ex, type + "='" + ex + "'");
+  }
+
+  check("video/mp4", "maybe");
+  check("audio/mp4", "maybe");
+  check("audio/x-m4a", "maybe");
+
+  // Not the MIME type that other browsers respond to, so we won't either.
+  check("audio/m4a", "");
+  // Only Safari responds affirmatively to "audio/aac",
+  // so we'll let x-m4a cover aac support.
+  check("audio/aac", "");
+
+  // H.264 Constrained Baseline Profile Level 3.0, AAC-LC
+  check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably");
+
+  // H.264 Constrained Baseline Profile Level 3.0, mp3
+  check("video/mp4; codecs=\"avc1.42E01E, mp3\"", "probably");
+  
+  check("video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.58A01E, mp4a.40.2\"", "probably");
+  
+  const ProbablyIfNotLinux = !IsLinuxGStreamer() ? "probably" : "";
+  
+  // H.264 Main Profile Level 3.0, AAC-LC
+  check("video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"", "probably");
+  // H.264 Main Profile Level 3.1, AAC-LC
+  check("video/mp4; codecs=\"avc1.4D401F, mp4a.40.2\"", ProbablyIfNotLinux);
+  // H.264 Main Profile Level 4.0, AAC-LC
+  check("video/mp4; codecs=\"avc1.4D4028, mp4a.40.2\"", ProbablyIfNotLinux);
+  // H.264 High Profile Level 3.0, AAC-LC
+  check("video/mp4; codecs=\"avc1.64001E, mp4a.40.2\"", "probably");
+  // H.264 High Profile Level 3.1, AAC-LC
+  check("video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"", "probably");
+
+  check("video/mp4; codecs=\"avc1.42E01E\"", "probably");
+  check("video/mp4; codecs=\"avc1.42001E\"", "probably");
+  check("video/mp4; codecs=\"avc1.58A01E\"", "probably");
+  check("video/mp4; codecs=\"avc1.4D401E\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001F\"", "probably");
+
+  // AAC-LC
+  check("audio/mp4; codecs=\"mp4a.40.2\"", "probably");
+  check("audio/mp4; codecs=mp4a.40.2", "probably");
+  check("audio/x-m4a; codecs=\"mp4a.40.2\"", "probably");
+  check("audio/x-m4a; codecs=mp4a.40.2", "probably");
+
+  // HE-AAC v1
+  check("audio/mp4; codecs=\"mp4a.40.5\"", ProbablyIfNotLinux);
+  check("audio/mp4; codecs=mp4a.40.5", ProbablyIfNotLinux);
+  check("audio/x-m4a; codecs=\"mp4a.40.5\"", ProbablyIfNotLinux);
+  check("audio/x-m4a; codecs=mp4a.40.5", ProbablyIfNotLinux);
+  
+}
+
+function check_mp3(v, enabled) {
+  function check(type, expected) {
+    var ex = enabled ? expected : "";
+    is(v.canPlayType(type), ex, type + "='" + ex + "'");
+  }
+
+  check("audio/mpeg", "maybe");
+  check("audio/mp3", "maybe");
+
+  check("audio/mpeg; codecs=\"mp3\"", "probably");
+  check("audio/mpeg; codecs=mp3", "probably");
+
+  check("audio/mp3; codecs=\"mp3\"", "probably");
+  check("audio/mp3; codecs=mp3", "probably");
+}
+
 function IsWindowsVistaOrLater() {
   var re = /Windows NT (\d+\.\d)/;
   var winver = navigator.userAgent.match(re);
   return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.0;
 }
 
 function IsMacOSLionOrLater() {
   var re = /Mac OS X (\d+)\.(\d+)/;
@@ -40,16 +113,21 @@ function IsMacOSLionOrLater() {
 function getPref(name) {
   var pref = false;
   try {
     pref = SpecialPowers.getBoolPref(name);
   } catch(ex) { }
   return pref;
 }
 
+function IsLinuxGStreamer() {
+  return /Linux/.test(navigator.userAgent) &&
+         getPref("media.gstreamer.enabled");
+}
+
 // Check whether we should expect the new MP4Reader-based support to work.
 function IsMP4ReaderAvailable() {
   var prefs = getPref("media.fragmented-mp4.enabled") &&
               getPref("media.fragmented-mp4.exposed");
   return prefs && (IsWindowsVistaOrLater() || IsMacOSLionOrLater());
 }
 
 var haveMp4 = (getPref("media.windows-media-foundation.enabled") && IsWindowsVistaOrLater()) ||
--- a/content/media/wmf/WMFDecoder.cpp
+++ b/content/media/wmf/WMFDecoder.cpp
@@ -49,48 +49,27 @@ IsSupportedH264Codec(const nsAString& aC
 {
   // According to the WMF documentation:
   // http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx
   // "The Media Foundation H.264 video decoder is a Media Foundation Transform
   // that supports decoding of Baseline, Main, and High profiles, up to level
   // 5.1.". We also report that we can play Extended profile, as there are
   // bitstreams that are Extended compliant that are also Baseline compliant.
 
-  // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
-  // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
-  // We ignore the constraint_set flags, as it's not clear from the WMF
-  // documentation what constraints the WMF H.264 decoder supports.
-  // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
-  // for more details.
-  if (aCodec.Length() != strlen("avc1.PPCCLL")) {
-    return false;
-  }
-
-  // Verify the codec starts with "avc1.".
-  const nsAString& sample = Substring(aCodec, 0, 5);
-  if (!sample.EqualsASCII("avc1.")) {
+  int16_t profile = 0, level = 0;
+  if (!ExtractH264CodecDetails(aCodec, profile, level)) {
     return false;
   }
 
-  // Extract the profile_idc and level_idc. Note: the constraint_set flags
-  // are ignored, it's not clear from the WMF documentation if they make a
-  // difference.
-  nsresult rv = NS_OK;
-  const int32_t profile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  const int32_t level = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  return level >= eAVEncH264VLevel1 &&
-         level <= eAVEncH264VLevel5_1 &&
-         (profile == eAVEncH264VProfile_Base ||
-          profile == eAVEncH264VProfile_Main ||
-          profile == eAVEncH264VProfile_Extended ||
-          profile == eAVEncH264VProfile_High);
+  return level >= H264_LEVEL_1 &&
+         level <= H264_LEVEL_5_1 &&
+         (profile == H264_PROFILE_BASE ||
+          profile == H264_PROFILE_MAIN ||
+          profile == H264_PROFILE_EXTENDED ||
+          profile == H264_PROFILE_HIGH);
 }
 
 bool
 WMFDecoder::CanPlayType(const nsACString& aType,
                         const nsAString& aCodecs)
 {
   if (!MediaDecoder::IsWMFEnabled() ||
       NS_FAILED(LoadDLLs())) {
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -619,16 +619,17 @@ Console::WrapObject(JSContext* aCx)
   }
 
 METHOD(Log, "log")
 METHOD(Info, "info")
 METHOD(Warn, "warn")
 METHOD(Error, "error")
 METHOD(Exception, "exception")
 METHOD(Debug, "debug")
+METHOD(Table, "table")
 
 void
 Console::Trace(JSContext* aCx)
 {
   const Sequence<JS::Value> data;
   Method(aCx, MethodTrace, NS_LITERAL_STRING("trace"), data);
 }
 
--- a/dom/base/Console.h
+++ b/dom/base/Console.h
@@ -62,16 +62,19 @@ public:
 
   void
   Exception(JSContext* aCx, const Sequence<JS::Value>& aData);
 
   void
   Debug(JSContext* aCx, const Sequence<JS::Value>& aData);
 
   void
+  Table(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
   Trace(JSContext* aCx);
 
   void
   Dir(JSContext* aCx, const Sequence<JS::Value>& aData);
 
   void
   Group(JSContext* aCx, const Sequence<JS::Value>& aData);
 
@@ -106,16 +109,17 @@ private:
   enum MethodName
   {
     MethodLog,
     MethodInfo,
     MethodWarn,
     MethodError,
     MethodException,
     MethodDebug,
+    MethodTable,
     MethodTrace,
     MethodDir,
     MethodGroup,
     MethodGroupCollapsed,
     MethodGroupEnd,
     MethodTime,
     MethodTimeEnd,
     MethodAssert,
--- a/dom/base/test/test_console.xul
+++ b/dom/base/test/test_console.xul
@@ -10,16 +10,17 @@
     <iframe id="iframe" />
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
   ok("console" in window, "Console exists");
   window.console.log(42);
+  ok("table" in console, "Console has the 'table' method.");
   window.console = 42;
   is(window.console, 42, "Console is replacable");
 
   var frame = document.getElementById("iframe");
   ok(frame, "Frame must exist");
   frame.src="http://mochi.test:8888/tests/dom/base/test/file_empty.html";
   frame.onload = function() {
     ok("console" in frame.contentWindow, "Console exists in the iframe");
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -24,18 +24,17 @@ namespace dom {
 jsid s_length_id = JSID_VOID;
 
 bool
 DefineStaticJSVals(JSContext* cx)
 {
   return InternJSString(cx, s_length_id, "length");
 }
 
-
-const char HandlerFamily = 0;
+const char DOMProxyHandler::family = 0;
 
 js::DOMProxyShadowsResult
 DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
 {
   JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
   if (v.isObject()) {
     bool hasOwn;
     Rooted<JSObject*> object(cx, &v.toObject());
@@ -55,17 +54,17 @@ DOMProxyShadows(JSContext* cx, JS::Handl
 
   return hasOwn ? js::Shadows : js::DoesntShadowUnique;
 }
 
 // Store the information for the specialized ICs.
 struct SetDOMProxyInformation
 {
   SetDOMProxyInformation() {
-    js::SetDOMProxyInformation((const void*) &HandlerFamily,
+    js::SetDOMProxyInformation((const void*) &DOMProxyHandler::family,
                                js::PROXY_EXTRA_SLOT + JSPROXYSLOT_EXPANDO, DOMProxyShadows);
   }
 };
 
 SetDOMProxyInformation gSetDOMProxyInformation;
 
 // static
 JSObject*
@@ -358,10 +357,30 @@ IdToInt32(JSContext* cx, JS::Handle<jsid
 bool
 DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                            JS::MutableHandle<JS::Value> vp, bool *done) const
 {
   *done = false;
   return true;
 }
 
+//static
+JSObject *
+DOMProxyHandler::GetExpandoObject(JSObject *obj)
+{
+  MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+  JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+  if (v.isObject()) {
+    return &v.toObject();
+  }
+
+  if (v.isUndefined()) {
+    return nullptr;
+  }
+
+  js::ExpandoAndGeneration* expandoAndGeneration =
+    static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+  v = expandoAndGeneration->expando;
+  return v.isUndefined() ? nullptr : &v.toObject();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -19,27 +19,16 @@ namespace mozilla {
 namespace dom {
 
 enum {
   JSPROXYSLOT_EXPANDO = 0
 };
 
 template<typename T> struct Prefable;
 
-// This variable exists solely to provide a unique address for use as an identifier.
-extern const char HandlerFamily;
-inline const void* ProxyFamily() { return &HandlerFamily; }
-
-inline bool IsDOMProxy(JSObject *obj)
-{
-    const js::Class* clasp = js::GetObjectClass(obj);
-    return clasp->isProxy() &&
-           js::GetProxyHandler(obj)->family() == ProxyFamily();
-}
-
 class BaseDOMProxyHandler : public js::BaseProxyHandler
 {
 public:
   explicit BaseDOMProxyHandler(const void* aProxyFamily, bool aHasPrototype = false)
     : js::BaseProxyHandler(aProxyFamily, aHasPrototype)
   {}
 
   // Implementations of traps that can be implemented in terms of
@@ -84,17 +73,17 @@ protected:
                                     bool ignoreNamedProps,
                                     JS::MutableHandle<JSPropertyDescriptor> desc) const = 0;
 };
 
 class DOMProxyHandler : public BaseDOMProxyHandler
 {
 public:
   DOMProxyHandler()
-    : BaseDOMProxyHandler(ProxyFamily())
+    : BaseDOMProxyHandler(&family)
   {
   }
 
   bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
   bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                       JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE
   {
     bool unused;
@@ -116,39 +105,33 @@ public:
   /*
    * If assigning to proxy[id] hits a named setter with OverrideBuiltins or
    * an indexed setter, call it and set *done to true on success. Otherwise, set
    * *done to false.
    */
   virtual bool setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                          JS::MutableHandle<JS::Value> vp, bool *done) const;
 
-  static JSObject* GetExpandoObject(JSObject* obj)
-  {
-    MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
-    JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
-    if (v.isObject()) {
-      return &v.toObject();
-    }
+  static JSObject* GetExpandoObject(JSObject* obj);
 
-    if (v.isUndefined()) {
-      return nullptr;
-    }
-
-    js::ExpandoAndGeneration* expandoAndGeneration =
-      static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
-    v = expandoAndGeneration->expando;
-    return v.isUndefined() ? nullptr : &v.toObject();
-  }
   /* GetAndClearExpandoObject does not DROP or clear the preserving wrapper flag. */
   static JSObject* GetAndClearExpandoObject(JSObject* obj);
   static JSObject* EnsureExpandoObject(JSContext* cx,
                                        JS::Handle<JSObject*> obj);
+
+  static const char family;
 };
 
+inline bool IsDOMProxy(JSObject *obj)
+{
+    const js::Class* clasp = js::GetObjectClass(obj);
+    return clasp->isProxy() &&
+           js::GetProxyHandler(obj)->family() == &DOMProxyHandler::family;
+}
+
 inline const DOMProxyHandler*
 GetDOMProxyHandler(JSObject* obj)
 {
   MOZ_ASSERT(IsDOMProxy(obj));
   return static_cast<const DOMProxyHandler*>(js::GetProxyHandler(obj));
 }
 
 extern jsid s_length_id;
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -67,16 +67,18 @@
 #include "mozilla/EnumeratedArrayCycleCollection.h"
 
 #include "Layers.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "mozilla/layers/ShadowLayers.h"
 #endif
 
+#include <queue>
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla::layers;
 
 WebGLObserver::WebGLObserver(WebGLContext* aContext)
     : mContext(aContext)
@@ -430,23 +432,21 @@ WebGLContext::SetContextOptions(JSContex
 
     WebGLContextOptions newOpts;
 
     newOpts.stencil = attributes.mStencil;
     newOpts.depth = attributes.mDepth;
     newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
     newOpts.antialias = attributes.mAntialias;
     newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
+
     if (attributes.mAlpha.WasPassed()) {
-      newOpts.alpha = attributes.mAlpha.Value();
+        newOpts.alpha = attributes.mAlpha.Value();
     }
 
-    // enforce that if stencil is specified, we also give back depth
-    newOpts.depth |= newOpts.stencil;
-
     // Don't do antialiasing if we've disabled MSAA.
     if (!gfxPrefs::MSAALevel()) {
       newOpts.antialias = false;
     }
 
 #if 0
     GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
                newOpts.antialias ? 1 : 0,
@@ -466,266 +466,467 @@ WebGLContext::SetContextOptions(JSContex
     mOptions = newOpts;
     return NS_OK;
 }
 
 #ifdef DEBUG
 int32_t
 WebGLContext::GetWidth() const
 {
-  return mWidth;
+    return mWidth;
 }
 
 int32_t
 WebGLContext::GetHeight() const
 {
-  return mHeight;
+    return mHeight;
 }
 #endif
 
+/* So there are a number of points of failure here. We might fail based
+ * on EGL vs. WGL, or we might fail to alloc a too-large size, or we
+ * might not be able to create a context with a certain combo of context
+ * creation attribs.
+ *
+ * We don't want to test the complete fallback matrix. (for now, at
+ * least) Instead, attempt creation in this order:
+ * 1. By platform API. (e.g. EGL vs. WGL)
+ * 2. By context creation attribs.
+ * 3. By size.
+ *
+ * That is, try to create headless contexts based on the platform API.
+ * Next, create dummy-sized backbuffers for the contexts with the right
+ * caps. Finally, resize the backbuffer to an acceptable size given the
+ * requested size.
+ */
+
+static bool
+IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
+{
+    int32_t status;
+    if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(feature, &status)))
+        return false;
+
+    return status != nsIGfxInfo::FEATURE_STATUS_OK;
+}
+
+static already_AddRefed<GLContext>
+CreateHeadlessNativeGL(bool forceEnabled,
+                       const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                       WebGLContext* webgl)
+{
+    if (!forceEnabled &&
+        IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
+    {
+        webgl->GenerateWarning("Refused to create native OpenGL context"
+                               " because of blacklisting.");
+        return nullptr;
+    }
+
+    nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateHeadless();
+    if (!gl) {
+        webgl->GenerateWarning("Error during native OpenGL init.");
+        return nullptr;
+    }
+    MOZ_ASSERT(!gl->IsANGLE());
+
+    return gl.forget();
+}
+
+// Note that we have a separate call for ANGLE and EGL, even though
+// right now, we get ANGLE implicitly by using EGL on Windows.
+// Eventually, we want to be able to pick ANGLE-EGL or native EGL.
+static already_AddRefed<GLContext>
+CreateHeadlessANGLE(bool forceEnabled,
+                    const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                    WebGLContext* webgl)
+{
+    nsRefPtr<GLContext> gl;
+
+#ifdef XP_WIN
+    if (!forceEnabled &&
+        IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_ANGLE))
+    {
+        webgl->GenerateWarning("Refused to create ANGLE OpenGL context"
+                               " because of blacklisting.");
+        return nullptr;
+    }
+
+    gl = gl::GLContextProviderEGL::CreateHeadless();
+    if (!gl) {
+        webgl->GenerateWarning("Error during ANGLE OpenGL init.");
+        return nullptr;
+    }
+    MOZ_ASSERT(gl->IsANGLE());
+#endif
+
+    return gl.forget();
+}
+
+static already_AddRefed<GLContext>
+CreateHeadlessEGL(bool forceEnabled,
+                  const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                  WebGLContext* webgl)
+{
+    nsRefPtr<GLContext> gl;
+
+#ifdef ANDROID
+    gl = gl::GLContextProviderEGL::CreateHeadless();
+    if (!gl) {
+        webgl->GenerateWarning("Error during EGL OpenGL init.");
+        return nullptr;
+    }
+    MOZ_ASSERT(!gl->IsANGLE());
+#endif
+
+    return gl.forget();
+}
+
+
+static already_AddRefed<GLContext>
+CreateHeadlessGL(bool forceEnabled,
+                 const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                 WebGLContext* webgl)
+{
+    bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
+    bool disableANGLE = Preferences::GetBool("webgl.disable-angle", false);
+
+    if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
+        disableANGLE = true;
+    }
+
+    nsRefPtr<GLContext> gl;
+
+    if (preferEGL)
+        gl = CreateHeadlessEGL(forceEnabled, gfxInfo, webgl);
+
+    if (!gl && !disableANGLE)
+        gl = CreateHeadlessANGLE(forceEnabled, gfxInfo, webgl);
+
+    if (!gl)
+        gl = CreateHeadlessNativeGL(forceEnabled, gfxInfo, webgl);
+
+    return gl.forget();
+}
+
+// Try to create a dummy offscreen with the given caps.
+static bool
+CreateOffscreenWithCaps(GLContext* gl, const SurfaceCaps& caps)
+{
+    gfx::IntSize dummySize(16, 16);
+    return gl->InitOffscreen(dummySize, caps);
+}
+
+static void
+PopulateCapFallbackQueue(const SurfaceCaps& baseCaps,
+                         std::queue<SurfaceCaps>* fallbackCaps)
+{
+    fallbackCaps->push(baseCaps);
+
+    // Dropping antialias drops our quality, but not our correctness.
+    // The user basically doesn't have to handle if this fails, they
+    // just get reduced quality.
+    if (baseCaps.antialias) {
+        SurfaceCaps nextCaps(baseCaps);
+        nextCaps.antialias = false;
+        PopulateCapFallbackQueue(nextCaps, fallbackCaps);
+    }
+
+    // If we have to drop one of depth or stencil, we'd prefer to keep
+    // depth. However, the client app will need to handle if this
+    // doesn't work.
+    if (baseCaps.stencil) {
+        SurfaceCaps nextCaps(baseCaps);
+        nextCaps.stencil = false;
+        PopulateCapFallbackQueue(nextCaps, fallbackCaps);
+    }
+
+    if (baseCaps.depth) {
+        SurfaceCaps nextCaps(baseCaps);
+        nextCaps.depth = false;
+        PopulateCapFallbackQueue(nextCaps, fallbackCaps);
+    }
+}
+
+static bool
+CreateOffscreen(GLContext* gl,
+                const WebGLContextOptions& options,
+                const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                WebGLContext* webgl,
+                layers::ISurfaceAllocator* surfAllocator)
+{
+    SurfaceCaps baseCaps;
+
+    baseCaps.color = true;
+    baseCaps.alpha = options.alpha;
+    baseCaps.antialias = options.antialias;
+    baseCaps.depth = options.depth;
+    baseCaps.preserve = options.preserveDrawingBuffer;
+    baseCaps.stencil = options.stencil;
+
+    // we should really have this behind a
+    // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
+    // for now it's just behind a pref for testing/evaluation.
+    baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
+
+#ifdef MOZ_WIDGET_GONK
+    baseCaps.surfaceAllocator = surfAllocator;
+#endif
+
+    // Done with baseCaps construction.
+
+    bool forceAllowAA = Preferences::GetBool("webgl.msaa-force", false);
+    if (!forceAllowAA &&
+        IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
+    {
+        webgl->GenerateWarning("Disallowing antialiased backbuffers due"
+                               " to blacklisting.");
+        baseCaps.antialias = false;
+    }
+
+    std::queue<SurfaceCaps> fallbackCaps;
+    PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
+
+    bool created = false;
+    while (!fallbackCaps.empty()) {
+        SurfaceCaps& caps = fallbackCaps.front();
+
+        created = CreateOffscreenWithCaps(gl, caps);
+        if (created)
+            break;
+
+        fallbackCaps.pop();
+    }
+
+    return created;
+}
+
+bool
+WebGLContext::CreateOffscreenGL(bool forceEnabled)
+{
+    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+
+    layers::ISurfaceAllocator* surfAllocator = nullptr;
+#ifdef MOZ_WIDGET_GONK
+    nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
+    if (docWidget) {
+        layers::LayerManager* layerManager = docWidget->GetLayerManager();
+        if (layerManager) {
+            // XXX we really want "AsSurfaceAllocator" here for generality
+            layers::ShadowLayerForwarder* forwarder = layerManager->AsShadowForwarder();
+            if (forwarder) {
+                surfAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
+            }
+        }
+    }
+#endif
+
+    gl = CreateHeadlessGL(forceEnabled, gfxInfo, this);
+
+    do {
+        if (!gl)
+            break;
+
+        if (!CreateOffscreen(gl, mOptions, gfxInfo, this, surfAllocator))
+            break;
+
+        if (!InitAndValidateGL())
+            break;
+
+        return true;
+    } while (false);
+
+    gl = nullptr;
+    return false;
+}
+
+// Fallback for resizes:
+bool
+WebGLContext::ResizeBackbuffer(uint32_t requestedWidth, uint32_t requestedHeight)
+{
+    uint32_t width = requestedWidth;
+    uint32_t height = requestedHeight;
+
+    bool resized = false;
+    while (width || height) {
+      width = width ? width : 1;
+      height = height ? height : 1;
+
+      gfx::IntSize curSize(width, height);
+      if (gl->ResizeOffscreen(curSize)) {
+          resized = true;
+          break;
+      }
+
+      width /= 2;
+      height /= 2;
+    }
+
+    if (!resized)
+        return false;
+
+    mWidth = gl->OffscreenSize().width;
+    mHeight = gl->OffscreenSize().height;
+    MOZ_ASSERT((uint32_t)mWidth == width);
+    MOZ_ASSERT((uint32_t)mHeight == height);
+
+    if (width != requestedWidth ||
+        height != requestedHeight)
+    {
+        GenerateWarning("Requested size %dx%d was too large, but resize"
+                          " to %dx%d succeeded.",
+                        requestedWidth, requestedHeight,
+                        width, height);
+    }
+    return true;
+}
+
+
 NS_IMETHODIMP
-WebGLContext::SetDimensions(int32_t width, int32_t height)
+WebGLContext::SetDimensions(int32_t sWidth, int32_t sHeight)
 {
     // Early error return cases
+    if (!GetCanvas())
+        return NS_ERROR_FAILURE;
 
-    if (width < 0 || height < 0) {
+    if (sWidth < 0 || sHeight < 0) {
         GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    if (!GetCanvas())
-        return NS_ERROR_FAILURE;
+    uint32_t width = sWidth;
+    uint32_t height = sHeight;
 
     // Early success return cases
-
     GetCanvas()->InvalidateCanvas();
 
-    if (gl && mWidth == width && mHeight == height)
-        return NS_OK;
-
     // Zero-sized surfaces can cause problems.
     if (width == 0) {
         width = 1;
     }
     if (height == 0) {
         height = 1;
     }
 
     // If we already have a gl context, then we just need to resize it
     if (gl) {
+        if ((uint32_t)mWidth == width &&
+            (uint32_t)mHeight == height)
+        {
+            return NS_OK;
+        }
+
+        if (IsContextLost())
+            return NS_OK;
+
         MakeContextCurrent();
 
         // If we've already drawn, we should commit the current buffer.
         PresentScreenBuffer();
 
         // ResizeOffscreen scraps the current prod buffer before making a new one.
-        gl->ResizeOffscreen(gfx::IntSize(width, height)); // Doesn't matter if it succeeds (soft-fail)
-        // It's unlikely that we'll get a proper-sized context if we recreate if we didn't on resize
+        if (!ResizeBackbuffer(width, height)) {
+            GenerateWarning("WebGL context failed to resize.");
+            ForceLoseContext();
+            return NS_OK;
+        }
 
         // everything's good, we're done here
-        mWidth = gl->OffscreenSize().width;
-        mHeight = gl->OffscreenSize().height;
         mResetLayer = true;
-
         mBackbufferNeedsClear = true;
 
         return NS_OK;
     }
 
     // End of early return cases.
     // At this point we know that we're not just resizing an existing context,
     // we are initializing a new context.
 
     // if we exceeded either the global or the per-principal limit for WebGL contexts,
     // lose the oldest-used context now to free resources. Note that we can't do that
     // in the WebGLContext constructor as we don't have a canvas element yet there.
     // Here is the right place to do so, as we are about to create the OpenGL context
     // and that is what can fail if we already have too many.
     LoseOldestWebGLContextIfLimitExceeded();
 
-    // Get some prefs for some preferred/overriden things
-    NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
-
-#ifdef XP_WIN
-    bool preferEGL =
-        Preferences::GetBool("webgl.prefer-egl", false);
-    bool preferOpenGL =
-        Preferences::GetBool("webgl.prefer-native-gl", false);
-#endif
-    bool forceEnabled =
-        Preferences::GetBool("webgl.force-enabled", false);
-    bool disabled =
-        Preferences::GetBool("webgl.disabled", false);
-    bool prefer16bit =
-        Preferences::GetBool("webgl.prefer-16bpp", false);
-
-    ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
-
-    if (disabled)
-        return NS_ERROR_FAILURE;
-
     // We're going to create an entirely new context.  If our
     // generation is not 0 right now (that is, if this isn't the first
     // context we're creating), we may have to dispatch a context lost
     // event.
 
     // If incrementing the generation would cause overflow,
     // don't allow it.  Allowing this would allow us to use
     // resource handles created from older context generations.
-    if (!(mGeneration + 1).isValid())
+    if (!(mGeneration + 1).isValid()) {
+        GenerateWarning("Too many WebGL contexts created this run.");
         return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
-
-    SurfaceCaps caps;
-
-    caps.color = true;
-    caps.alpha = mOptions.alpha;
-    caps.depth = mOptions.depth;
-    caps.stencil = mOptions.stencil;
-
-    // we should really have this behind a
-    // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
-    // for now it's just behind a pref for testing/evaluation.
-    caps.bpp16 = prefer16bit;
-
-    caps.preserve = mOptions.preserveDrawingBuffer;
-
-#ifdef MOZ_WIDGET_GONK
-    nsIWidget *docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
-    if (docWidget) {
-        layers::LayerManager *layerManager = docWidget->GetLayerManager();
-        if (layerManager) {
-            // XXX we really want "AsSurfaceAllocator" here for generality
-            layers::ShadowLayerForwarder *forwarder = layerManager->AsShadowForwarder();
-            if (forwarder) {
-                caps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
-            }
-        }
-    }
-#endif
-
-    bool forceMSAA =
-        Preferences::GetBool("webgl.msaa-force", false);
-
-    int32_t status;
-    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
-    if (mOptions.antialias &&
-        gfxInfo &&
-        NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) {
-        if (status == nsIGfxInfo::FEATURE_STATUS_OK || forceMSAA) {
-            caps.antialias = true;
-        }
     }
 
-#ifdef XP_WIN
-    if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) {
-        preferEGL = true;
-    }
-#endif
-
-    // Ask GfxInfo about what we should use
-    bool useOpenGL = true;
-
-#ifdef XP_WIN
-    bool useANGLE = true;
-#endif
+    // Get some prefs for some preferred/overriden things
+    NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
 
-    if (gfxInfo && !forceEnabled) {
-        if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) {
-            if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-                useOpenGL = false;
-            }
-        }
-#ifdef XP_WIN
-        if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) {
-            if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-                useANGLE = false;
-            }
-        }
-#endif
+    bool disabled = Preferences::GetBool("webgl.disabled", false);
+    if (disabled) {
+        GenerateWarning("WebGL creation is disabled, and so disallowed here.");
+        return NS_ERROR_FAILURE;
     }
 
-#ifdef XP_WIN
-    // allow forcing GL and not EGL/ANGLE
-    if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
-        preferEGL = false;
-        useANGLE = false;
-        useOpenGL = true;
-    }
-#endif
-
-    gfxIntSize size(width, height);
+    // Alright, now let's start trying.
+    bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
+    ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
 
-#ifdef XP_WIN
-    // if we want EGL, try it now
-    if (!gl && (preferEGL || useANGLE) && !preferOpenGL) {
-        gl = gl::GLContextProviderEGL::CreateOffscreen(size, caps);
-        if (!gl || !InitAndValidateGL()) {
-            GenerateWarning("Error during ANGLE OpenGL ES initialization");
-            return NS_ERROR_FAILURE;
-        }
+    if (!CreateOffscreenGL(forceEnabled)) {
+        GenerateWarning("WebGL creation failed.");
+        return NS_ERROR_FAILURE;
     }
-#endif
+    MOZ_ASSERT(gl);
 
-    // try the default provider, whatever that is
-    if (!gl && useOpenGL) {
-        gl = gl::GLContextProvider::CreateOffscreen(size, caps);
-        if (gl && !InitAndValidateGL()) {
-            GenerateWarning("Error during OpenGL initialization");
-            return NS_ERROR_FAILURE;
-        }
-    }
-
-    if (!gl) {
-        GenerateWarning("Can't get a usable WebGL context");
+    if (!ResizeBackbuffer(width, height)) {
+        GenerateWarning("Initializing WebGL backbuffer failed.");
         return NS_ERROR_FAILURE;
     }
 
 #ifdef DEBUG
     if (gl->DebugMode()) {
         printf_stderr("--- WebGL context created: %p\n", gl.get());
     }
 #endif
 
-    mWidth = width;
-    mHeight = height;
-    mViewportWidth = width;
-    mViewportHeight = height;
     mResetLayer = true;
     mOptionsFrozen = true;
 
     // increment the generation number
     ++mGeneration;
-#if 0
-    if (mGeneration > 0) {
-        // XXX dispatch context lost event
-    }
-#endif
 
     MakeContextCurrent();
 
+    gl->fViewport(0, 0, mWidth, mHeight);
+    mViewportWidth = mWidth;
+    mViewportHeight = mHeight;
+
     // Make sure that we clear this out, otherwise
     // we'll end up displaying random memory
     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
     AssertCachedBindings();
     AssertCachedState();
 
     // Clear immediately, because we need to present the cleared initial
     // buffer.
     mBackbufferNeedsClear = true;
     ClearBackbufferIfNeeded();
 
     mShouldPresent = true;
 
-    MOZ_ASSERT(gl->Caps().color == caps.color);
-    MOZ_ASSERT(gl->Caps().alpha == caps.alpha);
-    MOZ_ASSERT(gl->Caps().depth == caps.depth || !gl->Caps().depth);
-    MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil);
-    MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias);
-    MOZ_ASSERT(gl->Caps().preserve == caps.preserve);
+    MOZ_ASSERT(gl->Caps().color);
+    MOZ_ASSERT(gl->Caps().alpha == mOptions.alpha);
+    MOZ_ASSERT(gl->Caps().depth == mOptions.depth || !gl->Caps().depth);
+    MOZ_ASSERT(gl->Caps().stencil == mOptions.stencil || !gl->Caps().stencil);
+    MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias || !gl->Caps().antialias);
+    MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer);
 
     AssertCachedBindings();
     AssertCachedState();
 
     reporter.SetSuccessful();
     return NS_OK;
 }
 
@@ -1206,17 +1407,17 @@ WebGLContext::PresentScreenBuffer()
 
     if (!mShouldPresent) {
         return false;
     }
 
     gl->MakeCurrent();
     MOZ_ASSERT(!mBackbufferNeedsClear);
     if (!gl->PublishFrame()) {
-        this->ForceLoseContext();
+        ForceLoseContext();
         return false;
     }
 
     if (!mOptions.preserveDrawingBuffer) {
         mBackbufferNeedsClear = true;
     }
 
     mShouldPresent = false;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1031,17 +1031,19 @@ protected:
 
     bool InitWebGL2();
 
 
     // -------------------------------------------------------------------------
     // Validation functions (implemented in WebGLContextValidate.cpp)
     GLenum BaseTexFormat(GLenum internalFormat) const;
 
+    bool CreateOffscreenGL(bool forceEnabled);
     bool InitAndValidateGL();
+    bool ResizeBackbuffer(uint32_t width, uint32_t height);
     bool ValidateBlendEquationEnum(GLenum cap, const char *info);
     bool ValidateBlendFuncDstEnum(GLenum mode, const char *info);
     bool ValidateBlendFuncSrcEnum(GLenum mode, const char *info);
     bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char *info);
     bool ValidateTextureTargetEnum(GLenum target, const char *info);
     bool ValidateComparisonEnum(GLenum target, const char *info);
     bool ValidateStencilOpEnum(GLenum action, const char *info);
     bool ValidateFaceEnum(GLenum face, const char *info);
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -170,20 +170,20 @@ pref(webgl.force-layers-readback,true) r
 
 # Check that our experimental prefs still work:
 
 # 16bpp:
 skip-if(winWidget) pref(webgl.prefer-16bpp,true)                                        random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?16bpp           wrapper.html?colors.png
 skip-if(winWidget) pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-color-test.html?16bpp&readback  wrapper.html?colors.png
 
 # Force native GL (Windows):
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true)                                == webgl-clear-test.html?native-gl        wrapper.html?green.png
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true)                                == webgl-orientation-test.html?native-gl  wrapper.html?white-top-left.png
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true)                                == webgl-color-test.html?native-gl        wrapper.html?colors.png
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true) pref(webgl.prefer-16bpp,true)  == webgl-color-test.html?native-gl&16bpp  wrapper.html?colors.png
+skip-if(!winWidget) pref(webgl.disable-angle,true)                                == webgl-clear-test.html?native-gl        wrapper.html?green.png
+skip-if(!winWidget) pref(webgl.disable-angle,true)                                == webgl-orientation-test.html?native-gl  wrapper.html?white-top-left.png
+skip-if(!winWidget) pref(webgl.disable-angle,true)                                == webgl-color-test.html?native-gl        wrapper.html?colors.png
+skip-if(!winWidget) pref(webgl.disable-angle,true) pref(webgl.prefer-16bpp,true)  == webgl-color-test.html?native-gl&16bpp  wrapper.html?colors.png
 
 
 # Non-WebGL Reftests!
 
 # Do we correctly handle multiple clip paths?
 != clip-multiple-paths.html clip-multiple-paths-badref.html
 
 # Bug 815648
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -2276,16 +2276,44 @@ PeerConnectionWrapper.prototype = {
       ok(numRemoteCandidates, "Have remotecandidate stat(s)");
     } else {
       is(numLocalCandidates, 0, "Have no localcandidate stats");
       is(numRemoteCandidates, 0, "Have no remotecandidate stats");
     }
   },
 
   /**
+   * Property-matching function for finding a certain stat in passed-in stats
+   *
+   * @param {object} stats
+   *        The stats to check from this PeerConnectionWrapper
+   * @param {object} props
+   *        The properties to look for
+   * @returns {boolean} Whether an entry containing all match-props was found.
+   */
+  hasStat : function PCW_hasStat(stats, props) {
+    for (var key in stats) {
+      if (stats.hasOwnProperty(key)) {
+        var res = stats[key];
+        var match = true;
+        for (var prop in props) {
+          if (res[prop] !== props[prop]) {
+            match = false;
+            break;
+          }
+        }
+        if (match) {
+          return true;
+        }
+      }
+    }
+    return false;
+  },
+
+  /**
    * Closes the connection
    */
   close : function PCW_close() {
     // It might be that a test has already closed the pc. In those cases
     // we should not fail.
     try {
       this._pc.close();
       info(this + ": Closed connection.");
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -408,16 +408,184 @@ var commandsPeerConnection = [
   [
     'PC_REMOTE_CHECK_STATS',
     function (test) {
       test.pcRemote.getStats(null, function(stats) {
         test.pcRemote.checkStats(stats);
         test.next();
       });
     }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat outbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat outbound video rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat inbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat inbound video rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat outbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat outbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat inbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat inbound video rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
   ]
 ];
 
 
 /**
  * Default list of commands to execute for a Datachannel test.
  */
 var commandsDataChannel = [
--- a/dom/plugins/ipc/hangui/moz.build
+++ b/dom/plugins/ipc/hangui/moz.build
@@ -13,16 +13,17 @@ UNIFIED_SOURCES += [
     'PluginHangUIChild.cpp',
 ]
 include('/ipc/chromium/chromium-config.mozbuild')
 
 DEFINES['NS_NO_XPCOM'] = True
 DEFINES['_HAS_EXCEPTIONS'] = 0
 
 DISABLE_STL_WRAPPING = True
+USE_STATIC_LIBS = True
 
 if CONFIG['GNU_CC']:
     WIN32_EXE_LDFLAGS += ['-municode']
 
 RCINCLUDE = 'HangUIDlg.rc'
 
 OS_LIBS += [
     'comctl32',
--- a/dom/plugins/test/mochitest/dialog_watcher.js
+++ b/dom/plugins/test/mochitest/dialog_watcher.js
@@ -92,22 +92,32 @@ DialogWatcher.prototype.init = function(
   if (!this.getWindowTextW) {
     this.getWindowTextW = user32.declare("GetWindowTextW",
                                          ctypes.winapi_abi,
                                          ctypes.int,
                                          ctypes.uintptr_t,
                                          ctypes.jschar.ptr,
                                          ctypes.int);
   }
+  if (!this.messageBox) {
+    // Handy for debugging this code
+    this.messageBox = user32.declare("MessageBoxW",
+                                     ctypes.winapi_abi,
+                                     ctypes.int,
+                                     ctypes.uintptr_t,
+                                     ctypes.jschar.ptr,
+                                     ctypes.jschar.ptr,
+                                     ctypes.uint32_t);
+  }
 };
 
 DialogWatcher.prototype.getWindowText = function(hwnd) {
   var bufType = ctypes.ArrayType(ctypes.jschar);
   var buffer = new bufType(256);
-  
+
   if (this.getWindowTextW(hwnd, buffer, buffer.length)) {
     return buffer.readString();
   }
 };
 
 DialogWatcher.prototype.processWindowEvents = function(timeout) {
   var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
     var nhwnd = Number(hwnd)
@@ -149,23 +159,25 @@ DialogWatcher.prototype.processWindowEve
   }
 
   if (!timeout) {
     timeout = INFINITE;
   }
 
   var waitStatus = WAIT_OBJECT_0;
   var expectingStart = this.onDialogStart && this.hwnd === undefined;
+  var startWaitTime = Date.now();
   while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) {
     waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ?
                                                             INFINITE : timeout, 0);
     if (waitStatus == WAIT_OBJECT_0) {
       var msg = new this.msgType;
       this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE);
-    } else if (waitStatus == WAIT_TIMEOUT) {
+    }
+    if (waitStatus == WAIT_TIMEOUT || (Date.now() - startWaitTime) >= timeout) {
       break;
     }
   }
 
   this.unhookWinEvent(hook);
   // Returns true if the hook was successful, something was found, and we never timed out
   return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0;
 };
--- a/dom/plugins/test/mochitest/test_hangui.xul
+++ b/dom/plugins/test/mochitest/test_hangui.xul
@@ -4,26 +4,29 @@
                  type="text/css"?>
 <window title="Basic Plugin Tests"
   xmlns:html="http://www.w3.org/1999/xhtml"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <title>Plugin Hang UI Test</title>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript"
+          src="utils.js" />
+  <script type="application/javascript"
           src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hang_test.js" />
   <script type="application/javascript"
           src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hangui_common.js" />
 
 <body xmlns="http://www.w3.org/1999/xhtml">
   <iframe id="iframe1" src="hangui_subpage.html" width="400" height="400"></iframe>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const hangUITimeoutPref = "dom.ipc.plugins.hangUITimeoutSecs";
 const hangUIMinDisplayPref = "dom.ipc.plugins.hangUIMinDisplaySecs";
 const timeoutPref = "dom.ipc.plugins.timeoutSecs";
 
 var worker = new ChromeWorker("hangui_iface.js");
@@ -95,17 +98,18 @@ function finishTest() {
 function runTests() {
   if (!SimpleTest.testPluginIsOOP()) {
     ok(true, "Skipping this test when test plugin is not OOP.");
     SimpleTest.finish();
   }
 
   resetVars();
 
-  hanguiExpect("Prime ChromeWorker", false, false, "test1");
+  hanguiOperation("Prime ChromeWorker", 0, false, false, HANGUIOP_NOTHING, 0,
+                  false, "test1");
 }
 
 window.frameLoaded = runTests;
 
 var obsCount = 0;
 
 function onPluginCrashedHangUI(aEvent) {
   ok(true, "Plugin crashed notification received");
@@ -239,17 +243,17 @@ function test4() {
 }
 
 function test3() {
   hanguiContinue("test3: Continue button works", false, "test4");
   p.stall(STALL_DURATION);
 }
 
 function test2() {
-  // This test is identical to test1 because there were some bugs where the 
+  // This test is identical to test1 because there were some bugs where the
   // Hang UI would show on the first hang but not on subsequent hangs
   hanguiExpect("test2: Plugin Hang UI is showing", true, true, "test3");
   p.stall(STALL_DURATION);
 }
 
 function test1() {
   SpecialPowers.setIntPref(hangUITimeoutPref, 1);
   SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -491,16 +491,27 @@ nsGeolocationRequest::StopTimeoutTimer()
 void
 nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
 {
   if (mShutdown) {
     // Ignore SendLocationEvents issued before we were cleared.
     return;
   }
 
+  if (mOptions && mOptions->mMaximumAge > 0) {
+    DOMTimeStamp positionTime_ms;
+    aPosition->GetTimestamp(&positionTime_ms);
+    const uint32_t maximumAge_ms = mOptions->mMaximumAge;
+    const bool isTooOld =
+        DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) > positionTime_ms;
+    if (isTooOld) {
+      return;
+    }
+  }
+
   nsRefPtr<Position> wrapped;
 
   if (aPosition) {
     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
     aPosition->GetCoords(getter_AddRefs(coords));
     if (coords) {
       wrapped = new Position(ToSupports(mLocator), aPosition);
     }
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
 #include "GonkGPSGeolocationProvider.h"
 
 #include <pthread.h>
 #include <hardware/gps.h>
 
+#include "mozilla/Constants.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsGeoPosition.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsINetworkManager.h"
 #include "nsIObserverService.h"
 #include "nsJSUtils.h"
@@ -76,18 +77,18 @@ GonkGPSGeolocationProvider::LocationCall
   class UpdateLocationEvent : public nsRunnable {
   public:
     UpdateLocationEvent(nsGeoPosition* aPosition)
       : mPosition(aPosition)
     {}
     NS_IMETHOD Run() {
       nsRefPtr<GonkGPSGeolocationProvider> provider =
         GonkGPSGeolocationProvider::GetSingleton();
-      provider->mLastGPSDerivedLocationTime = PR_Now();
       nsCOMPtr<nsIGeolocationUpdate> callback = provider->mLocationCallback;
+      provider->mLastGPSPosition = mPosition;
       if (callback) {
         callback->Update(mPosition);
       }
       return NS_OK;
     }
   private:
     nsRefPtr<nsGeoPosition> mPosition;
   };
@@ -96,17 +97,24 @@ GonkGPSGeolocationProvider::LocationCall
 
   nsRefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude,
                                                         location->longitude,
                                                         location->altitude,
                                                         location->accuracy,
                                                         location->accuracy,
                                                         location->bearing,
                                                         location->speed,
-                                                        location->timestamp);
+                                                        PR_Now() / PR_USEC_PER_MSEC);
+  // Note above: Can't use location->timestamp as the time from the satellite is a
+  // minimum of 16 secs old (see http://leapsecond.com/java/gpsclock.htm).
+  // All code from this point on expects the gps location to be timestamped with the
+  // current time, most notably: the geolocation service which respects maximumAge
+  // set in the DOM JS.
+
+
   NS_DispatchToMainThread(new UpdateLocationEvent(somewhere));
 }
 
 void
 GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
 {
 }
 
@@ -694,51 +702,82 @@ GonkGPSGeolocationProvider::NetworkLocat
     return NS_ERROR_FAILURE;
   }
 
   double lat, lon, acc;
   coords->GetLatitude(&lat);
   coords->GetLongitude(&lon);
   coords->GetAccuracy(&acc);
 
-  double delta = MAXFLOAT;
+  double delta = -1.0;
 
   static double sLastMLSPosLat = 0;
   static double sLastMLSPosLon = 0;
 
   if (0 != sLastMLSPosLon || 0 != sLastMLSPosLat) {
     // Use spherical law of cosines to calculate difference
     // Not quite as correct as the Haversine but simpler and cheaper
     // Should the following be a utility function? Others might need this calc.
-    const double radsInDeg = 3.14159265 / 180.0;
+    const double radsInDeg = M_PI / 180.0;
     const double rNewLat = lat * radsInDeg;
     const double rNewLon = lon * radsInDeg;
     const double rOldLat = sLastMLSPosLat * radsInDeg;
     const double rOldLon = sLastMLSPosLon * radsInDeg;
     // WGS84 equatorial radius of earth = 6378137m
-    delta = acos( (sin(rNewLat) * sin(rOldLat)) +
-                  (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon)) )
-                  * 6378137;
+    double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
+                      (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
+    if (cosDelta > 1.0) {
+      cosDelta = 1.0;
+    } else if (cosDelta < -1.0) {
+      cosDelta = -1.0;
+    }
+    delta = acos(cosDelta) * 6378137;
   }
 
   sLastMLSPosLat = lat;
   sLastMLSPosLon = lon;
 
   // if the MLS coord change is smaller than this arbitrarily small value
   // assume the MLS coord is unchanged, and stick with the GPS location
   const double kMinMLSCoordChangeInMeters = 10;
 
-  // if we haven't seen anything from the GPS device for 10s,
-  // use this network derived location.
-  const int kMaxGPSDelayBeforeConsideringMLS = 10000;
-  int64_t diff = PR_Now() - provider->mLastGPSDerivedLocationTime;
-  if (provider->mLocationCallback && diff > kMaxGPSDelayBeforeConsideringMLS
-      && delta > kMinMLSCoordChangeInMeters)
-  {
-    provider->mLocationCallback->Update(position);
+  DOMTimeStamp time_ms = 0;
+  if (provider->mLastGPSPosition) {
+    provider->mLastGPSPosition->GetTimestamp(&time_ms);
+  }
+  const int64_t diff_ms = (PR_Now() / PR_USEC_PER_MSEC) - time_ms;
+
+  // We want to distinguish between the GPS being inactive completely
+  // and temporarily inactive. In the former case, we would use a low
+  // accuracy network location; in the latter, we only want a network
+  // location that appears to updating with movement.
+
+  const bool isGPSFullyInactive = diff_ms > 1000 * 60 * 2; // two mins
+  const bool isGPSTempInactive = diff_ms > 1000 * 10; // 10 secs
+
+  if (provider->mLocationCallback) {
+    if (isGPSFullyInactive ||
+       (isGPSTempInactive && delta > kMinMLSCoordChangeInMeters))
+    {
+      if (gGPSDebugging) {
+        nsContentUtils::LogMessageToConsole("geo: Using MLS, GPS age:%fs, MLS Delta:%fm\n",
+                                            diff_ms / 1000.0, delta);
+      }
+      provider->mLocationCallback->Update(position);
+    } else if (provider->mLastGPSPosition) {
+      if (gGPSDebugging) {
+        nsContentUtils::LogMessageToConsole("geo: Using old GPS age:%fs\n",
+                                            diff_ms / 1000.0);
+      }
+
+      // This is a fallback case so that the GPS provider responds with its last
+      // location rather than waiting for a more recent GPS or network location.
+      // The service decides if the location is too old, not the provider.
+      provider->mLocationCallback->Update(provider->mLastGPSPosition);
+    }
   }
 
   provider->InjectLocation(lat, lon, acc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GonkGPSGeolocationProvider::NetworkLocationUpdate::LocationUpdatePending()
@@ -774,17 +813,16 @@ GonkGPSGeolocationProvider::Startup()
   if (mNetworkLocationProvider) {
     nsresult rv = mNetworkLocationProvider->Startup();
     if (NS_SUCCEEDED(rv)) {
       nsRefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate();
       mNetworkLocationProvider->Watch(update);
     }
   }
 
-  mLastGPSDerivedLocationTime = 0;
   mStarted = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/system/gonk/GonkGPSGeolocationProvider.h
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.h
@@ -16,16 +16,17 @@
 
 #ifndef GonkGPSGeolocationProvider_h
 #define GonkGPSGeolocationProvider_h
 
 #include <hardware/gps.h> // for GpsInterface
 #include "nsCOMPtr.h"
 #include "nsIGeolocationProvider.h"
 #include "nsIObserver.h"
+#include "nsIDOMGeoPosition.h"
 #ifdef MOZ_B2G_RIL
 #include "nsIRadioInterfaceLayer.h"
 #endif
 #include "nsISettingsService.h"
 
 class nsIThread;
 
 #define GONK_GPS_GEOLOCATION_PROVIDER_CID \
@@ -106,19 +107,19 @@ private:
 
   const GpsInterface* mGpsInterface;
 #ifdef MOZ_B2G_RIL
   const AGpsInterface* mAGpsInterface;
   const AGpsRilInterface* mAGpsRilInterface;
   nsCOMPtr<nsIRadioInterface> mRadioInterface;
 #endif
   nsCOMPtr<nsIGeolocationUpdate> mLocationCallback;
-  PRTime mLastGPSDerivedLocationTime;
   nsCOMPtr<nsIThread> mInitThread;
   nsCOMPtr<nsIGeolocationProvider> mNetworkLocationProvider;
+  nsCOMPtr<nsIDOMGeoPosition> mLastGPSPosition;
 
   class NetworkLocationUpdate : public nsIGeolocationUpdate
   {
     public:
       NS_DECL_ISUPPORTS
       NS_DECL_NSIGEOLOCATIONUPDATE
 
       NetworkLocationUpdate() {}
--- a/dom/tests/mochitest/general/test_consoleAPI.html
+++ b/dom/tests/mochitest/general/test_consoleAPI.html
@@ -32,16 +32,17 @@ function doTest() {
     "groupCollapsed": "function",
     "groupEnd": "function",
     "time": "function",
     "timeEnd": "function",
     "profile": "function",
     "profileEnd": "function",
     "assert": "function",
     "count": "function",
+    "table": "function",
     "__noSuchMethod__": "function"
   };
 
   var foundProps = 0;
   for (var prop in console) {
     foundProps++;
     is(typeof(console[prop]), expectedProps[prop], "expect console prop " + prop + " exists");
   }
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -8,16 +8,17 @@
  Exposed=(Window,Worker)]
 interface Console {
   void log(any... data);
   void info(any... data);
   void warn(any... data);
   void error(any... data);
   void _exception(any... data);
   void debug(any... data);
+  void table(any... data);
   void trace();
   void dir(any... data);
   void group(any... data);
   void groupCollapsed(any... data);
   void groupEnd(any... data);
   void time(optional any time);
   void timeEnd(optional any time);
 
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -22,16 +22,17 @@ enum RTCStatsType {
 dictionary RTCStats {
   DOMHighResTimeStamp timestamp;
   RTCStatsType type;
   DOMString id;
 };
 
 dictionary RTCRTPStreamStats : RTCStats {
   DOMString ssrc;
+  DOMString mediaType;
   DOMString remoteId;
   boolean isRemote = false;
   DOMString mediaTrackId;
   DOMString transportId;
   DOMString codecId;
 
   // Video encoder/decoder measurements (absent for rtcp)
   double bitrateMean;
--- a/gfx/angle/src/libGLESv2/renderer/d3d9/RenderTarget9.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/d3d9/RenderTarget9.cpp
@@ -75,17 +75,19 @@ RenderTarget9::RenderTarget9(Renderer *r
         {
             requiresInitialization = gl_d3d9::RequiresTextureDataInitialization(internalFormat);
 
             result = device->CreateRenderTarget(width, height, renderFormat,
                                                 gl_d3d9::GetMultisampleType(supportedSamples),
                                                 0, FALSE, &mRenderTarget, NULL);
         }
 
-        if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
+        if (result == D3DERR_OUTOFVIDEOMEMORY ||
+            result == E_INVALIDARG ||
+            result == E_OUTOFMEMORY)
         {
             gl::error(GL_OUT_OF_MEMORY);
 
             return;
         }
 
         ASSERT(SUCCEEDED(result));
 
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -33,29 +33,34 @@ RenderbufferStorageBySamples(GLContext* 
                                              aSize.width, aSize.height);
     } else {
         aGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
                                   aInternalFormat,
                                   aSize.width, aSize.height);
     }
 }
 
-
 GLuint
 CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
               GLenum aType, const gfx::IntSize& aSize, bool linear)
 {
     GLuint tex = 0;
     aGL->fGenTextures(1, &tex);
     ScopedBindTexture autoTex(aGL, tex);
 
-    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, linear ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST);
-    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, linear ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST);
-    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
-    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
+                        LOCAL_GL_TEXTURE_MIN_FILTER, linear ? LOCAL_GL_LINEAR
+                                                            : LOCAL_GL_NEAREST);
+    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
+                        LOCAL_GL_TEXTURE_MAG_FILTER, linear ? LOCAL_GL_LINEAR
+                                                            : LOCAL_GL_NEAREST);
+    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
+                        LOCAL_GL_CLAMP_TO_EDGE);
+    aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
+                        LOCAL_GL_CLAMP_TO_EDGE);
 
     aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
                      0,
                      aInternalFormat,
                      aSize.width, aSize.height,
                      0,
                      aFormat,
                      aType,
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -272,17 +272,17 @@ GLContext::GLContext(const SurfaceCaps& 
     mIsOffscreen(isOffscreen),
     mContextLost(false),
     mVersion(0),
     mProfile(ContextProfile::Unknown),
     mVendor(GLVendor::Other),
     mRenderer(GLRenderer::Other),
     mHasRobustness(false),
 #ifdef DEBUG
-    mGLError(LOCAL_GL_NO_ERROR),
+    mIsInLocalErrorCheck(false),
 #endif
     mSharedContext(sharedContext),
     mCaps(caps),
     mScreen(nullptr),
     mLockedSurface(nullptr),
     mMaxTextureSize(0),
     mMaxCubeMapTextureSize(0),
     mMaxTextureImageSize(0),
@@ -1107,33 +1107,33 @@ GLContext::InitWithPrefix(const char *pr
     }
 
     if (mInitialized) {
         raw_fGetIntegerv(LOCAL_GL_VIEWPORT, mViewportRect);
         raw_fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mScissorRect);
         raw_fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
         raw_fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mMaxCubeMapTextureSize);
         raw_fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mMaxRenderbufferSize);
+        raw_fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
 
 #ifdef XP_MACOSX
         if (mWorkAroundDriverBugs) {
             if (mVendor == GLVendor::Intel) {
                 // see bug 737182 for 2D textures, bug 684882 for cube map textures.
                 mMaxTextureSize        = std::min(mMaxTextureSize,        4096);
                 mMaxCubeMapTextureSize = std::min(mMaxCubeMapTextureSize, 512);
                 // for good measure, we align renderbuffers on what we do for 2D textures
                 mMaxRenderbufferSize   = std::min(mMaxRenderbufferSize,   4096);
                 mNeedsTextureSizeChecks = true;
             } else if (mVendor == GLVendor::NVIDIA) {
                 if (nsCocoaFeatures::OnMountainLionOrLater()) {
                     // See bug 879656.  8192 fails, 8191 works.
                     mMaxTextureSize = std::min(mMaxTextureSize, 8191);
                     mMaxRenderbufferSize = std::min(mMaxRenderbufferSize, 8191);
-                }
-                else {
+                } else {
                     // See bug 877949.
                     mMaxTextureSize = std::min(mMaxTextureSize, 4096);
                     mMaxRenderbufferSize = std::min(mMaxRenderbufferSize, 4096);
                 }
 
                 // Part of the bug 879656, but it also doesn't hurt the 877949
                 mNeedsTextureSizeChecks = true;
             }
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -7,16 +7,17 @@
 #ifndef GLCONTEXT_H_
 #define GLCONTEXT_H_
 
 #include <stdio.h>
 #include <stdint.h>
 #include <ctype.h>
 #include <map>
 #include <bitset>
+#include <queue>
 
 #ifdef DEBUG
 #include <string.h>
 #endif
 
 #ifdef WIN32
 #include <windows.h>
 #endif
@@ -500,38 +501,33 @@ private:
     /**
      * Is this feature supported using the core (unsuffixed) symbols?
      */
     bool IsFeatureProvidedByCoreSymbols(GLFeature feature);
 
 // -----------------------------------------------------------------------------
 // Robustness handling
 public:
-
     bool HasRobustness() const {
         return mHasRobustness;
     }
 
     /**
      * The derived class is expected to provide information on whether or not it
      * supports robustness.
      */
     virtual bool SupportsRobustness() const = 0;
 
-
 private:
     bool mHasRobustness;
 
-
 // -----------------------------------------------------------------------------
 // Error handling
 public:
-
-    static const char* GLErrorToString(GLenum aError)
-    {
+    static const char* GLErrorToString(GLenum aError) {
         switch (aError) {
             case LOCAL_GL_INVALID_ENUM:
                 return "GL_INVALID_ENUM";
             case LOCAL_GL_INVALID_VALUE:
                 return "GL_INVALID_VALUE";
             case LOCAL_GL_INVALID_OPERATION:
                 return "GL_INVALID_OPERATION";
             case LOCAL_GL_STACK_OVERFLOW:
@@ -544,57 +540,124 @@ public:
                 return "GL_TABLE_TOO_LARGE";
             case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
                 return "GL_INVALID_FRAMEBUFFER_OPERATION";
             default:
                 return "";
         }
     }
 
-
     /** \returns the first GL error, and guarantees that all GL error flags are cleared,
      * i.e. that a subsequent GetError call will return NO_ERROR
      */
-    GLenum GetAndClearError()
-    {
+    GLenum GetAndClearError() {
         // the first error is what we want to return
         GLenum error = fGetError();
 
         if (error) {
             // clear all pending errors
             while(fGetError()) {}
         }
 
         return error;
     }
 
-
-    /*** In GL debug mode, we completely override glGetError ***/
-
-    GLenum fGetError()
-    {
-#ifdef DEBUG
-        // debug mode ends up eating the error in AFTER_GL_CALL
-        if (DebugMode()) {
-            GLenum err = mGLError;
-            mGLError = LOCAL_GL_NO_ERROR;
+private:
+    GLenum raw_fGetError() {
+        return mSymbols.fGetError();
+    }
+
+    std::queue<GLenum> mGLErrorQueue;
+
+public:
+    GLenum fGetError() {
+        if (!mGLErrorQueue.empty()) {
+            GLenum err = mGLErrorQueue.front();
+            mGLErrorQueue.pop();
             return err;
         }
-#endif // DEBUG
-
-        return mSymbols.fGetError();
+
+        return GetUnpushedError();
+    }
+
+private:
+    GLenum GetUnpushedError() {
+        return raw_fGetError();
+    }
+
+    void ClearUnpushedErrors() {
+        while (GetUnpushedError()) {
+            // Discard errors.
+        }
+    }
+
+    GLenum GetAndClearUnpushedErrors() {
+        GLenum err = GetUnpushedError();
+        if (err) {
+            ClearUnpushedErrors();
+        }
+        return err;
+    }
+
+    void PushError(GLenum err) {
+        mGLErrorQueue.push(err);
+    }
+
+    void GetAndPushAllErrors() {
+        while (true) {
+            GLenum err = GetUnpushedError();
+            if (!err)
+                break;
+
+            PushError(err);
+        }
     }
 
-
-#ifdef DEBUG
+    ////////////////////////////////////
+    // Use this safer option.
 private:
-
-    GLenum mGLError;
-#endif // DEBUG
-
+#ifdef DEBUG
+    bool mIsInLocalErrorCheck;
+#endif
+
+public:
+    class ScopedLocalErrorCheck {
+        GLContext* const mGL;
+        bool mHasBeenChecked;
+
+    public:
+        ScopedLocalErrorCheck(GLContext* gl)
+            : mGL(gl)
+            , mHasBeenChecked(false)
+        {
+#ifdef DEBUG
+            MOZ_ASSERT(!mGL->mIsInLocalErrorCheck);
+            mGL->mIsInLocalErrorCheck = true;
+#endif
+            mGL->GetAndPushAllErrors();
+        }
+
+        GLenum GetLocalError() {
+#ifdef DEBUG
+            MOZ_ASSERT(mGL->mIsInLocalErrorCheck);
+            mGL->mIsInLocalErrorCheck = false;
+#endif
+
+            MOZ_ASSERT(!mHasBeenChecked);
+            mHasBeenChecked = true;
+
+            return mGL->GetAndClearUnpushedErrors();
+        }
+
+        ~ScopedLocalErrorCheck() {
+            MOZ_ASSERT(mHasBeenChecked);
+        }
+    };
+
+private:
     static void GLAPIENTRY StaticDebugCallback(GLenum source,
                                                GLenum type,
                                                GLuint id,
                                                GLenum severity,
                                                GLsizei length,
                                                const GLchar* message,
                                                const GLvoid* userParam);
     void DebugCallback(GLenum source,
@@ -619,18 +682,17 @@ private:
 #  define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
 # elif defined(_MSC_VER)
 #  define MOZ_FUNCTION_NAME __FUNCTION__
 # else
 #  define MOZ_FUNCTION_NAME __func__  // defined in C99, supported in various C++ compilers. Just raw function name.
 # endif
 #endif
 
-    void BeforeGLCall(const char* glFunction)
-    {
+    void BeforeGLCall(const char* glFunction) {
         MOZ_ASSERT(IsCurrent());
         if (DebugMode()) {
             GLContext *currentGLContext = nullptr;
 
             currentGLContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS);
 
             if (DebugMode() & DebugTrace)
                 printf_stderr("[gl:%p] > %s\n", this, glFunction);
@@ -638,31 +700,33 @@ private:
                 printf_stderr("Fatal: %s called on non-current context %p. "
                               "The current context for this thread is %p.\n",
                               glFunction, this, currentGLContext);
                 NS_ABORT();
             }
         }
     }
 
-    void AfterGLCall(const char* glFunction)
-    {
+    void AfterGLCall(const char* glFunction) {
         if (DebugMode()) {
             // calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
             // the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
             // tend to be meaningless
             mSymbols.fFinish();
-            mGLError = mSymbols.fGetError();
+            GLenum err = GetUnpushedError();
+            PushError(err);
+
             if (DebugMode() & DebugTrace)
-                printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, mGLError);
-            if (mGLError != LOCAL_GL_NO_ERROR) {
+                printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, err);
+
+            if (err != LOCAL_GL_NO_ERROR) {
                 printf_stderr("GL ERROR: %s generated GL error %s(0x%04x)\n",
                               glFunction,
-                              GLErrorToString(mGLError),
-                              mGLError);
+                              GLErrorToString(err),
+                              err);
                 if (DebugMode() & DebugAbortOnError)
                     NS_ABORT();
             }
         }
     }
 
     GLContext *TrackingContext()
     {
@@ -2980,16 +3044,17 @@ protected:
 
     GLint mViewportRect[4];
     GLint mScissorRect[4];
 
     GLint mMaxTextureSize;
     GLint mMaxCubeMapTextureSize;
     GLint mMaxTextureImageSize;
     GLint mMaxRenderbufferSize;
+    GLint mMaxViewportDims[2];
     GLsizei mMaxSamples;
     bool mNeedsTextureSizeChecks;
     bool mWorkAroundDriverBugs;
 
     bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width, GLsizei height) const {
         if (mNeedsTextureSizeChecks) {
             // some drivers incorrectly handle some large texture sizes that are below the
             // max texture size that they report. So we check ourselves against our own values
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -243,34 +243,42 @@ CreateOffscreenFBOContext(bool aShare = 
 
     SurfaceCaps dummyCaps = SurfaceCaps::Any();
     nsRefPtr<GLContextCGL> glContext = new GLContextCGL(dummyCaps, shareContext, context, true);
 
     return glContext.forget();
 }
 
 already_AddRefed<GLContext>
+GLContextProviderCGL::CreateHeadless()
+{
+    nsRefPtr<GLContextCGL> glContext = CreateOffscreenFBOContext();
+    if (!glContext)
+        return nullptr;
+
+    if (!glContext->Init())
+        return nullptr;
+
+    return glContext.forget();
+}
+
+already_AddRefed<GLContext>
 GLContextProviderCGL::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps)
 {
-    nsRefPtr<GLContextCGL> glContext = CreateOffscreenFBOContext();
-    if (glContext &&
-        glContext->Init() &&
-        glContext->InitOffscreen(ToIntSize(size), caps))
-    {
-        return glContext.forget();
-    }
+    nsRefPtr<GLContext> glContext = CreateHeadless();
+    if (!glContext->InitOffscreen(ToIntSize(size), caps))
+        return nullptr;
 
-    // everything failed
-    return nullptr;
+    return glContext.forget();
 }
 
 static nsRefPtr<GLContext> gGlobalContext;
 
-GLContext *
+GLContext*
 GLContextProviderCGL::GetGlobalContext()
 {
     if (!sCGLLibrary.EnsureInitialized()) {
         return nullptr;
     }
 
     if (!gGlobalContext) {
         // There are bugs in some older drivers with pbuffers less
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -873,43 +873,52 @@ GLContextEGL::CreateEGLPixmapOffscreenCo
         return nullptr;
     }
 
     glContext->HoldSurface(thebesSurface);
 
     return glContext.forget();
 }
 
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateHeadless()
+{
+    if (!sEGLLibrary.EnsureInitialized()) {
+        return nullptr;
+    }
+
+    gfxIntSize dummySize = gfxIntSize(16, 16);
+    nsRefPtr<GLContext> glContext;
+    glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(dummySize);
+    if (!glContext)
+        return nullptr;
+
+    return glContext.forget();
+}
+
 // Under EGL, on Android, pbuffers are supported fine, though
 // often without the ability to texture from them directly.
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
-        return nullptr;
-    }
-
-    gfxIntSize dummySize = gfxIntSize(16, 16);
-    nsRefPtr<GLContextEGL> glContext;
-    glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(dummySize);
-
+    nsRefPtr<GLContext> glContext = CreateHeadless();
     if (!glContext)
         return nullptr;
 
     if (!glContext->InitOffscreen(ToIntSize(size), caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 // Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
 // and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
 // and 3) each EGL context eats 750k on B2G (bug 813783)
-GLContext *
+GLContext*
 GLContextProviderEGL::GetGlobalContext()
 {
     return nullptr;
 }
 
 void
 GLContextProviderEGL::Shutdown()
 {
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1208,23 +1208,31 @@ DONE_CREATING_PIXMAP:
                                                   true,
                                                   xsurface);
     }
 
     return glContext.forget();
 }
 
 already_AddRefed<GLContext>
+GLContextProviderGLX::CreateHeadless()
+{
+    gfxIntSize dummySize = gfxIntSize(16, 16);
+    nsRefPtr<GLContext> glContext = CreateOffscreenPixmapContext(dummySize);
+    if (!glContext)
+        return nullptr;
+
+    return glContext.forget();
+}
+
+already_AddRefed<GLContext>
 GLContextProviderGLX::CreateOffscreen(const gfxIntSize& size,
                                       const SurfaceCaps& caps)
 {
-    gfxIntSize dummySize = gfxIntSize(16, 16);
-    nsRefPtr<GLContextGLX> glContext =
-        CreateOffscreenPixmapContext(dummySize);
-
+    nsRefPtr<GLContext> glContext = CreateHeadless();
     if (!glContext)
         return nullptr;
 
     if (!glContext->InitOffscreen(ToIntSize(size), caps))
         return nullptr;
 
     return glContext.forget();
 }
--- a/gfx/gl/GLContextProviderImpl.h
+++ b/gfx/gl/GLContextProviderImpl.h
@@ -55,16 +55,20 @@ public:
      * @param aFormat The ContextFormat for this offscreen context.
      *
      * @return Context to use for offscreen rendering
      */
     static already_AddRefed<GLContext>
     CreateOffscreen(const gfxIntSize& size,
                     const SurfaceCaps& caps);
 
+    // Just create a context. We'll add offscreen stuff ourselves.
+    static already_AddRefed<GLContext>
+    CreateHeadless();
+
     /**
      * Create wrapping Gecko GLContext for external gl context.
      *
      * @param aContext External context which will be wrapped by Gecko GLContext.
      * @param aSurface External surface which is used for external context.
      *
      * @return Wrapping Context to use for rendering
      */
--- a/gfx/gl/GLContextProviderNull.cpp
+++ b/gfx/gl/GLContextProviderNull.cpp
@@ -17,18 +17,23 @@ GLContextProviderNull::CreateForWindow(n
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateOffscreen(const gfxIntSize&,
-                                       const SurfaceCaps&,
-                                       ContextFlags)
+                                       const SurfaceCaps&)
+{
+    return nullptr;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderNull::CreateHeadless()
 {
     return nullptr;
 }
 
 GLContext*
 GLContextProviderNull::GetGlobalContext(ContextFlags)
 {
     return nullptr;
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -602,18 +602,17 @@ CreateWindowOffscreenContext()
     nsRefPtr<GLContextWGL> glContext = new GLContextWGL(caps,
                                                         shareContext, true,
                                                         dc, context, win);
 
     return glContext.forget();
 }
 
 already_AddRefed<GLContext>
-GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size,
-                                      const SurfaceCaps& caps)
+GLContextProviderWGL::CreateHeadless()
 {
     if (!sWGLLib.EnsureInitialized()) {
         return nullptr;
     }
 
     nsRefPtr<GLContextWGL> glContext;
 
     // Always try to create a pbuffer context first, because we
@@ -631,16 +630,28 @@ GLContextProviderWGL::CreateOffscreen(co
     }
 
     if (!glContext ||
         !glContext->Init())
     {
         return nullptr;
     }
 
+    nsRefPtr<GLContext> retGL = glContext;
+    return retGL.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size,
+                                      const SurfaceCaps& caps)
+{
+    nsRefPtr<GLContext> glContext = CreateHeadless();
+    if (!glContext)
+        return nullptr;
+
     if (!glContext->InitOffscreen(ToIntSize(size), caps))
         return nullptr;
 
     return glContext.forget();
 }
 
 static nsRefPtr<GLContextWGL> gGlobalContext;
 
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -563,27 +563,30 @@ DrawBuffer::Create(GLContext* const gl,
     } else {
         if (!formats.depth)
             pDepthRB = nullptr;
 
         if (!formats.stencil)
             pStencilRB = nullptr;
     }
 
+    GLContext::ScopedLocalErrorCheck localError(gl);
+
     CreateRenderbuffersForOffscreen(gl, formats, size, caps.antialias,
                                     pColorMSRB, pDepthRB, pStencilRB);
 
     GLuint fb = 0;
     gl->fGenFramebuffers(1, &fb);
     gl->AttachBuffersToFB(0, colorMSRB, depthRB, stencilRB, fb);
 
     UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, fb, colorMSRB,
                                               depthRB, stencilRB) );
 
-    if (!gl->IsFramebufferComplete(fb))
+    GLenum err = localError.GetLocalError();
+    if (err || !gl->IsFramebufferComplete(fb))
         return false;
 
     *out_buffer = Move(ret);
     return true;
 }
 
 DrawBuffer::~DrawBuffer()
 {
@@ -619,16 +622,18 @@ ReadBuffer::Create(GLContext* gl,
     }
 
     GLuint depthRB = 0;
     GLuint stencilRB = 0;
 
     GLuint* pDepthRB   = caps.depth   ? &depthRB   : nullptr;
     GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr;
 
+    GLContext::ScopedLocalErrorCheck localError(gl);
+
     CreateRenderbuffersForOffscreen(gl, formats, surf->mSize, caps.antialias,
                                     nullptr, pDepthRB, pStencilRB);
 
     GLuint colorTex = 0;
     GLuint colorRB = 0;
     GLenum target = 0;
 
     switch (surf->mAttachType) {
@@ -646,17 +651,19 @@ ReadBuffer::Create(GLContext* gl,
 
     GLuint fb = 0;
     gl->fGenFramebuffers(1, &fb);
     gl->AttachBuffersToFB(colorTex, colorRB, depthRB, stencilRB, fb, target);
     gl->mFBOMapping[fb] = surf;
 
     UniquePtr<ReadBuffer> ret( new ReadBuffer(gl, fb, depthRB,
                                               stencilRB, surf) );
-    if (!gl->IsFramebufferComplete(fb)) {
+
+    GLenum err = localError.GetLocalError();
+    if (err || !gl->IsFramebufferComplete(fb)) {
         ret = nullptr;
     }
 
     return Move(ret);
 }
 
 ReadBuffer::~ReadBuffer()
 {
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -139,30 +139,38 @@ ChooseConfig(GLContext* gl,
 
     if (gl->DebugMode()) {
         egl->DumpEGLConfig(config);
     }
 
     return config;
 }
 
-// Returns EGL_NO_SURFACE on error.
+// Returns `EGL_NO_SURFACE` (`0`) on error.
 static EGLSurface
 CreatePBufferSurface(GLLibraryEGL* egl,
                      EGLDisplay display,
                      EGLConfig config,
                      const gfx::IntSize& size)
 {
+    auto width = size.width;
+    auto height = size.height;
+
     EGLint attribs[] = {
-        LOCAL_EGL_WIDTH, size.width,
-        LOCAL_EGL_HEIGHT, size.height,
+        LOCAL_EGL_WIDTH, width,
+        LOCAL_EGL_HEIGHT, height,
         LOCAL_EGL_NONE
     };
 
+    DebugOnly<EGLint> preCallErr = egl->fGetError();
+    MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
     EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
+    EGLint err = egl->fGetError();
+    if (err != LOCAL_EGL_SUCCESS)
+        return 0;
 
     return surface;
 }
 
 /*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
 SharedSurface_ANGLEShareHandle::Create(GLContext* gl,
                                        EGLContext context, EGLConfig config,
                                        const gfx::IntSize& size, bool hasAlpha)
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -18,21 +18,29 @@ using gfx::IntSize;
 using gfx::SurfaceFormat;
 
 /*static*/ UniquePtr<SharedSurface_Basic>
 SharedSurface_Basic::Create(GLContext* gl,
                             const GLFormats& formats,
                             const IntSize& size,
                             bool hasAlpha)
 {
+    UniquePtr<SharedSurface_Basic> ret;
     gl->MakeCurrent();
+
+    GLContext::ScopedLocalErrorCheck localError(gl);
     GLuint tex = CreateTexture(gl, formats.color_texInternalFormat,
                                formats.color_texFormat,
                                formats.color_texType,
                                size);
+    GLenum err = localError.GetLocalError();
+    if (err) {
+        gl->fDeleteTextures(1, &tex);
+        return Move(ret);
+    }
 
     SurfaceFormat format = SurfaceFormat::B8G8R8X8;
     switch (formats.color_texInternalFormat) {
     case LOCAL_GL_RGB:
     case LOCAL_GL_RGB8:
         if (formats.color_texType == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
             format = SurfaceFormat::R5G6B5;
         else
@@ -41,18 +49,17 @@ SharedSurface_Basic::Create(GLContext* g
     case LOCAL_GL_RGBA:
     case LOCAL_GL_RGBA8:
         format = SurfaceFormat::B8G8R8A8;
         break;
     default:
         MOZ_CRASH("Unhandled Tex format.");
     }
 
-    typedef SharedSurface_Basic ptrT;
-    UniquePtr<ptrT> ret( new ptrT(gl, size, hasAlpha, format, tex) );
+    ret.reset( new SharedSurface_Basic(gl, size, hasAlpha, format, tex) );
     return Move(ret);
 }
 
 SharedSurface_Basic::SharedSurface_Basic(GLContext* gl,
                                          const IntSize& size,
                                          bool hasAlpha,
                                          SurfaceFormat format,
                                          GLuint tex)
@@ -69,21 +76,18 @@ SharedSurface_Basic::SharedSurface_Basic
 
     ScopedBindFramebuffer autoFB(mGL, mFB);
     mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                               LOCAL_GL_COLOR_ATTACHMENT0,
                               LOCAL_GL_TEXTURE_2D,
                               mTex,
                               0);
 
-    GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
-        mGL->fDeleteFramebuffers(1, &mFB);
-        mFB = 0;
-    }
+    DebugOnly<GLenum> status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+    MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
 
     int32_t stride = gfx::GetAlignedStride<4>(size.width * BytesPerPixel(format));
     mData = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
     // Leave the extra return for clarity, in case we decide more code should
     // be added after this check, that should run even if mData is null.
     if (NS_WARN_IF(!mData)) {
         return;
     }
@@ -128,24 +132,34 @@ SharedSurface_GLTexture::Create(GLContex
     MOZ_ASSERT(!consGL || prodGL->SharesWith(consGL));
 
     prodGL->MakeCurrent();
 
     GLuint tex = texture;
 
     bool ownsTex = false;
 
+    UniquePtr<SharedSurface_GLTexture> ret;
+
     if (!tex) {
+      GLContext::ScopedLocalErrorCheck localError(prodGL);
+
       tex = CreateTextureForOffscreen(prodGL, formats, size);
+
+      GLenum err = localError.GetLocalError();
+      if (err) {
+          prodGL->fDeleteTextures(1, &tex);
+          return Move(ret);
+      }
+
       ownsTex = true;
     }
 
-    typedef SharedSurface_GLTexture ptrT;
-    UniquePtr<ptrT> ret( new ptrT(prodGL, consGL, size, hasAlpha, tex,
-                                  ownsTex) );
+    ret.reset( new SharedSurface_GLTexture(prodGL, consGL, size,
+                                           hasAlpha, tex, ownsTex) );
     return Move(ret);
 }
 
 SharedSurface_GLTexture::~SharedSurface_GLTexture()
 {
     if (!mGL->MakeCurrent())
         return;
 
--- a/gfx/gl/SurfaceTypes.cpp
+++ b/gfx/gl/SurfaceTypes.cpp
@@ -7,52 +7,52 @@
 
 #include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace gl {
 
 SurfaceCaps::SurfaceCaps()
 {
-  Clear();
+    Clear();
 }
 
 SurfaceCaps::SurfaceCaps(const SurfaceCaps& other)
 {
-  *this = other;
+    *this = other;
 }
 
 SurfaceCaps&
 SurfaceCaps::operator=(const SurfaceCaps& other)
 {
-  any = other.any;
-  color = other.color;
-  alpha = other.alpha;
-  bpp16 = other.bpp16;
-  depth = other.depth;
-  stencil = other.stencil;
-  antialias = other.antialias;
-  preserve = other.preserve;
-  surfaceAllocator = other.surfaceAllocator;
+    any = other.any;
+    color = other.color;
+    alpha = other.alpha;
+    bpp16 = other.bpp16;
+    depth = other.depth;
+    stencil = other.stencil;
+    antialias = other.antialias;
+    preserve = other.preserve;
+    surfaceAllocator = other.surfaceAllocator;
 
-  return *this;
+    return *this;
 }
 
 void
 SurfaceCaps::Clear()
 {
-  any = false;
-  color = false;
-  alpha = false;
-  bpp16 = false;
-  depth = false;
-  stencil = false;
-  antialias = false;
-  preserve = false;
-  surfaceAllocator = nullptr;
+    any = false;
+    color = false;
+    alpha = false;
+    bpp16 = false;
+    depth = false;
+    stencil = false;
+    antialias = false;
+    preserve = false;
+    surfaceAllocator = nullptr;
 }
 
 SurfaceCaps::~SurfaceCaps()
 {
 }
 
 } // namespace gl
 } // namespace mozilla
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -100,20 +100,27 @@ Compositor::DrawDiagnostics(DiagnosticFl
     return;
   }
 
   DrawDiagnosticsInternal(aFlags, aVisibleRect, aClipRect, aTransform,
                           aFlashCounter);
 }
 
 RenderTargetRect
-Compositor::ClipRectInLayersCoordinates(RenderTargetIntRect aClip) const {
+Compositor::ClipRectInLayersCoordinates(Layer* aLayer, RenderTargetIntRect aClip) const {
+  ContainerLayer* parent = aLayer->AsContainerLayer() ? aLayer->AsContainerLayer() : aLayer->GetParent();
+  while (!parent->UseIntermediateSurface() && parent->GetParent()) {
+    parent = parent->GetParent();
+  }
+
+  RenderTargetIntPoint renderTargetOffset = RenderTargetIntRect::FromUntyped(
+    parent->GetEffectiveVisibleRegion().GetBounds()).TopLeft();
+
   RenderTargetRect result;
-  aClip = aClip + RenderTargetIntPoint(GetCurrentRenderTarget()->GetOrigin().x,
-                                       GetCurrentRenderTarget()->GetOrigin().y);
+  aClip = aClip + renderTargetOffset;
   RenderTargetIntSize destSize = RenderTargetIntSize(GetWidgetSize().width,
                                                      GetWidgetSize().height);
 
   switch (mScreenRotation) {
     case ROTATION_0:
       result = RenderTargetRect(aClip.x, aClip.y, aClip.width, aClip.height);
       break;
     case ROTATION_90:
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -493,17 +493,17 @@ public:
 
   // On b2g the clip rect is in the coordinate space of the physical screen
   // independently of its rotation, while the coordinate space of the layers,
   // on the other hand, depends on the screen orientation.
   // This only applies to b2g as with other platforms, orientation is handled
   // at the OS level rather than in Gecko.
   // In addition, the clip rect needs to be offset by the rendering origin.
   // This becomes important if intermediate surfaces are used.
-  RenderTargetRect ClipRectInLayersCoordinates(RenderTargetIntRect aClip) const;
+  RenderTargetRect ClipRectInLayersCoordinates(Layer* aLayer, RenderTargetIntRect aClip) const;
 
 protected:
   void DrawDiagnosticsInternal(DiagnosticFlags aFlags,
                                const gfx::Rect& aVisibleRect,
                                const gfx::Rect& aClipRect,
                                const gfx::Matrix4x4& transform,
                                uint32_t aFlashCounter);
 
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -340,29 +340,32 @@ ClientTiledThebesLayer::RenderLayer()
     mValidRegion = nsIntRegion();
   }
 
   TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
   TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
   TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
 
   nsIntRegion neededRegion = mVisibleRegion;
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
+  // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
   if (MayResample()) {
     // If we're resampling then bilinear filtering can read up to 1 pixel
     // outside of our texture coords. Make the visible region a single rect,
     // and pad it out by 1 pixel (restricted to tile boundaries) so that
     // we always have valid content or transparent pixels to sample from.
     nsIntRect bounds = neededRegion.GetBounds();
     nsIntRect wholeTiles = bounds;
     wholeTiles.Inflate(nsIntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()));
     nsIntRect padded = bounds;
     padded.Inflate(1);
     padded.IntersectRect(padded, wholeTiles);
     neededRegion = padded;
   }
+#endif
 
   nsIntRegion invalidRegion;
   invalidRegion.Sub(neededRegion, mValidRegion);
   if (invalidRegion.IsEmpty()) {
     EndPaint();
     return;
   }
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -315,31 +315,32 @@ ClientTiledLayerBuffer::GetContentType(S
   gfxContentType content =
     mThebesLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
                                           gfxContentType::COLOR_ALPHA;
   SurfaceMode mode = mThebesLayer->GetSurfaceMode();
 
   if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
 #if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK)
      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+  }
 #else
       if (!mThebesLayer->GetParent() ||
           !mThebesLayer->GetParent()->SupportsComponentAlphaChildren() ||
           !gfxPrefs::TiledDrawTargetEnabled()) {
         mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
       } else {
         content = gfxContentType::COLOR;
       }
-#endif
   } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
     if (mThebesLayer->MayResample()) {
       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
       content = gfxContentType::COLOR_ALPHA;
     }
   }
+#endif
 
   if (aMode) {
     *aMode = mode;
   }
   return content;
 }
 
 gfxMemorySharedReadLock::gfxMemorySharedReadLock()
@@ -1149,22 +1150,16 @@ ClientTiledLayerBuffer::ValidateTile(Til
       if (backBufferOnWhite && !mCompositableClient->AddTextureClient(backBufferOnWhite)) {
         NS_WARNING("Failed to add tile TextureClient.");
         aTile.DiscardFrontBuffer();
         aTile.DiscardBackBuffer();
         return aTile;
       }
     }
 
-    if (backBuffer->HasInternalBuffer()) {
-      // If our new buffer has an internal buffer, we don't want to keep another
-      // TextureClient around unnecessarily, so discard the back-buffer.
-      aTile.DiscardBackBuffer();
-    }
-
     // prepare an array of Moz2D tiles that will be painted into in PostValidate
     gfx::Tile moz2DTile;
     RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
     RefPtr<DrawTarget> dtOnWhite;
     if (backBufferOnWhite) {
       dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
       moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
     } else {
@@ -1186,18 +1181,18 @@ ClientTiledLayerBuffer::ValidateTile(Til
                          dirtyRect->height);
       drawRect.Scale(mResolution);
 
       gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution),
                             NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
                             drawRect.width,
                             drawRect.height);
       gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
-      // Mark the newly updated area as invalid in the front buffer
-      aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
+      // Mark the newly updated area as invalid in the back buffer
+      aTile.mInvalidBack.Or(aTile.mInvalidBack, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
 
       if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
         dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
         dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
       } else if (content == gfxContentType::COLOR_ALPHA) {
         dt->ClearRect(drawRect);
       }
     }
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -171,16 +171,26 @@ ContainerPrepare(ContainerT* aContainer,
     }
 
     RenderTargetIntRect clipRect = layerToRender->GetLayer()->
         CalculateScissorRect(aClipRect, &aManager->GetWorldTransform());
     if (clipRect.IsEmpty()) {
       continue;
     }
 
+    RenderTargetRect quad = layerToRender->GetLayer()->
+      TransformRectToRenderTarget(LayerPixel::FromUntyped(
+        layerToRender->GetLayer()->GetEffectiveVisibleRegion().GetBounds()));
+
+    Compositor* compositor = aManager->GetCompositor();
+    if (!layerToRender->GetLayer()->AsContainerLayer() &&
+        !quad.Intersects(compositor->ClipRectInLayersCoordinates(layerToRender->GetLayer(), clipRect))) {
+      continue;
+    }
+
     CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
 
     nsIntRegion savedVisibleRegion;
     bool restoreVisibleRegion = false;
     gfx::Matrix matrix;
     bool is2D = layerToRender->GetLayer()->GetBaseTransform().Is2D(&matrix);
     if (i + 1 < children.Length() &&
         is2D && !matrix.HasNonIntegerTranslation()) {
@@ -264,17 +274,17 @@ RenderLayers(ContainerT* aContainer,
       // drawing anything for it. Hopefully the layers behind, if any, will
       // provide suitable content for the overscroll effect.
       if (color.a != 0.0) {
         EffectChain effectChain(aContainer);
         effectChain.mPrimaryEffect = new EffectSolidColor(ToColor(color));
         gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
         compositor->DrawQuad(
           RenderTargetPixel::ToUnknown(
-            compositor->ClipRectInLayersCoordinates(aClipRect)),
+            compositor->ClipRectInLayersCoordinates(aContainer, aClipRect)),
           clipRect, effectChain, opacity, Matrix4x4());
       }
     }
   }
 
   for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
     PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
     LayerComposite* layerToRender = preparedData.mLayer;
@@ -358,17 +368,16 @@ RenderIntermediate(ContainerT* aContaine
                    RefPtr<CompositingRenderTarget> surface)
 {
   Compositor* compositor = aManager->GetCompositor();
   RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
 
   if (!surface) {
     return;
   }
-
   compositor->SetRenderTarget(surface);
   // pre-render all of the layers into our temporary
   RenderLayers(aContainer, aManager, RenderTargetPixel::FromUntyped(aClipRect));
   // Unbind the current surface and rebind the previous one.
   compositor->SetRenderTarget(previousTarget);
 }
 
 template<class ContainerT> void
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -374,26 +374,26 @@ TiledContentHost::Composite(EffectChain&
       }
     }
   }
   float lowPrecisionOpacityReduction =
         (aOpacity == 1.0f && backgroundColor.a == 1.0f)
         ? gfxPrefs::LowPrecisionOpacity() : 1.0f;
 
   nsIntRegion tmpRegion;
-  const nsIntRegion* renderRegion;
+  const nsIntRegion* renderRegion = aVisibleRegion;
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
   if (PaintWillResample()) {
     // If we're resampling, then the texture image will contain exactly the
     // entire visible region's bounds, and we should draw it all in one quad
     // to avoid unexpected aliasing.
     tmpRegion = aVisibleRegion->GetBounds();
     renderRegion = &tmpRegion;
-  } else {
-    renderRegion = aVisibleRegion;
   }
+#endif
 
   // Render the low and high precision buffers.
   RenderLayerBuffer(mLowPrecisionTiledBuffer,
                     lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
                     aEffectChain, lowPrecisionOpacityReduction * aOpacity,
                     aFilter, aClipRect, *renderRegion, aTransform);
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter,
                     aClipRect, *renderRegion, aTransform);
@@ -430,18 +430,18 @@ TiledContentHost::RenderTile(const TileH
     // to warn, the texture update would have already caught this.
     return;
   }
 
   nsIntRect screenBounds = aScreenRegion.GetBounds();
   Rect layerQuad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
   RenderTargetRect quad = RenderTargetRect::FromUnknown(aTransform.TransformBounds(layerQuad));
 
-  if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(
-        RenderTargetIntRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height)))) {
+  if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(mLayer,
+      RenderTargetIntRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height)))) {
     return;
   }
 
   if (aBackgroundColor) {
     aEffectChain.mPrimaryEffect = new EffectSolidColor(ToColor(*aBackgroundColor));
     nsIntRegionRectIterator it(aScreenRegion);
     for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) {
       Rect graphicsRect(rect->x, rect->y, rect->width, rect->height);
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -1108,33 +1108,35 @@ nsRect nsRegion::GetLargestRectangle (co
                     yaxis.StopAt(bestRectIndices.top));
     bestRect.SizeTo(xaxis.StopAt(bestRectIndices.right) - bestRect.x,
                     yaxis.StopAt(bestRectIndices.bottom) - bestRect.y);
   }
 
   return bestRect;
 }
 
+std::ostream& operator<<(std::ostream& stream, const nsRegion& m) {
+  stream << "[";
+
+  int n;
+  pixman_box32_t *boxes = pixman_region32_rectangles(const_cast<pixman_region32_t*>(&m.mImpl), &n);
+  for (int i=0; i<n; i++) {
+    if (i != 0) {
+      stream << "; ";
+    }
+    stream << boxes[i].x1 << "," << boxes[i].y1 << "," << boxes[i].x2 << "," << boxes[i].y2;
+  }
+
+  stream << "]";
+  return stream;
+}
+
 nsCString
 nsRegion::ToString() const {
-    nsCString result;
-    result.Append('[');
-
-    int n;
-    pixman_box32_t *boxes = pixman_region32_rectangles(const_cast<pixman_region32_t*>(&mImpl), &n);
-    for (int i=0; i<n; i++) {
-        if (i != 0) {
-            result.AppendLiteral("; ");
-        }
-        result.Append(nsPrintfCString("%d,%d,%d,%d", boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2));
-
-    }
-    result.Append(']');
-
-    return result;
+  return nsCString(mozilla::ToString(this).c_str());
 }
 
 
 nsRegion nsIntRegion::ToAppUnits (nscoord aAppUnitsPerPixel) const
 {
   nsRegion result;
   nsIntRegionRectIterator rgnIter(*this);
   const nsIntRect* currentRect;
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -5,16 +5,17 @@
 
 #ifndef nsRegion_h__
 #define nsRegion_h__
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include <sys/types.h>                  // for int32_t
 #include "gfxCore.h"                    // for NS_GFX
+#include "mozilla/ToString.h"           // for mozilla::ToString
 #include "nsCoord.h"                    // for nscoord
 #include "nsError.h"                    // for nsresult
 #include "nsPoint.h"                    // for nsIntPoint, nsPoint
 #include "nsRect.h"                     // for nsIntRect, nsRect
 #include "nsMargin.h"                   // for nsIntMargin
 #include "nsStringGlue.h"               // for nsCString
 #include "xpcom-config.h"               // for CPP_THROW_NEW
 #include "mozilla/TypedEnum.h"          // for the VisitEdges typed enum
@@ -63,16 +64,18 @@ public:
  ~nsRegion () { pixman_region32_fini(&mImpl); }
   nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; }
   nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; }
   bool operator==(const nsRegion& aRgn) const
   {
     return IsEqual(aRgn);
   }
 
+  friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m);
+
   void Swap(nsRegion* aOther)
   {
     pixman_region32_t tmp = mImpl;
     mImpl = aOther->mImpl;
     aOther->mImpl = tmp;
   }
 
   static
@@ -457,16 +460,20 @@ public:
   nsIntRegion& operator = (const nsIntRect& aRect) { mImpl = ToRect (aRect); return *this; }
   nsIntRegion& operator = (const nsIntRegion& aRegion) { mImpl = aRegion.mImpl; return *this; }
 
   bool operator==(const nsIntRegion& aRgn) const
   {
     return IsEqual(aRgn);
   }
 
+  friend std::ostream& operator<<(std::ostream& stream, const nsIntRegion& m) {
+    return stream << m.mImpl;
+  }
+
   void Swap(nsIntRegion* aOther)
   {
     mImpl.Swap(&aOther->mImpl);
   }
 
   void AndWith(const nsIntRegion& aOther)
   {
     And(*this, aOther);
--- a/gfx/src/nsThemeConstants.h
+++ b/gfx/src/nsThemeConstants.h
@@ -277,8 +277,10 @@
 #define NS_THEME_WINDOW_BUTTON_MAXIMIZE                    238
 #define NS_THEME_WINDOW_BUTTON_RESTORE                     239
 #define NS_THEME_WINDOW_BUTTON_BOX                         240
 #define NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED               241
 
 // moz-apperance style used in setting proper glass margins
 #define NS_THEME_WIN_EXCLUDE_GLASS                         242
 
+#define NS_THEME_MAC_VIBRANCY_LIGHT                        243
+#define NS_THEME_MAC_VIBRANCY_DARK                         244
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -31,16 +31,37 @@ class gfxMatrix;
 class gfx3DMatrix
 {
 public:
   /**
    * Create matrix.
    */
   gfx3DMatrix(void);
 
+  friend std::ostream& operator<<(std::ostream& stream, const gfx3DMatrix& m) {
+    if (m.IsIdentity()) {
+      return stream << "[ I ]";
+    }
+
+    if (m.Is2D()) {
+      return stream << "["
+             << m._11 << " " << m._12 << "; "
+             << m._21 << " " << m._22 << "; "
+             << m._41 << " " << m._42
+             << "]";
+    }
+
+    return stream << "["
+           << m._11 << " " << m._12 << " " << m._13 << " " << m._14 << "; "
+           << m._21 << " " << m._22 << " " << m._23 << " " << m._24 << "; "
+           << m._31 << " " << m._32 << " " << m._33 << " " << m._34 << "; "
+           << m._41 << " " << m._42 << " " << m._43 << " " << m._44
+           << "]";
+  }
+
   /**
    * Matrix multiplication.
    */
   gfx3DMatrix operator*(const gfx3DMatrix &aMatrix) const;
   gfx3DMatrix& operator*=(const gfx3DMatrix &aMatrix);
 
   gfxPointH3D& operator[](int aIndex)
   {
--- a/gfx/thebes/gfxMatrix.h
+++ b/gfx/thebes/gfxMatrix.h
@@ -45,16 +45,28 @@ public:
      * Initializes the matrix from individual components. See the class
      * description for the layout of the matrix.
      */
     gfxMatrix(gfxFloat a, gfxFloat b, gfxFloat c, gfxFloat d, gfxFloat tx, gfxFloat ty) :
         _11(a),  _12(b),
         _21(c),  _22(d),
         _31(tx), _32(ty) { }
 
+    friend std::ostream& operator<<(std::ostream& stream, const gfxMatrix& m) {
+      if (m.IsIdentity()) {
+        return stream << "[identity]";
+      }
+
+      return stream << "["
+             << m._11 << " " << m._12
+             << m._21 << " " << m._22
+             << m._31 << " " << m._32
+             << "]";
+    }
+
     /**
      * Post-multiplies m onto the matrix.
      */
     const gfxMatrix& operator *= (const gfxMatrix& m);
 
     /**
      * Multiplies *this with m and returns the result.
      */
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -31,18 +31,24 @@ LOCAL_INCLUDES += [
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
     # For sandbox includes and the include dependencies those have
     LOCAL_INCLUDES += [
         '/security',
         '/security/sandbox',
         '/security/sandbox/chromium',
     ]
     USE_LIBS += [
-        'sandbox_s',
+        'sandbox_staticruntime_s',
     ]
+    DELAYLOAD_DLLS += [
+        'mozalloc.dll',
+        'nss3.dll',
+        'xul.dll'
+    ]
+    USE_STATIC_LIBS = True
 
 if CONFIG['_MSC_VER']:
     # Always enter a Windows program through wmain, whether or not we're
     # a console application.
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
 LDFLAGS += [CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']]
 
@@ -52,15 +58,17 @@ LDFLAGS += [CONFIG['MOZ_ALLOW_HEAP_EXECU
 #
 # The default heap size is 1MB on Win32.
 # The heap will grow if need be.
 #
 # Set it to 256k.  See bug 127069.
 if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
     LDFLAGS += ['/HEAP:0x40000']
 
-FAIL_ON_WARNINGS = True
+# Windows builds have dll linkage warnings due to USE_STATIC_LIBS
+if CONFIG['OS_ARCH'] != 'WINNT':
+    FAIL_ON_WARNINGS = True
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     OS_LIBS += [
         'binder',
         'utils',
     ]
--- a/ipc/glue/CrossProcessMutex.h
+++ b/ipc/glue/CrossProcessMutex.h
@@ -37,17 +37,17 @@ typedef HANDLE CrossProcessMutexHandle;
 #elif defined(OS_LINUX)
 typedef mozilla::ipc::SharedMemoryBasic::Handle CrossProcessMutexHandle;
 #else
 // Stub for other platforms. We can't use uintptr_t here since different
 // processes could disagree on its size.
 typedef uintptr_t CrossProcessMutexHandle;
 #endif
 
-class NS_COM_GLUE CrossProcessMutex
+class CrossProcessMutex
 {
 public:
   /**
    * CrossProcessMutex
    * @param name A name which can reference this lock (currently unused)
    **/
   explicit CrossProcessMutex(const char* aName);
   /**
--- a/ipc/ipdl/test/cxx/app/TestIPDL.cpp
+++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
@@ -1,16 +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/. */
 
 #include "nsXULAppAPI.h"
 
 #if defined(XP_WIN)
 #include <windows.h>
+#define XRE_DONT_SUPPORT_XPSP2 // this app doesn't ship
 #include "nsWindowsWMain.cpp"
 #endif
 
 int
 main(int argc, char** argv)
 {
     // the first argument specifies which IPDL test case/suite to load
     if (argc < 2)
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -40,23 +40,21 @@ WrapperOwner::idOf(JSObject *obj)
 
     ObjectId objId = BitwiseCast<uint64_t>(v.toDouble());
     MOZ_ASSERT(findCPOWById(objId) == obj);
     MOZ_ASSERT(objId);
 
     return objId;
 }
 
-int sCPOWProxyHandler;
-
 class CPOWProxyHandler : public BaseProxyHandler
 {
   public:
     CPOWProxyHandler()
-      : BaseProxyHandler(&sCPOWProxyHandler) {}
+      : BaseProxyHandler(&family) {}
     virtual ~CPOWProxyHandler() {}
 
     virtual bool finalizeInBackground(Value priv) const MOZ_OVERRIDE {
         return false;
     }
 
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
@@ -81,19 +79,21 @@ class CPOWProxyHandler : public BaseProx
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
                                JSContext *cx) const MOZ_OVERRIDE;
     virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
 
+    static const char family;
     static const CPOWProxyHandler singleton;
 };
 
+const char CPOWProxyHandler::family = 0;
 const CPOWProxyHandler CPOWProxyHandler::singleton;
 
 #define FORWARD(call, args)                                             \
     WrapperOwner *owner = OwnerOf(proxy);                               \
     if (!owner->active()) {                                             \
         JS_ReportError(cx, "cannot use a CPOW whose process is gone");  \
         return false;                                                   \
     }                                                                   \
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -596,17 +596,17 @@ JS_GetCustomIteratorCount(JSContext *cx)
 
 JS_FRIEND_API(bool)
 JS_IsDeadWrapper(JSObject *obj)
 {
     if (!obj->is<ProxyObject>()) {
         return false;
     }
 
-    return obj->as<ProxyObject>().handler()->family() == &DeadObjectProxy::sDeadObjectFamily;
+    return obj->as<ProxyObject>().handler()->family() == &DeadObjectProxy::family;
 }
 
 void
 js::TraceWeakMaps(WeakMapTracer *trc)
 {
     WeakMapBase::traceAllMappings(trc);
     WatchpointMap::traceAll(trc);
 }
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -792,16 +792,17 @@ class ScriptedIndirectProxyHandler : pub
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
 
+    static const char family;
     static const ScriptedIndirectProxyHandler singleton;
 };
 
 /*
  * Old-style indirect proxies allow callers to specify distinct scripted
  * [[Call]] and [[Construct]] traps. We use an intermediate object so that we
  * can stash this information in a single reserved slot on the proxy object.
  *
@@ -813,20 +814,20 @@ class ScriptedIndirectProxyHandler : pub
 static const Class CallConstructHolder = {
     "CallConstructHolder",
     JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
 };
 
 } /* anonymous namespace */
 
 // This variable exists solely to provide a unique address for use as an identifier.
-static const char sScriptedIndirectProxyHandlerFamily = 0;
+const char ScriptedIndirectProxyHandler::family = 0;
 
 ScriptedIndirectProxyHandler::ScriptedIndirectProxyHandler()
-        : BaseProxyHandler(&sScriptedIndirectProxyHandlerFamily)
+        : BaseProxyHandler(&family)
 {
 }
 
 ScriptedIndirectProxyHandler::~ScriptedIndirectProxyHandler()
 {
 }
 
 bool
@@ -1113,29 +1114,27 @@ class ScriptedDirectProxyHandler : publi
     /* ES6 Harmony traps */
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
 
     /* Spidermonkey extensions. */
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
 
+    static const char family;
     static const ScriptedDirectProxyHandler singleton;
 
     // The "proxy extra" slot index in which the handler is stored. Revocable proxies need to set
     // this at revocation time.
     static const int HANDLER_EXTRA = 0;
     // The "function extended" slot index in which the revocation object is stored. Per spec, this
     // is to be cleared during the first revocation.
     static const int REVOKE_SLOT = 0;
 };
 
-// This variable exists solely to provide a unique address for use as an identifier.
-static const char sScriptedDirectProxyHandlerFamily = 0;
-
 static inline bool
 IsDataDescriptor(const PropertyDescriptor &desc)
 {
     return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER));
 }
 
 static inline bool
 IsAccessorDescriptor(const PropertyDescriptor &desc)
@@ -1384,17 +1383,17 @@ ArrayToIdVector(JSContext *cx, HandleObj
         }
     }
 
     // step n
     return true;
 }
 
 ScriptedDirectProxyHandler::ScriptedDirectProxyHandler()
-        : DirectProxyHandler(&sScriptedDirectProxyHandlerFamily)
+        : DirectProxyHandler(&family)
 {
 }
 
 ScriptedDirectProxyHandler::~ScriptedDirectProxyHandler()
 {
 }
 
 // ES6 (22 May, 2014) 9.5.4 Proxy.[[PreventExtensions]]()
@@ -2203,16 +2202,17 @@ ScriptedDirectProxyHandler::construct(JS
     // step 10
     if (!args.rval().isObject()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT);
         return false;
     }
     return true;
 }
 
+const char ScriptedDirectProxyHandler::family = 0;
 const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
 
 #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall)                   \
     JS_BEGIN_MACRO                                                           \
         RootedObject proto(cx);                                              \
         if (!JSObject::getProto(cx, proxy, &proto))                          \
             return false;                                                    \
         if (!proto)                                                          \
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -82,16 +82,24 @@ class JS_FRIEND_API(Wrapper);
  * this class to define any custom behavior they want.
  *
  * Important: If you add a trap here, you should probably also add a Proxy::foo
  * entry point with an AutoEnterPolicy. If you don't, you need an explicit
  * override for the trap in SecurityWrapper. See bug 945826 comment 0.
  */
 class JS_FRIEND_API(BaseProxyHandler)
 {
+    /*
+     * Sometimes it's desirable to designate groups of proxy handlers as "similar".
+     * For this, we use the notion of a "family": A consumer-provided opaque pointer
+     * that designates the larger group to which this proxy belongs.
+     *
+     * If it will never be important to differentiate this proxy from others as
+     * part of a distinct group, nullptr may be used instead.
+     */
     const void *mFamily;
 
     /*
      * Proxy handlers can use mHasPrototype to request the following special
      * treatment from the JS engine:
      *
      *   - When mHasPrototype is true, the engine never calls these methods:
      *     getPropertyDescriptor, has, set, enumerate, iterate.  Instead, for
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -15,18 +15,16 @@
 #include "vm/ErrorObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
-const char js::sWrapperFamily = 0;
-
 /*
  * Wrapper forwards this call directly to the wrapped object for efficiency
  * and transparency. In particular, the hint is needed to properly stringify
  * Date objects in certain cases - see bug 646129. Note also the
  * SecurityWrapper overrides this trap to avoid information leaks. See bug
  * 720619.
  */
 bool
@@ -126,25 +124,26 @@ js::UnwrapOneChecked(JSObject *obj, bool
 bool
 js::IsCrossCompartmentWrapper(JSObject *obj)
 {
     return IsWrapper(obj) &&
            !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
 
 Wrapper::Wrapper(unsigned flags, bool hasPrototype, bool hasSecurityPolicy)
-  : DirectProxyHandler(&sWrapperFamily, hasPrototype, hasSecurityPolicy),
+  : DirectProxyHandler(&family, hasPrototype, hasSecurityPolicy),
     mFlags(flags)
 {
 }
 
 Wrapper::~Wrapper()
 {
 }
 
+const char Wrapper::family = 0;
 const Wrapper Wrapper::singleton((unsigned)0);
 const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
 JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
 
 /* Compartments. */
 
 extern JSObject *
 js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
@@ -743,17 +742,17 @@ SecurityWrapper<Base>::unwatch(JSContext
     return false;
 }
 
 
 template class js::SecurityWrapper<Wrapper>;
 template class js::SecurityWrapper<CrossCompartmentWrapper>;
 
 DeadObjectProxy::DeadObjectProxy()
-  : BaseProxyHandler(&sDeadObjectFamily)
+  : BaseProxyHandler(&family)
 {
 }
 
 bool
 DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
 {
     // This is kind of meaningless, but dead-object semantics aside,
     // [[Extensible]] always being true is consistent with other proxy types.
@@ -880,18 +879,18 @@ DeadObjectProxy::defaultValue(JSContext 
 
 bool
 DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const
 {
     protop.set(nullptr);
     return true;
 }
 
+const char DeadObjectProxy::family = 0;
 const DeadObjectProxy DeadObjectProxy::singleton;
-const char DeadObjectProxy::sDeadObjectFamily = 0;
 
 bool
 js::IsDeadProxyObject(JSObject *obj)
 {
     return obj->is<ProxyObject>() &&
            obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
 }
 
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -82,16 +82,17 @@ class JS_FRIEND_API(Wrapper) : public Di
     }
 
     explicit Wrapper(unsigned flags, bool hasPrototype = false, bool hasSecurityPolicy = false);
 
     virtual ~Wrapper();
 
     virtual bool finalizeInBackground(Value priv) const MOZ_OVERRIDE;
 
+    static const char family;
     static const Wrapper singleton;
     static const Wrapper singletonWithPrototype;
 
     static JSObject *defaultProto;
 };
 
 inline JSObject *
 WrapperOptions::proto() const
@@ -202,19 +203,16 @@ class JS_FRIEND_API(SecurityWrapper) : p
 };
 
 typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;
 typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
 
 class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
 {
   public:
-    // This variable exists solely to provide a unique address for use as an identifier.
-    static const char sDeadObjectFamily;
-
     explicit DeadObjectProxy();
 
     /* ES5 Harmony fundamental wrapper traps. */
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
@@ -238,32 +236,28 @@ class JS_FRIEND_API(DeadObjectProxy) : p
     virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
                               MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
                                 MutableHandleObject protop) const MOZ_OVERRIDE;
 
+    static const char family;
     static const DeadObjectProxy singleton;
 };
 
 extern JSObject *
 TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
                          HandleObject parent);
 
-// Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by
-// jsfriendapi users.
-// This variable exists solely to provide a unique address for use as an identifier.
-extern JS_FRIEND_DATA(const char) sWrapperFamily;
-
 inline bool
 IsWrapper(JSObject *obj)
 {
-    return IsProxy(obj) && GetProxyHandler(obj)->family() == &sWrapperFamily;
+    return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family;
 }
 
 // Given a JSObject, returns that object stripped of wrappers. If
 // stopAtOuter is true, then this returns the outer window if it was
 // previously wrapped. Otherwise, this returns the first object for
 // which JSObject::isWrapper returns false.
 JS_FRIEND_API(JSObject *)
 UncheckedUnwrap(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = nullptr);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1318,17 +1318,17 @@ class DebugScopeProxy : public BaseProxy
         if (!maybeScope)
             return true;
 
         argsObj.set(ArgumentsObject::createUnexpected(cx, maybeScope->frame()));
         return !!argsObj;
     }
 
   public:
-    static int family;
+    static const char family;
     static const DebugScopeProxy singleton;
 
     DebugScopeProxy() : BaseProxyHandler(&family) {}
 
     bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE
     {
         // always [[Extensible]], can't be made non-[[Extensible]], like most
         // proxies
@@ -1587,17 +1587,17 @@ class DebugScopeProxy : public BaseProxy
         RootedValue idval(cx, IdToValue(id));
         return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_CANT_DELETE,
                                         JSDVG_IGNORE_STACK, idval, NullPtr(), nullptr, nullptr);
     }
 };
 
 } /* anonymous namespace */
 
-int DebugScopeProxy::family = 0;
+const char DebugScopeProxy::family = 0;
 const DebugScopeProxy DebugScopeProxy::singleton;
 
 /* static */ DebugScopeObject *
 DebugScopeObject::create(JSContext *cx, ScopeObject &scope, HandleObject enclosing)
 {
     JS_ASSERT(scope.compartment() == cx->compartment());
     RootedValue priv(cx, ObjectValue(scope));
     JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -15,16 +15,17 @@
 #include "xpcshellMacUtils.h"
 #endif
 #ifdef XP_WIN
 #include <windows.h>
 #include <shlobj.h>
 
 // we want a wmain entry point
 #define XRE_DONT_PROTECT_DLL_LOAD
+#define XRE_DONT_SUPPORT_XPSP2 // xpcshell does not ship
 #define XRE_WANT_ENVIRON
 #include "nsWindowsWMain.cpp"
 #endif
 
 int
 main(int argc, char** argv, char** envp)
 {
 #ifdef XP_MACOSX
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2434,16 +2434,18 @@ nsDisplayThemedBackground::nsDisplayThem
   // Perform necessary RegisterThemeGeometry
   switch (disp->mAppearance) {
     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
     case NS_THEME_TOOLBAR:
     case NS_THEME_WINDOW_TITLEBAR:
     case NS_THEME_WINDOW_BUTTON_BOX:
     case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON:
     case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+    case NS_THEME_MAC_VIBRANCY_LIGHT:
+    case NS_THEME_MAC_VIBRANCY_DARK:
       RegisterThemeGeometry(aBuilder, aFrame);
       break;
     case NS_THEME_WIN_BORDERLESS_GLASS:
     case NS_THEME_WIN_GLASS:
       aBuilder->SetGlassDisplayItem(this);
       break;
   }
 
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1836,17 +1836,23 @@ GetDisplayFlagsForFlexItem(nsIFrame* aFr
   return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
 }
 
 void
 nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                        const nsRect&           aDirtyRect,
                                        const nsDisplayListSet& aLists)
 {
+  // XXXdholbert hacky temporary band-aid for bug 1059138: Trivially pass this
+  // assertion (skip it, basically) if the first child is part of a shadow DOM.
+  // (IsOrderLEQWithDOMFallback doesn't know how to compare tree-position of a
+  // shadow-DOM element vs. a non-shadow-DOM element.)
   NS_ASSERTION(
+    (!mFrames.IsEmpty() &&
+     mFrames.FirstChild()->GetContent()->GetContainingShadow()) ||
     nsIFrame::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames),
     "Child frames aren't sorted correctly");
 
   DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   // Our children are all block-level, so their borders/backgrounds all go on
   // the BlockBorderBackgrounds list.
   nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds());
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1043,17 +1043,17 @@ ScrollFrameHelper::WantAsyncScroll() con
   bool isVAsyncScrollable = isVScrollable && (mVScrollbarBox || isFocused);
   bool isHAsyncScrollable = isHScrollable && (mHScrollbarBox || isFocused);
   return isVAsyncScrollable || isHAsyncScrollable;
 }
 
 static nsRect
 GetOnePixelRangeAroundPoint(nsPoint aPoint, bool aIsHorizontal)
 {
-  nsRect allowedRange;
+  nsRect allowedRange(aPoint, nsSize());
   nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
   if (aIsHorizontal) {
     allowedRange.x = aPoint.x - halfPixel;
     allowedRange.width = halfPixel*2 - 1;
   } else {
     allowedRange.y = aPoint.y - halfPixel;
     allowedRange.height = halfPixel*2 - 1;
   }
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1645,19 +1645,19 @@ fuzzy-if(Android&&AndroidVersion>=15,8,3
 == 630835-1.html about:blank
 == 631352-1.html 631352-1-ref.html
 skip-if(!haveTestPlugin) skip-if(B2G) fails-if(Android) fuzzy-if(winWidget&&!layersGPUAccelerated,102,535)  == 632423-1.html 632423-1-ref.html
 skip-if(Android||B2G) random-if(winWidget) == 632781-verybig.html 632781-ref.html
 == 632781-normalsize.html 632781-ref.html
 == 633344-1.html 633344-1-ref.html
 == 634232-1.html 634232-1-ref.html
 fails-if(Android&&AndroidVersion<17&&AndroidVersion!=10) == 635302-1.html 635302-1-ref.html
-skip-if(B2G) random-if(d2d) fuzzy-if(winWidget&&!d2d,1,11) == 635373-1.html 635373-1-ref.html
-skip-if(B2G) random-if(d2d) fuzzy-if(winWidget&&!d2d,1,15) == 635373-2.html 635373-2-ref.html
-skip-if(B2G) random-if(d2d) fuzzy-if(winWidget&&!d2d,1,15) == 635373-3.html 635373-3-ref.html
+skip-if(B2G) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,11) == 635373-1.html 635373-1-ref.html
+skip-if(B2G) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,15) == 635373-2.html 635373-2-ref.html
+skip-if(B2G) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,15) == 635373-3.html 635373-3-ref.html
 HTTP(..) == 635639-1.html 635639-1-ref.html
 HTTP(..) == 635639-2.html 635639-2-ref.html
 random == 637597-1.html 637597-1-ref.html # bug 637597 was never really fixed!
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-1.html 637852-1-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-2.html 637852-2-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-3.html 637852-3-ref.html
 skip-if(B2G) == 641770-1.html 641770-1-ref.html
 == 641856-1.html 641856-1-ref.html
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -683,16 +683,18 @@ CSS_KEY(-moz-window-button-close, _moz_w
 CSS_KEY(-moz-window-button-minimize, _moz_window_button_minimize)
 CSS_KEY(-moz-window-button-maximize, _moz_window_button_maximize)
 CSS_KEY(-moz-window-button-restore, _moz_window_button_restore)
 CSS_KEY(-moz-window-button-box, _moz_window_button_box)
 CSS_KEY(-moz-window-button-box-maximized, _moz_window_button_box_maximized)
 CSS_KEY(-moz-mac-help-button, _moz_mac_help_button)
 CSS_KEY(-moz-mac-unified-toolbar, _moz_mac_unified_toolbar)
 CSS_KEY(-moz-win-exclude-glass, _moz_win_exclude_glass)
+CSS_KEY(-moz-mac-vibrancy-light, _moz_mac_vibrancy_light)
+CSS_KEY(-moz-mac-vibrancy-dark, _moz_mac_vibrancy_dark)
 CSS_KEY(alphabetic, alphabetic)
 CSS_KEY(bevel, bevel)
 CSS_KEY(butt, butt)
 CSS_KEY(central, central)
 CSS_KEY(crispedges, crispedges)
 //CSS_KEY(end, end)
 CSS_KEY(evenodd, evenodd)
 CSS_KEY(geometricprecision, geometricprecision)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -723,16 +723,18 @@ const KTableValue nsCSSProps::kAppearanc
   eCSSKeyword__moz_window_frame_bottom,       NS_THEME_WINDOW_FRAME_BOTTOM,
   eCSSKeyword__moz_window_button_close,       NS_THEME_WINDOW_BUTTON_CLOSE,
   eCSSKeyword__moz_window_button_minimize,    NS_THEME_WINDOW_BUTTON_MINIMIZE,
   eCSSKeyword__moz_window_button_maximize,    NS_THEME_WINDOW_BUTTON_MAXIMIZE,
   eCSSKeyword__moz_window_button_restore,     NS_THEME_WINDOW_BUTTON_RESTORE,
   eCSSKeyword__moz_window_button_box,         NS_THEME_WINDOW_BUTTON_BOX,
   eCSSKeyword__moz_window_button_box_maximized, NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED,
   eCSSKeyword__moz_win_exclude_glass,         NS_THEME_WIN_EXCLUDE_GLASS,
+  eCSSKeyword__moz_mac_vibrancy_light,        NS_THEME_MAC_VIBRANCY_LIGHT,
+  eCSSKeyword__moz_mac_vibrancy_dark,         NS_THEME_MAC_VIBRANCY_DARK,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const KTableValue nsCSSProps::kBackfaceVisibilityKTable[] = {
   eCSSKeyword_visible, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE,
   eCSSKeyword_hidden, NS_STYLE_BACKFACE_VISIBILITY_HIDDEN,
   eCSSKeyword_UNKNOWN,-1
 };
--- a/media/libstagefright/binding/DecoderData.cpp
+++ b/media/libstagefright/binding/DecoderData.cpp
@@ -181,16 +181,17 @@ VideoDecoderConfig::Update(sp<MetaData>&
 bool
 VideoDecoderConfig::IsValid()
 {
   return display_width > 0 && display_height > 0;
 }
 
 MP4Sample::MP4Sample()
   : mMediaBuffer(nullptr)
+  , decode_timestamp(0)
   , composition_timestamp(0)
   , duration(0)
   , byte_offset(0)
   , is_sync_point(0)
   , data(nullptr)
   , size(0)
 {
 }
@@ -201,16 +202,17 @@ MP4Sample::~MP4Sample()
     mMediaBuffer->release();
   }
 }
 
 void
 MP4Sample::Update()
 {
   sp<MetaData> m = mMediaBuffer->meta_data();
+  decode_timestamp = FindInt64(m, kKeyDecodingTime);
   composition_timestamp = FindInt64(m, kKeyTime);
   duration = FindInt64(m, kKeyDuration);
   byte_offset = FindInt64(m, kKey64BitFileOffset);
   is_sync_point = FindInt32(m, kKeyIsSyncFrame);
   data = reinterpret_cast<uint8_t*>(mMediaBuffer->data());
   size = mMediaBuffer->range_length();
 
   crypto.Update(m);
--- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
@@ -132,16 +132,17 @@ class MP4Sample
 public:
   MP4Sample();
   ~MP4Sample();
   void Update();
   void Pad(size_t aPaddingBytes);
 
   stagefright::MediaBuffer* mMediaBuffer;
 
+  Microseconds decode_timestamp;
   Microseconds composition_timestamp;
   Microseconds duration;
   int64_t byte_offset;
   bool is_sync_point;
 
   uint8_t* data;
   size_t size;
 
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -3227,27 +3227,28 @@ status_t MPEG4Source::read(
             mBuffer = NULL;
         }
 
         // fall through
     }
 
     off64_t offset = 0;
     size_t size = 0;
+    uint32_t dts = 0;
     uint32_t cts = 0;
     uint32_t duration = 0;
     bool isSyncSample = false;
     bool newBuffer = false;
     if (mBuffer == NULL) {
         newBuffer = true;
 
         status_t err =
             mSampleTable->getMetaDataForSample(
                     mCurrentSampleIndex, &offset, &size, &cts, &duration,
-                    &isSyncSample);
+                    &isSyncSample, &dts);
 
         if (err != OK) {
             return err;
         }
 
         int32_t max_size;
         CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
         mBuffer = new MediaBuffer(max_size);
@@ -3269,16 +3270,18 @@ status_t MPEG4Source::read(
             CHECK(mBuffer != NULL);
             mBuffer->set_range(0, size);
             mBuffer->meta_data()->clear();
             mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset);
             if (!mTimescale) {
                 return ERROR_MALFORMED;
             }
             mBuffer->meta_data()->setInt64(
+                    kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+            mBuffer->meta_data()->setInt64(
                     kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
             mBuffer->meta_data()->setInt64(
                     kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
 
             if (targetSampleTimeUs >= 0) {
                 mBuffer->meta_data()->setInt64(
                         kKeyTargetTime, targetSampleTimeUs);
             }
@@ -3396,16 +3399,18 @@ status_t MPEG4Source::read(
         }
 
         mBuffer->meta_data()->clear();
         mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset);
         if (!mTimescale) {
             return ERROR_MALFORMED;
         }
         mBuffer->meta_data()->setInt64(
+                kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+        mBuffer->meta_data()->setInt64(
                 kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
         mBuffer->meta_data()->setInt64(
                 kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
 
         if (targetSampleTimeUs >= 0) {
             mBuffer->meta_data()->setInt64(
                     kKeyTargetTime, targetSampleTimeUs);
         }
@@ -3475,16 +3480,17 @@ status_t MPEG4Source::fragmentedRead(
             mBuffer = NULL;
         }
 
         // fall through
     }
 
     off64_t offset = 0;
     size_t size = 0;
+    uint32_t dts = 0;
     uint32_t cts = 0;
     uint32_t duration = 0;
     bool isSyncSample = false;
     bool newBuffer = false;
     if (mBuffer == NULL) {
         newBuffer = true;
 
         if (mCurrentSampleIndex >= mCurrentSamples.size()) {
@@ -3514,16 +3520,17 @@ status_t MPEG4Source::fragmentedRead(
             if (mTrackFragmentData.mPresent) {
                 mCurrentTime = mTrackFragmentData.mBaseMediaDecodeTime;
             }
         }
 
         const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
         offset = smpl->offset;
         size = smpl->size;
+        dts = mCurrentTime;
         cts = mCurrentTime + smpl->ctsOffset;
         duration = smpl->duration;
         mCurrentTime += smpl->duration;
         isSyncSample = (mCurrentSampleIndex == 0); // XXX
 
         int32_t max_size;
         CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
         mBuffer = new MediaBuffer(max_size);
@@ -3559,16 +3566,18 @@ status_t MPEG4Source::fragmentedRead(
             }
 
             CHECK(mBuffer != NULL);
             mBuffer->set_range(0, size);
             if (!mTimescale) {
                 return ERROR_MALFORMED;
             }
             mBuffer->meta_data()->setInt64(
+                    kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+            mBuffer->meta_data()->setInt64(
                     kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
             mBuffer->meta_data()->setInt64(
                     kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
 
             if (targetSampleTimeUs >= 0) {
                 mBuffer->meta_data()->setInt64(
                         kKeyTargetTime, targetSampleTimeUs);
             }
@@ -3686,16 +3695,18 @@ status_t MPEG4Source::fragmentedRead(
             CHECK(mBuffer != NULL);
             mBuffer->set_range(0, dstOffset);
         }
 
         if (!mTimescale) {
             return ERROR_MALFORMED;
         }
         mBuffer->meta_data()->setInt64(
+                kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+        mBuffer->meta_data()->setInt64(
                 kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
         mBuffer->meta_data()->setInt64(
                 kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
 
         if (targetSampleTimeUs >= 0) {
             mBuffer->meta_data()->setInt64(
                     kKeyTargetTime, targetSampleTimeUs);
         }
--- a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
@@ -136,17 +136,18 @@ status_t SampleIterator::seekTo(uint32_t
     status_t err;
     if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
         ALOGE("findSampleTime return error");
         return err;
     }
 
     // mTTSDuration is set by findSampleTime()
     mCurrentSampleDuration = mTTSDuration;
-
+    mCurrentSampleDecodeTime = mTTSSampleTime + mTTSDuration * (sampleIndex -
+        mTTSSampleIndex);
     mCurrentSampleIndex = sampleIndex;
 
     mInitialized = true;
 
     return OK;
 }
 
 status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
--- a/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
@@ -775,17 +775,18 @@ status_t SampleTable::getSampleSize_l(
 }
 
 status_t SampleTable::getMetaDataForSample(
         uint32_t sampleIndex,
         off64_t *offset,
         size_t *size,
         uint32_t *compositionTime,
         uint32_t *duration,
-        bool *isSyncSample) {
+        bool *isSyncSample,
+        uint32_t *decodeTime) {
     Mutex::Autolock autoLock(mLock);
 
     status_t err;
     if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {
         return err;
     }
 
     if (offset) {
@@ -795,16 +796,20 @@ status_t SampleTable::getMetaDataForSamp
     if (size) {
         *size = mSampleIterator->getSampleSize();
     }
 
     if (compositionTime) {
         *compositionTime = mSampleIterator->getSampleTime();
     }
 
+    if (decodeTime) {
+        *decodeTime = mSampleIterator->getSampleDecodeTime();
+    }
+
     if (duration) {
         *duration = mSampleIterator->getSampleDuration();
     }
 
     if (isSyncSample) {
         *isSyncSample = false;
         if (mSyncSampleOffset < 0) {
             // Every sample is a sync sample.
--- a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h
@@ -28,16 +28,17 @@ struct SampleIterator {
 
     status_t seekTo(uint32_t sampleIndex);
 
     uint32_t getChunkIndex() const { return mCurrentChunkIndex; }
     uint32_t getDescIndex() const { return mChunkDesc; }
     off64_t getSampleOffset() const { return mCurrentSampleOffset; }
     size_t getSampleSize() const { return mCurrentSampleSize; }
     uint32_t getSampleTime() const { return mCurrentSampleTime; }
+    uint32_t getSampleDecodeTime() const { return mCurrentSampleDecodeTime; }
     uint32_t getSampleDuration() const { return mCurrentSampleDuration; }
 
     status_t getSampleSizeDirect(
             uint32_t sampleIndex, size_t *size);
 
 private:
     SampleTable *mTable;
 
@@ -60,16 +61,17 @@ private:
     uint32_t mTTSSampleTime;
     uint32_t mTTSCount;
     uint32_t mTTSDuration;
 
     uint32_t mCurrentSampleIndex;
     off64_t mCurrentSampleOffset;
     size_t mCurrentSampleSize;
     uint32_t mCurrentSampleTime;
+    uint32_t mCurrentSampleDecodeTime;
     uint32_t mCurrentSampleDuration;
 
     void reset();
     status_t findChunkRange(uint32_t sampleIndex);
     status_t getChunkOffset(uint32_t chunk, off64_t *offset);
     status_t findSampleTime(uint32_t sampleIndex, uint32_t *time);
 
     SampleIterator(const SampleIterator &);
--- a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h
@@ -62,17 +62,18 @@ public:
     status_t getMaxSampleSize(size_t *size);
 
     status_t getMetaDataForSample(
             uint32_t sampleIndex,
             off64_t *offset,
             size_t *size,
             uint32_t *compositionTime,
             uint32_t *duration = NULL,
-            bool *isSyncSample = NULL);
+            bool *isSyncSample = NULL,
+            uint32_t *decodeTime = NULL);
 
     enum {
         kFlagBefore,
         kFlagAfter,
         kFlagClosest
     };
     status_t findSampleAtTime(
             uint32_t req_time, uint32_t *sample_index, uint32_t flags);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1303,29 +1303,16 @@ public:
     mTransportStats.Construct();
     mIceComponentStats.Construct();
     mIceCandidatePairStats.Construct();
     mIceCandidateStats.Construct();
     mCodecStats.Construct();
     mTimestamp.Construct(now);
   }
 };
-
-// Specialized helper - push map[key] if specified or all map values onto array
-
-static void
-PushBackSelect(nsTArray<RefPtr<MediaPipeline>>& aDst,
-               const std::map<TrackID, RefPtr<mozilla::MediaPipeline>> & aSrc,
-               TrackID aKey = 0) {
-  auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin;
-  for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end();
-       it != end; ++it) {
-    aDst.AppendElement(it->second);
-  }
-}
 #endif
 
 NS_IMETHODIMP
 PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) {
   PC_AUTO_ENTER_API_CALL(true);
 
 #ifdef MOZILLA_INTERNAL_API
   if (!mMedia) {
@@ -2272,36 +2259,58 @@ PeerConnectionImpl::BuildStatsQuery_m(
   if (query->internalStats) {
     query->report->mLocalSdp.Construct(
         NS_ConvertASCIItoUTF16(mLocalSDP.c_str()));
     query->report->mRemoteSdp.Construct(
         NS_ConvertASCIItoUTF16(mRemoteSDP.c_str()));
   }
 
   // Gather up pipelines from mMedia so they may be inspected on STS
-  TrackID trackId = aSelector ? aSelector->GetTrackID() : 0;
 
   for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) {
-    PushBackSelect(query->pipelines,
-                   mMedia->GetLocalStream(i)->GetPipelines(),
-                   trackId);
+    auto& pipelines = mMedia->GetLocalStream(i)->GetPipelines();
+    if (aSelector) {
+      if (mMedia->GetLocalStream(i)->GetMediaStream()->HasTrack(*aSelector)) {
+        // XXX use type instead of TrackID - bug 1056650
+        for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+          if (it->second->IsVideo() == !!aSelector->AsVideoStreamTrack()) {
+            query->pipelines.AppendElement(it->second);
+          }
+        }
+      }
+    } else {
+      for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+        query->pipelines.AppendElement(it->second);
+      }
+    }
   }
 
   for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
-    PushBackSelect(query->pipelines,
-                   mMedia->GetRemoteStream(i)->GetPipelines(),
-                   trackId);
+    auto& pipelines = mMedia->GetRemoteStream(i)->GetPipelines();
+    if (aSelector) {
+      if (mMedia->GetRemoteStream(i)->GetMediaStream()->HasTrack(*aSelector)) {
+        for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+          if (it->second->trackid() == aSelector->GetTrackID()) {
+            query->pipelines.AppendElement(it->second);
+          }
+        }
+      }
+    } else {
+      for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+        query->pipelines.AppendElement(it->second);
+      }
+    }
   }
 
   query->iceCtx = mMedia->ice_ctx();
 
   // From the list of MediaPipelines, determine the set of NrIceMediaStreams
   // we are interested in.
   std::set<size_t> levelsToGrab;
-  if (trackId) {
+  if (aSelector) {
     for (size_t p = 0; p < query->pipelines.Length(); ++p) {
       size_t level = query->pipelines[p]->level();
       MOZ_ASSERT(level);
       levelsToGrab.insert(level);
     }
   } else {
     // We want to grab all streams, so ignore the pipelines (this also ends up
     // grabbing DataChannel streams, which is what we want)
@@ -2414,18 +2423,20 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
 
   ASSERT_ON_THREAD(query->iceCtx->thread());
 
   // Gather stats from pipelines provided (can't touch mMedia + stream on STS)
 
   for (size_t p = 0; p < query->pipelines.Length(); ++p) {
     const MediaPipeline& mp = *query->pipelines[p];
     bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
-    nsString idstr = isAudio ?
-        NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
+    nsString mediaType = isAudio ?
+        NS_LITERAL_STRING("audio") : NS_LITERAL_STRING("video");
+    nsString idstr = mediaType;
+    idstr.AppendLiteral("_");
     idstr.AppendInt(mp.trackid());
 
     // Gather pipeline stats.
     switch (mp.direction()) {
       case MediaPipeline::TRANSMIT: {
         nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
         nsString remoteId;
         nsString ssrc;
@@ -2451,16 +2462,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
             remoteId = NS_LITERAL_STRING("outbound_rtcp_") + idstr;
             RTCInboundRTPStreamStats s;
             s.mTimestamp.Construct(timestamp);
             s.mId.Construct(remoteId);
             s.mType.Construct(RTCStatsType::Inboundrtp);
             if (ssrc.Length()) {
               s.mSsrc.Construct(ssrc);
             }
+            s.mMediaType.Construct(mediaType);
             s.mJitter.Construct(double(jitterMs)/1000);
             s.mRemoteId.Construct(localId);
             s.mIsRemote = true;
             s.mPacketsReceived.Construct(packetsReceived);
             s.mBytesReceived.Construct(bytesReceived);
             s.mPacketsLost.Construct(packetsLost);
             s.mMozRtt.Construct(rtt);
             query->report->mInboundRTPStreamStats.Value().AppendElement(s);
@@ -2470,16 +2482,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
         {
           RTCOutboundRTPStreamStats s;
           s.mTimestamp.Construct(query->now);
           s.mId.Construct(localId);
           s.mType.Construct(RTCStatsType::Outboundrtp);
           if (ssrc.Length()) {
             s.mSsrc.Construct(ssrc);
           }
+          s.mMediaType.Construct(mediaType);
           s.mRemoteId.Construct(remoteId);
           s.mIsRemote = false;
           s.mPacketsSent.Construct(mp.rtp_packets_sent());
           s.mBytesSent.Construct(mp.rtp_bytes_sent());
 
           // Lastly, fill in video encoder stats if this is video
           if (!isAudio) {
             double framerateMean;
@@ -2521,31 +2534,33 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
             remoteId = NS_LITERAL_STRING("inbound_rtcp_") + idstr;
             RTCOutboundRTPStreamStats s;
             s.mTimestamp.Construct(timestamp);
             s.mId.Construct(remoteId);
             s.mType.Construct(RTCStatsType::Outboundrtp);
             if (ssrc.Length()) {
               s.mSsrc.Construct(ssrc);
             }
+            s.mMediaType.Construct(mediaType);
             s.mRemoteId.Construct(localId);
             s.mIsRemote = true;
             s.mPacketsSent.Construct(packetsSent);
             s.mBytesSent.Construct(bytesSent);
             query->report->mOutboundRTPStreamStats.Value().AppendElement(s);
           }
         }
         // Then, fill in local side (with cross-link to remote only if present)
         RTCInboundRTPStreamStats s;
         s.mTimestamp.Construct(query->now);
         s.mId.Construct(localId);
         s.mType.Construct(RTCStatsType::Inboundrtp);
         if (ssrc.Length()) {
           s.mSsrc.Construct(ssrc);
         }
+        s.mMediaType.Construct(mediaType);
         unsigned int jitterMs, packetsLost;
         if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) {
           s.mJitter.Construct(double(jitterMs)/1000);
           s.mPacketsLost.Construct(packetsLost);
         }
         if (remoteId.Length()) {
           s.mRemoteId.Construct(remoteId);
         }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -184,16 +184,20 @@ public:
 
   SourceStreamInfo(already_AddRefed<DOMMediaStream>& aMediaStream,
                    PeerConnectionMedia *aParent)
       : mMediaStream(aMediaStream),
         mParent(aParent) {
     MOZ_ASSERT(mMediaStream);
   }
 
+  DOMMediaStream* GetMediaStream() const {
+    return mMediaStream;
+  }
+
   // This method exists for stats and the unittests.
   // It allows visibility into the pipelines and flows.
   const std::map<mozilla::TrackID, mozilla::RefPtr<mozilla::MediaPipeline>>&
   GetPipelines() const { return mPipelines; }
   mozilla::RefPtr<mozilla::MediaPipeline> GetPipelineByLevel_m(int level);
 
 protected:
   std::map<mozilla::TrackID, mozilla::RefPtr<mozilla::MediaPipeline>> mPipelines;
@@ -209,19 +213,16 @@ class LocalSourceStreamInfo : public Sou
   }
 public:
   typedef mozilla::DOMMediaStream DOMMediaStream;
 
   LocalSourceStreamInfo(DOMMediaStream *aMediaStream,
                         PeerConnectionMedia *aParent)
       : SourceStreamInfo(aMediaStream, aParent) {}
 
-  DOMMediaStream* GetMediaStream() {
-    return mMediaStream;
-  }
   // Returns the mPipelines index for the track or -1.
 #if 0
   int HasTrack(DOMMediaStream* aStream, mozilla::TrackID aTrack);
 #endif
   int HasTrackType(DOMMediaStream* aStream, bool aIsVideo);
   // XXX NOTE: does not change mMediaStream, even if it replaces the last track
   // in a LocalSourceStreamInfo.  Revise when we have support for multiple tracks
   // of a type.
@@ -257,19 +258,16 @@ class RemoteSourceStreamInfo : public So
  public:
   typedef mozilla::DOMMediaStream DOMMediaStream;
 
   RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
                          PeerConnectionMedia *aParent)
     : SourceStreamInfo(aMediaStream, aParent),
       mTrackTypeHints(0) {}
 
-  DOMMediaStream* GetMediaStream() {
-    return mMediaStream;
-  }
   void StorePipeline(int aTrack, bool aIsVideo,
                      mozilla::RefPtr<mozilla::MediaPipelineReceive> aPipeline);
 
   bool SetUsingBundle_m(int aLevel, bool decision);
 
   void DetachTransport_s();
   void DetachMedia_m();
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_device_info_mac.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_device_info_mac.mm
@@ -31,20 +31,22 @@ DesktopDeviceInfoMac::DesktopDeviceInfoM
 DesktopDeviceInfoMac::~DesktopDeviceInfoMac() {
 }
 
 #if !defined(MULTI_MONITOR_SCREENSHARE)
 void DesktopDeviceInfoMac::MultiMonitorScreenshare()
 {
   DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
   if (pDesktopDeviceInfo) {
-    pDesktopDeviceInfo->setScreenId(0);
+    pDesktopDeviceInfo->setScreenId(CGMainDisplayID());
     pDesktopDeviceInfo->setDeviceName("Primary Monitor");
-    pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
 
+    char idStr[64];
+    snprintf(idStr, sizeof(idStr), "%ld", pDesktopDeviceInfo->getScreenId());
+    pDesktopDeviceInfo->setUniqueIdName(idStr);
     desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
   }
 }
 #endif
 
 void DesktopDeviceInfoMac::InitializeScreenList() {
 #if !defined(MULTI_MONITOR_SCREENSHARE)
   MultiMonitorScreenshare();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop_device_info_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop_device_info_win.cc
@@ -31,20 +31,22 @@ DesktopDeviceInfoWin::DesktopDeviceInfoW
 DesktopDeviceInfoWin::~DesktopDeviceInfoWin() {
 }
 
 #if !defined(MULTI_MONITOR_SCREENSHARE)
 void DesktopDeviceInfoWin::MultiMonitorScreenshare()
 {
   DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
   if (pDesktopDeviceInfo) {
-    pDesktopDeviceInfo->setScreenId(0);
+    pDesktopDeviceInfo->setScreenId(webrtc::kFullDesktopScreenId);
     pDesktopDeviceInfo->setDeviceName("Primary Monitor");
-    pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
 
+    char idStr[64];
+    _snprintf_s(idStr, sizeof(idStr), sizeof(idStr) - 1, "%ld", pDesktopDeviceInfo->getScreenId());
+    pDesktopDeviceInfo->setUniqueIdName(idStr);
     desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
   }
 }
 #endif
 
 void DesktopDeviceInfoWin::InitializeScreenList() {
 #if !defined(MULTI_MONITOR_SCREENSHARE)
   MultiMonitorScreenshare();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/desktop_device_info_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/desktop_device_info_x11.cc
@@ -30,20 +30,22 @@ DesktopDeviceInfoX11::DesktopDeviceInfoX
 DesktopDeviceInfoX11::~DesktopDeviceInfoX11() {
 }
 
 #if !defined(MULTI_MONITOR_SCREENSHARE)
 void DesktopDeviceInfoX11::MultiMonitorScreenshare()
 {
   DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
   if (pDesktopDeviceInfo) {
-    pDesktopDeviceInfo->setScreenId(0);
+    pDesktopDeviceInfo->setScreenId(webrtc::kFullDesktopScreenId);
     pDesktopDeviceInfo->setDeviceName("Primary Monitor");
-    pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
 
+    char idStr[64];
+    snprintf(idStr, sizeof(idStr), "%ld", idStr);
+    pDesktopDeviceInfo->setUniqueIdName(idStr);
     desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
   }
 }
 #endif
 
 void DesktopDeviceInfoX11::InitializeScreenList() {
 #if !defined(MULTI_MONITOR_SCREENSHARE)
   MultiMonitorScreenshare();
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
@@ -377,17 +377,17 @@ int32_t DesktopCaptureImpl::Init(const c
     MouseCursorMonitor *pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(options, webrtc::kFullDesktopScreenId);
     desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pAppCapturer, pMouseCursorMonitor));
   } else if (type == Screen) {
     ScreenCapturer *pScreenCapturer = ScreenCapturer::Create(options);
     if (!pScreenCapturer) {
       return -1;
     }
 
-    ScreenId screenid = webrtc::kFullDesktopScreenId;
+    ScreenId screenid = atoi(uniqueId);
     pScreenCapturer->SelectScreen(screenid);
     pScreenCapturer->SetMouseShapeObserver(this);
 
     MouseCursorMonitor *pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(options, screenid);
     desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pScreenCapturer, pMouseCursorMonitor));
   } else if (type == Window) {
     WindowCapturer *pWindowCapturer = WindowCapturer::Create();
     if (!pWindowCapturer) {
--- a/mobile/android/base/sync/setup/activities/SendTabActivity.java
+++ b/mobile/android/base/sync/setup/activities/SendTabActivity.java
@@ -141,18 +141,17 @@ public class SendTabActivity extends Loc
     TextView textView = (TextView) findViewById(R.id.title);
     textView.setText(sendTabData.title);
 
     textView = (TextView) findViewById(R.id.uri);
     textView.setText(sendTabData.uri);
 
     enableSend(false);
 
-    // will enableSend if appropriate.
-    updateClientList();
+    // Sending will be enabled in onResume, if appropriate.
   }
 
   protected static SendTabData getSendTabData(Intent intent) throws IllegalArgumentException {
     if (intent == null) {
       Logger.warn(LOG_TAG, "intent was null; aborting without sending tab.");
       throw new IllegalArgumentException();
     }
 
@@ -179,37 +178,37 @@ public class SendTabActivity extends Loc
 
     return sendTabData;
   }
 
   /**
    * Ensure that the view's list of clients is backed by a recently populated
    * array adapter.
    */
-  protected synchronized void updateClientList() {
+  protected synchronized void updateClientList(final TabSender sender, final ClientRecordArrayAdapter adapter) {
     // Fetching the client list hits the clients database, so we spin this onto
     // a background task.
     new AsyncTask<Void, Void, Collection<ClientRecord>>() {
 
       @Override
       protected Collection<ClientRecord> doInBackground(Void... params) {
-        return getOtherClients();
+        return getOtherClients(sender);
       }
 
       @Override
       protected void onPostExecute(final Collection<ClientRecord> clientArray) {
         // We're allowed to update the UI from here.
 
         Logger.debug(LOG_TAG, "Got " + clientArray.size() + " clients.");
-        arrayAdapter.setClientRecordList(clientArray);
+        adapter.setClientRecordList(clientArray);
         if (clientArray.size() == 1) {
-          arrayAdapter.checkItem(0, true);
+          adapter.checkItem(0, true);
         }
 
-        enableSend(arrayAdapter.getNumCheckedGUIDs() > 0);
+        enableSend(adapter.getNumCheckedGUIDs() > 0);
       }
     }.execute();
   }
 
   @Override
   public void onResume() {
     ActivityUtils.prepareLogging();
     Logger.info(LOG_TAG, "Called SendTabActivity.onResume.");
@@ -230,25 +229,31 @@ public class SendTabActivity extends Loc
         Logger.warn(LOG_TAG, "Firefox Account named like " + fxAccount.getObfuscatedEmail() +
             " needs action before it can send a tab; redirecting to status activity.");
         redirectToNewTask(FxAccountStatusActivity.class, false);
         return;
       }
 
       this.tabSender = new FxAccountTabSender(applicationContext, fxAccount);
 
+      // will enableSend if appropriate.
+      updateClientList(tabSender, this.arrayAdapter);
+
       Logger.info(LOG_TAG, "Allowing tab send for Firefox Account.");
       registerDisplayURICommand();
       return;
     }
 
     final Account[] syncAccounts = accountManager.getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC);
     if (syncAccounts.length > 0) {
       this.tabSender = new Sync11TabSender(applicationContext, syncAccounts[0], accountManager);
 
+      // will enableSend if appropriate.
+      updateClientList(tabSender, this.arrayAdapter);
+
       Logger.info(LOG_TAG, "Allowing tab send for Sync account.");
       registerDisplayURICommand();
       return;
     }
 
     // Offer to set up a Firefox Account, and finish this activity.
     redirectToNewTask(FxAccountGetStartedActivity.class, false);
   }
@@ -355,28 +360,28 @@ public class SendTabActivity extends Loc
     } finally {
       db.close();
     }
   }
 
   /**
    * @return a collection of client records, excluding our own.
    */
-  protected Collection<ClientRecord> getOtherClients() {
+  protected Collection<ClientRecord> getOtherClients(final TabSender sender) {
+    if (sender == null) {
+      Logger.warn(LOG_TAG, "No tab sender when fetching other client IDs.");
+      return new ArrayList<ClientRecord>(0);
+    }
+
     final Map<String, ClientRecord> all = getAllClients();
     if (all == null) {
       return new ArrayList<ClientRecord>(0);
     }
 
-    if (this.tabSender == null) {
-      Logger.warn(LOG_TAG, "No tab sender when fetching other client IDs.");
-      return new ArrayList<ClientRecord>(0);
-    }
-
-    final String ourGUID = this.tabSender.getAccountGUID();
+    final String ourGUID = sender.getAccountGUID();
     if (ourGUID == null) {
       return all.values();
     }
 
     final ArrayList<ClientRecord> out = new ArrayList<ClientRecord>(all.size());
     for (Entry<String, ClientRecord> entry : all.entrySet()) {
       if (ourGUID.equals(entry.getKey())) {
         continue;
--- a/mobile/android/search/java/org/mozilla/search/MainActivity.java
+++ b/mobile/android/search/java/org/mozilla/search/MainActivity.java
@@ -54,16 +54,18 @@ public class MainActivity extends Fragme
 
     private AsyncQueryHandler queryHandler;
 
     // Main views in layout.
     private ClearableEditText editText;
     private View preSearch;
     private View postSearch;
 
+    private View settingsButton;
+
     private View suggestions;
     private SuggestionsFragment suggestionsFragment;
 
     private static final int SUGGESTION_TRANSITION_DURATION = 300;
     private static final Interpolator SUGGESTION_TRANSITION_INTERPOLATOR =
             new AccelerateDecelerateInterpolator();
 
     // Views used for suggestion animation.
@@ -114,16 +116,26 @@ public class MainActivity extends Fragme
             public void onFocusChange(boolean hasFocus) {
                 setEditState(hasFocus ? EditState.EDITING : EditState.WAITING);
             }
         });
 
         preSearch = findViewById(R.id.presearch);
         postSearch = findViewById(R.id.postsearch);
 
+        settingsButton = findViewById(R.id.settings_button);
+
+        // Apply click handler to settings button.
+        settingsButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                startActivity(new Intent(MainActivity.this, SearchPreferenceActivity.class));
+            }
+        });
+
         suggestions = findViewById(R.id.suggestions);
         suggestionsFragment = (SuggestionsFragment) getSupportFragmentManager().findFragmentById(R.id.suggestions);
 
         animationText = (TextView) findViewById(R.id.animation_text);
         animationCard = findViewById(R.id.animation_card);
 
         cardPaddingX = getResources().getDimensionPixelSize(R.dimen.card_background_padding_x);
         cardPaddingY = getResources().getDimensionPixelSize(R.dimen.card_background_padding_y);
@@ -150,16 +162,17 @@ public class MainActivity extends Fragme
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
         queryHandler = null;
         editText = null;
         preSearch = null;
         postSearch = null;
+        settingsButton = null;
         suggestionsFragment = null;
         suggestions = null;
         animationText = null;
         animationCard = null;
     }
 
     @Override
     protected void onStart() {
@@ -190,17 +203,17 @@ public class MainActivity extends Fragme
 
         outState.putString(KEY_SEARCH_STATE, searchState.toString());
         outState.putString(KEY_EDIT_STATE, editState.toString());
         outState.putString(KEY_QUERY, editText.getText());
     }
 
     @Override
     public void onSuggest(String query) {
-       editText.setText(query);
+        editText.setText(query);
     }
 
     @Override
     public void onSearch(String query) {
         onSearch(query, null);
     }
 
     @Override
@@ -218,18 +231,18 @@ public class MainActivity extends Fragme
             setEditState(EditState.WAITING);
             setSearchState(SearchState.POSTSEARCH);
         }
     }
 
     /**
      * Animates search suggestion to search bar. This animation has 2 main parts:
      *
-     *   1) Vertically translate query text from suggestion card to search bar.
-     *   2) Expand suggestion card to fill the results view area.
+     * 1) Vertically translate query text from suggestion card to search bar.
+     * 2) Expand suggestion card to fill the results view area.
      *
      * @param query
      * @param suggestionAnimation
      */
     private void animateSuggestion(final String query, final SuggestionAnimation suggestionAnimation) {
         animationText.setText(query);
 
         final Rect startBounds = suggestionAnimation.getStartBounds();
@@ -291,30 +304,43 @@ public class MainActivity extends Fragme
     }
 
     private void setEditState(EditState editState) {
         if (this.editState == editState) {
             return;
         }
         this.editState = editState;
 
+        updateSettingsButtonVisibility();
+
         editText.setActive(editState == EditState.EDITING);
         suggestions.setVisibility(editState == EditState.EDITING ? View.VISIBLE : View.INVISIBLE);
     }
 
     private void setSearchState(SearchState searchState) {
         if (this.searchState == searchState) {
             return;
         }
         this.searchState = searchState;
 
+        updateSettingsButtonVisibility();
+
         preSearch.setVisibility(searchState == SearchState.PRESEARCH ? View.VISIBLE : View.INVISIBLE);
         postSearch.setVisibility(searchState == SearchState.POSTSEARCH ? View.VISIBLE : View.INVISIBLE);
     }
 
+    private void updateSettingsButtonVisibility() {
+        // Show button on launch screen when keyboard is down.
+        if (searchState == SearchState.PRESEARCH && editState == EditState.WAITING) {
+            settingsButton.setVisibility(View.VISIBLE);
+        } else {
+            settingsButton.setVisibility(View.INVISIBLE);
+        }
+    }
+
     @Override
     public void onBackPressed() {
         if (editState == EditState.EDITING) {
             setEditState(EditState.WAITING);
         } else if (searchState == SearchState.POSTSEARCH) {
             setSearchState(SearchState.PRESEARCH);
         } else {
             super.onBackPressed();
--- a/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.search;
 
 import android.app.Activity;
-import android.content.Intent;
 import android.database.Cursor;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
@@ -110,23 +109,16 @@ public class PreSearchFragment extends F
                         public Rect getStartBounds() {
                             return startBounds;
                         }
                     });
                 }
             }
         });
 
-        // Apply click handler to settings button.
-        mainView.findViewById(R.id.settings_button).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                startActivity(new Intent(getActivity(), SearchPreferenceActivity.class));
-            }
-        });
         return mainView;
     }
 
     @Override
     public void onDestroyView() {
         super.onDestroyView();
         listView.setAdapter(null);
         listView = null;
new file mode 100644
--- /dev/null
+++ b/mobile/android/search/java/org/mozilla/search/ui/BackCaptureEditText.java
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.search.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+/**
+ * An EditText subclass that loses focus when the keyboard
+ * is dismissed.
+ */
+public class BackCaptureEditText extends EditText {
+    public BackCaptureEditText(Context context) {
+        super(context);
+    }
+
+    public BackCaptureEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public BackCaptureEditText(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
+            clearFocus();
+        }
+        return super.onKeyPreIme(keyCode, event);
+    }
+}
--- a/mobile/android/search/res/layout/clearable_edit_text.xml
+++ b/mobile/android/search/res/layout/clearable_edit_text.xml
@@ -1,15 +1,15 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <EditText
+    <org.mozilla.search.ui.BackCaptureEditText
         android:id="@+id/edit_text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:imeOptions="actionSearch"
         android:inputType="textNoSuggestions"
         android:drawableLeft="@drawable/search_icon_inactive"
         android:drawablePadding="5dp"
--- a/mobile/android/search/res/layout/search_activity_main.xml
+++ b/mobile/android/search/res/layout/search_activity_main.xml
@@ -37,16 +37,27 @@
     <fragment
         android:id="@+id/suggestions"
         android:name="org.mozilla.search.autocomplete.SuggestionsFragment"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_marginTop="@dimen/search_bar_height"
         android:layout_gravity="top"/>
 
+
+    <ImageButton
+        android:id="@+id/settings_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@android:color/transparent"
+        android:padding="15dp"
+        android:src="@drawable/ic_action_settings"
+        android:layout_gravity="bottom|right"
+        android:contentDescription="@string/search_pref_button_content_description"/>
+
     <View
         android:id="@+id/animation_card"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_marginTop="@dimen/search_bar_height"
         android:background="@color/card_background"
         android:visibility="invisible"
         android:layout_gravity="top"/>
--- a/mobile/android/search/res/layout/search_fragment_pre_search.xml
+++ b/mobile/android/search/res/layout/search_fragment_pre_search.xml
@@ -11,19 +11,9 @@
     <ListView
         android:id="@+id/list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:divider="@null"
         android:dividerHeight="0dp"
         android:listSelector="@android:color/transparent"/>
 
-    <ImageButton
-        android:id="@+id/settings_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="@android:color/transparent"
-        android:padding="15dp"
-        android:src="@drawable/ic_action_settings"
-        android:layout_gravity="bottom|right"
-        android:contentDescription="@string/search_pref_button_content_description"/>
-
 </FrameLayout>
--- a/mobile/android/search/search_activity_sources.mozbuild
+++ b/mobile/android/search/search_activity_sources.mozbuild
@@ -12,9 +12,10 @@ search_activity_sources = [
     'java/org/mozilla/search/Constants.java',
     'java/org/mozilla/search/MainActivity.java',
     'java/org/mozilla/search/PostSearchFragment.java',
     'java/org/mozilla/search/PreSearchFragment.java',
     'java/org/mozilla/search/providers/SearchEngine.java',
     'java/org/mozilla/search/providers/SearchEngineManager.java',
     'java/org/mozilla/search/SearchPreferenceActivity.java',
     'java/org/mozilla/search/SearchWidget.java',
+    'java/org/mozilla/search/ui/BackCaptureEditText.java',
 ]
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -229,20 +229,23 @@ pref("print.shrink-to-fit.scale-limit-pe
 pref("media.cache_size", 512000);
 
 // Master HTML5 media volume scale.
 pref("media.volume_scale", "1.0");
 
 // Timeout for wakelock release
 pref("media.wakelock_timeout", 2000);
 
+// Whether we should play videos opened in a "video document", i.e. videos
+// opened as top-level documents, as opposed to inside a media element.
+pref("media.play-stand-alone", true);
+
 #ifdef MOZ_WMF
 pref("media.windows-media-foundation.enabled", true);
 pref("media.windows-media-foundation.use-dxva", true);
-pref("media.windows-media-foundation.play-stand-alone", true);
 #endif
 #ifdef MOZ_DIRECTSHOW
 pref("media.directshow.enabled", true);
 #endif
 #ifdef MOZ_FMP4
 pref("media.fragmented-mp4.enabled", true);
 pref("media.fragmented-mp4.ffmpeg.enabled", false);
 #if defined(XP_WIN) && defined(MOZ_WMF) || defined(XP_MACOSX)
@@ -3707,17 +3710,17 @@ pref("image.onload.decode.limit", 0);
 // Disable MSAA on mobile.
 pref("gl.msaa-level", 0);
 #else
 pref("gl.msaa-level", 2);
 #endif
 pref("webgl.force-enabled", false);
 pref("webgl.disabled", false);
 pref("webgl.shader_validator", true);
-pref("webgl.prefer-native-gl", false);
+pref("webgl.disable-angle", false);
 pref("webgl.min_capability_mode", false);
 pref("webgl.disable-extensions", false);
 pref("webgl.msaa-force", false);
 pref("webgl.prefer-16bpp", false);
 pref("webgl.default-no-alpha", false);
 pref("webgl.force-layers-readback", false);
 pref("webgl.lose-context-on-memory-preasure", false);
 pref("webgl.can-lose-context-in-foreground", true);
--- a/security/manager/boot/src/StaticHPKPins.h
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -358,16 +358,30 @@ struct StaticFingerprints {
 };
 
 struct StaticPinset {
   const StaticFingerprints* sha1;
   const StaticFingerprints* sha256;
 };
 
 /* PreloadedHPKPins.json pinsets */
+static const char* kPinset_facebook_sha256_Data[] = {
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_facebook_sha256 = {
+  sizeof(kPinset_facebook_sha256_Data) / sizeof(const char*),
+  kPinset_facebook_sha256_Data
+};
+
+static const StaticPinset kPinset_facebook = {
+  nullptr,
+  &kPinset_facebook_sha256
+};
+
 static const char* kPinset_google_root_pems_sha256_Data[] = {
   kEquifax_Secure_CAFingerprint,
   kAmerica_Online_Root_Certification_Authority_2Fingerprint,
   kComodo_Trusted_Services_rootFingerprint,
   kCOMODO_ECC_Certification_AuthorityFingerprint,
   kStartCom_Certification_AuthorityFingerprint,
   kStartCom_Certification_AuthorityFingerprint,
   kThawte_Premium_Server_CAFingerprint,
@@ -769,16 +783,17 @@ static const TransportSecurityPreload kP
   { "dl.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "domains.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
   { "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "dropbox.com", false, false, false, -1, &kPinset_dropbox },
   { "encrypted.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
+  { "facebook.com", true, true, false, -1, &kPinset_facebook },
   { "g.co", true, false, false, -1, &kPinset_google_root_pems },
   { "glass.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "gmail.com", false, false, false, -1, &kPinset_google_root_pems },
   { "goo.gl", true, false, false, -1, &kPinset_google_root_pems },
   { "google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
   { "google.ac", true, false, false, -1, &kPinset_google_root_pems },
   { "google.ad", true, false, false, -1, &kPinset_google_root_pems },
   { "google.ae", true, false, false, -1, &kPinset_google_root_pems },
@@ -1062,13 +1077,13 @@ static const TransportSecurityPreload kP
   { "www.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "xbrlsuccess.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
   { "youtu.be", true, false, false, -1, &kPinset_google_root_pems },
   { "youtube-nocookie.com", true, false, false, -1, &kPinset_google_root_pems },
   { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
   { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
 };
 
-// Pinning Preload List Length = 329;
+// Pinning Preload List Length = 330;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1417256260438000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1417640422391000);
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/ClientAuthServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/ClientAuthServer.cpp
@@ -31,17 +31,17 @@ struct ClientAuthHost
 };
 
 // Hostname, cert nickname pairs.
 static const ClientAuthHost sClientAuthHosts[] =
 {
   { "noclientauth.example.com", false, false },
   { "requestclientauth.example.com", true, false },
   { "requireclientauth.example.com", true, true },
-  { nullptr, nullptr }
+  { nullptr, false, false }
 };
 
 static const unsigned char sClientCertFingerprint[] =
 {
   0xD2, 0x2F, 0x00, 0x9A, 0x9E, 0xED, 0x79, 0xDC,
   0x8D, 0x17, 0x98, 0x8E, 0xEC, 0x76, 0x05, 0x91,
   0xA5, 0xF6, 0xC9, 0xFA, 0x16, 0x8B, 0xD2, 0x5F,
   0xE1, 0x52, 0x04, 0x7C, 0xF4, 0x76, 0x42, 0x9D
--- a/security/manager/tools/PreloadedHPKPins.json
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -180,16 +180,25 @@
         "Verisign Class 3 Public Primary Certification Authority - G2",
         "Verisign Class 3 Public Primary Certification Authority - G3",
         "VeriSign Class 3 Public Primary Certification Authority - G4",
         "VeriSign Class 3 Public Primary Certification Authority - G5",
         "Verisign Class 4 Public Primary Certification Authority - G3",
         "VeriSign Universal Root Certification Authority",
         "XRamp Global CA Root"
       ]
+    },
+    // For pinning tests on pinning.example.com, the certificate must be 'End
+    // Entity Test Cert'
+    {
+      "name": "facebook",
+      "sha256_hashes": [
+        "Verisign Class 3 Public Primary Certification Authority - G3",
+        "DigiCert High Assurance EV Root CA"
+      ]
     }
   ],
 
   "entries": [
     // Only domains that are operationally crucial to Firefox can have per-host
     // telemetry reporting (the "id") field
     { "name": "addons.mozilla.org", "include_subdomains": true,
       "pins": "mozilla", "test_mode": false, "id": 1 },
@@ -215,11 +224,14 @@
       "include_subdomains": false, "pins": "mozilla_test",
       "test_mode": false, "id": 0 },
     { "name": "test-mode.pinning.example.com", "include_subdomains": true,
       "pins": "mozilla_test", "test_mode": true },
     // Expand twitter's pinset to include all of *.twitter.com and use
     // twitterCDN. More specific rules take precedence because we search for
     // exact domain name first.
     { "name": "twitter.com", "include_subdomains": true,
-      "pins": "twitterCDN", "test_mode": false }
+      "pins": "twitterCDN", "test_mode": false },
+    // Facebook (not pinned by Chrome)
+    { "name": "facebook.com", "include_subdomains": true,
+      "pins": "facebook", "test_mode": true }
   ]
 }
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_17_RTM
+NSS_3_17_1_BETA1
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/certhigh/certvfypkix.c
+++ b/security/nss/lib/certhigh/certvfypkix.c
@@ -22,30 +22,16 @@
 #include "secasn1.h"
 #include "secder.h"
 #include "pkit.h"
 
 #include "pkix_pl_common.h"
 
 extern PRLogModuleInfo *pkixLog;
 
-#ifdef DEBUG_volkov
-/* Temporary declarations of functioins. Will be removed with fix for
- * 391183 */
-extern char *
-pkix_Error2ASCII(PKIX_Error *error, void *plContext);
-
-extern void
-cert_PrintCert(PKIX_PL_Cert *pkixCert, void *plContext);
-
-extern PKIX_Error *
-cert_PrintCertChain(PKIX_List *pkixCertChain, void *plContext);
-
-#endif /* DEBUG */
-
 #ifdef PKIX_OBJECT_LEAK_TEST
 
 extern PKIX_UInt32
 pkix_pl_lifecycle_ObjectLeakCheck(int *);
 
 extern SECStatus
 pkix_pl_lifecycle_ObjectTableUpdate(int *objCountTable);
 
@@ -893,21 +879,16 @@ cert_GetLogFromVerifyNode(
 
     PKIX_ENTER(CERTVFYPKIX, "cert_GetLogFromVerifyNode");
 
     children = node->children;
 
     if (children == NULL) {
         PKIX_ERRORCODE errCode = PKIX_ANCHORDIDNOTCHAINTOCERT;
         if (node->error && node->error->errCode != errCode) {
-#ifdef DEBUG_volkov
-            char *string = pkix_Error2ASCII(node->error, plContext);
-            fprintf(stderr, "Branch search finished with error: \t%s\n", string);
-            PKIX_PL_Free(string, NULL);
-#endif
             if (log != NULL) {
                 SECErrorCodes nssErrorCode = 0;
                 CERTCertificate *cert = NULL;
 
                 cert = node->verifyCert->nssCert;
 
                 PKIX_CHECK(
                     cert_PkixErrorToNssCode(node->error, &nssErrorCode,
@@ -998,32 +979,24 @@ cert_GetBuildResults(
     void             *plContext)
 {
     PKIX_ValidateResult *validResult = NULL;
     CERTCertList        *validChain = NULL;
     CERTCertificate     *trustedRoot = NULL;
     PKIX_TrustAnchor    *trustAnchor = NULL;
     PKIX_PL_Cert        *trustedCert = NULL;
     PKIX_List           *pkixCertChain = NULL;
-#ifdef DEBUG_volkov
-    PKIX_Error          *tmpPkixError = NULL;
-#endif /* DEBUG */
             
     PKIX_ENTER(CERTVFYPKIX, "cert_GetBuildResults");
     if (buildResult == NULL && error == NULL) {
         PKIX_ERROR(PKIX_NULLARGUMENT);
     }
 
     if (error) {
         SECErrorCodes nssErrorCode = 0;
-#ifdef DEBUG_volkov        
-        char *temp = pkix_Error2ASCII(error, plContext);
-        fprintf(stderr, "BUILD ERROR:\n%s\n", temp);
-        PKIX_PL_Free(temp, NULL);
-#endif /* DEBUG */
         if (verifyNode) {
             PKIX_Error *tmpError =
                 cert_GetLogFromVerifyNode(log, verifyNode, plContext);
             if (tmpError) {
                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext);
             }
         }
         cert_PkixErrorToNssCode(error, &nssErrorCode, plContext);
@@ -1032,23 +1005,16 @@ cert_GetBuildResults(
     }
 
     if (pvalidChain) {
         PKIX_CHECK(
             PKIX_BuildResult_GetCertChain(buildResult, &pkixCertChain,
                                           plContext),
             PKIX_BUILDRESULTGETCERTCHAINFAILED);
 
-#ifdef DEBUG_volkov
-        tmpPkixError = cert_PrintCertChain(pkixCertChain, plContext);
-        if (tmpPkixError) {
-            PKIX_PL_Object_DecRef((PKIX_PL_Object*)tmpPkixError, plContext);
-        }
-#endif        
-
         PKIX_CHECK(
             cert_PkixToNssCertsChain(pkixCertChain, &validChain, plContext),
             PKIX_CERTCHAINTONSSCHAINFAILED);
     }
 
     if (ptrustedRoot) {
         PKIX_CHECK(
             PKIX_BuildResult_GetValidateResult(buildResult, &validResult,
@@ -1060,23 +1026,17 @@ cert_GetBuildResults(
                                                plContext),
             PKIX_VALIDATERESULTGETTRUSTANCHORFAILED);
 
         PKIX_CHECK(
             PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustedCert,
                                             plContext),
             PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED);
 
-#ifdef DEBUG_volkov
-        if (pvalidChain == NULL) {
-            cert_PrintCert(trustedCert, plContext);
-        }
-#endif        
-
-       PKIX_CHECK(
+        PKIX_CHECK(
             PKIX_PL_Cert_GetCERTCertificate(trustedCert, &trustedRoot,
                                             plContext),
             PKIX_CERTGETCERTCERTIFICATEFAILED);
     }
  
     PORT_Assert(!PKIX_ERROR_RECEIVED);
 
     if (trustedRoot) {
@@ -1153,20 +1113,16 @@ cert_VerifyCertChainPkix(
 {
     PKIX_ProcessingParams *procParams = NULL;
     PKIX_BuildResult      *result = NULL;
     PKIX_VerifyNode       *verifyNode = NULL;
     PKIX_Error            *error = NULL;
 
     SECStatus              rv = SECFailure;
     void                  *plContext = NULL;
-#ifdef DEBUG_volkov
-    CERTCertificate       *trustedRoot = NULL;
-    CERTCertList          *validChain = NULL;
-#endif /* DEBUG */
 
 #ifdef PKIX_OBJECT_LEAK_TEST
     int  leakedObjNum = 0;
     int  memLeakLoopCount = 0;
     int  objCountTable[PKIX_NUMTYPES]; 
     int  fnInvLocalCount = 0;
     PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine;
 
@@ -1191,20 +1147,16 @@ cert_VerifyCertChainPkix(
 
 do {
     rv = SECFailure;
     plContext = NULL;
     procParams = NULL;
     result = NULL;
     verifyNode = NULL;
     error = NULL;
-#ifdef DEBUG_volkov
-    trustedRoot = NULL;
-    validChain = NULL;
-#endif /* DEBUG */
     errorGenerated = PKIX_FALSE;
     stackPosition = 0;
 
     if (leakedObjNum) {
         pkix_pl_lifecycle_ObjectTableUpdate(objCountTable); 
     }
     memLeakLoopCount += 1;
 #endif /* PKIX_OBJECT_LEAK_TEST */
@@ -1237,39 +1189,21 @@ do {
     }
     if (pSigerror) {
         /* Currently always PR_FALSE. Will be fixed as a part of 394077 */
         *pSigerror = PR_FALSE;
     }
     rv = SECSuccess;
 
 cleanup:
-    error = cert_GetBuildResults(result, verifyNode, error, log,
-#ifdef DEBUG_volkov                                 
-                                 &trustedRoot, &validChain,
-#else
-                                 NULL, NULL,
-#endif /* DEBUG */
+    error = cert_GetBuildResults(result, verifyNode, error, log, NULL, NULL,
                                  plContext);
     if (error) {
-#ifdef DEBUG_volkov        
-        char *temp = pkix_Error2ASCII(error, plContext);
-        fprintf(stderr, "GET BUILD RES ERRORS:\n%s\n", temp);
-        PKIX_PL_Free(temp, NULL);
-#endif /* DEBUG */
         PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
     }
-#ifdef DEBUG_volkov
-    if (trustedRoot) {
-        CERT_DestroyCertificate(trustedRoot);
-    }
-    if (validChain) {
-        CERT_DestroyCertList(validChain);
-    }
-#endif /* DEBUG */
     if (procParams) {
         PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext);
     }
     if (plContext) {
         PKIX_PL_NssContext_Destroy(plContext);
     }
 
 #ifdef PKIX_OBJECT_LEAK_TEST
deleted file mode 100644
--- a/security/nss/lib/certhigh/certvfypkixprint.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/* 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/. */
-/*
- * nss_pkix_proxy.h
- *
- * PKIX - NSS proxy functions
- *
- */
-#include "cert.h"
-#include "pkix_pl_common.h"
-
-#ifdef DEBUG
-
-char *
-pkix_Error2ASCII(PKIX_Error *error, void *plContext)
-{
-        PKIX_UInt32 length;
-        char *asciiString = NULL;
-        PKIX_PL_String *pkixString = NULL;
-        PKIX_Error *errorResult = NULL;
-
-        errorResult = PKIX_PL_Object_ToString
-                ((PKIX_PL_Object*)error, &pkixString, plContext);
-        if (errorResult) goto cleanup;
-
-        errorResult = PKIX_PL_String_GetEncoded
-                (pkixString,
-                PKIX_ESCASCII,
-                (void **)&asciiString,
-                &length,
-                plContext);
-
-cleanup:
-
-        if (pkixString){
-                if (PKIX_PL_Object_DecRef
-                    ((PKIX_PL_Object*)pkixString, plContext)){
-                        return (NULL);
-                }
-        }
-
-        if (errorResult){
-            PKIX_PL_Object_DecRef((PKIX_PL_Object*)errorResult, plContext);
-            return (NULL);
-        }
-
-        return (asciiString);
-}
-
-char *
-pkix_Object2ASCII(PKIX_PL_Object *object)
-{
-        PKIX_UInt32 length;
-        char *asciiString = NULL;
-        PKIX_PL_String *pkixString = NULL;
-        PKIX_Error *errorResult = NULL;
-
-        errorResult = PKIX_PL_Object_ToString
-                (object, &pkixString, NULL);
-        if (errorResult) goto cleanup;
-
-        errorResult = PKIX_PL_String_GetEncoded
-            (pkixString, PKIX_ESCASCII, (void **)&asciiString, &length, NULL);
-
-cleanup:
-
-        if (pkixString){
-                if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixString, NULL)){
-                        return (NULL);
-                }
-        }
-
-        if (errorResult){
-                return (NULL);
-        }
-
-        return (asciiString);
-}
-
-char *
-pkix_Cert2ASCII(PKIX_PL_Cert *cert)
-{
-        PKIX_PL_X500Name *issuer = NULL;
-        void *issuerAscii = NULL;
-        PKIX_PL_X500Name *subject = NULL;
-        void *subjectAscii = NULL;
-        void *asciiString = NULL;
-        PKIX_Error *errorResult = NULL;
-        PKIX_UInt32 numChars;
-        PKIX_UInt32 refCount = 0;
-
-        /* Issuer */
-        errorResult = PKIX_PL_Cert_GetIssuer(cert, &issuer, NULL);
-        if (errorResult) goto cleanup;
-
-        issuerAscii = pkix_Object2ASCII((PKIX_PL_Object*)issuer);
-
-        /* Subject */
-        errorResult = PKIX_PL_Cert_GetSubject(cert, &subject, NULL);
-        if (errorResult) goto cleanup;
-
-        if (subject){
-                subjectAscii = pkix_Object2ASCII((PKIX_PL_Object*)subject);
-        }
-
-/*         errorResult = PKIX_PL_Object_GetRefCount((PKIX_PL_Object*)cert, &refCount, NULL); */
-        if (errorResult) goto cleanup;
-
-        errorResult = PKIX_PL_Malloc(200, &asciiString, NULL);
-        if (errorResult) goto cleanup;
-
-        numChars =
-                PR_snprintf
-                (asciiString,
-                200,
-                "Ref: %d   Subject=%s\nIssuer=%s\n",
-                 refCount,
-                subjectAscii,
-                issuerAscii);
-
-        if (!numChars) goto cleanup;
-
-cleanup:
-
-        if (issuer){
-                if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)issuer, NULL)){
-                        return (NULL);
-                }
-        }
-
-        if (subject){
-                if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)subject, NULL)){
-                        return (NULL);
-                }
-        }
-
-        if (PKIX_PL_Free((PKIX_PL_Object*)issuerAscii, NULL)){
-                return (NULL);
-        }
-
-        if (PKIX_PL_Free((PKIX_PL_Object*)subjectAscii, NULL)){
-                return (NULL);
-        }
-
-        if (errorResult){
-                return (NULL);
-        }
-
-        return (asciiString);
-}
-
-PKIX_Error *
-cert_PrintCertChain(
-    PKIX_List *pkixCertChain,
-    void *plContext)
-{
-    PKIX_PL_Cert *cert = NULL;
-    PKIX_UInt32 numCerts = 0, i = 0;
-    char *asciiResult = NULL;
-    
-    PKIX_ENTER(CERTVFYPKIX, "cert_PrintCertChain");
-
-    PKIX_CHECK(
-        PKIX_List_GetLength(pkixCertChain, &numCerts, plContext),
-        PKIX_LISTGETLENGTHFAILED);
-    
-    fprintf(stderr, "\n");
-    
-    for (i = 0; i < numCerts; i++){
-        PKIX_CHECK
-            (PKIX_List_GetItem
-             (pkixCertChain, i, (PKIX_PL_Object**)&cert, plContext),
-             PKIX_LISTGETITEMFAILED);
-        
-        asciiResult = pkix_Cert2ASCII(cert);
-        
-        fprintf(stderr, "CERT[%d]:\n%s\n", i, asciiResult);
-        
-        PKIX_PL_Free(asciiResult, plContext);
-        asciiResult = NULL;
-        
-        PKIX_DECREF(cert);
-    }
-
-cleanup:
-    PKIX_DECREF(cert);
-
-    PKIX_RETURN(CERTVFYPKIX);
-}
-
-void
-cert_PrintCert(
-    PKIX_PL_Cert *pkixCert,
-    void *plContext)
-{
-    char *asciiResult = NULL;
-    
-    asciiResult = pkix_Cert2ASCII(pkixCert);
-    
-    fprintf(stderr, "CERT[0]:\n%s\n", asciiResult);
-    
-    PKIX_PL_Free(asciiResult, plContext);
-}
-
-#endif /* DEBUG */
--- a/security/nss/lib/certhigh/manifest.mn
+++ b/security/nss/lib/certhigh/manifest.mn
@@ -20,16 +20,15 @@ CSRCS = \
 	certhtml.c \
 	certreq.c \
 	crlv2.c \
 	ocsp.c \
 	ocspsig.c \
 	certhigh.c \
  	certvfy.c \
  	certvfypkix.c \
- 	certvfypkixprint.c \
  	xcrldist.c \
 	$(NULL)
 
 LIBRARY_NAME = certhi
 
 # This part of the code, including all sub-dirs, can be optimized for size
 export ALLOW_OPT_CODE_SIZE = 1
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -28,22 +28,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION  "3.17" _NSS_ECC_STRING _NSS_CUSTOMIZED
+#define NSS_VERSION  "3.17.1" _NSS_ECC_STRING _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR   3
 #define NSS_VMINOR   17
-#define NSS_VPATCH   0
+#define NSS_VPATCH   1
 #define NSS_VBUILD   0
-#define NSS_BETA     PR_FALSE
+#define NSS_BETA     PR_TRUE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/pk11wrap/dev3hack.c
+++ b/security/nss/lib/pk11wrap/dev3hack.c
@@ -87,24 +87,24 @@ nssSlot_CreateSession
 }
 
 NSS_IMPLEMENT PRStatus
 nssSession_Destroy
 (
   nssSession *s
 )
 {
-    CK_RV ckrv = CKR_OK;
+    PRStatus rv = PR_SUCCESS;
     if (s) {
 	if (s->isRW) {
 	    PK11_RestoreROSession(s->slot->pk11slot, s->handle);
 	}
-	nss_ZFreeIf(s);
+	rv = nss_ZFreeIf(s);
     }
-    return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE;
+    return rv;
 }
 
 static NSSSlot *
 nssSlot_CreateFromPK11SlotInfo(NSSTrustDomain *td, PK11SlotInfo *nss3slot)
 {
     NSSSlot *rvSlot;
     NSSArena *arena;
     arena = nssArena_Create();
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -20,16 +20,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION  "3.17" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION  "3.17.1" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR   3
 #define SOFTOKEN_VMINOR   17
-#define SOFTOKEN_VPATCH   0
+#define SOFTOKEN_VPATCH   1
 #define SOFTOKEN_VBUILD   0
-#define SOFTOKEN_BETA     PR_FALSE
+#define SOFTOKEN_BETA     PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -413,8 +413,12 @@ ER3(SSL_ERROR_DIGEST_FAILURE, (SSL_ERROR
 ER3(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 128),
 "Incorrect signature algorithm specified in a digitally-signed element.")
 
 ER3(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK, (SSL_ERROR_BASE + 129),
 "The next protocol negotiation extension was enabled, but the callback was cleared prior to being needed.")
 
 ER3(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL, (SSL_ERROR_BASE + 130),
 "The server supports no protocols that the client advertises in the ALPN extension.")
+
+ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 131),
+"The server rejected the handshake because the client downgraded to a lower "
+"TLS version than the server supports.")
--- a/security/nss/lib/ssl/config.mk
+++ b/security/nss/lib/ssl/config.mk
@@ -2,16 +2,21 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 ifdef NISCC_TEST
 DEFINES += -DNISCC_TEST
 endif
 
+# Allow build-time configuration of TLS 1.3 (Experimental)
+ifdef NSS_ENABLE_TLS_1_3
+DEFINES += -DNSS_ENABLE_TLS_1_3
+endif
+
 ifdef NSS_NO_PKCS11_BYPASS
 DEFINES += -DNO_PKCS11_BYPASS
 else
 CRYPTOLIB=$(SOFTOKEN_LIB_DIR)/$(LIB_PREFIX)freebl.$(LIB_SUFFIX)
 
 EXTRA_LIBS += \
 	$(CRYPTOLIB) \
 	$(NULL)
--- a/security/nss/lib/ssl/dtlscon.c
+++ b/security/nss/lib/ssl/dtlscon.c
@@ -47,26 +47,30 @@ static const ssl3CipherSuite nonDTLSSuit
 };
 
 /* Map back and forth between TLS and DTLS versions in wire format.
  * Mapping table is:
  *
  * TLS             DTLS
  * 1.1 (0302)      1.0 (feff)
  * 1.2 (0303)      1.2 (fefd)
+ * 1.3 (0304)      1.3 (fefc)
  */
 SSL3ProtocolVersion
 dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv)
 {
     if (tlsv == SSL_LIBRARY_VERSION_TLS_1_1) {
         return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
     }
     if (tlsv == SSL_LIBRARY_VERSION_TLS_1_2) {
         return SSL_LIBRARY_VERSION_DTLS_1_2_WIRE;
     }
+    if (tlsv == SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SSL_LIBRARY_VERSION_DTLS_1_3_WIRE;
+    }
 
     /* Anything other than TLS 1.1 or 1.2 is an error, so return
      * the invalid version 0xffff. */
     return 0xffff;
 }
 
 /* Map known DTLS versions to known TLS versions.
  * - Invalid versions (< 1.0) return a version of 0
@@ -80,16 +84,19 @@ dtls_DTLSVersionToTLSVersion(SSL3Protoco
     }
 
     if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_0_WIRE) {
         return SSL_LIBRARY_VERSION_TLS_1_1;
     }
     if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) {
         return SSL_LIBRARY_VERSION_TLS_1_2;
     }
+    if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_3_WIRE) {
+        return SSL_LIBRARY_VERSION_TLS_1_3;
+    }
 
     /* Return a fictional higher version than we know of */
     return SSL_LIBRARY_VERSION_TLS_1_2 + 1;
 }
 
 /* On this socket, Disable non-DTLS cipher suites in the argument's list */
 SECStatus
 ssl3_DisableNonDTLSSuites(sslSocket * ss)
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -183,16 +183,19 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
 #define SSL_ENABLE_ALPN 26
 
 /* SSL_REUSE_SERVER_ECDHE_KEY controls whether the ECDHE server key is
  * reused for multiple handshakes or generated each time.
  * SSL_REUSE_SERVER_ECDHE_KEY is currently enabled by default.
  */
 #define SSL_REUSE_SERVER_ECDHE_KEY 27
 
+#define SSL_ENABLE_FALLBACK_SCSV       28 /* Send fallback SCSV in
+                                           * handshakes. */
+
 #ifdef SSL_DEPRECATED_FUNCTION 
 /* Old deprecated function names */
 SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRBool on);
 SSL_IMPORT SECStatus SSL_EnableDefault(int option, PRBool on);
 #endif
 
 /* New function names */
 SSL_IMPORT SECStatus SSL_OptionSet(PRFileDesc *fd, PRInt32 option, PRBool on);
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -210,17 +210,20 @@ static const int compressionMethodsCount
 static PRBool
 compressionEnabled(sslSocket *ss, SSLCompressionMethod compression)
 {
     switch (compression) {
     case ssl_compression_null:
 	return PR_TRUE;  /* Always enabled */
 #ifdef NSS_ENABLE_ZLIB
     case ssl_compression_deflate:
-	return ss->opt.enableDeflate;
+        if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+            return ss->opt.enableDeflate;
+        }
+        return PR_FALSE;
 #endif
     default:
 	return PR_FALSE;
     }
 }
 
 static const /*SSL3ClientCertificateType */ PRUint8 certificate_types [] = {
     ct_RSA_sign,
@@ -632,24 +635,26 @@ ssl3_CipherSuiteAllowedForVersionRange(
      *   TLS_DH_anon_EXPORT_WITH_RC4_40_MD5:     never implemented
      *   TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA:  never implemented
      */
 	return vrange->min <= SSL_LIBRARY_VERSION_TLS_1_0;
 
     case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
     case TLS_RSA_WITH_AES_256_CBC_SHA256:
     case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
-    case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
     case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-    case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
     case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-    case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
     case TLS_RSA_WITH_AES_128_CBC_SHA256:
     case TLS_RSA_WITH_AES_128_GCM_SHA256:
     case TLS_RSA_WITH_NULL_SHA256:
+        return vrange->max == SSL_LIBRARY_VERSION_TLS_1_2;
+
+    case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+    case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+    case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
 	return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_2;
 
     /* RFC 4492: ECC cipher suites need TLS extensions to negotiate curves and
      * point formats.*/
     case TLS_ECDH_ECDSA_WITH_NULL_SHA:
     case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
     case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
     case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
@@ -664,20 +669,21 @@ ssl3_CipherSuiteAllowedForVersionRange(
     case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
     case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
     case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
     case TLS_ECDHE_RSA_WITH_NULL_SHA:
     case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
     case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
     case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
     case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-	return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_0;
+        return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_0 &&
+               vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
 
     default:
-	return PR_TRUE;
+        return vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
     }
 }
 
 /* return pointer to ssl3CipherSuiteDef for suite, or NULL */
 /* XXX This does a linear search.  A binary search would be better. */
 static const ssl3CipherSuiteDef *
 ssl_LookupCipherSuiteDef(ssl3CipherSuite suite)
 {
@@ -3347,16 +3353,19 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffe
     case no_certificate: 	error = SSL_ERROR_NO_CERTIFICATE;	  break;
     case bad_certificate: 	error = SSL_ERROR_BAD_CERT_ALERT; 	  break;
     case unsupported_certificate:error = SSL_ERROR_UNSUPPORTED_CERT_ALERT;break;
     case certificate_revoked: 	error = SSL_ERROR_REVOKED_CERT_ALERT; 	  break;
     case certificate_expired: 	error = SSL_ERROR_EXPIRED_CERT_ALERT; 	  break;
     case certificate_unknown: 	error = SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT;
 			        					  break;
     case illegal_parameter: 	error = SSL_ERROR_ILLEGAL_PARAMETER_ALERT;break;
+    case inappropriate_fallback:
+        error = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT;
+        break;
 
     /* All alerts below are TLS only. */
     case unknown_ca: 		error = SSL_ERROR_UNKNOWN_CA_ALERT;       break;
     case access_denied: 	error = SSL_ERROR_ACCESS_DENIED_ALERT;    break;
     case decode_error: 		error = SSL_ERROR_DECODE_ERROR_ALERT;     break;
     case decrypt_error: 	error = SSL_ERROR_DECRYPT_ERROR_ALERT;    break;
     case export_restriction: 	error = SSL_ERROR_EXPORT_RESTRICTION_ALERT; 
     									  break;
@@ -4868,16 +4877,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
     sslSessionID *   sid;
     ssl3CipherSpec * cwSpec;
     SECStatus        rv;
     int              i;
     int              length;
     int              num_suites;
     int              actual_count = 0;
     PRBool           isTLS = PR_FALSE;
+    PRBool           requestingResume = PR_FALSE, fallbackSCSV = PR_FALSE;
     PRInt32          total_exten_len = 0;
     unsigned         paddingExtensionLen;
     unsigned         numCompressionMethods;
     PRInt32          flags;
 
     SSL_TRC(3, ("%d: SSL3[%d]: send client_hello handshake", SSL_GETPID(),
 		ss->fd));
 
@@ -5010,16 +5020,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 	    if (ss->sec.uncache)
                 (*ss->sec.uncache)(sid);
 	    ssl_FreeSID(sid);
 	    sid = NULL;
 	}
     }
 
     if (sid) {
+	requestingResume = PR_TRUE;
 	SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
 
 	PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
 		      sid->u.ssl3.sessionIDLength));
 
 	ss->ssl3.policy = sid->u.ssl3.policy;
     } else {
 	SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_misses );
@@ -5124,18 +5135,25 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
     }
 
     /* how many suites are permitted by policy and user preference? */
     num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
     if (!num_suites) {
     	if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
     	return SECFailure;	/* count_cipher_suites has set error code. */
     }
+
+    fallbackSCSV = ss->opt.enableFallbackSCSV && (!requestingResume ||
+						  ss->version < sid->version);
+    /* make room for SCSV */
     if (ss->ssl3.hs.sendingSCSV) {
-	++num_suites;   /* make room for SCSV */
+	++num_suites;
+    }
+    if (fallbackSCSV) {
+	++num_suites;
     }
 
     /* count compression methods */
     numCompressionMethods = 0;
     for (i = 0; i < compressionMethodsCount; i++) {
 	if (compressionEnabled(ss, compressions[i]))
 	    numCompressionMethods++;
     }
@@ -5231,16 +5249,25 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
 	rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
 					sizeof(ssl3CipherSuite));
 	if (rv != SECSuccess) {
 	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 	    return rv;	/* err set by ssl3_AppendHandshake* */
 	}
 	actual_count++;
     }
+    if (fallbackSCSV) {
+	rv = ssl3_AppendHandshakeNumber(ss, TLS_FALLBACK_SCSV,
+					sizeof(ssl3CipherSuite));
+	if (rv != SECSuccess) {
+	    if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
+	    return rv;	/* err set by ssl3_AppendHandshake* */
+	}
+	actual_count++;
+    }
     for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
 	ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
 	if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
 	    actual_count++;
 	    if (actual_count > num_suites) {
 		if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
 		/* set error card removal/insertion error */
 		PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
@@ -7706,22 +7733,41 @@ ssl3_HandleClientHello(sslSocket *ss, SS
     }
 
     /* grab the list of cipher suites. */
     rv = ssl3_ConsumeHandshakeVariable(ss, &suites, 2, &b, &length);
     if (rv != SECSuccess) {
 	goto loser;		/* malformed */
     }
 
+    /* If the ClientHello version is less than our maximum version, check for a
+     * TLS_FALLBACK_SCSV and reject the connection if found. */
+    if (ss->vrange.max > ss->clientHelloVersion) {
+	for (i = 0; i + 1 < suites.len; i += 2) {
+	    PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
+	    if (suite_i != TLS_FALLBACK_SCSV)
+		continue;
+	    desc = inappropriate_fallback;
+	    errCode = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT;
+	    goto alert_loser;
+	}
+    }
+
     /* grab the list of compression methods. */
     rv = ssl3_ConsumeHandshakeVariable(ss, &comps, 1, &b, &length);
     if (rv != SECSuccess) {
 	goto loser;		/* malformed */
     }
 
+    /* TLS 1.3 requires that compression be empty */
+    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+        if (comps.len != 1 || comps.data[0] != ssl_compression_null) {
+            goto loser;
+        }
+    }
     desc = handshake_failure;
 
     /* Handle TLS hello extensions for SSL3 & TLS. We do not know if
      * we are restarting a previous session until extensions have been
      * parsed, since we might have received a SessionTicket extension.
      * Note: we allow extensions even when negotiating SSL3 for the sake
      * of interoperability (and backwards compatibility).
      */
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -77,16 +77,21 @@ static SECStatus ssl3_ClientHandleStatus
                                                    SECItem *data);
 static PRInt32 ssl3_ClientSendStatusRequestXtn(sslSocket * ss, PRBool append,
                                                PRUint32 maxBytes);
 static PRInt32 ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append,
                                          PRUint32 maxBytes);
 static SECStatus ssl3_ServerHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type,
                                              SECItem *data);
 
+static PRInt32 ssl3_ClientSendDraftVersionXtn(sslSocket *ss, PRBool append,
+                                              PRUint32 maxBytes);
+static SECStatus ssl3_ServerHandleDraftVersionXtn(sslSocket *ss, PRUint16 ex_type,
+                                                  SECItem *data);
+
 /*
  * Write bytes.  Using this function means the SECItem structure
  * cannot be freed.  The caller is expected to call this function
  * on a shallow copy of the structure.
  */
 static SECStatus
 ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
 {
@@ -240,16 +245,17 @@ static const ssl3HelloExtensionHandler c
 #endif
     { ssl_session_ticket_xtn,     &ssl3_ServerHandleSessionTicketXtn },
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { ssl_next_proto_nego_xtn,    &ssl3_ServerHandleNextProtoNegoXtn },
     { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
     { ssl_use_srtp_xtn,           &ssl3_HandleUseSRTPXtn },
     { ssl_cert_status_xtn,        &ssl3_ServerHandleStatusRequestXtn },
     { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
+    { ssl_tls13_draft_version_xtn, &ssl3_ServerHandleDraftVersionXtn },
     { -1, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn,        &ssl3_HandleServerNameXtn },
     /* TODO: add a handler for ssl_ec_point_formats_xtn */
@@ -281,17 +287,18 @@ ssl3HelloExtensionSender clientHelloSend
     { ssl_elliptic_curves_xtn,    &ssl3_SendSupportedCurvesXtn },
     { ssl_ec_point_formats_xtn,   &ssl3_SendSupportedPointFormatsXtn },
 #endif
     { ssl_session_ticket_xtn,     &ssl3_SendSessionTicketXtn },
     { ssl_next_proto_nego_xtn,    &ssl3_ClientSendNextProtoNegoXtn },
     { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
     { ssl_use_srtp_xtn,           &ssl3_SendUseSRTPXtn },
     { ssl_cert_status_xtn,        &ssl3_ClientSendStatusRequestXtn },
-    { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
+    { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+    { ssl_tls13_draft_version_xtn, &ssl3_ClientSendDraftVersionXtn },
     /* any extra entries will appear as { 0, NULL }    */
 };
 
 static const
 ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
     { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
     /* any extra entries will appear as { 0, NULL }    */
 };
@@ -2416,8 +2423,98 @@ ssl3_AppendPaddingExtension(sslSocket *s
         return -1;
     if (SECSuccess != ssl3_AppendHandshakeNumber(ss, paddingLen, 2))
         return -1;
     if (SECSuccess != ssl3_AppendHandshake(ss, padding, paddingLen))
         return -1;
 
     return extensionLen;
 }
+
+/* ssl3_ClientSendDraftVersionXtn sends the TLS 1.3 temporary draft
+ * version extension.
+ * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
+static PRInt32
+ssl3_ClientSendDraftVersionXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
+{
+    PRInt32 extension_length;
+
+    if (ss->version != SSL_LIBRARY_VERSION_TLS_1_3) {
+        return 0;
+    }
+
+    extension_length = 6;  /* Type + length + number */
+    if (append && maxBytes >= extension_length) {
+        SECStatus rv;
+        rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_draft_version_xtn, 2);
+        if (rv != SECSuccess)
+            goto loser;
+        rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
+        if (rv != SECSuccess)
+            goto loser;
+        rv = ssl3_AppendHandshakeNumber(ss, TLS_1_3_DRAFT_VERSION, 2);
+        if (rv != SECSuccess)
+            goto loser;
+        ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+                ssl_tls13_draft_version_xtn;
+    } else if (maxBytes < extension_length) {
+        PORT_Assert(0);
+        return 0;
+    }
+
+    return extension_length;
+
+loser:
+    return -1;
+}
+
+/* ssl3_ServerHandleDraftVersionXtn handles the TLS 1.3 temporary draft
+ * version extension.
+ * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
+static SECStatus
+ssl3_ServerHandleDraftVersionXtn(sslSocket * ss, PRUint16 ex_type,
+                                 SECItem *data)
+{
+    PRInt32 draft_version;
+
+    /* Ignore this extension if we aren't doing TLS 1.3 */
+    if (ss->version != SSL_LIBRARY_VERSION_TLS_1_3) {
+        return SECSuccess;
+    }
+
+    if (data->len != 2)
+        goto loser;
+
+    /* Get the draft version out of the handshake */
+    draft_version = ssl3_ConsumeHandshakeNumber(ss, 2,
+                                                &data->data, &data->len);
+    if (draft_version < 0) {
+        goto loser;
+    }
+
+    /*  Keep track of negotiated extensions. */
+    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+    /* Compare the version */
+    if (draft_version != TLS_1_3_DRAFT_VERSION) {
+        SSL_TRC(30, ("%d: SSL3[%d]: Incompatible version of TLS 1.3 (%d), "
+                     "expected %d",
+                     SSL_GETPID(), ss->fd, draft_version, TLS_1_3_DRAFT_VERSION));
+        goto loser;
+    }
+
+    return SECSuccess;
+
+loser:
+    /*
+     * Incompatible/broken TLS 1.3 implementation. Fall back to TLS 1.2.
+     * TODO(ekr@rtfm.com): It's not entirely clear it's safe to roll back
+     * here. Need to double-check.
+     * TODO(ekr@rtfm.com): Currently we fall back even on broken extensions.
+     * because SECFailure does not cause handshake failures. See bug
+     * 753136.
+     */
+    SSL_TRC(30, ("%d: SSL3[%d]: Rolling back to TLS 1.2", SSL_GETPID(), ss->fd));
+    ss->version = SSL_LIBRARY_VERSION_TLS_1_2;
+
+    return SECSuccess;
+}
+
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -9,16 +9,21 @@
 #ifndef __ssl3proto_h_
 #define __ssl3proto_h_
 
 typedef PRUint8 SSL3Opaque;
 
 typedef PRUint16 SSL3ProtocolVersion;
 /* version numbers are defined in sslproto.h */
 
+/* The TLS 1.3 draft version. Used to avoid negotiating
+ * between incompatible pre-standard TLS 1.3 drafts.
+ * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
+#define TLS_1_3_DRAFT_VERSION  3
+
 typedef PRUint16 ssl3CipherSuite;
 /* The cipher suites are defined in sslproto.h */
 
 #define MAX_CERT_TYPES                  10
 #define MAX_COMPRESSION_METHODS         10
 #define MAX_MAC_LENGTH                  64
 #define MAX_PADDING_LENGTH              64
 #define MAX_KEY_LENGTH                  64
@@ -93,16 +98,17 @@ typedef enum {
     unknown_ca              = 48,
     access_denied           = 49,
     decode_error            = 50,
     decrypt_error           = 51,
     export_restriction      = 60,
     protocol_version        = 70,
     insufficient_security   = 71,
     internal_error          = 80,
+    inappropriate_fallback  = 86,	/* could also be sent for SSLv3 */
     user_canceled           = 90,
     no_renegotiation        = 100,
 
 /* Alerts for client hello extensions */
     unsupported_extension           = 110,
     certificate_unobtainable        = 111,
     unrecognized_name               = 112,
     bad_certificate_status_response = 113,
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -191,13 +191,15 @@ SSL_ERROR_RX_UNEXPECTED_CERT_STATUS     
 
 SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM    = (SSL_ERROR_BASE + 126),
 SSL_ERROR_DIGEST_FAILURE                = (SSL_ERROR_BASE + 127),
 SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 128),
 
 SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK     = (SSL_ERROR_BASE + 129),
 SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL     = (SSL_ERROR_BASE + 130),
 
+SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT  = (SSL_ERROR_BASE + 131),
+
 SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 #endif /* __SSL_ERR_H_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -322,16 +322,17 @@ typedef struct sslOptionsStr {
     unsigned int enableRenegotiation    : 2;  /* 20-21 */
     unsigned int requireSafeNegotiation : 1;  /* 22 */
     unsigned int enableFalseStart       : 1;  /* 23 */
     unsigned int cbcRandomIV            : 1;  /* 24 */
     unsigned int enableOCSPStapling     : 1;  /* 25 */
     unsigned int enableNPN              : 1;  /* 26 */
     unsigned int enableALPN             : 1;  /* 27 */
     unsigned int reuseServerECDHEKey    : 1;  /* 28 */
+    unsigned int enableFallbackSCSV     : 1;  /* 29 */
 } sslOptions;
 
 typedef enum { sslHandshakingUndetermined = 0,
 	       sslHandshakingAsClient,
 	       sslHandshakingAsServer 
 } sslHandshakingType;
 
 typedef struct sslServerCertsStr {
@@ -1530,17 +1531,21 @@ extern PRInt32   ssl3_SendRecord(sslSock
  */
 #define SSL_LIBRARY_VERSION_NONE 0
 
 /* SSL_LIBRARY_VERSION_MAX_SUPPORTED is the maximum version that this version 
  * of libssl supports. Applications should use SSL_VersionRangeGetSupported at
  * runtime to determine which versions are supported by the version of libssl
  * in use.
  */
+#ifdef NSS_ENABLE_TLS_1_3
+#define SSL_LIBRARY_VERSION_MAX_SUPPORTED SSL_LIBRARY_VERSION_TLS_1_3
+#else
 #define SSL_LIBRARY_VERSION_MAX_SUPPORTED SSL_LIBRARY_VERSION_TLS_1_2
+#endif
 
 /* Rename this macro SSL_ALL_VERSIONS_DISABLED when SSL 2.0 is removed. */
 #define SSL3_ALL_VERSIONS_DISABLED(vrange) \
     ((vrange)->min == SSL_LIBRARY_VERSION_NONE)
 
 extern PRBool ssl3_VersionIsSupported(SSLProtocolVariant protocolVariant,
 				      SSL3ProtocolVersion version);
 
--- a/security/nss/lib/ssl/sslproto.h
+++ b/security/nss/lib/ssl/sslproto.h
@@ -11,26 +11,30 @@
 #define __sslproto_h_
 
 /* All versions less than 3_0 are treated as SSL version 2 */
 #define SSL_LIBRARY_VERSION_2                   0x0002
 #define SSL_LIBRARY_VERSION_3_0                 0x0300
 #define SSL_LIBRARY_VERSION_TLS_1_0             0x0301
 #define SSL_LIBRARY_VERSION_TLS_1_1             0x0302
 #define SSL_LIBRARY_VERSION_TLS_1_2             0x0303
+#define SSL_LIBRARY_VERSION_TLS_1_3             0x0304
+
 /* Note: this is the internal format, not the wire format */
 #define SSL_LIBRARY_VERSION_DTLS_1_0            0x0302
 #define SSL_LIBRARY_VERSION_DTLS_1_2            0x0303
+#define SSL_LIBRARY_VERSION_DTLS_1_3            0x0304
 
 /* deprecated old name */
 #define SSL_LIBRARY_VERSION_3_1_TLS SSL_LIBRARY_VERSION_TLS_1_0
 
 /* The DTLS versions used in the spec */
 #define SSL_LIBRARY_VERSION_DTLS_1_0_WIRE       ((~0x0100) & 0xffff)
 #define SSL_LIBRARY_VERSION_DTLS_1_2_WIRE       ((~0x0102) & 0xffff)
+#define SSL_LIBRARY_VERSION_DTLS_1_3_WIRE       ((~0x0103) & 0xffff)
 
 /* Header lengths of some of the messages */
 #define SSL_HL_ERROR_HBYTES                     3
 #define SSL_HL_CLIENT_HELLO_HBYTES              9
 #define SSL_HL_CLIENT_MASTER_KEY_HBYTES         10
 #define SSL_HL_CLIENT_FINISHED_HBYTES           1
 #define SSL_HL_SERVER_HELLO_HBYTES              11
 #define SSL_HL_SERVER_VERIFY_HBYTES             1
@@ -203,16 +207,21 @@
 #define TLS_DHE_DSS_WITH_AES_128_GCM_SHA256     0x00A2
 
 /* TLS "Signaling Cipher Suite Value" (SCSV). May be requested by client.
  * Must NEVER be chosen by server.  SSL 3.0 server acknowledges by sending
  * back an empty Renegotiation Info (RI) server hello extension.
  */
 #define TLS_EMPTY_RENEGOTIATION_INFO_SCSV       0x00FF
 
+/* TLS_FALLBACK_SCSV is a signaling cipher suite value that indicates that a
+ * handshake is the result of TLS version fallback.
+ */
+#define TLS_FALLBACK_SCSV                       0x5600
+
 /* Cipher Suite Values starting with 0xC000 are defined in informational
  * RFCs.
  */
 #define TLS_ECDH_ECDSA_WITH_NULL_SHA            0xC001
 #define TLS_ECDH_ECDSA_WITH_RC4_128_SHA         0xC002
 #define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA    0xC003
 #define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA     0xC004
 #define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA     0xC005
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -76,17 +76,18 @@ static sslOptions ssl_defaults = {
     PR_FALSE,   /* enableDeflate      */
     2,          /* enableRenegotiation (default: requires extension) */
     PR_FALSE,   /* requireSafeNegotiation */
     PR_FALSE,   /* enableFalseStart   */
     PR_TRUE,    /* cbcRandomIV        */
     PR_FALSE,   /* enableOCSPStapling */
     PR_TRUE,    /* enableNPN          */
     PR_FALSE,   /* enableALPN         */
-    PR_TRUE     /* reuseServerECDHEKey */
+    PR_TRUE,    /* reuseServerECDHEKey */
+    PR_FALSE    /* enableFallbackSCSV */
 };
 
 /*
  * default range of enabled SSL/TLS protocols
  */
 static SSLVersionRange versions_defaults_stream = {
     SSL_LIBRARY_VERSION_3_0,
     SSL_LIBRARY_VERSION_TLS_1_0
@@ -784,16 +785,20 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
       case SSL_ENABLE_ALPN:
         ss->opt.enableALPN = on;
         break;
 
       case SSL_REUSE_SERVER_ECDHE_KEY:
         ss->opt.reuseServerECDHEKey = on;
         break;
 
+      case SSL_ENABLE_FALLBACK_SCSV:
+        ss->opt.enableFallbackSCSV = on;
+        break;
+
       default:
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         rv = SECFailure;
     }
 
     /* We can't use the macros for releasing the locks here,
      * because ss->opt.noLocks might have changed just above.
      * We must release these locks (monitors) here, if we aquired them above,
@@ -858,16 +863,17 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
                                   on = ss->opt.requireSafeNegotiation; break;
     case SSL_ENABLE_FALSE_START:  on = ss->opt.enableFalseStart;   break;
     case SSL_CBC_RANDOM_IV:       on = ss->opt.cbcRandomIV;        break;
     case SSL_ENABLE_OCSP_STAPLING: on = ss->opt.enableOCSPStapling; break;
     case SSL_ENABLE_NPN:          on = ss->opt.enableNPN;          break;
     case SSL_ENABLE_ALPN:         on = ss->opt.enableALPN;         break;
     case SSL_REUSE_SERVER_ECDHE_KEY:
                                   on = ss->opt.reuseServerECDHEKey; break;
+    case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break;
 
     default:
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         rv = SECFailure;
     }
 
     ssl_ReleaseSSL3HandshakeLock(ss);
     ssl_Release1stHandshakeLock(ss);
@@ -924,16 +930,19 @@ SSL_OptionGetDefault(PRInt32 which, PRBo
     case SSL_ENABLE_OCSP_STAPLING:
        on = ssl_defaults.enableOCSPStapling;
        break;
     case SSL_ENABLE_NPN:          on = ssl_defaults.enableNPN;          break;
     case SSL_ENABLE_ALPN:         on = ssl_defaults.enableALPN;         break;
     case SSL_REUSE_SERVER_ECDHE_KEY:
        on = ssl_defaults.reuseServerECDHEKey;
        break;
+    case SSL_ENABLE_FALLBACK_SCSV:
+       on = ssl_defaults.enableFallbackSCSV;
+       break;
 
     default:
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         rv = SECFailure;
     }
 
     *pOn = on;
     return rv;
@@ -1103,16 +1112,20 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
       case SSL_ENABLE_ALPN:
         ssl_defaults.enableALPN = on;
         break;
 
       case SSL_REUSE_SERVER_ECDHE_KEY:
         ssl_defaults.reuseServerECDHEKey = on;
         break;
 
+      case SSL_ENABLE_FALLBACK_SCSV:
+        ssl_defaults.enableFallbackSCSV = on;
+        break;
+
       default:
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
     return SECSuccess;
 }
 
 /* function tells us if the cipher suite is one that we no longer support. */
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -186,14 +186,15 @@ typedef enum {
     ssl_ec_point_formats_xtn         = 11,
 #endif
     ssl_signature_algorithms_xtn     = 13,
     ssl_use_srtp_xtn                 = 14,
     ssl_app_layer_protocol_xtn       = 16,
     ssl_padding_xtn                  = 21,
     ssl_session_ticket_xtn           = 35,
     ssl_next_proto_nego_xtn          = 13172,
-    ssl_renegotiation_info_xtn       = 0xff01	/* experimental number */
+    ssl_renegotiation_info_xtn       = 0xff01,
+    ssl_tls13_draft_version_xtn      = 0xff02   /* experimental number */
 } SSLExtensionType;
 
-#define SSL_MAX_EXTENSIONS             10 /* doesn't include ssl_padding_xtn. */
+#define SSL_MAX_EXTENSIONS             11 /* doesn't include ssl_padding_xtn. */
 
 #endif /* __sslt_h_ */
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION  "3.17"
+#define NSSUTIL_VERSION  "3.17.1 Beta"
 #define NSSUTIL_VMAJOR   3
 #define NSSUTIL_VMINOR   17
-#define NSSUTIL_VPATCH   0
+#define NSSUTIL_VPATCH   1
 #define NSSUTIL_VBUILD   0
-#define NSSUTIL_BETA     PR_FALSE
+#define NSSUTIL_BETA     PR_TRUE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);
 
--- a/security/nss/tests/pkcs11/netscape/suites/security/ssl/sslt.c
+++ b/security/nss/tests/pkcs11/netscape/suites/security/ssl/sslt.c
@@ -1,14 +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/. */
 
-#define VERION_MAJOR 1
-#define VERION_MINOR 0
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
 #define VERSION_POINT 7
 /* NSPR header files */
 #include <prinit.h>
 #include <prprf.h>
 #include <prsystem.h>
 #include <prmem.h>
 #include <plstr.h>
 #include <prnetdb.h>
@@ -169,17 +169,17 @@ PRIntn main(PRIntn ac, char **av, char *
   int c;
   
 
   if( ac == 1 ) {
      PR_fprintf(PR_STDERR,
 "\nSSL Test Suite Version %d.%d.%d\n\
 All Rights Reserved\n\
 Usage: sslt [-c client_nickname] [-n server_nickname] [-p passwd] [-d] testid\n",
-VERION_MAJOR, VERION_MINOR, VERSION_POINT);
+VERSION_MAJOR, VERSION_MINOR, VERSION_POINT);
 
     exit(0);
   }
 
   for (c = 1; c<ac; c++) {
 	if (!PL_strcmp(av[c],"-c")) {
 	
 		  c++;
--- a/security/sandbox/moz.build
+++ b/security/sandbox/moz.build
@@ -8,132 +8,23 @@ if CONFIG['OS_ARCH'] == 'Linux':
     DIRS += ['linux']
 elif CONFIG['OS_ARCH'] == 'Darwin':
     DIRS += ['mac']
 elif CONFIG['OS_ARCH'] == 'WINNT':
     LIBRARY_NAME = 'sandbox_s'
     FORCE_STATIC_LIB = True
 
     DIRS += [
+        'staticruntime',
         'win/src/sandboxbroker',
         'win/src/sandboxtarget',
     ]
 
-    SOURCES += [
-        'chromium/base/at_exit.cc',
-        'chromium/base/base_switches.cc',
-        'chromium/base/callback_internal.cc',
-        'chromium/base/cpu.cc',
-        'chromium/base/debug/alias.cc',
-        'chromium/base/debug/profiler.cc',
-        'chromium/base/lazy_instance.cc',
-        'chromium/base/location.cc',
-        'chromium/base/memory/ref_counted.cc',
-        'chromium/base/memory/singleton.cc',
-        'chromium/base/shim/base/logging.cpp',
-        'chromium/base/strings/nullable_string16.cc',
-        'chromium/base/strings/string_number_conversions.cc',
-        'chromium/base/strings/string_piece.cc',
-        'chromium/base/strings/string_util_constants.cc',
-        'chromium/base/strings/string_util_stripped.cc',
-        'chromium/base/strings/stringprintf.cc',
-        'chromium/base/strings/utf_string_conversion_utils.cc',
-        'chromium/base/strings/utf_string_conversions.cc',
-        'chromium/base/synchronization/lock.cc',
-        'chromium/base/synchronization/lock_impl_win.cc',
-        'chromium/base/third_party/dmg_fp/dtoa.cc',
-        'chromium/base/third_party/dmg_fp/g_fmt.cc',
-        'chromium/base/third_party/icu/icu_utf.cc',
-        'chromium/base/threading/platform_thread_win.cc',
-        'chromium/base/threading/thread_collision_warner.cc',
-        'chromium/base/threading/thread_id_name_manager.cc',
-        'chromium/base/threading/thread_local_win.cc',
-        'chromium/base/threading/thread_restrictions.cc',
-        'chromium/base/time/time.cc',
-        'chromium/base/time/time_win.cc',
-        'chromium/base/win/event_trace_provider.cc',
-        'chromium/base/win/pe_image.cc',
-        'chromium/base/win/registry.cc',
-        'chromium/base/win/scoped_handle.cc',
-        'chromium/base/win/scoped_process_information.cc',
-        'chromium/base/win/startup_information.cc',
-        'chromium/base/win/windows_version.cc',
-        'win/src/acl.cc',
-        'win/src/app_container.cc',
-        'win/src/broker_services.cc',
-        'win/src/crosscall_server.cc',
-        'win/src/eat_resolver.cc',
-        'win/src/filesystem_dispatcher.cc',
-        'win/src/filesystem_interception.cc',
-        'win/src/filesystem_policy.cc',
-        'win/src/handle_closer.cc',
-        'win/src/handle_closer_agent.cc',
-        'win/src/handle_dispatcher.cc',
-        'win/src/handle_interception.cc',
-        'win/src/handle_policy.cc',
-        'win/src/handle_table.cc',
-        'win/src/interception.cc',
-        'win/src/interception_agent.cc',
-        'win/src/job.cc',
-        'win/src/named_pipe_dispatcher.cc',
-        'win/src/named_pipe_interception.cc',
-        'win/src/named_pipe_policy.cc',
-        'win/src/policy_broker.cc',
-        'win/src/policy_engine_opcodes.cc',
-        'win/src/policy_engine_processor.cc',
-        'win/src/policy_low_level.cc',
-        'win/src/policy_target.cc',
-        'win/src/process_mitigations.cc',
-        'win/src/process_thread_dispatcher.cc',
-        'win/src/process_thread_interception.cc',
-        'win/src/process_thread_policy.cc',
-        'win/src/registry_dispatcher.cc',
-        'win/src/registry_interception.cc',
-        'win/src/registry_policy.cc',
-        'win/src/resolver.cc',
-        'win/src/restricted_token.cc',
-        'win/src/restricted_token_utils.cc',
-        'win/src/sandbox.cc',
-        'win/src/sandbox_nt_util.cc',
-        'win/src/sandbox_policy_base.cc',
-        'win/src/sandbox_utils.cc',
-        'win/src/service_resolver.cc',
-        'win/src/shared_handles.cc',
-        'win/src/sharedmem_ipc_client.cc',
-        'win/src/sharedmem_ipc_server.cc',
-        'win/src/sid.cc',
-        'win/src/sync_dispatcher.cc',
-        'win/src/sync_interception.cc',
-        'win/src/sync_policy.cc',
-        'win/src/target_interceptions.cc',
-        'win/src/target_process.cc',
-        'win/src/target_services.cc',
-        'win/src/win2k_threadpool.cc',
-        'win/src/win_utils.cc',
-        'win/src/window.cc',
-    ]
-
-    if CONFIG['CPU_ARCH'] == 'x86_64':
-        SOURCES += [
-            'win/src/interceptors_64.cc',
-            'win/src/resolver_64.cc',
-            'win/src/service_resolver_64.cc',
-            'win/src/Wow64_64.cc',
-        ]
-    else:
-        SOURCES += [
-            'win/src/resolver_32.cc',
-            'win/src/service_resolver_32.cc',
-            'win/src/sidestep/ia32_modrm_map.cpp',
-            'win/src/sidestep/ia32_opcode_map.cpp',
-            'win/src/sidestep/mini_disassembler.cpp',
-            'win/src/sidestep/preamble_patcher_with_stub.cpp',
-            'win/src/sidestep_resolver.cc',
-            'win/src/Wow64.cc',
-        ]
+    include('objs.mozbuild')
+    SOURCES += security_sandbox_cppsrcs
 
     for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS',
                 'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'):
         DEFINES[var] = True
 
     LOCAL_INCLUDES += ['/security/sandbox/chromium/base/shim']
     LOCAL_INCLUDES += ['/security/sandbox/chromium']
     LOCAL_INCLUDES += ['/security']
copy from security/sandbox/moz.build
copy to security/sandbox/objs.mozbuild
--- a/security/sandbox/moz.build
+++ b/security/sandbox/objs.mozbuild
@@ -1,28 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-if CONFIG['OS_ARCH'] == 'Linux':
-    DIRS += ['linux']
-elif CONFIG['OS_ARCH'] == 'Darwin':
-    DIRS += ['mac']
-elif CONFIG['OS_ARCH'] == 'WINNT':
-    LIBRARY_NAME = 'sandbox_s'
-    FORCE_STATIC_LIB = True
-
-    DIRS += [
-        'win/src/sandboxbroker',
-        'win/src/sandboxtarget',
-    ]
-
-    SOURCES += [
+if CONFIG['OS_ARCH'] == 'WINNT':
+    security_sandbox_lcppsrcs = [
         'chromium/base/at_exit.cc',
         'chromium/base/base_switches.cc',
         'chromium/base/callback_internal.cc',
         'chromium/base/cpu.cc',
         'chromium/base/debug/alias.cc',
         'chromium/base/debug/profiler.cc',
         'chromium/base/lazy_instance.cc',
         'chromium/base/location.cc',
@@ -107,46 +95,30 @@ elif CONFIG['OS_ARCH'] == 'WINNT':
         'win/src/target_process.cc',
         'win/src/target_services.cc',
         'win/src/win2k_threadpool.cc',
         'win/src/win_utils.cc',
         'win/src/window.cc',
     ]
 
     if CONFIG['CPU_ARCH'] == 'x86_64':
-        SOURCES += [
+        security_sandbox_lcppsrcs += [
             'win/src/interceptors_64.cc',
             'win/src/resolver_64.cc',
             'win/src/service_resolver_64.cc',
             'win/src/Wow64_64.cc',
         ]
     else:
-        SOURCES += [
+        security_sandbox_lcppsrcs += [
             'win/src/resolver_32.cc',
             'win/src/service_resolver_32.cc',
             'win/src/sidestep/ia32_modrm_map.cpp',
             'win/src/sidestep/ia32_opcode_map.cpp',
             'win/src/sidestep/mini_disassembler.cpp',
             'win/src/sidestep/preamble_patcher_with_stub.cpp',
             'win/src/sidestep_resolver.cc',
             'win/src/Wow64.cc',
         ]
 
-    for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS',
-                'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'):
-        DEFINES[var] = True
-
-    LOCAL_INCLUDES += ['/security/sandbox/chromium/base/shim']
-    LOCAL_INCLUDES += ['/security/sandbox/chromium']
-    LOCAL_INCLUDES += ['/security']
-    LOCAL_INCLUDES += ['/nsprpub']
-
-    DISABLE_STL_WRAPPING = True
-
-    # Suppress warnings in third-party code.
-    if CONFIG['_MSC_VER']:
-        CXXFLAGS += [
-            '-wd4275', # non dll-interface class exception used as base for dll-interface class
-            '-wd4717', # recursive on all control paths, function will cause runtime stack overflow
-            '-wd4996', # 'GetVersionExW': was declared deprecated
-        ]
-
-FAIL_ON_WARNINGS = True
+    security_sandbox_cppsrcs = [
+        '%s/security/sandbox/%s' % (TOPSRCDIR, s)
+            for s in sorted(security_sandbox_lcppsrcs)
+    ]
new file mode 100644
--- /dev/null
+++ b/security/sandbox/staticruntime/moz.build
@@ -0,0 +1,35 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    LIBRARY_NAME = 'sandbox_staticruntime_s'
+    FORCE_STATIC_LIB = True
+    USE_STATIC_LIBS = True
+
+    include('../objs.mozbuild')
+    SOURCES += security_sandbox_cppsrcs
+
+    for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS',
+                'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'):
+        DEFINES[var] = True
+
+    LOCAL_INCLUDES += ['/security/sandbox/chromium/base/shim']
+    LOCAL_INCLUDES += ['/security/sandbox/chromium']
+    LOCAL_INCLUDES += ['/security/sandbox']
+    LOCAL_INCLUDES += ['/security']
+    LOCAL_INCLUDES += ['/nsprpub']
+
+    DISABLE_STL_WRAPPING = True
+
+    # Suppress warnings in third-party code.
+    if CONFIG['_MSC_VER']:
+        CXXFLAGS += [
+            '-wd4275', # non dll-interface class exception used as base for dll-interface class
+            '-wd4717', # recursive on all control paths, function will cause runtime stack overflow
+            '-wd4996', # 'GetVersionExW': was declared deprecated
+        ]
+
+FAIL_ON_WARNINGS = True
--- a/services/sync/modules/browserid_identity.js
+++ b/services/sync/modules/browserid_identity.js
@@ -183,17 +183,21 @@ this.BrowserIDManager.prototype = {
     this._log.trace("initializeWithCurrentIdentity");
 
     // Reset the world before we do anything async.
     this.whenReadyToAuthenticate = Promise.defer();
     this.whenReadyToAuthenticate.promise.then(null, (err) => {
       this._log.error("Could not authenticate: " + err);
     });
 
-    this._shouldHaveSyncKeyBundle = false;
+    // initializeWithCurrentIdentity() can be called after the
+    // identity module was first initialized, e.g., after the
+    // user completes a force authentication, so we should make
+    // sure all credentials are reset before proceeding.
+    this.resetCredentials();
     this._authFailureReason = null;
 
     return this._fxaService.getSignedInUser().then(accountData => {
       if (!accountData) {
         this._log.info("initializeWithCurrentIdentity has no user logged in");
         this.account = null;
         // and we are as ready as we can ever be for auth.
         this._shouldHaveSyncKeyBundle = true;
@@ -574,19 +578,20 @@ this.BrowserIDManager.prototype = {
           // set it to the "fatal" LOGIN_FAILED_LOGIN_REJECTED reason.
           this._authFailureReason = LOGIN_FAILED_LOGIN_REJECTED;
         } else {
           this._log.error("Non-authentication error in _fetchTokenForUser: "
                           + (err.message || err));
           // for now assume it is just a transient network related problem.
           this._authFailureReason = LOGIN_FAILED_NETWORK_ERROR;
         }
-        // Drop the sync key bundle, but still expect to have one.
-        // This will arrange for us to be in the right 'currentAuthState'
-        // such that UI will show the right error.
+        // this._authFailureReason being set to be non-null in the above if clause
+        // ensures we are in the correct currentAuthState, and
+        // this._shouldHaveSyncKeyBundle being true ensures everything that cares knows
+        // that there is no authentication dance still under way.
         this._shouldHaveSyncKeyBundle = true;
         Weave.Status.login = this._authFailureReason;
         Services.obs.notifyObservers(null, "weave:service:login:error", null);
         throw err;
       });
   },
 
   // Returns a promise that is resolved when we have a valid token for the
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -247,17 +247,19 @@ class MochiRemote(Mochitest):
 
     def cleanup(self, options):
         if self._dm.fileExists(self.remoteLog):
             self._dm.getFile(self.remoteLog, self.localLog)
             self._dm.removeFile(self.remoteLog)
         else:
             self.log.warning("Unable to retrieve log file (%s) from remote device" % self.remoteLog)
         self._dm.removeDir(self.remoteProfile)
-        self._dm.getDirectory(self.remoteNSPR, os.environ["MOZ_UPLOAD_DIR"])
+        blobberUploadDir = os.environ.get('MOZ_UPLOAD_DIR', None)
+        if blobberUploadDir:
+            self._dm.getDirectory(self.remoteNSPR, blobberUploadDir)
         Mochitest.cleanup(self, options)
 
     def findPath(self, paths, filename = None):
         for path in paths:
             p = path
             if filename:
                 p = os.path.join(p, filename)
             if os.path.exists(self.getFullPath(p)):
--- a/testing/mozbase/mozrunner/mozrunner/base/device.py
+++ b/testing/mozbase/mozrunner/mozrunner/base/device.py
@@ -67,19 +67,16 @@ class DeviceRunner(BaseRunner):
         self.device.connect()
         self.device.setup_profile(self.profile)
 
         # TODO: this doesn't work well when the device is running but dropped
         # wifi for some reason. It would be good to probe the state of the device
         # to see if we have the homescreen running, or something, before waiting here
         self.device.wait_for_net()
 
-        if not isinstance(self.device, Emulator):
-            self.device.reboot()
-
         if not self.device.wait_for_net():
             raise Exception("Network did not come up when starting device")
         self.app_ctx.stop_application()
 
         # In this case we need to pass in env as part of the command.
         # Make this empty so BaseRunner doesn't pass anything into the
         # process class.
         self._env = self.env
--- a/toolkit/components/places/ColorAnalyzer_worker.js
+++ b/toolkit/components/places/ColorAnalyzer_worker.js
@@ -114,17 +114,17 @@ onmessage = function(event) {
     weight *= chroma / CHROMA_WEIGHT_MIDDLE;
 
     metadata.desirability *= weight;
     return metadata;
   });
 
   // only send back the most desirable colors
   mergedColors.sort(function(a, b) {
-    return b.desirability - a.desirability;
+    return b.desirability != a.desirability ? b.desirability - a.desirability : b.color - a.color;
   });
   mergedColors = mergedColors.map(function(metadata) {
     return metadata.color;
   }).slice(0, maxColors);
   postMessage({ colors: mergedColors });
 };
 
 /**
@@ -211,17 +211,17 @@ function mergeColors(colorFrequencies, n
     };
   });
 
   let merged = clusterlib.hcluster(items, distance, merge, threshold);
   return merged;
 }
 
 function descendingFreqSort(a, b) {
-  return b.freq - a.freq;
+  return b.freq != a.freq ? b.freq - a.freq : b.color - a.color;
 }
 
 /**
  * Given two items for a pair of clusters (as created in mergeColors above),
  * determine the distance between them so we know if we should merge or not.
  * Uses the euclidean distance between their mean colors in the lab color
  * space, weighted so larger items are harder to merge.
  *
--- a/toolkit/components/places/tests/browser/browser_colorAnalyzer.js
+++ b/toolkit/components/places/tests/browser/browser_colorAnalyzer.js
@@ -325,21 +325,21 @@ tests.push(function test_perfBigImage() 
 const filePrefix = getRootDirectory(gTestPath) + "colorAnalyzer/";
 
 tests.push(function test_categoryDiscover() {
   frcTest(filePrefix + "category-discover.png", 0xB28D3A,
           "category-discover analysis returns red");
 });
 
 tests.push(function test_localeGeneric() {
-  frcTest(filePrefix + "localeGeneric.png", 0x00A400,
-          "localeGeneric analysis returns orange");
+  frcTest(filePrefix + "localeGeneric.png", 0x3EC23E,
+          "localeGeneric analysis returns green");
 });
 
 tests.push(function test_dictionaryGeneric() {
-  frcTest(filePrefix + "dictionaryGeneric-16.png", 0x502E1E,
-          "dictionaryGeneric-16 analysis returns blue");
+  frcTest(filePrefix + "dictionaryGeneric-16.png", 0x854C30,
+          "dictionaryGeneric-16 analysis returns brown");
 });
 
 tests.push(function test_extensionGeneric() {
   frcTest(filePrefix + "extensionGeneric-16.png", 0x53BA3F,
           "extensionGeneric-16 analysis returns green");
 });
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -682,17 +682,21 @@ this.SocialService = {
     let string = Cc["@mozilla.org/supports-string;1"].
                  createInstance(Ci.nsISupportsString);
     string.data = JSON.stringify(manifest);
     Services.prefs.setComplexValue(getPrefnameFromOrigin(manifest.origin), Ci.nsISupportsString, string);
 
     // overwrite the existing provider then notify the front end so it can
     // handle any reload that might be necessary.
     if (ActiveProviders.has(manifest.origin)) {
-      let provider = new SocialProvider(manifest);
+      // unload the worker prior to replacing the provider instance, also
+      // ensures the workerapi instance is terminated.
+      let provider = SocialServiceInternal.providers[manifest.origin];
+      provider.enabled = false;
+      provider = new SocialProvider(manifest);
       SocialServiceInternal.providers[provider.origin] = provider;
       // update the cache and ui, reload provider if necessary
       this.getOrderedProviderList(providers => {
         this._notifyProviderListeners("provider-update", provider.origin, providers);
       });
     }
 
   },
@@ -751,18 +755,20 @@ function SocialProvider(input) {
     this.domain = etld.getBaseDomainFromHost(originUri.host);
   } catch(e) {
     this.domain = originUri.host;
   }
 }
 
 SocialProvider.prototype = {
   reload: function() {
-    this._terminate();
-    this._activate();
+    // calling terminate/activate does not set the enabled state whereas setting
+    // enabled will call terminate/activate
+    this.enabled = false;
+    this.enabled = true;
     Services.obs.notifyObservers(null, "social:provider-reload", this.origin);
   },
 
   // Provider enabled/disabled state. Disabled providers do not have active
   // connections to their FrameWorkers.
   _enabled: false,
   get enabled() {
     return this._enabled;
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -754,19 +754,19 @@ nsTypeAheadFind::GetSearchContainers(nsI
 
   // Hack for XMLPrettyPrinter. nsFind can't handle complex anonymous content.
   // If the root node has an XBL binding then there's not much we can do in
   // in general, but we can try searching the binding's first child, which
   // in the case of XMLPrettyPrinter contains the visible pretty-printed
   // content.
   nsXBLBinding* binding = rootContent->GetXBLBinding();
   if (binding) {
-    nsIContent* child = binding->GetAnonymousContent()->GetFirstChild();
-    if (child) {
-      searchRootNode = do_QueryInterface(child);
+    nsIContent* anonContent = binding->GetAnonymousContent();
+    if (anonContent) {
+      searchRootNode = do_QueryInterface(anonContent->GetFirstChild());
     }
   }
   mSearchRange->SelectNodeContents(searchRootNode);
 
   if (!mStartPointRange) {
     mStartPointRange = new nsRange(doc);
   }
   mStartPointRange->SetStart(searchRootNode, 0);
--- a/toolkit/components/xulstore/XULStore.js
+++ b/toolkit/components/xulstore/XULStore.js
@@ -21,17 +21,18 @@ const STOREDB_FILENAME = "xulstore.json"
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 function XULStore() {
-  this.init();
+  if (!Services.appinfo.inSafeMode)
+    this.load();
 }
 
 XULStore.prototype = {
   classID: XULSTORE_CID,
   classInfo: XPCOMUtils.generateCI({classID: XULSTORE_CID,
                                     contractID: XULSTORE_CONTRACTID,
                                     classDescription: "XULStore",
                                     interfaces: [Ci.nsIXULStore]}),
@@ -53,34 +54,33 @@ XULStore.prototype = {
    *      ...
    *  }
    */
   _data: {},
   _storeFile: null,
   _needsSaving: false,
   _saveAllowed: true,
   _writeTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
-  _writeTimerInitialized: false,
 
-  init: function () {
+  load: function () {
     Services.obs.addObserver(this, "profile-before-change", true);
 
     this._storeFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
     this._storeFile.append(STOREDB_FILENAME);
 
     if (!this._storeFile.exists()) {
       this.import();
     } else {
       this.readFile();
     }
   },
 
   observe: function(subject, topic, data) {
+    this.writeFile();
     if (topic == "profile-before-change") {
-      this.writeFile();
       this._saveAllowed = false;
     }
   },
 
   /*
    * Internal function for logging debug messages to the Error Console window
    */
   log: function (message) {
@@ -177,28 +177,22 @@ XULStore.prototype = {
                               { tmpPath: this._storeFile.path + ".tmp" });
     } catch (e) {
       this.log("Failed to write xulstore.json: " + e);
       throw e;
     }
   }),
 
   markAsChanged: function() {
-    this._needsSaving = true;
-    if (this._writeTimerInitialized)
+    if (this._needsSaving || !this._storeFile)
       return;
 
-    let callback = () => {
-      this._writeTimerInitialized = false;
-      this.writeFile();
-    };
-
     // Don't write the file more than once every 30 seconds.
-    this._writeTimerInitialized = true;
-    this._writeTimer.initWithCallback(callback, WRITE_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
+    this._needsSaving = true;
+    this._writeTimer.init(this, WRITE_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   /* ---------- interface implementation ---------- */
 
   setValue: function (docURI, id, attr, value) {
     this.log("Saving " + attr + "=" + value + " for id=" + id + ", doc=" + docURI);
 
     if (!this._saveAllowed) {
--- a/toolkit/crashreporter/breakpad-windows-libxul/moz.build
+++ b/toolkit/crashreporter/breakpad-windows-libxul/moz.build
@@ -1,14 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+DIRS += [
+    'staticruntime'
+]
+
 SOURCES += [
     '../google-breakpad/src/common/windows/http_upload.cc',
 ]
 
 LIBRARY_NAME = 'google_breakpad_libxul_s'
 
 FINAL_LIBRARY = 'xul'
 
copy from toolkit/crashreporter/breakpad-windows-libxul/moz.build
copy to toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build
--- a/toolkit/crashreporter/breakpad-windows-libxul/moz.build
+++ b/toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build
@@ -1,21 +1,19 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
-    '../google-breakpad/src/common/windows/http_upload.cc',
+    '../../google-breakpad/src/common/windows/http_upload.cc',
 ]
 
-LIBRARY_NAME = 'google_breakpad_libxul_s'
-
-FINAL_LIBRARY = 'xul'
+LIBRARY_NAME = 'google_breakpad_libxul_staticruntime_s'
 
 for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD', 'NOMINMAX'):
     DEFINES[var] = True
 
 LOCAL_INCLUDES += [
     '/toolkit/crashreporter/google-breakpad/src',
 ]
 
@@ -25,8 +23,9 @@ include('/toolkit/crashreporter/google-b
 include('/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mozbuild')
 
 SOURCES += objs_common
 SOURCES += objs_crash_generation
 SOURCES += objs_handler
 SOURCES += objs_sender
 
 DISABLE_STL_WRAPPING = True
+USE_STATIC_LIBS = True
--- a/toolkit/crashreporter/client/moz.build
+++ b/toolkit/crashreporter/client/moz.build
@@ -17,24 +17,25 @@ LOCAL_INCLUDES += [
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     UNIFIED_SOURCES += [
         'crashreporter_win.cpp',
     ]
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
     USE_LIBS += [
-        'google_breakpad_libxul_s',
+        'google_breakpad_libxul_staticruntime_s',
     ]
     OS_LIBS += [
         'comctl32',
         'shell32',
         'wininet',
         'shlwapi',
     ]
+    USE_STATIC_LIBS = True
 elif CONFIG['OS_ARCH'] == 'Darwin':
     UNIFIED_SOURCES += [
         'crashreporter_osx.mm',
         'crashreporter_unix_common.cpp',
     ]
     LOCAL_INCLUDES += [
         '../google-breakpad/src/common/mac',
     ]
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -1787,17 +1787,23 @@ ThreadClient.prototype = {
     }
     // Otherwise, force a pause in order to set the breakpoint.
     this.interrupt((aResponse) => {
       if (aResponse.error) {
         // Can't set the breakpoint if pausing failed.
         aOnResponse(aResponse);
         return;
       }
-      doSetBreakpoint(this.resume.bind(this));
+
+      const { type, why } = aResponse;
+      const cleanUp = type == "paused" && why.type == "interrupted"
+        ? () => this.resume()
+        : noop;
+
+      doSetBreakpoint(cleanUp);
     });
   },
 
   /**
    * Release multiple thread-lifetime object actors. If any pause-lifetime
    * actors are included in the request, a |notReleasable| error will return,
    * but all the thread-lifetime ones will have been released.
    *
--- a/toolkit/devtools/server/actors/common.js
+++ b/toolkit/devtools/server/actors/common.js
@@ -161,8 +161,32 @@ ActorPool.prototype = {
       actor.disconnect();
     }
     this._cleanups = {};
   }
 }
 
 exports.ActorPool = ActorPool;
 
+// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when it is
+// implemented.
+exports.getOffsetColumn = function getOffsetColumn(aOffset, aScript) {
+  let bestOffsetMapping = null;
+  for (let offsetMapping of aScript.getAllColumnOffsets()) {
+    if (!bestOffsetMapping ||
+        (offsetMapping.offset <= aOffset &&
+         offsetMapping.offset > bestOffsetMapping.offset)) {
+      bestOffsetMapping = offsetMapping;
+    }
+  }
+
+  if (!bestOffsetMapping) {
+    // XXX: Try not to completely break the experience of using the debugger for
+    // the user by assuming column 0. Simultaneously, report the error so that
+    // there is a paper trail if the assumption is bad and the debugging
+    // experience becomes wonky.
+    reportError(new Error("Could not find a column for offset " + aOffset
+                          + " in the script " + aScript));
+    return 0;
+  }
+
+  return bestOffsetMapping.columnNumber;
+}
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -3,17 +3,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/. */
 
 "use strict";
 
 const Services = require("Services");
 const { Cc, Ci, Cu, components, ChromeWorker } = require("chrome");
-const { ActorPool } = require("devtools/server/actors/common");
+const { ActorPool, getOffsetColumn } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const { dbg_assert, dumpn, update } = DevToolsUtils;
 const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
 const promise = require("promise");
 const Debugger = require("Debugger");
 const xpcInspector = require("xpcInspector");
 const mapURIToAddonID = require("./utils/map-uri-to-addon-id");
@@ -5221,41 +5221,16 @@ ThreadSources.prototype = {
     }
   }
 };
 
 exports.ThreadSources = ThreadSources;
 
 // Utility functions.
 
-// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when it is
-// implemented.
-function getOffsetColumn(aOffset, aScript) {
-  let bestOffsetMapping = null;
-  for (let offsetMapping of aScript.getAllColumnOffsets()) {
-    if (!bestOffsetMapping ||
-        (offsetMapping.offset <= aOffset &&
-         offsetMapping.offset > bestOffsetMapping.offset)) {
-      bestOffsetMapping = offsetMapping;
-    }
-  }
-
-  if (!bestOffsetMapping) {
-    // XXX: Try not to completely break the experience of using the debugger for
-    // the user by assuming column 0. Simultaneously, report the error so that
-    // there is a paper trail if the assumption is bad and the debugging
-    // experience becomes wonky.
-    reportError(new Error("Could not find a column for offset " + aOffset
-                          + " in the script " + aScript));
-    return 0;
-  }
-
-  return bestOffsetMapping.columnNumber;
-}
-
 /**
  * Return the non-source-mapped location of the given Debugger.Frame. If the
  * frame does not have a script, the location's properties are all null.
  *
  * @param Debugger.Frame aFrame
  *        The frame whose location we are getting.
  * @returns Object
  *          Returns an object of the form { url, line, column }
--- a/toolkit/devtools/server/actors/tracer.js
+++ b/toolkit/devtools/server/actors/tracer.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cu } = require("chrome");
 const { DebuggerServer } = require("devtools/server/main");
 const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 const Debugger = require("Debugger");
+const { getOffsetColumn } = require("devtools/server/actors/common");
 
 // TODO bug 943125: remove this polyfill and use Debugger.Frame.prototype.depth
 // once it is implemented.
 if (!Object.getOwnPropertyDescriptor(Debugger.Frame.prototype, "depth")) {
   Debugger.Frame.prototype._depth = null;
   Object.defineProperty(Debugger.Frame.prototype, "depth", {
     get: function () {
       if (this._depth === null) {
@@ -55,16 +56,17 @@ const MAX_PROPERTIES = 3;
  */
 const TRACE_TYPES = new Set([
   "time",
   "return",
   "throw",
   "yield",
   "name",
   "location",
+  "hitCount",
   "callsite",
   "parameterNames",
   "arguments",
   "depth"
 ]);
 
 /**
  * Creates a TracerActor. TracerActor provides a stream of function
@@ -76,16 +78,17 @@ function TracerActor(aConn, aParent)
   this._parent = aParent;
   this._attached = false;
   this._activeTraces = new MapStack();
   this._totalTraces = 0;
   this._startTime = 0;
   this._sequence = 0;
   this._bufferSendTimer = null;
   this._buffer = [];
+  this._hitCounts = new WeakMap();
 
   // Keep track of how many different trace requests have requested what kind of
   // tracing info. This way we can minimize the amount of data we are collecting
   // at any given time.
   this._requestsForTraceType = Object.create(null);
   for (let type of TRACE_TYPES) {
     this._requestsForTraceType[type] = 0;
   }
@@ -231,16 +234,21 @@ TracerActor.prototype = {
       name = this._activeTraces.peekKey();
       stoppedTraceTypes = this._activeTraces.pop();
     }
 
     for (let traceType of stoppedTraceTypes) {
       this._requestsForTraceType[traceType]--;
     }
 
+    // Clear hit counts if no trace is requesting them.
+    if (!this._requestsForTraceType.hitCount) {
+      this._hitCounts.clear();
+    }
+
     if (this.idle) {
       this.dbg.enabled = false;
     }
 
     return {
       type: "stoppedTrace",
       why: "requested",
       name
@@ -267,26 +275,36 @@ TracerActor.prototype = {
     };
 
     if (this._requestsForTraceType.name) {
       packet.name = aFrame.callee
         ? aFrame.callee.displayName || "(anonymous function)"
         : "(" + aFrame.type + ")";
     }
 
-    if (this._requestsForTraceType.location && aFrame.script) {
-      // We should return the location of the start of the script, but
-      // Debugger.Script does not provide complete start locations (bug
-      // 901138). Instead, return the current offset (the location of the first
-      // statement in the function).
-      packet.location = {
-        url: aFrame.script.url,
-        line: aFrame.script.startLine,
-        column: getOffsetColumn(aFrame.offset, aFrame.script)
-      };
+    if (aFrame.script) {
+      if (this._requestsForTraceType.hitCount) {
+        // Increment hit count.
+        let previousHitCount = this._hitCounts.get(aFrame.script) || 0;
+        this._hitCounts.set(aFrame.script, previousHitCount + 1);
+
+        packet.hitCount = this._hitCounts.get(aFrame.script);
+      }
+
+      if (this._requestsForTraceType.location) {
+        // We should return the location of the start of the script, but
+        // Debugger.Script does not provide complete start locations (bug
+        // 901138). Instead, return the current offset (the location of the first
+        // statement in the function).
+        packet.location = {
+          url: aFrame.script.url,
+          line: aFrame.script.startLine,
+          column: getOffsetColumn(aFrame.offset, aFrame.script)
+        };
+      }
     }
 
     if (this._parent.threadActor && aFrame.script) {
       packet.blackBoxed = this._parent.threadActor.sources.isBlackBoxed(aFrame.script.url);
     } else {
       packet.blackBoxed = false;
     }
 
@@ -492,22 +510,16 @@ MapStack.prototype = {
       let keyIndex = this._stack.lastIndexOf(aKey);
       this._stack.splice(keyIndex, 1);
       delete this._map[aKey];
     }
     return value;
   }
 };
 
-// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when
-// it is implemented.
-function getOffsetColumn(aOffset, aScript) {
-  return 0;
-}
-
 // Serialization helper functions. Largely copied from script.js and modified
 // for use in serialization rather than object actor requests.
 
 /**
  * Create a grip for the given debuggee value.
  *
  * @param aValue Debugger.Object|primitive
  *        The value to describe with the created grip.
--- a/toolkit/devtools/server/tests/unit/test_trace_actor-05.js
+++ b/toolkit/devtools/server/tests/unit/test_trace_actor-05.js
@@ -83,19 +83,20 @@ function test_enter_exit_frame()
     .then(function() {
       let url = getFileUrl("tracerlocations.js");
 
       check_location(traces[0].location, { url: url, line: 1, column: 0 });
 
       do_check_eq(traces[1].name, "foo");
 
       // XXX: foo's definition is at tracerlocations.js:3:0, but Debugger.Script
-      // does not provide complete definition locations (bug 901138). |column|
-      // will always be 0 until we can get bug 863089 fixed.
-      check_location(traces[1].location, { url: url, line: 3, column: 0 });
+      // does not provide complete definition locations (bug 901138). Therefore,
+      // we use the first statement in the function (tracerlocations.js:4:2) for
+      // a column approximation.
+      check_location(traces[1].location, { url: url, line: 3, column: 2 });
       check_location(traces[1].callsite, { url: url, line: 8, column: 0 });
 
       do_check_eq(typeof traces[1].parameterNames, "object");
       do_check_eq(traces[1].parameterNames.length, 1);
       do_check_eq(traces[1].parameterNames[0], "x");
 
       do_check_eq(typeof traces[1].arguments, "object");
       do_check_true(Array.isArray(traces[1].arguments));
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_trace_actor-11.js
@@ -0,0 +1,124 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that hit counts from tracer count function frames correctly, even after
+ * restarting the trace.
+ */
+
+var gDebuggee;
+var gClient;
+var gTraceClient;