Merge mozilla-central to inbound. a=merge CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Mon, 19 Feb 2018 18:46:41 +0200
changeset 404413 176fb0d46b643a81168d22165c55e25c5e158081
parent 404384 040692f892ab2c8c7c748e65a3dc9da2c553cf07 (current diff)
parent 404412 f60cfdc81e946cf052cc29b8beae7c50613a3039 (diff)
child 404414 e645473ee112196fe72083441762fa6e004c1000
child 404424 dc70d241f90df43505ece5ac12261339e9694c50
push id100000
push userncsoregi@mozilla.com
push dateMon, 19 Feb 2018 16:46:49 +0000
treeherdermozilla-inbound@176fb0d46b64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
browser/base/content/pageinfo/feeds.xml
devtools/client/webconsole/new-console-output/test/fixtures/Services.js
testing/marionette/harness/marionette_harness/www/testSize.html
testing/web-platform/meta/shadow-dom/untriaged/styles/test-005.html.ini
third_party/rust/flate2/examples/flatereadext.rs
third_party/rust/miniz-sys/.cargo-checksum.json
third_party/rust/miniz-sys/Cargo.toml
third_party/rust/miniz-sys/build.rs
third_party/rust/miniz-sys/lib.rs
third_party/rust/miniz-sys/miniz.c
--- a/browser/base/content/pageinfo/feeds.js
+++ b/browser/base/content/pageinfo/feeds.js
@@ -1,32 +1,60 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Via pageInfo.xul -> utilityOverlay.js
 /* import-globals-from ../utilityOverlay.js */
+/* import-globals-from ./pageInfo.js */
 
 function initFeedTab(feeds) {
-  for (let feed of feeds) {
-    let [name, type, url] = feed;
+  for (const [name, type, url] of feeds) {
     addRow(name, type, url);
   }
 
-  var feedListbox = document.getElementById("feedListbox");
+  const feedListbox = document.getElementById("feedListbox");
   document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0;
 }
 
-function onSubscribeFeed() {
-  var listbox = document.getElementById("feedListbox");
-  openUILinkIn(listbox.selectedItem.getAttribute("feedURL"), "current",
-               { ignoreAlt: true });
-}
+function addRow(name, type, url) {
+  const item = document.createElement("richlistitem");
+
+  const top = document.createElement("hbox");
+  top.setAttribute("flex", "1");
+  item.appendChild(top);
+
+  const bottom = document.createElement("hbox");
+  bottom.setAttribute("flex", "1");
+  item.appendChild(bottom);
+
+  const nameLabel = document.createElement("label");
+  nameLabel.className = "feedTitle";
+  nameLabel.textContent = name;
+  nameLabel.setAttribute("flex", "1");
+  top.appendChild(nameLabel);
 
-function addRow(name, type, url) {
-  var item = document.createElement("richlistitem");
-  item.setAttribute("feed", "true");
-  item.setAttribute("name", name);
-  item.setAttribute("type", type);
-  item.setAttribute("feedURL", url);
+  const typeLabel = document.createElement("label");
+  typeLabel.textContent = type;
+  top.appendChild(typeLabel);
+
+  const urlContainer = document.createElement("hbox");
+  urlContainer.setAttribute("flex", "1");
+  bottom.appendChild(urlContainer);
+
+  const urlLabel = document.createElement("label");
+  urlLabel.className = "text-link";
+  urlLabel.textContent = url;
+  urlLabel.setAttribute("tooltiptext", url);
+  urlLabel.addEventListener("click", ev => openUILink(this.value, ev));
+  urlContainer.appendChild(urlLabel);
+
+  const subscribeButton = document.createElement("button");
+  subscribeButton.className = "feed-subscribe";
+  subscribeButton.addEventListener("click",
+    () => openUILinkIn(url, "current", { ignoreAlt: true }));
+  subscribeButton.setAttribute("label", gBundle.getString("feedSubscribe"));
+  subscribeButton.setAttribute("accesskey", gBundle.getString("feedSubscribe.accesskey"));
+  bottom.appendChild(subscribeButton);
+
   document.getElementById("feedListbox").appendChild(item);
 }
deleted file mode 100644
--- a/browser/base/content/pageinfo/feeds.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.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/. -->
-
-<!DOCTYPE bindings [
-  <!ENTITY % pageInfoDTD SYSTEM "chrome://browser/locale/pageInfo.dtd">
-  %pageInfoDTD;
-]>
-
-<bindings id="feedBindings"
-          xmlns="http://www.mozilla.org/xbl"
-          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-          xmlns:xbl="http://www.mozilla.org/xbl">
-
-  <binding id="feed" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
-    <content>
-      <xul:vbox flex="1">
-        <xul:hbox flex="1">
-          <xul:textbox flex="1" readonly="true" xbl:inherits="value=name"
-                       class="feedTitle"/>
-          <xul:label xbl:inherits="value=type"/>
-        </xul:hbox>
-        <xul:vbox>
-          <xul:vbox align="start">
-            <xul:hbox>
-              <xul:label xbl:inherits="value=feedURL,tooltiptext=feedURL" class="text-link" flex="1"
-                         onclick="openUILink(this.value, event);" crop="end"/>
-            </xul:hbox>
-          </xul:vbox>
-        </xul:vbox>
-        <xul:hbox flex="1" class="feed-subscribe">
-          <xul:spacer flex="1"/>
-          <xul:button label="&feedSubscribe;" accesskey="&feedSubscribe.accesskey;"
-                      oncommand="onSubscribeFeed()"/>
-        </xul:hbox> 
-      </xul:vbox>
-    </content>
-  </binding>
-</bindings>
--- a/browser/base/content/pageinfo/pageInfo.css
+++ b/browser/base/content/pageinfo/pageInfo.css
@@ -7,26 +7,25 @@
   -moz-box-align: center;
 }
 
 /* Hide the radio button for the section headers */
 #viewGroup > radio > .radio-check {
   display: none;
 }
 
-
-richlistitem[feed] {
-  -moz-binding: url("chrome://browser/content/pageinfo/feeds.xml#feed");
+#feedListbox richlistitem {
+  -moz-box-orient: vertical;
 }
 
-richlistitem[feed]:not([selected="true"]) .feed-subscribe {
+#feedListbox richlistitem:not([selected="true"]) .feed-subscribe {
   display: none;
 }
 
-groupbox[closed="true"] > .groupbox-body { 
+groupbox[closed="true"] > .groupbox-body {
   visibility: collapse;
 }
 
 #thepreviewimage {
   display: block;
 /* This following entry can be removed when Bug 522850 is fixed. */
   min-width: 1px;
 }
--- a/browser/base/content/test/pageinfo/browser_pageInfo.js
+++ b/browser/base/content/test/pageinfo/browser_pageInfo.js
@@ -13,23 +13,24 @@ function test() {
   function observer(win, topic, data) {
     Services.obs.removeObserver(observer, "page-info-dialog-loaded");
     pageInfo.onFinished.push(handlePageInfo);
   }
 
   function handlePageInfo() {
     ok(pageInfo.document.getElementById("feedTab"), "Feed tab");
     let feedListbox = pageInfo.document.getElementById("feedListbox");
-    ok(feedListbox, "Feed list");
+    ok(feedListbox, "Feed list should exist.");
 
     var feedRowsNum = feedListbox.getRowCount();
-    is(feedRowsNum, 3, "Number of feeds listed");
+    is(feedRowsNum, 3, "Number of feeds listed should be correct.");
 
     for (var i = 0; i < feedRowsNum; i++) {
       let feedItem = feedListbox.getItemAtIndex(i);
-      is(feedItem.getAttribute("name"), i + 1, "Feed name");
+      let feedTitle = feedItem.querySelector(".feedTitle");
+      is(feedTitle.textContent, i + 1, "Feed name should be correct.");
     }
 
     pageInfo.close();
     gBrowser.removeCurrentTab();
     finish();
   }
 }
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -99,17 +99,16 @@ browser.jar:
         content/browser/newtab/newTab.xhtml           (content/newtab/newTab.xhtml)
 *       content/browser/newtab/newTab.js              (content/newtab/newTab.js)
         content/browser/newtab/newTab.css             (content/newtab/newTab.css)
         content/browser/newtab/alternativeDefaultSites.json   (content/newtab/alternativeDefaultSites.json)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
-        content/browser/pageinfo/feeds.xml            (content/pageinfo/feeds.xml)
         content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
         content/browser/pageinfo/security.js          (content/pageinfo/security.js)
         content/browser/robot.ico                     (content/robot.ico)
         content/browser/static-robot.png              (content/static-robot.png)
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xul                  (content/safeMode.xul)
         content/browser/sanitize.xul                  (content/sanitize.xul)
--- a/browser/locales/en-US/chrome/browser/pageInfo.dtd
+++ b/browser/locales/en-US/chrome/browser/pageInfo.dtd
@@ -41,18 +41,16 @@
 <!ENTITY  mediaBlockImage.accesskey "B">
 <!ENTITY  mediaSaveAs           "Save As…">
 <!ENTITY  mediaSaveAs.accesskey "A">
 <!ENTITY  mediaSaveAs2.accesskey "e">
 <!ENTITY  mediaPreview          "Media Preview:">
 
 <!ENTITY  feedTab               "Feeds">
 <!ENTITY  feedTab.accesskey     "F">
-<!ENTITY  feedSubscribe         "Subscribe">
-<!ENTITY  feedSubscribe.accesskey "u">
 
 <!ENTITY  permTab               "Permissions">
 <!ENTITY  permTab.accesskey     "P">
 <!ENTITY  permUseDefault        "Use Default">
 <!ENTITY  permAskAlways         "Always ask">
 <!ENTITY  permAllow             "Allow">
 <!ENTITY  permAllowSession      "Allow for Session">
 <!ENTITY  permHide              "Hide">
--- a/browser/locales/en-US/chrome/browser/pageInfo.properties
+++ b/browser/locales/en-US/chrome/browser/pageInfo.properties
@@ -36,16 +36,18 @@ generalQuirksMode=Quirks mode
 generalStrictMode=Standards compliance mode
 generalSize=%S KB (%S bytes)
 generalMetaTag=Meta (1 tag)
 generalMetaTags=Meta (%S tags)
 
 feedRss=RSS
 feedAtom=Atom
 feedXML=XML
+feedSubscribe=Subscribe
+feedSubscribe.accesskey=u
 
 securityNoOwner=This website does not supply ownership information.
 # LOCALIZATION NOTE (securityVisitsNumber):
 # Semi-colon list of plural forms.
 # See: https://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the number of visits and can be used in all plural forms as needed, e.g.
 # for '1': 'Yes, #1 time'
 securityVisitsNumber=Yes, once;Yes, #1 times
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -649,16 +649,30 @@ notification[value="translation"] menuli
 
   :root[tabsintitlebar][sizemode="normal"] > #titlebar {
     -moz-appearance: -moz-window-titlebar;
   }
   :root[tabsintitlebar][sizemode="maximized"] > #titlebar {
     -moz-appearance: -moz-window-titlebar-maximized;
   }
 
+  /* Add extra space to titlebar for dragging */
+  :root[sizemode="normal"][chromehidden~="menubar"] #TabsToolbar,
+  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar {
+    margin-top: var(--space-above-tabbar);
+  }
+
+  /* Private browsing and accessibility indicators */
+  :root[sizemode="normal"][chromehidden~="menubar"] #TabsToolbar > .private-browsing-indicator,
+  :root[sizemode="normal"][chromehidden~="menubar"] #TabsToolbar > .accessibility-indicator,
+  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar > .private-browsing-indicator,
+  :root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar > .accessibility-indicator {
+    margin-top: calc(-1 * var(--space-above-tabbar));
+  }
+
   /* The button box must appear on top of the navigator-toolbox in order for
    * click and hover mouse events to work properly for the button in the restored
    * window state. Otherwise, elements in the navigator-toolbox, like the menubar,
    * can swallow those events.
    */
   #titlebar-buttonbox {
     z-index: 1;
   }
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -250,17 +250,26 @@ pref("devtools.webconsole.filter.secwarn
 pref("devtools.webconsole.filter.serviceworkers", true);
 pref("devtools.webconsole.filter.sharedworkers", false);
 pref("devtools.webconsole.filter.windowlessworkers", false);
 pref("devtools.webconsole.filter.servererror", false);
 pref("devtools.webconsole.filter.serverwarn", false);
 pref("devtools.webconsole.filter.serverinfo", false);
 pref("devtools.webconsole.filter.serverlog", false);
 
-// Remember the Browser Console filters
+// Browser console filters
+pref("devtools.browserconsole.filter.error", true);
+pref("devtools.browserconsole.filter.warn", true);
+pref("devtools.browserconsole.filter.info", true);
+pref("devtools.browserconsole.filter.log", true);
+pref("devtools.browserconsole.filter.debug", true);
+pref("devtools.browserconsole.filter.css", false);
+pref("devtools.browserconsole.filter.net", false);
+pref("devtools.browserconsole.filter.netxhr", false);
+// Remember the Browser Console filters (old frontend)
 pref("devtools.browserconsole.filter.network", true);
 pref("devtools.browserconsole.filter.networkinfo", false);
 pref("devtools.browserconsole.filter.netwarn", true);
 pref("devtools.browserconsole.filter.netxhr", false);
 pref("devtools.browserconsole.filter.csserror", true);
 pref("devtools.browserconsole.filter.cssparser", false);
 pref("devtools.browserconsole.filter.csslog", false);
 pref("devtools.browserconsole.filter.exception", true);
@@ -275,18 +284,20 @@ pref("devtools.browserconsole.filter.sec
 pref("devtools.browserconsole.filter.serviceworkers", true);
 pref("devtools.browserconsole.filter.sharedworkers", true);
 pref("devtools.browserconsole.filter.windowlessworkers", true);
 pref("devtools.browserconsole.filter.servererror", false);
 pref("devtools.browserconsole.filter.serverwarn", false);
 pref("devtools.browserconsole.filter.serverinfo", false);
 pref("devtools.browserconsole.filter.serverlog", false);
 
-// Web console filter settings bar
+// Web console filter bar settings
 pref("devtools.webconsole.ui.filterbar", false);
+// Browser console filter bar settings
+pref("devtools.browserconsole.ui.filterbar", false);
 
 // Max number of inputs to store in web console history.
 pref("devtools.webconsole.inputHistoryCount", 50);
 
 // Persistent logging: |true| if you want the relevant tool to keep all of the
 // logged messages after reloading the page, |false| if you want the output to
 // be cleared each time page navigation happens.
 pref("devtools.webconsole.persistlog", false);
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -61,16 +61,22 @@
 .objectBox-textNode,
 .objectBox-string,
 .objectBox-symbol {
   color: var(--string-color);
 }
 
 .objectBox-string a, .objectBox-string a:visited {
   color: currentColor;
+  text-decoration: none;
+  font-style: italic;
+}
+
+.objectBox-string a:hover {
+  text-decoration: underline;
 }
 
 .objectBox-function,
 .objectBox-stackTrace,
 .objectBox-profile {
   color: var(--object-color);
 }
 
@@ -144,26 +150,33 @@
   color: var(--node-color);
 }
 
 .angleBracket {
   color: var(--theme-body-color);
 }
 
 /******************************************************************************/
+/* Length bubble for arraylikes and maplikes */
+
+.objectLengthBubble {
+  color: var(--null-color);
+}
+
+/******************************************************************************/
 
 .objectLeftBrace,
 .objectRightBrace,
 .arrayLeftBracket,
 .arrayRightBracket {
   color: var(--object-color);
 }
 
 /******************************************************************************/
-/* Cycle reference*/
+/* Cycle reference */
 
 .objectBox-Reference {
   font-weight: bold;
   color: var(--reference-color);
 }
 
 [class*="objectBox"] > .objectTitle {
   color: var(--object-color);
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -2,17 +2,17 @@
 	if(typeof exports === 'object' && typeof module === 'object')
 		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
 	else if(typeof define === 'function' && define.amd)
 		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
 	else {
 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_7__) {
+})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_8__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -65,17 +65,17 @@ return /******/ (function(modules) { // 
 /******/
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 /******/
 /******/ 	// __webpack_public_path__
 /******/ 	__webpack_require__.p = "/assets/build";
 /******/
 /******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 19);
+/******/ 	return __webpack_require__(__webpack_require__.s = 20);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -455,16 +455,22 @@ function isURL(token) {
     }
     new URL(token);
     return true;
   } catch (e) {
     return false;
   }
 }
 
+const ellipsisElement = span({
+  key: "more",
+  className: "more-ellipsis",
+  title: `more${ELLIPSIS}`
+}, ELLIPSIS);
+
 module.exports = {
   isGrip,
   isURL,
   cropString,
   containsURL,
   rawCropString,
   sanitizeString,
   escapeString,
@@ -473,16 +479,17 @@ module.exports = {
   parseURLParams,
   parseURLEncodedText,
   getFileName,
   getURLDisplayString,
   maybeEscapePropertyName,
   getGripPreviewItems,
   getGripType,
   tokenSplitRegex,
+  ellipsisElement,
   ELLIPSIS
 };
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports) {
 
 module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
@@ -518,25 +525,24 @@ module.exports = {
 
 "use strict";
 
 
 /* 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/. */
 
-__webpack_require__(20);
+__webpack_require__(21);
 
 // Load all existing rep templates
-const Undefined = __webpack_require__(21);
-const Null = __webpack_require__(22);
-const StringRep = __webpack_require__(6);
-const LongStringRep = __webpack_require__(23);
+const Undefined = __webpack_require__(22);
+const Null = __webpack_require__(23);
+const StringRep = __webpack_require__(7);
 const Number = __webpack_require__(24);
-const ArrayRep = __webpack_require__(10);
+const ArrayRep = __webpack_require__(5);
 const Obj = __webpack_require__(25);
 const SymbolRep = __webpack_require__(26);
 const InfinityRep = __webpack_require__(27);
 const NaNRep = __webpack_require__(28);
 const Accessor = __webpack_require__(29);
 
 // DOM types (grips)
 const Attribute = __webpack_require__(30);
@@ -550,24 +556,24 @@ const StyleSheet = __webpack_require__(4
 const CommentNode = __webpack_require__(41);
 const ElementNode = __webpack_require__(42);
 const TextNode = __webpack_require__(43);
 const ErrorRep = __webpack_require__(44);
 const Window = __webpack_require__(45);
 const ObjectWithText = __webpack_require__(46);
 const ObjectWithURL = __webpack_require__(47);
 const GripArray = __webpack_require__(13);
-const GripMap = __webpack_require__(14);
-const GripMapEntry = __webpack_require__(15);
-const Grip = __webpack_require__(8);
+const GripMap = __webpack_require__(15);
+const GripMapEntry = __webpack_require__(16);
+const Grip = __webpack_require__(9);
 
 // List of all registered template.
 // XXX there should be a way for extensions to register a new
 // or modify an existing rep.
-let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
+let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
 
 /**
  * Generic rep that is using for rendering native JS types or an object.
  * The right template used for rendering is picked automatically according
  * to the current value type. The value must be passed is as 'object'
  * property.
  */
 const Rep = function (props) {
@@ -625,17 +631,16 @@ module.exports = {
     ErrorRep,
     Event,
     Func,
     Grip,
     GripArray,
     GripMap,
     GripMapEntry,
     InfinityRep,
-    LongStringRep,
     NaNRep,
     Null,
     Number,
     Obj,
     ObjectWithText,
     ObjectWithURL,
     PromiseRep,
     RegExp,
@@ -658,16 +663,163 @@ module.exports = {
 "use strict";
 
 
 /* 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/. */
 
 // Dependencies
+const dom = __webpack_require__(1);
+const PropTypes = __webpack_require__(2);
+const {
+  wrapRender
+} = __webpack_require__(0);
+const { MODE } = __webpack_require__(3);
+const { span } = dom;
+
+const ModePropType = PropTypes.oneOf(
+// @TODO Change this to Object.values once it's supported in Node's version of V8
+Object.keys(MODE).map(key => MODE[key]));
+
+/**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+ArrayRep.propTypes = {
+  mode: ModePropType,
+  object: PropTypes.array.isRequired
+};
+
+function ArrayRep(props) {
+  let {
+    object,
+    mode = MODE.SHORT
+  } = props;
+
+  let items;
+  let brackets;
+  let needSpace = function (space) {
+    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+  };
+
+  if (mode === MODE.TINY) {
+    let isEmpty = object.length === 0;
+    if (isEmpty) {
+      items = [];
+    } else {
+      items = [span({
+        className: "more-ellipsis",
+        title: "more…"
+      }, "…")];
+    }
+    brackets = needSpace(false);
+  } else {
+    items = arrayIterator(props, object, maxLengthMap.get(mode));
+    brackets = needSpace(items.length > 0);
+  }
+
+  return span({
+    className: "objectBox objectBox-array" }, span({
+    className: "arrayLeftBracket"
+  }, brackets.left), ...items, span({
+    className: "arrayRightBracket"
+  }, brackets.right), span({
+    className: "arrayProperties",
+    role: "group" }));
+}
+
+function arrayIterator(props, array, max) {
+  let items = [];
+
+  for (let i = 0; i < array.length && i < max; i++) {
+    let config = {
+      mode: MODE.TINY,
+      delim: i == array.length - 1 ? "" : ", "
+    };
+    let item;
+
+    try {
+      item = ItemRep(Object.assign({}, props, config, {
+        object: array[i]
+      }));
+    } catch (exc) {
+      item = ItemRep(Object.assign({}, props, config, {
+        object: exc
+      }));
+    }
+    items.push(item);
+  }
+
+  if (array.length > max) {
+    items.push(span({
+      className: "more-ellipsis",
+      title: "more…"
+    }, "…"));
+  }
+
+  return items;
+}
+
+/**
+ * Renders array item. Individual values are separated by a comma.
+ */
+ItemRep.propTypes = {
+  object: PropTypes.any.isRequired,
+  delim: PropTypes.string.isRequired,
+  mode: ModePropType
+};
+
+function ItemRep(props) {
+  const { Rep } = __webpack_require__(4);
+
+  let {
+    object,
+    delim,
+    mode
+  } = props;
+  return span({}, Rep(Object.assign({}, props, {
+    object: object,
+    mode: mode
+  })), delim);
+}
+
+function getLength(object) {
+  return object.length;
+}
+
+function supportsObject(object) {
+  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(ArrayRep),
+  supportsObject,
+  maxLengthMap,
+  getLength,
+  ModePropType
+};
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/. */
+
+// Dependencies
 const PropTypes = __webpack_require__(2);
 const {
   maybeEscapePropertyName,
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
@@ -697,17 +849,17 @@ PropRep.propTypes = {
 /**
  * Function that given a name, a delimiter and an object returns an array
  * of React elements representing an object property (e.g. `name: value`)
  *
  * @param {Object} props
  * @return {Array} Array of React elements.
  */
 function PropRep(props) {
-  const Grip = __webpack_require__(8);
+  const Grip = __webpack_require__(9);
   const { Rep } = __webpack_require__(4);
 
   let {
     name,
     mode,
     equal,
     suppressQuotes
   } = props;
@@ -733,17 +885,17 @@ function PropRep(props) {
     "className": "objectEqual"
   }, equal), Rep(Object.assign({}, props))];
 }
 
 // Exports from this module
 module.exports = wrapRender(PropRep);
 
 /***/ }),
-/* 6 */
+/* 7 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -754,75 +906,154 @@ const PropTypes = __webpack_require__(2)
 const {
   containsURL,
   isURL,
   escapeString,
   getGripType,
   rawCropString,
   sanitizeString,
   wrapRender,
+  isGrip,
   tokenSplitRegex,
   ELLIPSIS
 } = __webpack_require__(0);
 
 const dom = __webpack_require__(1);
 const { a, span } = dom;
 
 /**
  * Renders a string. String value is enclosed within quotes.
  */
 StringRep.propTypes = {
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
   style: PropTypes.object,
-  object: PropTypes.string.isRequired,
-  member: PropTypes.any,
-  cropLimit: PropTypes.number,
+  cropLimit: PropTypes.number.isRequired,
+  member: PropTypes.string,
+  object: PropTypes.object.isRequired,
   openLink: PropTypes.func,
   className: PropTypes.string,
   omitLinkHref: PropTypes.bool
 };
 
 function StringRep(props) {
   let {
     className,
+    style,
     cropLimit,
-    object: text,
-    member,
-    style,
+    object,
     useQuotes = true,
     escapeWhitespace = true,
+    member,
     openLink,
     omitLinkHref = true
   } = props;
 
+  let text = object;
+
+  const isLong = isLongString(object);
+  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
+
+  if (isLong) {
+    text = maybeCropLongString({
+      shouldCrop,
+      cropLimit
+    }, text);
+  }
+
+  text = formatText({
+    useQuotes,
+    escapeWhitespace
+  }, text);
+
+  const config = getElementConfig({
+    className,
+    style,
+    actor: object.actor
+  });
+
+  if (!isLong) {
+    if (containsURL(text)) {
+      return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
+    }
+
+    // Cropping of longString has been handled before formatting.
+    text = maybeCropString({
+      isLong,
+      shouldCrop,
+      cropLimit
+    }, text);
+  }
+
+  return span(config, text);
+}
+
+function maybeCropLongString(opts, text) {
+  const {
+    shouldCrop,
+    cropLimit
+  } = opts;
+
+  const {
+    fullText,
+    initial,
+    length
+  } = text;
+
+  text = shouldCrop ? initial.substring(0, cropLimit) : fullText || initial;
+
+  if (text.length < length) {
+    text += ELLIPSIS;
+  }
+
+  return text;
+}
+
+function formatText(opts, text) {
+  let {
+    useQuotes,
+    escapeWhitespace
+  } = opts;
+
+  return useQuotes ? escapeString(text, escapeWhitespace) : sanitizeString(text);
+}
+
+function getElementConfig(opts) {
+  const {
+    className,
+    style,
+    actor
+  } = opts;
+
+  const config = {};
+
+  if (actor) {
+    config["data-link-actor-id"] = actor;
+  }
+
   const classNames = ["objectBox", "objectBox-string"];
   if (className) {
     classNames.push(className);
   }
-  let config = { className: classNames.join(" ") };
+  config.className = classNames.join(" ");
+
   if (style) {
     config.style = style;
   }
 
-  if (useQuotes) {
-    text = escapeString(text, escapeWhitespace);
-  } else {
-    text = sanitizeString(text);
-  }
-
-  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
-  if (!containsURL(text)) {
-    if (shouldCrop) {
-      text = rawCropString(text, cropLimit);
-    }
-    return span(config, text);
-  }
-
-  return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
+  return config;
+}
+
+function maybeCropString(opts, text) {
+  const {
+    shouldCrop,
+    cropLimit
+  } = opts;
+
+  return shouldCrop ? rawCropString(text, cropLimit) : text;
 }
 
 /**
  * Get an array of the elements representing the string, cropped if needed,
  * with actual links.
  *
  * @param {String} text: The actual string to linkify.
  * @param {Integer | null} cropLimit
@@ -921,35 +1152,43 @@ function getCroppedString(text, offset =
     // The string should be cropped at the beginning.
     const cutIndex = endCropIndex - start;
     return text.substring(cutIndex);
   }
 
   return text;
 }
 
+function isLongString(object) {
+  return object && object.type === "longString";
+}
+
 function supportsObject(object, noGrip = false) {
+  if (noGrip === false && isGrip(object)) {
+    return isLongString(object);
+  }
+
   return getGripType(object, noGrip) == "string";
 }
 
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(StringRep),
   supportsObject
 };
 
 /***/ }),
-/* 7 */
+/* 8 */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_7__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_8__;
 
 /***/ }),
-/* 8 */
+/* 9 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -957,17 +1196,17 @@ module.exports = __WEBPACK_EXTERNAL_MODU
 // ReactJS
 const PropTypes = __webpack_require__(2);
 
 // Dependencies
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders generic grip. Grip is client representation
  * of remote JS object and is used as an input object
@@ -1239,16 +1478,20 @@ function getPropValue(property) {
 }
 
 // Registration
 function supportsObject(object, noGrip = false) {
   if (noGrip === true || !isGrip(object)) {
     return false;
   }
 
+  if (object.class === "DeadObject") {
+    return true;
+  }
+
   return object.preview ? typeof object.preview.ownProperties !== "undefined" : typeof object.ownPropertyLength !== "undefined";
 }
 
 const maxLengthMap = new Map();
 maxLengthMap.set(MODE.SHORT, 3);
 maxLengthMap.set(MODE.LONG, 10);
 
 // Grip is used in propIterator and has to be defined here.
@@ -1257,33 +1500,33 @@ let Grip = {
   supportsObject,
   maxLengthMap
 };
 
 // Exports from this module
 module.exports = Grip;
 
 /***/ }),
-/* 9 */
+/* 10 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 var _svgInlineReact = __webpack_require__(11);
 
 var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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/. */
 
-const React = __webpack_require__(7);
+const React = __webpack_require__(8);
 const PropTypes = __webpack_require__(2);
 
 
 const svg = {
   "open-inspector": __webpack_require__(36),
   "jump-definition": __webpack_require__(37)
 };
 
@@ -1304,187 +1547,41 @@ function Svg(name, props) {
   }
   props = Object.assign({}, props, { className, src: svg[name] });
   return React.createElement(_svgInlineReact2.default, props);
 }
 
 module.exports = Svg;
 
 /***/ }),
-/* 10 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/. */
-
-// Dependencies
-const dom = __webpack_require__(1);
-const PropTypes = __webpack_require__(2);
-const {
-  wrapRender
-} = __webpack_require__(0);
-const { MODE } = __webpack_require__(3);
-const { span } = dom;
-
-const ModePropType = PropTypes.oneOf(
-// @TODO Change this to Object.values once it's supported in Node's version of V8
-Object.keys(MODE).map(key => MODE[key]));
-
-/**
- * Renders an array. The array is enclosed by left and right bracket
- * and the max number of rendered items depends on the current mode.
- */
-ArrayRep.propTypes = {
-  mode: ModePropType,
-  object: PropTypes.array.isRequired
-};
-
-function ArrayRep(props) {
-  let {
-    object,
-    mode = MODE.SHORT
-  } = props;
-
-  let items;
-  let brackets;
-  let needSpace = function (space) {
-    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
-  };
-
-  if (mode === MODE.TINY) {
-    let isEmpty = object.length === 0;
-    if (isEmpty) {
-      items = [];
-    } else {
-      items = [span({
-        className: "more-ellipsis",
-        title: "more…"
-      }, "…")];
-    }
-    brackets = needSpace(false);
-  } else {
-    items = arrayIterator(props, object, maxLengthMap.get(mode));
-    brackets = needSpace(items.length > 0);
-  }
-
-  return span({
-    className: "objectBox objectBox-array" }, span({
-    className: "arrayLeftBracket"
-  }, brackets.left), ...items, span({
-    className: "arrayRightBracket"
-  }, brackets.right), span({
-    className: "arrayProperties",
-    role: "group" }));
-}
-
-function arrayIterator(props, array, max) {
-  let items = [];
-
-  for (let i = 0; i < array.length && i < max; i++) {
-    let config = {
-      mode: MODE.TINY,
-      delim: i == array.length - 1 ? "" : ", "
-    };
-    let item;
-
-    try {
-      item = ItemRep(Object.assign({}, props, config, {
-        object: array[i]
-      }));
-    } catch (exc) {
-      item = ItemRep(Object.assign({}, props, config, {
-        object: exc
-      }));
-    }
-    items.push(item);
-  }
-
-  if (array.length > max) {
-    items.push(span({
-      className: "more-ellipsis",
-      title: "more…"
-    }, "…"));
-  }
-
-  return items;
-}
-
-/**
- * Renders array item. Individual values are separated by a comma.
- */
-ItemRep.propTypes = {
-  object: PropTypes.any.isRequired,
-  delim: PropTypes.string.isRequired,
-  mode: ModePropType
-};
-
-function ItemRep(props) {
-  const { Rep } = __webpack_require__(4);
-
-  let {
-    object,
-    delim,
-    mode
-  } = props;
-  return span({}, Rep(Object.assign({}, props, {
-    object: object,
-    mode: mode
-  })), delim);
-}
-
-function getLength(object) {
-  return object.length;
-}
-
-function supportsObject(object) {
-  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
-}
-
-const maxLengthMap = new Map();
-maxLengthMap.set(MODE.SHORT, 3);
-maxLengthMap.set(MODE.LONG, 10);
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(ArrayRep),
-  supportsObject,
-  maxLengthMap,
-  getLength
-};
-
-/***/ }),
 /* 11 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
     value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
 
-var _react = __webpack_require__(7);
+var _react = __webpack_require__(8);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _propTypes = __webpack_require__(2);
 
 var _util = __webpack_require__(35);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
 
 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
 
 var process = process || { env: {} };
@@ -1594,34 +1691,38 @@ module.exports = {
 
 
 /* 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/. */
 
 // Dependencies
 const PropTypes = __webpack_require__(2);
+
+const { lengthBubble } = __webpack_require__(14);
 const {
   getGripType,
   isGrip,
-  wrapRender
+  wrapRender,
+  ellipsisElement
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
+const { ModePropType } = __webpack_require__(5);
 
 /**
  * Renders an array. The array is enclosed by left and right bracket
  * and the max number of rendered items depends on the current mode.
  */
 GripArray.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  mode: ModePropType,
   provider: PropTypes.object,
   onDOMNodeMouseOver: PropTypes.func,
   onDOMNodeMouseOut: PropTypes.func,
   onInspectIconClick: PropTypes.func
 };
 
 function GripArray(props) {
   let {
@@ -1630,46 +1731,43 @@ function GripArray(props) {
   } = props;
 
   let items;
   let brackets;
   let needSpace = function (space) {
     return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
   };
 
+  const config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-array"
+  };
+
+  const title = getTitle(props, object);
+
   if (mode === MODE.TINY) {
-    let objectLength = getLength(object);
-    let isEmpty = objectLength === 0;
-    let ellipsis;
-    if (!isEmpty) {
-      ellipsis = span({
-        className: "more-ellipsis",
-        title: "more…"
-      }, "…");
+    const isEmpty = getLength(object) === 0;
+
+    // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
+    if (!isEmpty && object.class !== "Array") {
+      return span(config, title);
     }
 
-    let title;
-    if (object.class != "Array") {
-      title = object.class + " ";
-    }
     brackets = needSpace(false);
-    return span({
-      "data-link-actor-id": object.actor,
-      className: "objectBox objectBox-array" }, title, span({
+    return span(config, title, span({
       className: "arrayLeftBracket"
-    }, brackets.left), ellipsis, span({
+    }, brackets.left), isEmpty ? null : ellipsisElement, span({
       className: "arrayRightBracket"
     }, brackets.right));
   }
+
   let max = maxLengthMap.get(mode);
   items = arrayIterator(props, object, max);
   brackets = needSpace(items.length > 0);
 
-  let title = getTitle(props, object);
-
   return span({
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-array" }, title, span({
     className: "arrayLeftBracket"
   }, brackets.left), ...interleaveCommas(items), span({
     className: "arrayRightBracket"
   }, brackets.right), span({
     className: "arrayProperties",
@@ -1689,24 +1787,46 @@ function getLength(grip) {
   if (!grip.preview) {
     return 0;
   }
 
   return grip.preview.length || grip.preview.childNodesLength || 0;
 }
 
 function getTitle(props, object) {
-  if (props.mode === MODE.TINY) {
-    return "";
-  }
+  let objectLength = getLength(object);
+  let isEmpty = objectLength === 0;
 
   let title = props.title || object.class || "Array";
+
+  const length = lengthBubble({
+    object,
+    mode: props.mode,
+    maxLengthMap,
+    getLength
+  });
+
+  if (props.mode === MODE.TINY) {
+    if (isEmpty) {
+      return object.class === "Array" ? "" : span({
+        className: "objectTitle" }, title, " ");
+    }
+
+    let trailingSpace;
+    if (object.class === "Array") {
+      title = "";
+      trailingSpace = " ";
+    }
+
+    return span({
+      className: "objectTitle" }, title, length, trailingSpace);
+  }
+
   return span({
-    className: "objectTitle"
-  }, title + " ");
+    className: "objectTitle" }, title, length, " ");
 }
 
 function getPreviewItems(grip) {
   if (!grip.preview) {
     return null;
   }
 
   return grip.preview.items || grip.preview.childNodes || [];
@@ -1765,20 +1885,17 @@ function arrayIterator(props, grip, max)
   // Handle trailing empty slots if there are some.
   if (items.length < max && emptySlots > 0) {
     items.push(getEmptySlotsElement(emptySlots));
     foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
   }
 
   const itemsShown = items.length + foldedEmptySlots;
   if (gripLength > itemsShown) {
-    items.push(span({
-      className: "more-ellipsis",
-      title: "more…"
-    }, "…"));
+    items.push(ellipsisElement);
   }
 
   return items;
 }
 
 function getEmptySlotsElement(number) {
   // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
   return `<${number} empty slot${number > 1 ? "s" : ""}>`;
@@ -1806,40 +1923,94 @@ module.exports = {
 
 /***/ }),
 /* 14 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
+const PropTypes = __webpack_require__(2);
+
+const { wrapRender } = __webpack_require__(0);
+const { MODE } = __webpack_require__(3);
+const { ModePropType } = __webpack_require__(5);
+
+const dom = __webpack_require__(1);
+const { span } = dom;
+
+GripLengthBubble.propTypes = {
+  object: PropTypes.object.isRequired,
+  maxLengthMap: PropTypes.instanceOf(Map).isRequired,
+  getLength: PropTypes.func.isRequired,
+  mode: ModePropType,
+  visibilityThreshold: PropTypes.number
+};
+
+function GripLengthBubble(props) {
+  const {
+    object,
+    mode = MODE.SHORT,
+    visibilityThreshold = 2,
+    maxLengthMap,
+    getLength,
+    showZeroLength = false
+  } = props;
+
+  const length = getLength(object);
+  const isEmpty = length === 0;
+  const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
+  if (isEmpty && !showZeroLength || isObvious) {
+    return "";
+  }
+
+  return span({
+    className: "objectLengthBubble"
+  }, `(${length})`);
+}
+
+module.exports = {
+  lengthBubble: wrapRender(GripLengthBubble)
+};
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
 /* 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/. */
 
 // Dependencies
+
+const { lengthBubble } = __webpack_require__(14);
 const PropTypes = __webpack_require__(2);
 const {
   isGrip,
-  wrapRender
+  wrapRender,
+  ellipsisElement
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
+const { ModePropType } = __webpack_require__(5);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders an map. A map is represented by a list of its
  * entries enclosed in curly brackets.
  */
 GripMap.propTypes = {
   object: PropTypes.object,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  mode: ModePropType,
   isInterestingEntry: PropTypes.func,
   onDOMNodeMouseOver: PropTypes.func,
   onDOMNodeMouseOut: PropTypes.func,
   onInspectIconClick: PropTypes.func,
   title: PropTypes.string
 };
 
 function GripMap(props) {
@@ -1848,34 +2019,42 @@ function GripMap(props) {
     object
   } = props;
 
   const config = {
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-object"
   };
 
-  if (mode === MODE.TINY) {
-    return span(config, getTitle(props, object));
-  }
-
-  let propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
-
-  return span(config, getTitle(props, object), span({
+  const title = getTitle(props, object);
+  const isEmpty = getLength(object) === 0;
+
+  if (isEmpty || mode === MODE.TINY) {
+    return span(config, title);
+  }
+
+  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
+
+  return span(config, title, span({
     className: "objectLeftBrace"
   }, " { "), ...propsArray, span({
     className: "objectRightBrace"
   }, " }"));
 }
 
 function getTitle(props, object) {
-  let title = props.title || (object && object.class ? object.class : "Map");
+  const title = props.title || (object && object.class ? object.class : "Map");
   return span({
-    className: "objectTitle"
-  }, title);
+    className: "objectTitle" }, title, lengthBubble({
+    object,
+    mode: props.mode,
+    maxLengthMap,
+    getLength,
+    showZeroLength: true
+  }));
 }
 
 function safeEntriesIterator(props, object, max) {
   max = typeof max === "undefined" ? 3 : max;
   try {
     return entriesIterator(props, object, max);
   } catch (err) {
     console.error(err);
@@ -1897,21 +2076,17 @@ function entriesIterator(props, object, 
     indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
       return !isInterestingEntry(t, value, name);
     }));
   }
 
   let entries = getEntries(props, mapEntries, indexes);
   if (entries.length < getLength(object)) {
     // There are some undisplayed entries. Then display "…".
-    entries.push(span({
-      key: "more",
-      className: "more-ellipsis",
-      title: "more…"
-    }, "…"));
+    entries.push(ellipsisElement);
   }
 
   return unfoldEntries(entries);
 }
 
 function unfoldEntries(items) {
   return items.reduce((res, item, index) => {
     if (Array.isArray(item)) {
@@ -2008,17 +2183,17 @@ maxLengthMap.set(MODE.LONG, 10);
 module.exports = {
   rep: wrapRender(GripMap),
   supportsObject,
   maxLengthMap,
   getLength
 };
 
 /***/ }),
-/* 15 */
+/* 16 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -2026,17 +2201,17 @@ module.exports = {
 // Dependencies
 const PropTypes = __webpack_require__(2);
 // Shortcuts
 const dom = __webpack_require__(1);
 const { span } = dom;
 const {
   wrapRender
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 /**
  * Renders an map entry. A map entry is represented by its key, a column and its value.
  */
 GripMapEntry.propTypes = {
   object: PropTypes.object,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -2086,38 +2261,38 @@ function createGripMapEntry(key, value) 
 // Exports from this module
 module.exports = {
   rep: wrapRender(GripMapEntry),
   createGripMapEntry,
   supportsObject
 };
 
 /***/ }),
-/* 16 */
+/* 17 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
 
-const client = __webpack_require__(17);
+const client = __webpack_require__(18);
 const loadProperties = __webpack_require__(55);
-const node = __webpack_require__(18);
+const node = __webpack_require__(19);
 
 module.exports = {
   client,
   loadProperties,
   node
 };
 
 /***/ }),
-/* 17 */
+/* 18 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 async function enumIndexedProperties(objectClient, start, end) {
   try {
     const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
@@ -2182,32 +2357,32 @@ module.exports = {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   enumSymbols,
   getPrototype
 };
 
 /***/ }),
-/* 18 */
+/* 19 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
 
 const { get, has } = __webpack_require__(56);
 const { maybeEscapePropertyName } = __webpack_require__(0);
-const ArrayRep = __webpack_require__(10);
+const ArrayRep = __webpack_require__(5);
 const GripArrayRep = __webpack_require__(13);
-const GripMap = __webpack_require__(14);
-const GripMapEntryRep = __webpack_require__(15);
+const GripMap = __webpack_require__(15);
+const GripMapEntryRep = __webpack_require__(16);
 
 const MAX_NUMERICAL_PROPERTIES = 100;
 
 const NODE_TYPES = {
   BUCKET: Symbol("[n…n]"),
   DEFAULT_PROPERTIES: Symbol("[default properties]"),
   ENTRIES: Symbol("<entries>"),
   GET: Symbol("<get>"),
@@ -2282,16 +2457,37 @@ function nodeIsFunction(item) {
   return value && value.class === "Function";
 }
 
 function nodeIsOptimizedOut(item) {
   const value = getValue(item);
   return !nodeHasChildren(item) && value && value.optimizedOut;
 }
 
+function nodeIsUninitializedBinding(item) {
+  const value = getValue(item);
+  return value && value.uninitialized;
+}
+
+// Used to check if an item represents a binding that exists in a sourcemap's
+// original file content, but does not match up with a binding found in the
+// generated code.
+function nodeIsUnmappedBinding(item) {
+  const value = getValue(item);
+  return value && value.unmapped;
+}
+
+// Used to check if an item represents a binding that exists in the debugger's
+// parser result, but does not match up with a binding returned by the
+// debugger server.
+function nodeIsUnscopedBinding(item) {
+  const value = getValue(item);
+  return value && value.unscoped;
+}
+
 function nodeIsMissingArguments(item) {
   const value = getValue(item);
   return !nodeHasChildren(item) && value && value.missingArguments;
 }
 
 function nodeHasProperties(item) {
   return !nodeHasChildren(item) && nodeIsObject(item);
 }
@@ -2806,41 +3002,44 @@ module.exports = {
   nodeIsMissingArguments,
   nodeIsObject,
   nodeIsOptimizedOut,
   nodeIsPrimitive,
   nodeIsPromise,
   nodeIsPrototype,
   nodeIsProxy,
   nodeIsSetter,
+  nodeIsUninitializedBinding,
+  nodeIsUnmappedBinding,
+  nodeIsUnscopedBinding,
   nodeIsWindow,
   nodeNeedsNumericalBuckets,
   nodeSupportsNumericalBucketing,
   setNodeChildren,
   sortProperties,
   NODE_TYPES,
   // Export for testing purpose.
   SAFE_PATH_PREFIX
 };
 
 /***/ }),
-/* 19 */
+/* 20 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
 
 const { MODE } = __webpack_require__(3);
 const { REPS, getRep } = __webpack_require__(4);
 const ObjectInspector = __webpack_require__(48);
-const ObjectInspectorUtils = __webpack_require__(16);
+const ObjectInspectorUtils = __webpack_require__(17);
 
 const {
   parseURLEncodedText,
   parseURLParams,
   maybeEscapePropertyName,
   getGripPreviewItems
 } = __webpack_require__(0);
 
@@ -2852,23 +3051,23 @@ module.exports = {
   parseURLEncodedText,
   parseURLParams,
   getGripPreviewItems,
   ObjectInspector,
   ObjectInspectorUtils
 };
 
 /***/ }),
-/* 20 */
+/* 21 */
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
-/* 21 */
+/* 22 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -2900,17 +3099,17 @@ function supportsObject(object, noGrip =
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(Undefined),
   supportsObject
 };
 
 /***/ }),
-/* 22 */
+/* 23 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -2942,93 +3141,16 @@ function supportsObject(object, noGrip =
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(Null),
   supportsObject
 };
 
 /***/ }),
-/* 23 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/. */
-
-// Dependencies
-const PropTypes = __webpack_require__(2);
-const {
-  escapeString,
-  sanitizeString,
-  isGrip,
-  wrapRender
-} = __webpack_require__(0);
-
-const dom = __webpack_require__(1);
-const { span } = dom;
-
-/**
- * Renders a long string grip.
- */
-LongStringRep.propTypes = {
-  useQuotes: PropTypes.bool,
-  escapeWhitespace: PropTypes.bool,
-  style: PropTypes.object,
-  cropLimit: PropTypes.number.isRequired,
-  member: PropTypes.string,
-  object: PropTypes.object.isRequired
-};
-
-function LongStringRep(props) {
-  let {
-    cropLimit,
-    member,
-    object,
-    style,
-    useQuotes = true,
-    escapeWhitespace = true
-  } = props;
-  let { fullText, initial, length } = object;
-
-  let config = {
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-string"
-  };
-
-  if (style) {
-    config.style = style;
-  }
-
-  let string = member && member.open ? fullText || initial : initial.substring(0, cropLimit);
-
-  if (string.length < length) {
-    string += "\u2026";
-  }
-  let formattedString = useQuotes ? escapeString(string, escapeWhitespace) : sanitizeString(string);
-  return span(config, formattedString);
-}
-
-function supportsObject(object, noGrip = false) {
-  if (noGrip === true || !isGrip(object)) {
-    return false;
-  }
-  return object.type === "longString";
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(LongStringRep),
-  supportsObject
-};
-
-/***/ }),
 /* 24 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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
@@ -3084,19 +3206,20 @@ module.exports = {
 
 /* 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/. */
 
 // Dependencies
 const PropTypes = __webpack_require__(2);
 const {
-  wrapRender
+  wrapRender,
+  ellipsisElement
 } = __webpack_require__(0);
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 const DEFAULT_TITLE = "Object";
 
 /**
@@ -3116,21 +3239,17 @@ function ObjectRep(props) {
 
   if (props.mode === MODE.TINY) {
     const tinyModeItems = [];
     if (getTitle(props, object) !== DEFAULT_TITLE) {
       tinyModeItems.push(getTitleElement(props, object));
     } else {
       tinyModeItems.push(span({
         className: "objectLeftBrace"
-      }, "{"), propsArray.length > 0 ? span({
-        key: "more",
-        className: "more-ellipsis",
-        title: "more…"
-      }, "…") : null, span({
+      }, "{"), propsArray.length > 0 ? ellipsisElement : null, span({
         className: "objectRightBrace"
       }, "}"));
     }
 
     return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
   }
 
   return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
@@ -3140,17 +3259,17 @@ function ObjectRep(props) {
   }, " }"));
 }
 
 function getTitleElement(props, object) {
   return span({ className: "objectTitle" }, getTitle(props, object));
 }
 
 function getTitle(props, object) {
-  return props.title || object.class || DEFAULT_TITLE;
+  return props.title || DEFAULT_TITLE;
 }
 
 function safePropIterator(props, object, max) {
   max = typeof max === "undefined" ? 3 : max;
   try {
     return propIterator(props, object, max);
   } catch (err) {
     console.error(err);
@@ -3223,21 +3342,17 @@ function propIterator(props, object, max
         continue;
       }
 
       pushPropRep(name, value);
     }
   }
 
   if (propertiesNumber < propertiesNames.length) {
-    elements.push(span({
-      key: "more",
-      className: "more-ellipsis",
-      title: ", more…"
-    }, "…"));
+    elements.push(ellipsisElement);
   }
 
   return elements;
 }
 
 function isInterestingProp(value) {
   const type = typeof value;
   return type == "boolean" || type == "number" || type == "string" && value;
@@ -3477,17 +3592,17 @@ const dom = __webpack_require__(1);
 const { span } = dom;
 
 // Reps
 const {
   getGripType,
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const { rep: StringRep } = __webpack_require__(6);
+const { rep: StringRep } = __webpack_require__(7);
 
 /**
  * Renders DOM attribute
  */
 Attribute.propTypes = {
   object: PropTypes.object.isRequired
 };
 
@@ -3641,17 +3756,18 @@ function getTitle(grip) {
 }
 
 // Registration
 function supportsObject(object, noGrip = false) {
   if (noGrip === true || !isGrip(object)) {
     return false;
   }
 
-  return object.preview && getGripType(object, noGrip) == "HTMLDocument";
+  const type = getGripType(object, noGrip);
+  return object.preview && (type === "HTMLDocument" || type === "XULDocument");
 }
 
 // Exports from this module
 module.exports = {
   rep: wrapRender(Document),
   supportsObject
 };
 
@@ -3671,17 +3787,17 @@ const PropTypes = __webpack_require__(2)
 
 // Reps
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 
 const { MODE } = __webpack_require__(3);
-const { rep } = __webpack_require__(8);
+const { rep } = __webpack_require__(9);
 
 /**
  * Renders DOM event objects.
  */
 Event.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -3779,17 +3895,17 @@ const PropTypes = __webpack_require__(2)
 // Reps
 const {
   getGripType,
   isGrip,
   cropString,
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
-const Svg = __webpack_require__(9);
+const Svg = __webpack_require__(10);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 const IGNORED_SOURCE_URLS = ["debugger eval code"];
 
 /**
  * This component represents a template for Function objects.
@@ -4005,17 +4121,17 @@ module.exports = "<!-- This Source Code 
 const PropTypes = __webpack_require__(2);
 // Dependencies
 const {
   getGripType,
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 
-const PropRep = __webpack_require__(5);
+const PropRep = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders a DOM Promise object.
  */
@@ -4307,20 +4423,20 @@ module.exports = {
 // ReactJS
 const PropTypes = __webpack_require__(2);
 
 // Utils
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const { rep: StringRep } = __webpack_require__(6);
+const { rep: StringRep } = __webpack_require__(7);
 const { MODE } = __webpack_require__(3);
 const nodeConstants = __webpack_require__(12);
-const Svg = __webpack_require__(9);
+const Svg = __webpack_require__(10);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders DOM element node.
  */
 ElementNode.propTypes = {
@@ -4441,17 +4557,17 @@ const PropTypes = __webpack_require__(2)
 
 // Reps
 const {
   isGrip,
   cropString,
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
-const Svg = __webpack_require__(9);
+const Svg = __webpack_require__(10);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders DOM #text node.
  */
 TextNode.propTypes = {
@@ -4711,17 +4827,17 @@ module.exports = {
 const PropTypes = __webpack_require__(2);
 
 // Reps
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 
-const String = __webpack_require__(6).rep;
+const String = __webpack_require__(7).rep;
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders a grip object with textual data.
  */
 ObjectWithText.propTypes = {
@@ -4839,17 +4955,17 @@ var _devtoolsComponents = __webpack_requ
 var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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/. */
 
-const { Component, createFactory } = __webpack_require__(7);
+const { Component, createFactory } = __webpack_require__(8);
 const PropTypes = __webpack_require__(2);
 const dom = __webpack_require__(1);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
 __webpack_require__(53);
 
 const classnames = __webpack_require__(54);
 
@@ -4858,17 +4974,17 @@ const {
     Rep,
     Grip
   }
 } = __webpack_require__(4);
 const {
   MODE
 } = __webpack_require__(3);
 
-const Utils = __webpack_require__(16);
+const Utils = __webpack_require__(17);
 
 const {
   getChildren,
   getClosestGripNode,
   getParent,
   getValue,
   nodeHasAccessors,
   nodeHasProperties,
@@ -4876,16 +4992,19 @@ const {
   nodeIsFunction,
   nodeIsGetter,
   nodeIsMapEntry,
   nodeIsMissingArguments,
   nodeIsOptimizedOut,
   nodeIsPrimitive,
   nodeIsPrototype,
   nodeIsSetter,
+  nodeIsUninitializedBinding,
+  nodeIsUnmappedBinding,
+  nodeIsUnscopedBinding,
   nodeIsWindow
 } = Utils.node;
 
 const {
   loadItemProperties
 } = Utils.loadProperties;
 
 // This implements a component that renders an interactive inspector
@@ -5068,17 +5187,23 @@ class ObjectInspector extends Component 
     let objectValue;
     let label = item.name;
     let itemValue = getValue(item);
 
     const isPrimitive = nodeIsPrimitive(item);
 
     const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
 
-    if (nodeIsOptimizedOut(item)) {
+    if (nodeIsUninitializedBinding(item)) {
+      objectValue = dom.span({ className: "unavailable" }, "(uninitialized)");
+    } else if (nodeIsUnmappedBinding(item)) {
+      objectValue = dom.span({ className: "unavailable" }, "(unmapped)");
+    } else if (nodeIsUnscopedBinding(item)) {
+      objectValue = dom.span({ className: "unavailable" }, "(unscoped)");
+    } else if (nodeIsOptimizedOut(item)) {
       objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
     } else if (nodeIsMissingArguments(item) || unavailable) {
       objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
     } else if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
       objectValue = undefined;
       label = this.renderGrip(item, Object.assign({}, this.props, {
         functionName: label
       }));
@@ -5242,17 +5367,17 @@ module.exports = {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
-var _react = __webpack_require__(7);
+var _react = __webpack_require__(8);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _reactDomFactories = __webpack_require__(1);
 
 var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
 
 var _propTypes = __webpack_require__(2);
@@ -6115,33 +6240,33 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   getPrototype,
   enumSymbols
-} = __webpack_require__(17);
+} = __webpack_require__(18);
 
 const {
   getClosestGripNode,
   getClosestNonBucketNode,
   getValue,
   nodeHasAccessors,
   nodeHasAllEntriesInPreview,
   nodeHasProperties,
   nodeIsBucket,
   nodeIsDefaultProperties,
   nodeIsEntries,
   nodeIsMapEntry,
   nodeIsPrimitive,
   nodeIsProxy,
   nodeNeedsNumericalBuckets
-} = __webpack_require__(18);
+} = __webpack_require__(19);
 
 function loadItemProperties(item, createObjectClient, loadedProperties) {
   const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
 
   let objectClient;
   const getObjectClient = () => {
     if (objectClient) {
       return objectClient;
--- a/devtools/client/webconsole/new-console-output/actions/filters.js
+++ b/devtools/client/webconsole/new-console-output/actions/filters.js
@@ -2,17 +2,16 @@
 /* vim: set ft=javascript ts=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/. */
 
 "use strict";
 
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
-const Services = require("Services");
 
 const {
   FILTER_TEXT_SET,
   FILTER_TOGGLE,
   FILTERS_CLEAR,
   DEFAULT_FILTERS_RESET,
   PREFS,
   FILTERS,
@@ -22,60 +21,59 @@ const {
 function filterTextSet(text) {
   return {
     type: FILTER_TEXT_SET,
     text
   };
 }
 
 function filterToggle(filter) {
-  return (dispatch, getState) => {
+  return (dispatch, getState, {prefsService}) => {
     dispatch({
       type: FILTER_TOGGLE,
       filter,
     });
     const filterState = getAllFilters(getState());
-    Services.prefs.setBoolPref(PREFS.FILTER[filter.toUpperCase()],
-      filterState[filter]);
+    prefsService.setBoolPref(PREFS.FILTER[filter.toUpperCase()], filterState[filter]);
   };
 }
 
 function filtersClear() {
-  return (dispatch, getState) => {
+  return (dispatch, getState, {prefsService}) => {
     dispatch({
       type: FILTERS_CLEAR,
     });
 
     const filterState = getAllFilters(getState());
     for (let filter in filterState) {
       if (filter !== FILTERS.TEXT) {
-        Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+        prefsService.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
       }
     }
   };
 }
 
 /**
  * Set the default filters to their original values.
  * This is different than filtersClear where we reset
  * all the filters to their original values. Here we want
  * to keep non-default filters the user might have set.
  */
 function defaultFiltersReset() {
-  return (dispatch, getState) => {
+  return (dispatch, getState, {prefsService}) => {
     // Get the state before dispatching so the action does not alter prefs reset.
     const filterState = getAllFilters(getState());
 
     dispatch({
       type: DEFAULT_FILTERS_RESET,
     });
 
     DEFAULT_FILTERS.forEach(filter => {
       if (filterState[filter] === false) {
-        Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+        prefsService.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
       }
     });
   };
 }
 
 module.exports = {
   filterTextSet,
   filterToggle,
--- a/devtools/client/webconsole/new-console-output/actions/ui.js
+++ b/devtools/client/webconsole/new-console-output/actions/ui.js
@@ -3,46 +3,45 @@
 /* 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 { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
 const { getMessage } = require("devtools/client/webconsole/new-console-output/selectors/messages");
-const Services = require("Services");
 
 const {
   FILTER_BAR_TOGGLE,
   INITIALIZE,
   PERSIST_TOGGLE,
   PREFS,
   SELECT_NETWORK_MESSAGE_TAB,
   SIDEBAR_CLOSE,
   SHOW_OBJECT_IN_SIDEBAR,
   TIMESTAMPS_TOGGLE,
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 function filterBarToggle(show) {
-  return (dispatch, getState) => {
+  return (dispatch, getState, {prefsService}) => {
     dispatch({
       type: FILTER_BAR_TOGGLE,
     });
-    const uiState = getAllUi(getState());
-    Services.prefs.setBoolPref(PREFS.UI.FILTER_BAR, uiState.filterBarVisible);
+    const {filterBarVisible} = getAllUi(getState());
+    prefsService.setBoolPref(PREFS.UI.FILTER_BAR, filterBarVisible);
   };
 }
 
 function persistToggle(show) {
-  return (dispatch, getState) => {
+  return (dispatch, getState, {prefsService}) => {
     dispatch({
       type: PERSIST_TOGGLE,
     });
     const uiState = getAllUi(getState());
-    Services.prefs.setBoolPref(PREFS.UI.PERSIST, uiState.persistLogs);
+    prefsService.setBoolPref(PREFS.UI.PERSIST, uiState.persistLogs);
   };
 }
 
 function timestampsToggle(visible) {
   return {
     type: TIMESTAMPS_TOGGLE,
     visible,
   };
--- a/devtools/client/webconsole/new-console-output/components/Message.js
+++ b/devtools/client/webconsole/new-console-output/components/Message.js
@@ -160,18 +160,20 @@ class Message extends Component {
       attachment = this.props.attachment;
     } else if (stacktrace && open) {
       attachment = dom.div(
         {
           className: "stacktrace devtools-monospace"
         },
         StackTrace({
           stacktrace: stacktrace,
-          onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
-          onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad,
+          onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger
+            || serviceContainer.onViewSource,
+          onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad
+            || serviceContainer.onViewSource,
           sourceMapService: serviceContainer.sourceMapService,
         })
       );
     }
 
     // If there is an expandable part, make it collapsible.
     let collapse = null;
     if (collapsible) {
@@ -188,17 +190,17 @@ class Message extends Component {
         { className: "message-flex-body error-note" },
         dom.span({ className: "message-body devtools-monospace" },
           "note: " + note.messageBody
         ),
         dom.span({ className: "message-location devtools-monospace" },
           note.frame ? FrameView({
             frame: note.frame,
             onClick: serviceContainer
-              ? serviceContainer.onViewSourceInDebugger
+              ? serviceContainer.onViewSourceInDebugger || serviceContainer.onViewSource
               : undefined,
             showEmptyPathAsHost: true,
             sourceMapService: serviceContainer
               ? serviceContainer.sourceMapService
               : undefined
           }) : null
         )));
     } else {
@@ -208,21 +210,23 @@ class Message extends Component {
     const repeat = this.props.repeat && this.props.repeat > 1 ?
       MessageRepeat({repeat: this.props.repeat}) : null;
 
     let onFrameClick;
     if (serviceContainer && frame) {
       if (source === MESSAGE_SOURCE.CSS) {
         onFrameClick = serviceContainer.onViewSourceInStyleEditor;
       } else if (/^Scratchpad\/\d+$/.test(frame.source)) {
-        onFrameClick = serviceContainer.onViewSourceInScratchpad;
+        onFrameClick = serviceContainer.onViewSourceInScratchpad
+          || serviceContainer.onViewSource;
       } else {
         // Point everything else to debugger, if source not available,
         // it will fall back to view-source.
-        onFrameClick = serviceContainer.onViewSourceInDebugger;
+        onFrameClick = serviceContainer.onViewSourceInDebugger
+          || serviceContainer.onViewSource;
       }
     }
 
     // Configure the location.
     const location = dom.span({ className: "message-location devtools-monospace" },
       frame ? FrameView({
         frame,
         onClick: onFrameClick,
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -25,29 +25,35 @@ const actionTypes = {
   SELECT_NETWORK_MESSAGE_TAB: "SELECT_NETWORK_MESSAGE_TAB",
   SIDEBAR_CLOSE: "SIDEBAR_CLOSE",
   SHOW_OBJECT_IN_SIDEBAR: "SHOW_OBJECT_IN_SIDEBAR",
   TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
 };
 
 const prefs = {
   PREFS: {
+    // Filter preferences only have the suffix since they can be used either for the
+    // webconsole or the browser console.
     FILTER: {
-      ERROR: "devtools.webconsole.filter.error",
-      WARN: "devtools.webconsole.filter.warn",
-      INFO: "devtools.webconsole.filter.info",
-      LOG: "devtools.webconsole.filter.log",
-      DEBUG: "devtools.webconsole.filter.debug",
-      CSS: "devtools.webconsole.filter.css",
-      NET: "devtools.webconsole.filter.net",
-      NETXHR: "devtools.webconsole.filter.netxhr",
+      ERROR: "filter.error",
+      WARN: "filter.warn",
+      INFO: "filter.info",
+      LOG: "filter.log",
+      DEBUG: "filter.debug",
+      CSS: "filter.css",
+      NET: "filter.net",
+      NETXHR: "filter.netxhr",
     },
     UI: {
-      FILTER_BAR: "devtools.webconsole.ui.filterbar",
+      // Filter bar UI preference only have the suffix since it can be used either for
+      // the webconsole or the browser console.
+      FILTER_BAR: "ui.filterbar",
+      // Persist is only used by the webconsole.
       PERSIST: "devtools.webconsole.persistlog",
+      // We use the same pref to enable the sidebar on webconsole and browser console.
       SIDEBAR_TOGGLE: "devtools.webconsole.sidebarToggle",
     }
   }
 };
 
 const FILTERS = {
   CSS: "css",
   DEBUG: "debug",
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -95,16 +95,21 @@ NewConsoleOutputWrapper.prototype = {
           return this.document.createElement(nodename);
         },
         getLongString: (grip) => {
           return hud.proxy.webConsoleClient.getString(grip);
         },
         requestData(id, type) {
           return hud.proxy.networkDataProvider.requestData(id, type);
         },
+        onViewSource(frame) {
+          if (hud && hud.owner && hud.owner.viewSource) {
+            hud.owner.viewSource(frame.url, frame.line);
+          }
+        }
       };
 
       // Set `openContextMenu` this way so, `serviceContainer` variable
       // is available in the current scope and we can pass it into
       // `createContextMenu` method.
       serviceContainer.openContextMenu = (e, message) => {
         let { screenX, screenY, target } = e;
 
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -6,80 +6,92 @@
 const {FilterState} = require("devtools/client/webconsole/new-console-output/reducers/filters");
 const {PrefState} = require("devtools/client/webconsole/new-console-output/reducers/prefs");
 const {UiState} = require("devtools/client/webconsole/new-console-output/reducers/ui");
 const {
   applyMiddleware,
   compose,
   createStore
 } = require("devtools/client/shared/vendor/redux");
-const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
 const {
   BATCH_ACTIONS
 } = require("devtools/client/shared/redux/middleware/debounce");
 const {
   MESSAGE_OPEN,
   MESSAGES_ADD,
   MESSAGES_CLEAR,
   REMOVED_ACTORS_CLEAR,
   NETWORK_MESSAGE_UPDATE,
   PREFS,
 } = require("devtools/client/webconsole/new-console-output/constants");
 const { reducers } = require("./reducers/index");
-const Services = require("Services");
 const {
   getMessage,
   getAllMessagesUiById,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const DataProvider = require("devtools/client/netmonitor/src/connector/firefox-data-provider");
 const {
   getAllNetworkMessagesUpdateById,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
+const {getPrefsService} = require("devtools/client/webconsole/new-console-output/utils/prefs");
 
 /**
  * Create and configure store for the Console panel. This is the place
  * where various enhancers and middleware can be registered.
  */
 function configureStore(hud, options = {}) {
+  const prefsService = getPrefsService(hud);
+  const {
+    getBoolPref,
+    getIntPref,
+  } = prefsService;
+
   const logLimit = options.logLimit
-    || Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1);
-
-  const sidebarToggle = Services.prefs.getBoolPref(PREFS.UI.SIDEBAR_TOGGLE);
+    || Math.max(getIntPref("devtools.hud.loglimit"), 1);
+  const sidebarToggle = getBoolPref(PREFS.UI.SIDEBAR_TOGGLE);
 
   const initialState = {
     prefs: PrefState({ logLimit, sidebarToggle }),
     filters: FilterState({
-      error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR),
-      warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
-      info: Services.prefs.getBoolPref(PREFS.FILTER.INFO),
-      debug: Services.prefs.getBoolPref(PREFS.FILTER.DEBUG),
-      log: Services.prefs.getBoolPref(PREFS.FILTER.LOG),
-      css: Services.prefs.getBoolPref(PREFS.FILTER.CSS),
-      net: Services.prefs.getBoolPref(PREFS.FILTER.NET),
-      netxhr: Services.prefs.getBoolPref(PREFS.FILTER.NETXHR),
+      error: getBoolPref(PREFS.FILTER.ERROR),
+      warn: getBoolPref(PREFS.FILTER.WARN),
+      info: getBoolPref(PREFS.FILTER.INFO),
+      debug: getBoolPref(PREFS.FILTER.DEBUG),
+      log: getBoolPref(PREFS.FILTER.LOG),
+      css: getBoolPref(PREFS.FILTER.CSS),
+      net: getBoolPref(PREFS.FILTER.NET),
+      netxhr: getBoolPref(PREFS.FILTER.NETXHR),
     }),
     ui: UiState({
-      filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR),
+      filterBarVisible: getBoolPref(PREFS.UI.FILTER_BAR),
       networkMessageActiveTabId: "headers",
-      persistLogs: Services.prefs.getBoolPref(PREFS.UI.PERSIST),
+      persistLogs: getBoolPref(PREFS.UI.PERSIST),
     })
   };
 
   return createStore(
     createRootReducer(),
     initialState,
     compose(
-      applyMiddleware(thunk),
+      applyMiddleware(thunk.bind(null, {prefsService})),
       enableActorReleaser(hud),
       enableBatching(),
       enableNetProvider(hud)
     )
   );
 }
 
+function thunk(options = {}, { dispatch, getState }) {
+  return next => action => {
+    return (typeof action === "function")
+      ? action(dispatch, getState, options)
+      : next(action);
+  };
+}
+
 function createRootReducer() {
   return function rootReducer(state, action) {
     // We want to compute the new state for all properties except "messages".
     const newState = [...Object.entries(reducers)].reduce((res, [key, reducer]) => {
       if (key !== "messages") {
         res[key] = reducer(state[key], action);
       }
       return res;
--- a/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
@@ -16,23 +16,22 @@ const FilterBar = createFactory(require(
 const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
 const {
   MESSAGES_CLEAR,
   FILTERS,
   PREFS,
 } = require("devtools/client/webconsole/new-console-output/constants");
 
-const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
+const { setupStore, prefsService, clearPrefs } = require("devtools/client/webconsole/new-console-output/test/helpers");
 const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
-const ServicesMock = require("Services");
 
 describe("FilterBar component:", () => {
   afterEach(() => {
-    ServicesMock.prefs.testHelpers.clearPrefs();
+    clearPrefs();
   });
 
   it("initial render", () => {
     const store = setupStore();
 
     const wrapper = render(Provider({store}, FilterBar({
       serviceContainer,
       hidePersistLogsCheckbox: false,
@@ -214,26 +213,26 @@ describe("FilterBar component:", () => {
     const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
     expect(toolbar.exists()).toBeFalsy();
   });
 
   it("displays filter bar when button is clicked", () => {
     const store = setupStore();
 
     expect(getAllUi(store.getState()).filterBarVisible).toBe(false);
-    expect(ServicesMock.prefs.getBoolPref(PREFS.UI.FILTER_BAR), false);
+    expect(prefsService.getBoolPref(PREFS.UI.FILTER_BAR), false);
 
     const wrapper = mount(Provider({store}, FilterBar({
       serviceContainer,
       hidePersistLogsCheckbox: false,
     })));
     wrapper.find(".devtools-filter-icon").simulate("click");
 
     expect(getAllUi(store.getState()).filterBarVisible).toBe(true);
-    expect(ServicesMock.prefs.getBoolPref(PREFS.UI.FILTER_BAR), true);
+    expect(prefsService.getBoolPref(PREFS.UI.FILTER_BAR), true);
 
     const secondaryBar = wrapper.find(".webconsole-filterbar-secondary");
     expect(secondaryBar.length).toBe(1);
 
     // Buttons are displayed
     const filterBtn = props => FilterButton(
       Object.assign({}, {
         active: true,
@@ -285,26 +284,26 @@ describe("FilterBar component:", () => {
     wrapper.find(".devtools-plaininput").simulate("input", { target: { value: "a" } });
     expect(store.getState().filters.text).toBe("a");
   });
 
   it("toggles persist logs when checkbox is clicked", () => {
     const store = setupStore();
 
     expect(getAllUi(store.getState()).persistLogs).toBe(false);
-    expect(ServicesMock.prefs.getBoolPref(PREFS.UI.PERSIST), false);
+    expect(prefsService.getBoolPref(PREFS.UI.PERSIST), false);
 
     const wrapper = mount(Provider({store}, FilterBar({
       serviceContainer,
       hidePersistLogsCheckbox: false,
     })));
     wrapper.find(".filter-checkbox input").simulate("change");
 
     expect(getAllUi(store.getState()).persistLogs).toBe(true);
-    expect(ServicesMock.prefs.getBoolPref(PREFS.UI.PERSIST), true);
+    expect(prefsService.getBoolPref(PREFS.UI.PERSIST), true);
   });
 
   it(`doesn't render "Persist logs" input when "hidePersistLogsCheckbox" is true`, () => {
     const store = setupStore();
 
     const wrapper = render(Provider({store}, FilterBar({
       serviceContainer,
       hidePersistLogsCheckbox: true,
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/components/message-location.test.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test utils.
+const expect = require("expect");
+const { mount } = require("enzyme");
+const sinon = require("sinon");
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
+
+let { MessageContainer } = require("devtools/client/webconsole/new-console-output/components/MessageContainer");
+MessageContainer = createFactory(MessageContainer);
+
+// Test fakes.
+const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
+
+describe("Message - location element", () => {
+  it("Calls onViewSourceInDebugger when clicked", () => {
+    const onViewSourceInDebugger = sinon.spy();
+    const onViewSource = sinon.spy();
+
+    const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+    const wrapper = mount(MessageContainer({
+      getMessage: () => message,
+      serviceContainer: Object.assign({}, serviceContainer, {
+        onViewSourceInDebugger,
+        onViewSource,
+      })
+    }));
+
+    // There should be the location
+    const locationLink = wrapper.find(`.message-location a`);
+    expect(locationLink.length).toBe(1);
+    expect(locationLink.text()).toBe("test-console-api.html:1:27");
+
+    locationLink.simulate("click");
+
+    expect(onViewSourceInDebugger.calledOnce).toBe(true);
+    expect(onViewSource.notCalled).toBe(true);
+  });
+
+  it.only("Calls onViewSource when clicked and onViewSourceInDebugger undefined", () => {
+    const onViewSource = sinon.spy();
+
+    const message = stubPreparedMessages.get("console.log('foobar', 'test')");
+
+    const wrapper = mount(MessageContainer({
+      getMessage: () => message,
+      serviceContainer: Object.assign({}, serviceContainer, {
+        onViewSource,
+        onViewSourceInDebugger: undefined,
+      })
+    }));
+
+    // There should be the location
+    const locationLink = wrapper.find(`.message-location a`);
+
+    locationLink.simulate("click");
+    expect(onViewSource.calledOnce).toBe(true);
+  });
+});
deleted file mode 100644
--- a/devtools/client/webconsole/new-console-output/test/fixtures/Services.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-const {
-  DEFAULT_FILTERS_VALUES,
-  FILTERS,
-  PREFS
-} = require("devtools/client/webconsole/new-console-output/constants");
-
-function getDefaultPrefs() {
-  return Object.assign({
-    "devtools.hud.loglimit": 1000,
-    [PREFS.UI.FILTER_BAR]: false,
-    [PREFS.UI.PERSIST]: false,
-  }, Object.entries(PREFS.FILTER).reduce((res, [key, pref]) => {
-    res[pref] = DEFAULT_FILTERS_VALUES[FILTERS[key]];
-    return res;
-  }, {}));
-}
-
-let prefs = Object.assign({}, getDefaultPrefs());
-
-module.exports = {
-  prefs: {
-    getIntPref: pref => prefs[pref],
-    getBoolPref: pref => prefs[pref],
-    setBoolPref: (pref, value) => {
-      prefs[pref] = value;
-    },
-    clearUserPref: (pref) => {
-      prefs[pref] = (getDefaultPrefs())[pref];
-    },
-    testHelpers: {
-      getAllPrefs: () => prefs,
-      getFiltersPrefs: () => Object.values(PREFS.FILTER).reduce((res, pref) => {
-        res[pref] = prefs[pref];
-        return res;
-      }, {}),
-      clearPrefs: () => {
-        prefs = Object.assign({}, getDefaultPrefs());
-      }
-    }
-  }
-};
--- a/devtools/client/webconsole/new-console-output/test/helpers.js
+++ b/devtools/client/webconsole/new-console-output/test/helpers.js
@@ -2,19 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const reduxActions = require("devtools/client/webconsole/new-console-output/actions/index");
 const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
 const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
-const {
-  getAllMessagesById,
-} = require("devtools/client/webconsole/new-console-output/selectors/messages");
+const { getAllMessagesById } = require("devtools/client/webconsole/new-console-output/selectors/messages");
+const { getPrefsService } = require("devtools/client/webconsole/new-console-output/utils/prefs");
+const prefsService = getPrefsService({});
+const { PREFS } = require("devtools/client/webconsole/new-console-output/constants");
 
 /**
  * Prepare actions for use in testing.
  */
 function setupActions() {
   // Some actions use dependency injection. This helps them avoid using state in
   // a hard-to-test way. We need to inject stubbed versions of these dependencies.
   const wrappedActions = Object.assign({}, reduxActions);
@@ -91,16 +92,34 @@ function getFirstMessage(state) {
  * @param {object} state - The redux state of the console.
  * @return {Message} - The last message, or undefined if there are no message in store.
  */
 function getLastMessage(state) {
   const lastIndex = getAllMessagesById(state).size - 1;
   return getMessageAt(state, lastIndex);
 }
 
+function getFiltersPrefs() {
+  return Object.values(PREFS.FILTER).reduce((res, pref) => {
+    res[pref] = prefsService.getBoolPref(pref);
+    return res;
+  }, {});
+}
+
+function clearPrefs() {
+  [
+    "devtools.hud.loglimit",
+    ...Object.values(PREFS.FILTER),
+    ...Object.values(PREFS.UI),
+  ].forEach(prefsService.clearUserPref);
+}
+
 module.exports = {
+  clearPrefs,
   clonePacket,
-  getMessageAt,
+  getFiltersPrefs,
   getFirstMessage,
   getLastMessage,
+  getMessageAt,
+  prefsService,
   setupActions,
   setupStore,
 };
--- a/devtools/client/webconsole/new-console-output/test/mocha-test-setup.js
+++ b/devtools/client/webconsole/new-console-output/test/mocha-test-setup.js
@@ -1,15 +1,33 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const mcRoot = `${__dirname}/../../../../../`;
 const getModule = mcPath => `module.exports = require("${mcRoot}${mcPath}");`;
 
+const { Services: { pref } } = require("devtools-modules");
+pref("devtools.debugger.remote-timeout", 10000);
+pref("devtools.hud.loglimit", 10000);
+pref("devtools.webconsole.filter.error", true);
+pref("devtools.webconsole.filter.warn", true);
+pref("devtools.webconsole.filter.info", true);
+pref("devtools.webconsole.filter.log", true);
+pref("devtools.webconsole.filter.debug", true);
+pref("devtools.webconsole.filter.css", false);
+pref("devtools.webconsole.filter.net", false);
+pref("devtools.webconsole.filter.netxhr", false);
+pref("devtools.webconsole.ui.filterbar", false);
+pref("devtools.webconsole.inputHistoryCount", 50);
+pref("devtools.webconsole.persistlog", false);
+pref("devtools.webconsole.timestampMessages", false);
+pref("devtools.webconsole.autoMultiline", true);
+pref("devtools.webconsole.sidebarToggle", true);
+
 // Point to vendored-in files and mocks when needed.
 const requireHacker = require("require-hacker");
 requireHacker.global_hook("default", (path, module) => {
   switch (path) {
     // For Enzyme
     case "react-dom":
       return getModule("devtools/client/shared/vendor/react-dom");
     case "react-dom/server":
@@ -30,18 +48,17 @@ requireHacker.global_hook("default", (pa
     case "devtools/shared/l10n":
       return getModule(
         "devtools/client/webconsole/new-console-output/test/fixtures/LocalizationHelper");
     case "devtools/shared/plural-form":
       return getModule(
         "devtools/client/webconsole/new-console-output/test/fixtures/PluralForm");
     case "Services":
     case "Services.default":
-      return getModule(
-        "devtools/client/webconsole/new-console-output/test/fixtures/Services");
+      return `module.exports = require("devtools-modules/src/Services")`;
     case "devtools/shared/client/object-client":
       return `() => {}`;
     case "devtools/client/netmonitor/src/components/TabboxPanel":
       return "{}";
   }
 
   // We need to rewrite all the modules assuming the root is mozilla-central and give them
   // an absolute path.
--- a/devtools/client/webconsole/new-console-output/test/package.json
+++ b/devtools/client/webconsole/new-console-output/test/package.json
@@ -9,21 +9,23 @@
       "Here's the script to run tests with `npm test`. Here's what it does: ",
       " * Run mocha on components, middleware, store and utils folders, on .test.js files.",
       "   We need to specify them so we don't run unwanted tests (e.g. in node_modules).",
       " * We require jsdom-global to inject `document` and `window` objects which are",
       "   not in nodejs natively.",
       " * Finally we require mocha-test-setup where we configure Enzyme and",
       "   intercept require() calls with require-hacker and modify them if needed."
     ],
-    "test": "mocha \"./{,@(components|middleware|store|utils)/**/}*.test.js\" -r jsdom-global/register -r ./mocha-test-setup.js"
+    "test": "mocha \"./{,@(components|middleware|store|utils)/**/}*.test.js\" -r mock-local-storage -r jsdom-global/register -r ./mocha-test-setup.js"
   },
   "dependencies": {
+    "devtools-modules": "0.0.31",
     "enzyme": "^3.3.0",
     "enzyme-adapter-react-16": "^1.1.1",
     "expect": "^1.16.0",
     "jsdom": "^9.4.1",
     "jsdom-global": "^2.0.0",
     "mocha": "^5.0.1",
+    "mock-local-storage": "^1.0.5",
     "require-hacker": "^2.1.4",
     "sinon": "^1.17.5"
   }
 }
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js
@@ -5,21 +5,20 @@
 
 const expect = require("expect");
 
 const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const { messagesAdd } = require("devtools/client/webconsole/new-console-output/actions/index");
 const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
 const { getVisibleMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
-const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
+const { setupStore, getFiltersPrefs } = require("devtools/client/webconsole/new-console-output/test/helpers");
 const { FILTERS, PREFS } = require("devtools/client/webconsole/new-console-output/constants");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
-const ServicesMock = require("Services");
 
 describe("Filtering", () => {
   let store;
   let numMessages;
   // Number of messages in prepareBaseStore which are not filtered out, i.e. Evaluation
   // Results, console commands and console.groups .
   const numUnfilterableMessages = 3;
 
@@ -221,17 +220,17 @@ describe("Clear filters", () => {
       [FILTERS.DEBUG]: true,
       // changed
       [FILTERS.ERROR]: false,
       [FILTERS.CSS]: true,
       [FILTERS.NET]: true,
       [FILTERS.NETXHR]: true,
       [FILTERS.TEXT]: "foobar",
     });
-    expect(ServicesMock.prefs.testHelpers.getFiltersPrefs()).toEqual({
+    expect(getFiltersPrefs()).toEqual({
       [PREFS.FILTER.WARN]: true,
       [PREFS.FILTER.LOG]: true,
       [PREFS.FILTER.INFO]: true,
       [PREFS.FILTER.DEBUG]: true,
       [PREFS.FILTER.ERROR]: false,
       [PREFS.FILTER.CSS]: true,
       [PREFS.FILTER.NET]: true,
       [PREFS.FILTER.NETXHR]: true,
@@ -246,17 +245,17 @@ describe("Clear filters", () => {
       [FILTERS.INFO]: true,
       [FILTERS.LOG]: true,
       [FILTERS.NET]: false,
       [FILTERS.NETXHR]: false,
       [FILTERS.WARN]: true,
       [FILTERS.TEXT]: "",
     });
 
-    expect(ServicesMock.prefs.testHelpers.getFiltersPrefs()).toEqual({
+    expect(getFiltersPrefs()).toEqual({
       [PREFS.FILTER.CSS]: false,
       [PREFS.FILTER.DEBUG]: true,
       [PREFS.FILTER.ERROR]: true,
       [PREFS.FILTER.INFO]: true,
       [PREFS.FILTER.LOG]: true,
       [PREFS.FILTER.NET]: false,
       [PREFS.FILTER.NETXHR]: false,
       [PREFS.FILTER.WARN]: true,
@@ -285,17 +284,17 @@ describe("Resets filters", () => {
       [FILTERS.ERROR]: false,
       [FILTERS.LOG]: false,
       [FILTERS.CSS]: true,
       [FILTERS.NET]: true,
       [FILTERS.NETXHR]: true,
       [FILTERS.TEXT]: "foobar",
     });
 
-    expect(ServicesMock.prefs.testHelpers.getFiltersPrefs()).toEqual({
+    expect(getFiltersPrefs()).toEqual({
       [PREFS.FILTER.WARN]: true,
       [PREFS.FILTER.INFO]: true,
       [PREFS.FILTER.DEBUG]: true,
       [PREFS.FILTER.ERROR]: false,
       [PREFS.FILTER.LOG]: false,
       [PREFS.FILTER.CSS]: true,
       [PREFS.FILTER.NET]: true,
       [PREFS.FILTER.NETXHR]: true,
@@ -312,17 +311,17 @@ describe("Resets filters", () => {
       [FILTERS.DEBUG]: true,
       [FILTERS.TEXT]: "",
       // non-default filters weren't changed
       [FILTERS.CSS]: true,
       [FILTERS.NET]: true,
       [FILTERS.NETXHR]: true,
     });
 
-    expect(ServicesMock.prefs.testHelpers.getFiltersPrefs()).toEqual({
+    expect(getFiltersPrefs()).toEqual({
       [PREFS.FILTER.ERROR]: true,
       [PREFS.FILTER.WARN]: true,
       [PREFS.FILTER.LOG]: true,
       [PREFS.FILTER.INFO]: true,
       [PREFS.FILTER.DEBUG]: true,
       [PREFS.FILTER.CSS]: true,
       [PREFS.FILTER.NET]: true,
       [PREFS.FILTER.NETXHR]: true,
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -312,37 +312,44 @@ describe("Message reducer:", () => {
       packet = stubPackets.get("console.log(undefined)");
       dispatch(actions.messagesAdd([packet]));
 
       // repeatById should now be empty.
       expect(getAllRepeatById(getState())).toEqual({});
     });
 
     it("properly limits number of messages", () => {
-      const { dispatch, getState } = setupStore();
+      const logLimit = 1000;
+      const { dispatch, getState } = setupStore([], {
+        storeOptions: {
+          logLimit,
+        }
+      });
 
-      const logLimit = 1000;
       const packet = clonePacket(stubPackets.get("console.log(undefined)"));
 
       for (let i = 1; i <= logLimit + 2; i++) {
         packet.message.arguments = [`message num ${i}`];
         dispatch(actions.messagesAdd([packet]));
       }
 
       const messages = getAllMessagesById(getState());
       expect(messages.size).toBe(logLimit);
       expect(getFirstMessage(getState()).parameters[0]).toBe(`message num 3`);
       expect(getLastMessage(getState()).parameters[0])
         .toBe(`message num ${logLimit + 2}`);
     });
 
     it("properly limits number of messages when there are nested groups", () => {
-      const { dispatch, getState } = setupStore();
-
       const logLimit = 1000;
+      const { dispatch, getState } = setupStore([], {
+        storeOptions: {
+          logLimit,
+        }
+      });
 
       const packet = clonePacket(stubPackets.get("console.log(undefined)"));
       const packetGroup = clonePacket(stubPackets.get("console.group('bar')"));
       const packetGroupEnd = clonePacket(stubPackets.get("console.groupEnd()"));
 
       packetGroup.message.arguments = [`group-1`];
       dispatch(actions.messagesAdd([packetGroup]));
       packetGroup.message.arguments = [`group-1-1`];
--- a/devtools/client/webconsole/new-console-output/utils/moz.build
+++ b/devtools/client/webconsole/new-console-output/utils/moz.build
@@ -3,9 +3,10 @@
 # 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/.
 
 DevToolsModules(
     'context-menu.js',
     'id-generator.js',
     'messages.js',
     'object-inspector.js',
+    'prefs.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/utils/prefs.js
@@ -0,0 +1,44 @@
+/* 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");
+
+function getPreferenceName(hud, suffix) {
+  if (!suffix) {
+    console.error("Suffix shouldn't be falsy", {suffix});
+    return null;
+  }
+
+  if (!hud) {
+    console.error("hud shouldn't be falsy", {hud});
+    return null;
+  }
+
+  if (suffix.startsWith("devtools.")) {
+    // We don't have a suffix but a full pref name. Let's return it.
+    return suffix;
+  }
+
+  const component = hud.isBrowserConsole
+    ? "browserconsole"
+    : "webconsole";
+  return `devtools.${component}.${suffix}`;
+}
+
+function getPrefsService(hud) {
+  const getPrefName = pref => getPreferenceName(hud, pref);
+
+  return {
+    getBoolPref: (pref, deflt) => Services.prefs.getBoolPref(getPrefName(pref), deflt),
+    getIntPref: (pref, deflt) => Services.prefs.getIntPref(getPrefName(pref), deflt),
+    setBoolPref: (pref, value) => Services.prefs.setBoolPref(getPrefName(pref), value),
+    clearUserPref: pref => Services.prefs.clearUserPref(getPrefName(pref)),
+    getPrefName,
+  };
+}
+
+module.exports = {
+  getPrefsService,
+};
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -203,22 +203,28 @@ ShadowRoot::RemoveSlot(HTMLSlotElement* 
       }
     }
   }
 }
 
 void
 ShadowRoot::StyleSheetChanged()
 {
-  mProtoBinding->FlushSkinSheets();
+  nsIDocument* doc = OwnerDoc();
 
-  if (nsIPresShell* shell = OwnerDoc()->GetShell()) {
-    OwnerDoc()->BeginUpdate(UPDATE_STYLE);
-    shell->RecordShadowStyleChange(this);
-    OwnerDoc()->EndUpdate(UPDATE_STYLE);
+  if (doc->IsStyledByServo()) {
+    mProtoBinding->SyncServoStyles();
+  } else {
+    mProtoBinding->FlushSkinSheets();
+  }
+
+  if (nsIPresShell* shell = doc->GetShell()) {
+    doc->BeginUpdate(UPDATE_STYLE);
+    shell->RecordShadowStyleChange(*this);
+    doc->EndUpdate(UPDATE_STYLE);
   }
 }
 
 void
 ShadowRoot::InsertSheet(StyleSheet* aSheet,
                         nsIContent* aLinkingContent)
 {
   nsCOMPtr<nsIStyleSheetLinkingElement>
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -11876,16 +11876,20 @@ nsIDocument::DocAddSizeOfExcludingThis(n
     aSizes.mPropertyTablesSize +=
       mExtraPropertyTables[i]->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
   }
 
   if (EventListenerManager* elm = GetExistingListenerManager()) {
     aSizes.mDOMEventListenersCount += elm->ListenerCount();
   }
 
+  if (mNodeInfoManager) {
+    mNodeInfoManager->AddSizeOfIncludingThis(aSizes);
+  }
+
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - many!
 }
 
 void
 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const
 {
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -24,16 +24,17 @@
 #include "nsGkAtoms.h"
 #include "nsComponentManagerUtils.h"
 #include "nsLayoutStatics.h"
 #include "nsBindingManager.h"
 #include "nsHashKeys.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsNameSpaceManager.h"
 #include "nsDocument.h"
+#include "nsWindowSizes.h"
 #include "NullPrincipal.h"
 
 using namespace mozilla;
 using mozilla::dom::NodeInfo;
 
 #include "mozilla/Logging.h"
 
 static LazyLogModule gNodeInfoManagerLeakPRLog("NodeInfoManagerLeak");
@@ -499,8 +500,23 @@ nsNodeInfoManager::InternalMathMLEnabled
   // If the mathml.disabled pref. is true, convert all MathML nodes into
   // disabled MathML nodes by swapping the namespace.
   nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
   bool conclusion = ((nsmgr && !nsmgr->mMathMLDisabled) ||
                      nsContentUtils::IsSystemPrincipal(mPrincipal));
   mMathMLEnabled = conclusion ? eTriTrue : eTriFalse;
   return conclusion;
 }
+
+void
+nsNodeInfoManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
+{
+  aSizes.mDOMOtherSize += aSizes.mState.mMallocSizeOf(this);
+
+  if (mBindingManager) {
+    aSizes.mBindingsSize +=
+      mBindingManager->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
+  }
+
+  // Measurement of the following members may be added later if DMD finds it
+  // is worthwhile:
+  // - mNodeInfoHash
+}
--- a/dom/base/nsNodeInfoManager.h
+++ b/dom/base/nsNodeInfoManager.h
@@ -17,16 +17,17 @@
 #include "nsStringFwd.h"
 #include "plhash.h"                       // for typedef PLHashNumber
 
 class nsBindingManager;
 class nsAtom;
 class nsIDocument;
 class nsIDOMDocumentType;
 class nsIPrincipal;
+class nsWindowSizes;
 struct PLHashEntry;
 struct PLHashTable;
 template<class T> struct already_AddRefed;
 
 namespace mozilla {
 namespace dom {
 class NodeInfo;
 } // namespace dom
@@ -131,16 +132,18 @@ public:
    */
   bool MathMLEnabled()
   {
     return mMathMLEnabled == eTriTrue
              ? true
              : mMathMLEnabled == eTriFalse ? false : InternalMathMLEnabled();
   }
 
+  void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const;
+
 protected:
   friend class nsDocument;
   friend class nsXULPrototypeDocument;
   friend nsresult NS_NewDOMDocumentType(nsIDOMDocumentType** ,
                                         nsNodeInfoManager *,
                                         nsAtom *,
                                         const nsAString& ,
                                         const nsAString& ,
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -426,16 +426,19 @@ CollectWindowReports(nsGlobalWindowInner
               "elements.");
 
   REPORT_SIZE("/layout/computed-values/visited", mLayoutComputedValuesVisited,
               "Memory used by ComputedValues objects used for visited styles.");
 
   REPORT_SIZE("/property-tables", mPropertyTablesSize,
               "Memory used for the property tables within a window.");
 
+  REPORT_SIZE("/bindings", mBindingsSize,
+              "Memory used by bindings within a window.");
+
   REPORT_COUNT("/dom/event-targets", mDOMEventTargetsCount,
                "Number of non-node event targets in the event targets table "
                "in a window's DOM, such as XHRs.");
 
   REPORT_COUNT("/dom/event-listeners", mDOMEventListenersCount,
                "Number of event listeners in a window, including event "
                "listeners on nodes and other event targets.");
 
--- a/dom/base/nsWindowSizes.h
+++ b/dom/base/nsWindowSizes.h
@@ -190,16 +190,17 @@ class nsWindowSizes
   macro(Style, mLayoutServoElementDataObjects) \
   macro(Other, mLayoutTextRunsSize) \
   macro(Other, mLayoutPresContextSize) \
   macro(Other, mLayoutFramePropertiesSize) \
   macro(Style, mLayoutComputedValuesDom) \
   macro(Style, mLayoutComputedValuesNonDom) \
   macro(Style, mLayoutComputedValuesVisited) \
   macro(Other, mPropertyTablesSize) \
+  macro(Other, mBindingsSize) \
 
 public:
   explicit nsWindowSizes(mozilla::SizeOfState& aState)
     :
       FOR_EACH_SIZE(ZERO_SIZE)
       mDOMEventTargetsCount(0),
       mDOMEventListenersCount(0),
       mArenaSizes(),
--- a/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.cpp
@@ -2,19 +2,23 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FFVPXRuntimeLinker.h"
 #include "FFmpegLibWrapper.h"
 #include "FFmpegLog.h"
-#include "nsIFile.h"
+#include "mozilla/FileUtils.h"
+#include "nsLocalFile.h"
 #include "prmem.h"
 #include "prlink.h"
+#ifdef XP_WIN
+#include <windows.h>
+#endif
 
 // We use a known symbol located in lgpllibs to determine its location.
 // soundtouch happens to be always included in lgpllibs
 #include "soundtouch/SoundTouch.h"
 
 namespace mozilla {
 
 template <int V> class FFmpegDecoderModule
@@ -24,83 +28,86 @@ public:
 };
 
 static FFmpegLibWrapper sFFVPXLib;
 
 FFVPXRuntimeLinker::LinkStatus FFVPXRuntimeLinker::sLinkStatus =
   LinkStatus_INIT;
 
 static PRLibrary*
-MozAVLink(const char* aName)
+MozAVLink(nsIFile* aFile)
 {
   PRLibSpec lspec;
+  PathString path = aFile->NativePath();
+#ifdef XP_WIN
+  lspec.type = PR_LibSpec_PathnameU;
+  lspec.value.pathname_u = path.get();
+#else
   lspec.type = PR_LibSpec_Pathname;
-  lspec.value.pathname = aName;
+  lspec.value.pathname = path.get();
+#endif
 #ifdef MOZ_WIDGET_ANDROID
   PRLibrary* lib = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_GLOBAL);
 #else
   PRLibrary* lib = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
 #endif
   if (!lib) {
-    FFMPEG_LOG("unable to load library %s", aName);
+    FFMPEG_LOG("unable to load library %s", aFile->HumanReadablePath().get());
   }
   return lib;
 }
 
 /* static */ bool
 FFVPXRuntimeLinker::Init()
 {
   if (sLinkStatus) {
     return sLinkStatus == LinkStatus_SUCCEEDED;
   }
 
   sLinkStatus = LinkStatus_FAILED;
 
   // We retrieve the path of the lgpllibs library as this is where mozavcodec
   // and mozavutil libs are located.
-  char* lgpllibsname = PR_GetLibraryName(nullptr, "lgpllibs");
-  if (!lgpllibsname) {
+  PathString lgpllibsname = GetLibraryName(nullptr, "lgpllibs");
+  if (lgpllibsname.IsEmpty()) {
     return false;
   }
-  char* path =
-    PR_GetLibraryFilePathname(lgpllibsname,
-                              (PRFuncPtr)&soundtouch::SoundTouch::getVersionId);
-  PR_FreeLibraryName(lgpllibsname);
-  if (!path) {
+  PathString path =
+    GetLibraryFilePathname(lgpllibsname.get(),
+                           (PRFuncPtr)&soundtouch::SoundTouch::getVersionId);
+  if (path.IsEmpty()) {
     return false;
   }
-  nsCOMPtr<nsIFile> xulFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
-  if (!xulFile ||
-      NS_FAILED(xulFile->InitWithNativePath(nsDependentCString(path)))) {
-    PR_Free(path); // PR_GetLibraryFilePathname() uses PR_Malloc().
+  RefPtr<nsLocalFile> xulFile = new nsLocalFile(path);
+  if (xulFile->NativePath().IsEmpty()) {
     return false;
   }
-  PR_Free(path); // PR_GetLibraryFilePathname() uses PR_Malloc().
 
   nsCOMPtr<nsIFile> rootDir;
   if (NS_FAILED(xulFile->GetParent(getter_AddRefs(rootDir))) || !rootDir) {
     return false;
   }
-  nsAutoCString rootPath;
-  if (NS_FAILED(rootDir->GetNativePath(rootPath))) {
+  PathString rootPath = rootDir->NativePath();
+
+  /* Get the platform-dependent library name of the module */
+  PathString libname = GetLibraryName(rootPath.get(), "mozavutil");
+  if (libname.IsEmpty()) {
     return false;
   }
-
-  char* libname = NULL;
-  /* Get the platform-dependent library name of the module */
-  libname = PR_GetLibraryName(rootPath.get(), "mozavutil");
-  if (!libname) {
+  RefPtr<nsLocalFile> libFile = new nsLocalFile(libname);
+  if (libFile->NativePath().IsEmpty()) {
     return false;
   }
-  sFFVPXLib.mAVUtilLib = MozAVLink(libname);
-  PR_FreeLibraryName(libname);
-  libname = PR_GetLibraryName(rootPath.get(), "mozavcodec");
-  if (libname) {
-    sFFVPXLib.mAVCodecLib = MozAVLink(libname);
-    PR_FreeLibraryName(libname);
+  sFFVPXLib.mAVUtilLib = MozAVLink(libFile);
+  libname = GetLibraryName(rootPath.get(), "mozavcodec");
+  if (!libname.IsEmpty()) {
+    libFile = new nsLocalFile(libname);
+    if (!libFile->NativePath().IsEmpty()) {
+      sFFVPXLib.mAVCodecLib = MozAVLink(libFile);
+    }
   }
   if (sFFVPXLib.Link() == FFmpegLibWrapper::LinkResult::Success) {
     sLinkStatus = LinkStatus_SUCCEEDED;
     return true;
   }
   return false;
 }
 
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -408,27 +408,28 @@ MediaEngineRemoteVideoSource::NumCapabil
 
   // The default for devices that don't return discrete capabilities: treat
   // them as supporting all capabilities orthogonally. E.g. screensharing.
   // CaptureCapability defaults key values to 0, which means accept any value.
   mHardcodedCapabilities.AppendElement(webrtc::CaptureCapability());
   return mHardcodedCapabilities.Length(); // 1
 }
 
-void
-MediaEngineRemoteVideoSource::GetCapability(size_t aIndex,
-                                            webrtc::CaptureCapability& aOut) const
+webrtc::CaptureCapability
+MediaEngineRemoteVideoSource::GetCapability(size_t aIndex) const
 {
   AssertIsOnOwningThread();
+  webrtc::CaptureCapability result;
   if (!mHardcodedCapabilities.IsEmpty()) {
     MOZ_ASSERT(aIndex < mHardcodedCapabilities.Length());
-    aOut = mHardcodedCapabilities.SafeElementAt(aIndex, webrtc::CaptureCapability());
+    result = mHardcodedCapabilities.SafeElementAt(aIndex, webrtc::CaptureCapability());
   }
   camera::GetChildAndCall(&camera::CamerasChild::GetCaptureCapability,
-                          mCapEngine, mUniqueId.get(), aIndex, aOut);
+                          mCapEngine, mUniqueId.get(), aIndex, result);
+  return result;
 }
 
 void
 MediaEngineRemoteVideoSource::Pull(const RefPtr<const AllocationHandle>& aHandle,
                                    const RefPtr<SourceMediaStream>& aStream,
                                    TrackID aTrackID,
                                    StreamTime aDesiredTime,
                                    const PrincipalHandle& aPrincipalHandle)
@@ -632,55 +633,53 @@ MediaEngineRemoteVideoSource::GetFeasibi
     uint64_t(aCandidate.maxFPS ? H::FeasibilityDistance(double(aCandidate.maxFPS),
                                                         aConstraints.mFrameRate) : 0);
   return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
 }
 
 // Find best capability by removing inferiors. May leave >1 of equal distance
 
 /* static */ void
-MediaEngineRemoteVideoSource::TrimLessFitCandidates(nsTArray<CapabilityCandidate>& set)
+MediaEngineRemoteVideoSource::TrimLessFitCandidates(nsTArray<CapabilityCandidate>& aSet)
 {
   uint32_t best = UINT32_MAX;
-  for (auto& candidate : set) {
+  for (auto& candidate : aSet) {
     if (best > candidate.mDistance) {
       best = candidate.mDistance;
     }
   }
-  for (size_t i = 0; i < set.Length();) {
-    if (set[i].mDistance > best) {
-      set.RemoveElementAt(i);
+  for (size_t i = 0; i < aSet.Length();) {
+    if (aSet[i].mDistance > best) {
+      aSet.RemoveElementAt(i);
     } else {
       ++i;
     }
   }
-  MOZ_ASSERT(set.Length());
+  MOZ_ASSERT(aSet.Length());
 }
 
 uint32_t
 MediaEngineRemoteVideoSource::GetBestFitnessDistance(
     const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
     const nsString& aDeviceId) const
 {
   AssertIsOnOwningThread();
 
   size_t num = NumCapabilities();
-
   nsTArray<CapabilityCandidate> candidateSet;
   for (size_t i = 0; i < num; i++) {
-    candidateSet.AppendElement(i);
+    candidateSet.AppendElement(CapabilityCandidate(GetCapability(i)));
   }
 
   bool first = true;
   for (const NormalizedConstraintSet* ns : aConstraintSets) {
     for (size_t i = 0; i < candidateSet.Length();  ) {
       auto& candidate = candidateSet[i];
-      webrtc::CaptureCapability cap;
-      GetCapability(candidate.mIndex, cap);
-      uint32_t distance = GetFitnessDistance(cap, *ns, aDeviceId);
+      uint32_t distance =
+        GetFitnessDistance(candidate.mCapability, *ns, aDeviceId);
       if (distance == UINT32_MAX) {
         candidateSet.RemoveElementAt(i);
       } else {
         ++i;
         if (first) {
           candidate.mDistance = distance;
         }
       }
@@ -808,53 +807,98 @@ MediaEngineRemoteVideoSource::ChooseCapa
       aCapability.maxFPS =
         c.mFrameRate.Clamp(c.mFrameRate.mIdeal.valueOr(aPrefs.mFPS));
       return true;
     }
     default:
       break;
   }
 
+  nsTArray<CapabilityCandidate> candidateSet;
   size_t num = NumCapabilities();
+  for (size_t i = 0; i < num; i++) {
+    candidateSet.AppendElement(CapabilityCandidate(GetCapability(i)));
+  }
+
+  if (!mHardcodedCapabilities.IsEmpty() &&
+      mMediaSource == MediaSourceEnum::Camera) {
+    // We have a hardcoded capability, which means this camera didn't report
+    // discrete capabilities. It might still allow a ranged capability, so we
+    // add a couple of default candidates based on prefs and constraints.
+    // The chosen candidate will be propagated to StartCapture() which will fail
+    // for an invalid candidate.
+    MOZ_DIAGNOSTIC_ASSERT(mHardcodedCapabilities.Length() == 1);
+    MOZ_DIAGNOSTIC_ASSERT(candidateSet.Length() == 1);
+    candidateSet.Clear();
 
-  nsTArray<CapabilityCandidate> candidateSet;
-  for (size_t i = 0; i < num; i++) {
-    candidateSet.AppendElement(i);
+    FlattenedConstraints c(aConstraints);
+    // Reuse the code across both the low-definition (`false`) pref and
+    // the high-definition (`true`) pref.
+    // If there are constraints we try to satisfy them but we default to prefs.
+    // Note that since constraints are from content and can literally be
+    // anything we put (rather generous) caps on them.
+    for (bool isHd : {false, true}) {
+      webrtc::CaptureCapability cap;
+      int32_t prefWidth = aPrefs.GetWidth(isHd);
+      int32_t prefHeight = aPrefs.GetHeight(isHd);
+
+      cap.width = c.mWidth.Get(prefWidth);
+      cap.width = std::max(0, std::min(cap.width, 7680));
+
+      cap.height = c.mHeight.Get(prefHeight);
+      cap.height = std::max(0, std::min(cap.height, 4320));
+
+      cap.maxFPS = c.mFrameRate.Get(aPrefs.mFPS);
+      cap.maxFPS = std::max(0, std::min(cap.maxFPS, 480));
+
+      if (cap.width != prefWidth) {
+        // Width was affected by constraints.
+        // We'll adjust the height too so the aspect ratio is retained.
+        cap.height = cap.width * prefHeight / prefWidth;
+      } else if (cap.height != prefHeight) {
+        // Height was affected by constraints but not width.
+        // We'll adjust the width too so the aspect ratio is retained.
+        cap.width = cap.height * prefWidth / prefHeight;
+      }
+
+      if (candidateSet.Contains(cap, CapabilityComparator())) {
+        continue;
+      }
+      LogCapability("Hardcoded capability", cap, 0);
+      candidateSet.AppendElement(CapabilityCandidate(Move(cap)));
+    }
   }
 
   // First, filter capabilities by required constraints (min, max, exact).
 
   for (size_t i = 0; i < candidateSet.Length();) {
     auto& candidate = candidateSet[i];
-    webrtc::CaptureCapability cap;
-    GetCapability(candidate.mIndex, cap);
-    candidate.mDistance = GetDistance(cap, aConstraints, aDeviceId, aCalculate);
-    LogCapability("Capability", cap, candidate.mDistance);
+    candidate.mDistance =
+      GetDistance(candidate.mCapability, aConstraints, aDeviceId, aCalculate);
+    LogCapability("Capability", candidate.mCapability, candidate.mDistance);
     if (candidate.mDistance == UINT32_MAX) {
       candidateSet.RemoveElementAt(i);
     } else {
       ++i;
     }
   }
 
-  if (!candidateSet.Length()) {
-    LOG(("failed to find capability match from %zu choices",num));
+  if (candidateSet.IsEmpty()) {
+    LOG(("failed to find capability match from %zu choices", candidateSet.Length()));
     return false;
   }
 
   // Filter further with all advanced constraints (that don't overconstrain).
 
   for (const auto &cs : aConstraints.mAdvanced) {
     nsTArray<CapabilityCandidate> rejects;
     for (size_t i = 0; i < candidateSet.Length();) {
-      auto& candidate = candidateSet[i];
-      webrtc::CaptureCapability cap;
-      GetCapability(candidate.mIndex, cap);
-      if (GetDistance(cap, cs, aDeviceId, aCalculate) == UINT32_MAX) {
-        rejects.AppendElement(candidate);
+      if (GetDistance(candidateSet[i].mCapability,
+                      cs, aDeviceId, aCalculate) == UINT32_MAX) {
+        rejects.AppendElement(candidateSet[i]);
         candidateSet.RemoveElementAt(i);
       } else {
         ++i;
       }
     }
     if (!candidateSet.Length()) {
       candidateSet.AppendElements(Move(rejects));
     }
@@ -872,41 +916,39 @@ MediaEngineRemoteVideoSource::ChooseCapa
   {
     MediaTrackConstraintSet prefs;
     prefs.mWidth.SetAsLong() = aPrefs.GetWidth();
     prefs.mHeight.SetAsLong() = aPrefs.GetHeight();
     prefs.mFrameRate.SetAsDouble() = aPrefs.mFPS;
     NormalizedConstraintSet normPrefs(prefs, false);
 
     for (auto& candidate : candidateSet) {
-      webrtc::CaptureCapability cap;
-      GetCapability(candidate.mIndex, cap);
-      candidate.mDistance = GetDistance(cap, normPrefs, aDeviceId, aCalculate);
+      candidate.mDistance =
+        GetDistance(candidate.mCapability, normPrefs, aDeviceId, aCalculate);
     }
     TrimLessFitCandidates(candidateSet);
   }
 
   // Any remaining multiples all have the same distance, but may vary on
   // format. Some formats are more desirable for certain use like WebRTC.
   // E.g. I420 over RGB24 can remove a needless format conversion.
 
   bool found = false;
   for (auto& candidate : candidateSet) {
-    webrtc::CaptureCapability cap;
-    GetCapability(candidate.mIndex, cap);
+    const webrtc::CaptureCapability& cap = candidate.mCapability;
     if (cap.rawType == webrtc::RawVideoType::kVideoI420 ||
         cap.rawType == webrtc::RawVideoType::kVideoYUY2 ||
         cap.rawType == webrtc::RawVideoType::kVideoYV12) {
       aCapability = cap;
       found = true;
       break;
     }
   }
   if (!found) {
-    GetCapability(candidateSet[0].mIndex, aCapability);
+    aCapability = candidateSet[0].mCapability;
   }
 
   LogCapability("Chosen capability", aCapability, sameDistance);
   return true;
 }
 
 void
 MediaEngineRemoteVideoSource::GetSettings(MediaTrackSettings& aOutSettings) const
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.h
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.h
@@ -64,23 +64,33 @@ enum DistanceCalculation {
  * The WebRTC implementation of the MediaEngine interface.
  */
 class MediaEngineRemoteVideoSource : public MediaEngineSource,
                                      public camera::FrameRelay
 {
   ~MediaEngineRemoteVideoSource() = default;
 
   struct CapabilityCandidate {
-    explicit CapabilityCandidate(uint8_t index, uint32_t distance = 0)
-    : mIndex(index), mDistance(distance) {}
+    explicit CapabilityCandidate(webrtc::CaptureCapability&& aCapability,
+                                 uint32_t aDistance = 0)
+    : mCapability(Forward<webrtc::CaptureCapability>(aCapability))
+    , mDistance(aDistance) {}
 
-    size_t mIndex;
+    const webrtc::CaptureCapability mCapability;
     uint32_t mDistance;
   };
-  typedef nsTArray<CapabilityCandidate> CapabilitySet;
+
+  class CapabilityComparator {
+  public:
+    bool Equals(const CapabilityCandidate& aCandidate,
+                const webrtc::CaptureCapability& aCapability) const
+    {
+      return aCandidate.mCapability == aCapability;
+    }
+  };
 
   bool ChooseCapability(const NormalizedConstraints& aConstraints,
                         const MediaEnginePrefs& aPrefs,
                         const nsString& aDeviceId,
                         webrtc::CaptureCapability& aCapability,
                         const DistanceCalculation aCalculate);
 
   uint32_t GetDistance(const webrtc::CaptureCapability& aCandidate,
@@ -91,17 +101,17 @@ class MediaEngineRemoteVideoSource : pub
   uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
                               const NormalizedConstraintSet &aConstraints,
                               const nsString& aDeviceId) const;
 
   uint32_t GetFeasibilityDistance(const webrtc::CaptureCapability& aCandidate,
                               const NormalizedConstraintSet &aConstraints,
                               const nsString& aDeviceId) const;
 
-  static void TrimLessFitCandidates(CapabilitySet& set);
+  static void TrimLessFitCandidates(nsTArray<CapabilityCandidate>& aSet);
 
   uint32_t GetBestFitnessDistance(
       const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) const override;
 
 public:
   MediaEngineRemoteVideoSource(int aIndex,
                                camera::CaptureEngine aCapEngine,
@@ -163,22 +173,21 @@ private:
   /**
    * Returns the number of capabilities for the underlying device.
    *
    * Guaranteed to return at least one capability.
    */
   size_t NumCapabilities() const;
 
   /**
-   * Fills `aOut` with the capability properties of the device capability with
-   * index `aIndex`.
+   * Returns the capability with index `aIndex` for our assigned device.
    *
    * It is an error to call this with `aIndex >= NumCapabilities()`.
    */
-  void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut) const;
+  webrtc::CaptureCapability GetCapability(size_t aIndex) const;
 
   int mCaptureIndex;
   const dom::MediaSourceEnum mMediaSource; // source of media (camera | application | screen)
   const camera::CaptureEngine mCapEngine;
   const bool mScary;
 
   // mMutex protects certain members on 3 threads:
   // MediaManager, Cameras IPC and MediaStreamGraph.
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -1109,8 +1109,32 @@ nsBindingManager::FindNestedSingleInsert
     if (newParent == parent) {
       break;
     }
     parent = newParent;
   }
 
   return parent;
 }
+
+size_t
+nsBindingManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = aMallocSizeOf(this);
+
+#define SHALLOW_SIZE_INCLUDING(field_) \
+  n += field_ ? field_->ShallowSizeOfIncludingThis(aMallocSizeOf) : 0;
+  SHALLOW_SIZE_INCLUDING(mBoundContentSet);
+  SHALLOW_SIZE_INCLUDING(mWrapperTable);
+  SHALLOW_SIZE_INCLUDING(mLoadingDocTable);
+#undef SHALLOW_SIZE_INCLUDING
+  n += mAttachedStack.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+  if (mDocumentTable) {
+    n += mDocumentTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
+    for (auto iter = mDocumentTable->Iter(); !iter.Done(); iter.Next()) {
+      nsXBLDocumentInfo* docInfo = iter.UserData();
+      n += docInfo->SizeOfIncludingThis(aMallocSizeOf);
+    }
+  }
+
+  return n;
+}
--- a/dom/xbl/nsBindingManager.h
+++ b/dom/xbl/nsBindingManager.h
@@ -14,16 +14,17 @@
 #include "nsInterfaceHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsURIHashKey.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsXBLBinding.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "mozilla/MediaFeatureChange.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/EventStates.h"
 
 struct ElementDependentRuleProcessorData;
 class nsIXPConnectWrappedJS;
 class nsAtom;
 class nsIDOMNodeList;
 class nsIDocument;
@@ -168,16 +169,18 @@ public:
   // points and their insertion parents.
   void ClearInsertionPointsRecursively(nsIContent* aContent);
 
   // Called when the document is going away
   void DropDocumentReference();
 
   nsIContent* FindNestedSingleInsertionPoint(nsIContent* aContainer, bool* aMulti);
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
   // Enumerate each bound content's bindings (including its base bindings)
   // in mBoundContentSet. Return false from the callback to stop enumeration.
   using BoundContentBindingCallback = std::function<bool (nsXBLBinding*)>;
   bool EnumerateBoundContentBindings(
     const BoundContentBindingCallback& aCallback) const;
 
 protected:
   nsIXPConnectWrappedJS* GetWrappedJS(nsIContent* aContent);
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -320,8 +320,22 @@ nsXBLDocumentInfo::FlushSkinStylesheets(
 #ifdef DEBUG
 void
 AssertInCompilationScope()
 {
   AutoJSContext cx;
   MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
 }
 #endif
+
+size_t
+nsXBLDocumentInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = aMallocSizeOf(this);
+  if (mBindingTable) {
+    n += mBindingTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
+    for (auto iter = mBindingTable->Iter(); !iter.Done(); iter.Next()) {
+      nsXBLPrototypeBinding* binding = iter.UserData();
+      n += binding->SizeOfIncludingThis(aMallocSizeOf);
+    }
+  }
+  return n;
+}
--- a/dom/xbl/nsXBLDocumentInfo.h
+++ b/dom/xbl/nsXBLDocumentInfo.h
@@ -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/. */
 
 #ifndef nsXBLDocumentInfo_h__
 #define nsXBLDocumentInfo_h__
 
 #include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsWeakReference.h"
 #include "nsIDocument.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsXBLPrototypeBinding;
 
@@ -44,16 +45,18 @@ public:
 
   bool IsChrome() { return mIsChrome; }
 
   void MarkInCCGeneration(uint32_t aGeneration);
 
   static nsresult ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo,
                                         nsIDocument* aBoundDocument);
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsXBLDocumentInfo)
 
 private:
   virtual ~nsXBLDocumentInfo();
 
   nsCOMPtr<nsIDocument> mDocument;
   bool mScriptAccess;
   bool mIsChrome;
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -77,27 +77,41 @@ public:
 
   nsAtom* GetSrcAttribute() { return mSrcAttribute; }
   nsAtom* GetDstAttribute() { return mDstAttribute; }
   int32_t GetDstNameSpace() { return mDstNameSpace; }
 
   Element* GetElement() { return mElement; }
 
   nsXBLAttributeEntry* GetNext() { return mNext; }
+  const nsXBLAttributeEntry* GetNext() const { return mNext; }
   void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; }
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
 protected:
   Element* mElement;
 
   RefPtr<nsAtom> mSrcAttribute;
   RefPtr<nsAtom> mDstAttribute;
   int32_t mDstNameSpace;
   nsXBLAttributeEntry* mNext;
 };
 
+size_t
+nsXBLAttributeEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = 0;
+  for (const nsXBLAttributeEntry* entry = this;
+       entry; entry = entry->GetNext()) {
+    n += aMallocSizeOf(entry);
+  }
+  return n;
+}
+
 // =============================================================================
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLPrototypeBinding::nsXBLPrototypeBinding()
 : mImplementation(nullptr),
   mBaseBinding(nullptr),
@@ -1693,8 +1707,39 @@ nsXBLPrototypeBinding::HasStyleSheets() 
 void
 nsXBLPrototypeBinding::AppendStyleSheetsTo(
                                       nsTArray<StyleSheet*>& aResult) const
 {
   if (mResources) {
     mResources->AppendStyleSheetsTo(aResult);
   }
 }
+
+size_t
+nsXBLPrototypeBinding::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = aMallocSizeOf(this);
+  n += mResources ? mResources->SizeOfIncludingThis(aMallocSizeOf) : 0;
+
+  if (mAttributeTable) {
+    n += mAttributeTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
+    for (auto iter = mAttributeTable->Iter(); !iter.Done(); iter.Next()) {
+      InnerAttributeTable* table = iter.UserData();
+      n += table->ShallowSizeOfIncludingThis(aMallocSizeOf);
+      for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
+        n += iter2.UserData()->SizeOfIncludingThis(aMallocSizeOf);
+      }
+    }
+  }
+
+  n += mInterfaceTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  n += mKeyHandlers.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+  // Measurement of the following members may be added later if DMD finds it
+  // is worthwhile:
+  // - mBindingURI
+  // - mAlternateBindingURI
+  // - mPrototypeHandler
+  // - mBaseBindingURI
+  // - mImplementation
+
+  return n;
+}
--- a/dom/xbl/nsXBLPrototypeBinding.h
+++ b/dom/xbl/nsXBLPrototypeBinding.h
@@ -14,16 +14,17 @@
 #include "nsICSSLoaderObserver.h"
 #include "nsInterfaceHashtable.h"
 #include "nsWeakReference.h"
 #include "nsXBLDocumentInfo.h"
 #include "nsXBLProtoImpl.h"
 #include "nsXBLProtoImplMethod.h"
 #include "nsXBLPrototypeHandler.h"
 #include "nsXBLPrototypeResources.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/StyleSheet.h"
 
 class nsAtom;
 class nsIContent;
 class nsIDocument;
 class nsXBLAttributeEntry;
 class nsXBLBinding;
@@ -130,20 +131,28 @@ public:
   mozilla::StyleSheet* StyleSheetAt(size_t aIndex) const;
   size_t SheetCount() const;
   bool HasStyleSheets() const;
   void AppendStyleSheetsTo(nsTArray<mozilla::StyleSheet*>& aResult) const;
 
 #ifdef MOZ_OLD_STYLE
   nsIStyleRuleProcessor* GetRuleProcessor();
 #endif
+
   const RawServoAuthorStyles* GetServoStyles() const
   {
     return mResources ? mResources->GetServoStyles() : nullptr;
   }
+
+  void SyncServoStyles()
+  {
+    MOZ_ASSERT(mResources);
+    mResources->SyncServoStyles();
+  }
+
   RawServoAuthorStyles* GetServoStyles()
   {
     return mResources
       ? const_cast<RawServoAuthorStyles*>(mResources->GetServoStyles())
       : nullptr;
   }
 
   mozilla::ServoStyleRuleMap* GetServoStyleRuleMap()
@@ -265,16 +274,18 @@ public:
                 nsXBLDocumentInfo* aInfo,
                 mozilla::dom::Element* aElement,
                 bool aFirstBinding = false);
 
   void Traverse(nsCycleCollectionTraversalCallback &cb) const;
   void Unlink();
   void Trace(const TraceCallbacks& aCallbacks, void *aClosure) const;
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 // Internal member functions.
 public:
   /**
    * GetImmediateChild locates the immediate child of our binding element which
    * has the localname given by aTag and is in the XBL namespace.
    */
   mozilla::dom::Element* GetImmediateChild(nsAtom* aTag);
   mozilla::dom::Element* LocateInstance(mozilla::dom::Element* aBoundElt,
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -182,23 +182,29 @@ nsXBLPrototypeResources::GatherRuleProce
   mRuleProcessor = new nsCSSRuleProcessor(Move(sheets),
                                           SheetType::Doc,
                                           nullptr,
                                           mRuleProcessor);
 }
 #endif
 
 void
-nsXBLPrototypeResources::ComputeServoStyles(const ServoStyleSet& aMasterStyleSet)
+nsXBLPrototypeResources::SyncServoStyles()
 {
   mStyleRuleMap.reset(nullptr);
   mServoStyles.reset(Servo_AuthorStyles_Create());
   for (auto& sheet : mStyleSheetList) {
     Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), sheet->AsServo());
   }
+}
+
+void
+nsXBLPrototypeResources::ComputeServoStyles(const ServoStyleSet& aMasterStyleSet)
+{
+  SyncServoStyles();
   Servo_AuthorStyles_Flush(mServoStyles.get(), aMasterStyleSet.RawSet());
 }
 
 ServoStyleRuleMap*
 nsXBLPrototypeResources::GetServoStyleRuleMap()
 {
   if (!HasStyleSheets() || !mServoStyles) {
     return nullptr;
@@ -231,8 +237,32 @@ nsXBLPrototypeResources::InsertStyleShee
 }
 
 void
 nsXBLPrototypeResources::AppendStyleSheetsTo(
                                       nsTArray<StyleSheet*>& aResult) const
 {
   aResult.AppendElements(mStyleSheetList);
 }
+
+MOZ_DEFINE_MALLOC_SIZE_OF(ServoAuthorStylesMallocSizeOf)
+MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoAuthorStylesMallocEnclosingSizeOf)
+
+size_t
+nsXBLPrototypeResources::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = aMallocSizeOf(this);
+  n += mStyleSheetList.ShallowSizeOfExcludingThis(aMallocSizeOf);
+#ifdef MOZ_OLD_STYLE
+  n += mRuleProcessor ? mRuleProcessor->SizeOfIncludingThis(aMallocSizeOf) : 0;
+#endif
+  n += mServoStyles ? Servo_AuthorStyles_SizeOfIncludingThis(
+      ServoAuthorStylesMallocSizeOf,
+      ServoAuthorStylesMallocEnclosingSizeOf,
+      mServoStyles.get()) : 0;
+  n += mStyleRuleMap ? mStyleRuleMap->SizeOfIncludingThis(aMallocSizeOf) : 0;
+
+  // Measurement of the following members may be added later if DMD finds it
+  // is worthwhile:
+  // - mLoader
+
+  return n;
+}
--- a/dom/xbl/nsXBLPrototypeResources.h
+++ b/dom/xbl/nsXBLPrototypeResources.h
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #ifndef nsXBLPrototypeResources_h__
 #define nsXBLPrototypeResources_h__
 
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/ServoStyleRuleMap.h"
 #include "nsICSSLoaderObserver.h"
 
 class nsCSSRuleProcessor;
 class nsAtom;
 class nsIContent;
 class nsXBLPrototypeBinding;
@@ -77,23 +78,27 @@ public:
   nsCSSRuleProcessor* GetRuleProcessor() const { return mRuleProcessor; }
 #endif
 
   const RawServoAuthorStyles* GetServoStyles() const
   {
     return mServoStyles.get();
   }
 
+  void SyncServoStyles();
+
   mozilla::ServoStyleRuleMap* GetServoStyleRuleMap();
 
   // Updates the ServoStyleSet object that holds the result of cascading the
   // sheets in mStyleSheetList. Equivalent to GatherRuleProcessor(), but for
   // the Servo style backend.
   void ComputeServoStyles(const mozilla::ServoStyleSet& aMasterStyleSet);
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
   // A loader object. Exists only long enough to load resources, and then it dies.
   RefPtr<nsXBLResourceLoader> mLoader;
 
   // A list of loaded stylesheets for this binding.
   //
   // FIXME(emilio): Remove when the old style system is gone, defer to
   // mServoStyles.
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6305,17 +6305,17 @@ public:
   }
 
 private:
   PresShell* mShell;
   uint32_t mFlags;
 };
 
 void
-PresShell::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
+nsIPresShell::RecordShadowStyleChange(ShadowRoot& aShadowRoot)
 {
   mStyleSet->RecordShadowStyleChange(aShadowRoot);
 }
 
 void
 PresShell::Paint(nsView*         aViewToPaint,
                  const nsRegion& aDirtyRegion,
                  uint32_t        aFlags)
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -387,19 +387,16 @@ public:
   void RebuildApproximateFrameVisibility(nsRect* aRect = nullptr,
                                          bool aRemoveOnly = false) override;
 
   void EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) override;
   void RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) override;
 
   bool AssumeAllFramesVisible() override;
 
-
-  virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) override;
-
   virtual bool CanDispatchEvent(
       const mozilla::WidgetGUIEvent* aEvent = nullptr) const override;
 
   void SetNextPaintCompressed() { mNextPaintCompressed = true; }
 
   void NotifyStyleSheetServiceSheetAdded(mozilla::StyleSheet* aSheet,
                                          uint32_t aSheetType) override;
   void NotifyStyleSheetServiceSheetRemoved(mozilla::StyleSheet* aSheet,
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -535,20 +535,20 @@ public:
    * Note that this may destroy frames for an ancestor instead.
    */
   virtual void DestroyFramesForAndRestyle(mozilla::dom::Element* aElement) = 0;
 
   void PostRecreateFramesFor(mozilla::dom::Element* aElement);
   void RestyleForAnimation(mozilla::dom::Element* aElement,
                            nsRestyleHint aHint);
 
-  // ShadowRoot has APIs that can change styles so we only
-  // want to restyle elements in the ShadowRoot and not the whole
-  // document.
-  virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) = 0;
+  // ShadowRoot has APIs that can change styles. This notifies the shell that
+  // stlyes applicable in the shadow tree have potentially changed.
+  void RecordShadowStyleChange(mozilla::dom::ShadowRoot& aShadowRoot);
+
 
   /**
    * Determine if it is safe to flush all pending notifications.
    */
   bool IsSafeToFlush() const;
 
   /**
    * Flush pending notifications of the type specified.  This method
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -1449,25 +1449,16 @@ Declaration::GetPropertyValueInternal(
           aValue.Append(char16_t(' '));
         }
       }
       if (!isDefaultColor) {
         AppendValueToString(eCSSProperty_text_emphasis_color, aValue);
       }
       break;
     }
-    case eCSSProperty__moz_transform: {
-      // shorthands that are just aliases with different parsing rules
-      const nsCSSPropertyID* subprops =
-        nsCSSProps::SubpropertyEntryFor(aProperty);
-      MOZ_ASSERT(subprops[1] == eCSSProperty_UNKNOWN,
-                 "must have exactly one subproperty");
-      AppendValueToString(subprops[0], aValue);
-      break;
-    }
     case eCSSProperty_overscroll_behavior: {
       const nsCSSValue& xValue =
         *data->ValueFor(eCSSProperty_overscroll_behavior_x);
       const nsCSSValue& yValue =
         *data->ValueFor(eCSSProperty_overscroll_behavior_y);
       AppendValueToString(eCSSProperty_overscroll_behavior_x, aValue);
       if (yValue != xValue) {
         aValue.Append(char16_t(' '));
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -187,16 +187,20 @@ SERVO_BINDING_FUNC(Servo_AuthorStyles_Ap
                    const mozilla::ServoStyleSheet* gecko_sheet)
 SERVO_BINDING_FUNC(Servo_AuthorStyles_ForceDirty, void,
                    RawServoAuthorStylesBorrowedMut self)
 // TODO(emilio): This will need to take an element to implement invalidation for
 // Shadow DOM.
 SERVO_BINDING_FUNC(Servo_AuthorStyles_Flush, void,
                    RawServoAuthorStylesBorrowedMut self,
                    RawServoStyleSetBorrowed document_styles)
+SERVO_BINDING_FUNC(Servo_AuthorStyles_SizeOfIncludingThis, size_t,
+                   mozilla::MallocSizeOf malloc_size_of,
+                   mozilla::MallocSizeOf malloc_enclosing_size_of,
+                   RawServoAuthorStylesBorrowed self)
 
 SERVO_BINDING_FUNC(Servo_StyleContext_AddRef, void, ServoStyleContextBorrowed ctx);
 SERVO_BINDING_FUNC(Servo_StyleContext_Release, void, ServoStyleContextBorrowed ctx);
 
 SERVO_BINDING_FUNC(Servo_StyleSet_MightHaveAttributeDependency, bool,
                    RawServoStyleSetBorrowed set,
                    RawGeckoElementBorrowed element,
                    nsAtom* local_name)
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -156,16 +156,26 @@ ServoStyleSet::Init(nsPresContext* aPres
                  "We should only append non-null raw sheets.");
       Servo_StyleSet_AppendStyleSheet(mRawSet.get(), sheet);
     }
   }
 
   // We added prefilled stylesheets into mRawSet, so the stylist is dirty.
   // The Stylist should be updated later when necessary.
   SetStylistStyleSheetsDirty();
+
+  // We may have Shadow DOM style changes that we weren't notified about because
+  // the document didn't have a shell, if the ShadowRoot was created in a
+  // display: none iframe.
+  //
+  // Now that we got a shell, we may need to get them up-to-date.
+  //
+  // TODO(emilio, bug 1418159): This wouldn't be needed if the StyleSet was
+  // owned by the document.
+  SetStylistXBLStyleSheetsDirty();
 }
 
 void
 ServoStyleSet::Shutdown()
 {
   // Make sure we drop our cached style contexts before the presshell arena
   // starts going away.
   ClearNonInheritingStyleContexts();
@@ -178,16 +188,30 @@ ServoStyleSet::InvalidateStyleForCSSRule
 {
   MOZ_ASSERT(StylistNeedsUpdate());
   if (nsPresContext* pc = GetPresContext()) {
     pc->RestyleManager()->AsServo()->PostRestyleEventForCSSRuleChanges();
   }
 }
 
 void
+ServoStyleSet::RecordShadowStyleChange(ShadowRoot& aShadowRoot)
+{
+  // TODO(emilio): We could keep track of the actual shadow roots that need
+  // their styles recomputed.
+  SetStylistXBLStyleSheetsDirty();
+
+  // FIXME(emilio): This should be done using stylesheet invalidation instead.
+  if (nsPresContext* pc = GetPresContext()) {
+    pc->RestyleManager()->PostRestyleEvent(
+      aShadowRoot.Host(), eRestyle_Subtree, nsChangeHint(0));
+  }
+}
+
+void
 ServoStyleSet::InvalidateStyleForDocumentStateChanges(EventStates aStatesChanged)
 {
   MOZ_ASSERT(mDocument);
   MOZ_ASSERT(!aStatesChanged.IsEmpty());
 
   nsPresContext* pc = GetPresContext();
   if (!pc) {
     return;
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -115,21 +115,17 @@ public:
 
   // All the relevant changes are handled in RuleAdded / RuleRemoved / etc, and
   // the relevant AppendSheet / RemoveSheet...
   void RecordStyleSheetChange(ServoStyleSheet*, StyleSheet::ChangeType) {}
 
   // Runs style invalidation due to document state changes.
   void InvalidateStyleForDocumentStateChanges(EventStates aStatesChanged);
 
-  void RecordShadowStyleChange(dom::ShadowRoot* aShadowRoot) {
-    // FIXME(emilio): When we properly support shadow dom we'll need to do
-    // better.
-    MarkOriginsDirty(OriginFlags::All);
-  }
+  void RecordShadowStyleChange(dom::ShadowRoot&);
 
   bool StyleSheetsHaveChanged() const
   {
     return StylistNeedsUpdate();
   }
 
   nsRestyleHint MediumFeaturesChanged(MediaFeatureChangeReason);
 
--- a/layout/style/StyleSetHandle.h
+++ b/layout/style/StyleSetHandle.h
@@ -187,17 +187,17 @@ public:
     inline nsresult AddDocStyleSheet(StyleSheet* aSheet, nsIDocument* aDocument);
 
     inline void RuleRemoved(StyleSheet&, css::Rule&);
     inline void RuleAdded(StyleSheet&, css::Rule&);
     inline void RuleChanged(StyleSheet&, css::Rule*);
 
     // TODO(emilio): Remove in favor of Rule* methods.
     inline void RecordStyleSheetChange(StyleSheet* aSheet, StyleSheet::ChangeType);
-    inline void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
+    inline void RecordShadowStyleChange(mozilla::dom::ShadowRoot& aShadowRoot);
     inline bool StyleSheetsHaveChanged() const;
     inline void InvalidateStyleForCSSRuleChanges();
     inline nsRestyleHint MediumFeaturesChanged(mozilla::MediaFeatureChangeReason);
     inline already_AddRefed<nsStyleContext>
     ProbePseudoElementStyle(dom::Element* aParentElement,
                             mozilla::CSSPseudoElementType aType,
                             nsStyleContext* aParentContext);
     inline already_AddRefed<nsStyleContext>
--- a/layout/style/StyleSetHandleInlines.h
+++ b/layout/style/StyleSetHandleInlines.h
@@ -301,17 +301,17 @@ void
 StyleSetHandle::Ptr::RecordStyleSheetChange(StyleSheet* aSheet,
                                             StyleSheet::ChangeType aChangeType)
 {
   FORWARD_CONCRETE(RecordStyleSheetChange, (aSheet->AsGecko(), aChangeType),
                                            (aSheet->AsServo(), aChangeType));
 }
 
 void
-StyleSetHandle::Ptr::RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot)
+StyleSetHandle::Ptr::RecordShadowStyleChange(mozilla::dom::ShadowRoot& aShadowRoot)
 {
   FORWARD(RecordShadowStyleChange, (aShadowRoot));
 }
 
 bool
 StyleSetHandle::Ptr::StyleSheetsHaveChanged() const
 {
   FORWARD(StyleSheetsHaveChanged, ());
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -926,17 +926,17 @@ protected:
   bool ParseFontVariationSettings(nsCSSValue& aValue);
   bool ParseFontSrc(nsCSSValue& aValue);
   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
   bool ParseFontRanges(nsCSSValue& aValue);
   bool ParseListStyle();
   bool ParseListStyleType(nsCSSValue& aValue);
   bool ParseMargin();
   bool ParseClipPath(nsCSSValue& aValue);
-  bool ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
+  bool ParseTransform(nsCSSPropertyID aProperty,
                       bool aDisallowRelativeValues = false);
   bool ParseObjectPosition();
   bool ParseOutline();
   bool ParseOverflow();
   bool ParseOverflowClipBox();
   bool ParsePadding();
   bool ParseQuotes();
   bool ParseTextAlign(nsCSSValue& aValue,
@@ -1245,17 +1245,17 @@ protected:
   bool ParseCircleOrEllipseFunction(nsCSSKeyword, nsCSSValue& aValue);
   bool ParseInsetFunction(nsCSSValue& aValue);
   // We parse position values differently for basic-shape, by expanding defaults
   // and replacing keywords with percentages
   bool ParsePositionValueForBasicShape(nsCSSValue& aOut);
 
 
   /* Functions for transform Parsing */
-  bool ParseSingleTransform(bool aIsPrefixed, bool aDisallowRelativeValues,
+  bool ParseSingleTransform(bool aDisallowRelativeValues,
                             nsCSSValue& aValue);
   bool ParseFunction(nsCSSKeyword aFunction, const uint32_t aAllowedTypes[],
                      uint32_t aVariantMaskAll, uint16_t aMinElems,
                      uint16_t aMaxElems, nsCSSValue &aValue);
   bool ParseFunctionInternals(const uint32_t aVariantMask[],
                               uint32_t aVariantMaskAll,
                               uint16_t aMinElems,
                               uint16_t aMaxElems,
@@ -1822,17 +1822,17 @@ CSSParserImpl::ParseTransformProperty(co
 
   mData.AssertInitialState();
   mTempData.AssertInitialState();
 
   nsCSSScanner scanner(aPropValue, 0);
   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
 
-  bool parsedOK = ParseTransform(false, eCSSProperty_transform,
+  bool parsedOK = ParseTransform(eCSSProperty_transform,
                                  aDisallowRelativeValues);
   // We should now be at EOF
   if (parsedOK && GetToken(true)) {
     parsedOK = false;
     mTempData.ClearProperty(eCSSProperty_transform);
   }
 
   bool changed = false;
@@ -11781,19 +11781,17 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_text_decoration:
     return ParseTextDecoration();
   case eCSSProperty_text_emphasis:
     return ParseTextEmphasis();
   case eCSSProperty_will_change:
     return ParseWillChange();
   case eCSSProperty_transform:
   case eCSSProperty__moz_window_transform:
-    return ParseTransform(false, aPropID);
-  case eCSSProperty__moz_transform:
-    return ParseTransform(true, eCSSProperty_transform);
+    return ParseTransform(aPropID);
   case eCSSProperty_transform_origin:
   case eCSSProperty_perspective_origin:
   case eCSSProperty__moz_window_transform_origin:
     return ParseTransformOrigin(aPropID);
   case eCSSProperty_transition:
     return ParseTransition();
   case eCSSProperty_animation:
     return ParseAnimation();
@@ -15856,27 +15854,24 @@ CSSParserImpl::ParseFunction(nsCSSKeywor
 
 /**
  * Given a token, determines the minimum and maximum number of function
  * parameters to read, along with the mask that should be used to read
  * those function parameters.  If the token isn't a transform function,
  * returns an error.
  *
  * @param aToken The token identifying the function.
- * @param aIsPrefixed If true, parse matrices using the matrix syntax
- *   for -moz-transform.
  * @param aDisallowRelativeValues If true, only allow variants that are
  *   numbers or have non-relative dimensions.
  * @param aMinElems [out] The minimum number of elements to read.
  * @param aMaxElems [out] The maximum number of elements to read
  * @param aVariantMask [out] The variant mask to use during parsing
  * @return Whether the information was loaded successfully.
  */
 static bool GetFunctionParseInformation(nsCSSKeyword aToken,
-                                        bool aIsPrefixed,
                                         bool aDisallowRelativeValues,
                                         uint16_t &aMinElems,
                                         uint16_t &aMaxElems,
                                         const uint32_t *& aVariantMask)
 {
 /* These types represent the common variant masks that will be used to
    * parse out the individual functions.  The order in the enumeration
    * must match the order in which the masks are declared.
@@ -15892,19 +15887,17 @@ static bool GetFunctionParseInformation(
          eTwoAngles,
          eNumber,
          eNonNegativeLength,
          eNonNegativeAbsoluteLength,
          eTwoNumbers,
          eThreeNumbers,
          eThreeNumbersOneAngle,
          eMatrix,
-         eMatrixPrefixed,
          eMatrix3d,
-         eMatrix3dPrefixed,
          eNumVariantMasks };
   static const int32_t kMaxElemsPerFunction = 16;
   static const uint32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
     {VARIANT_LPCALC},
     {VARIANT_LCALC},
     {VARIANT_LB},
     {VARIANT_LPCALC, VARIANT_LPCALC},
     {VARIANT_LBCALC, VARIANT_LBCALC},
@@ -15916,25 +15909,19 @@ static bool GetFunctionParseInformation(
     {VARIANT_LENGTH|VARIANT_NONNEGATIVE_DIMENSION},
     {VARIANT_LB|VARIANT_NONNEGATIVE_DIMENSION},
     {VARIANT_NUMBER, VARIANT_NUMBER},
     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
      VARIANT_NUMBER, VARIANT_NUMBER},
     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
-     VARIANT_LPNCALC, VARIANT_LPNCALC},
-    {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
-     VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
-    {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
-     VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
-     VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
-     VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
+     VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER}};
   // Map from a mask to a congruent mask that excludes relative variants.
   static const int32_t kNonRelativeVariantMap[eNumVariantMasks] = {
     eAbsoluteLengthCalc,
     eAbsoluteLengthCalc,
     eAbsoluteLengthCalc,
     eTwoAbsoluteLengthCalcs,
     eTwoAbsoluteLengthCalcs,
     eThreeAbsoluteLengthCalc,
@@ -15943,23 +15930,21 @@ static bool GetFunctionParseInformation(
     eTwoAngles,
     eNumber,
     eNonNegativeAbsoluteLength,
     eNonNegativeAbsoluteLength,
     eTwoNumbers,
     eThreeNumbers,
     eThreeNumbersOneAngle,
     eMatrix,
-    eMatrix,
-    eMatrix3d,
     eMatrix3d };
 
 #ifdef DEBUG
   static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
-    {1, 1, 1, 2, 2, 3, 3, 1, 2, 1, 1, 1, 2, 3, 4, 6, 6, 16, 16};
+    {1, 1, 1, 2, 2, 3, 3, 1, 2, 1, 1, 1, 2, 3, 4, 6, 16};
 #endif
 
   int32_t variantIndex = eNumVariantMasks;
 
   switch (aToken) {
   case eCSSKeyword_translatex:
   case eCSSKeyword_translatey:
     /* Exactly one length or percent. */
@@ -16034,23 +16019,23 @@ static bool GetFunctionParseInformation(
   case eCSSKeyword_skewy:
     /* Exactly one angle. */
     variantIndex = eAngle;
     aMinElems = 1U;
     aMaxElems = 1U;
     break;
   case eCSSKeyword_matrix:
     /* Six values, all numbers. */
-    variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
+    variantIndex = eMatrix;
     aMinElems = 6U;
     aMaxElems = 6U;
     break;
   case eCSSKeyword_matrix3d:
     /* 16 matrix values, all numbers */
-    variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
+    variantIndex = eMatrix3d;
     aMinElems = 16U;
     aMaxElems = 16U;
     break;
   case eCSSKeyword_perspective:
     /* Exactly one scale number. */
     variantIndex = eNonNegativeLength;
     aMinElems = 1U;
     aMaxElems = 1U;
@@ -16130,33 +16115,32 @@ bool CSSParserImpl::ParseWillChange()
   AppendValue(eCSSProperty_will_change, listValue);
   return true;
 }
 
 /* Reads a single transform function from the tokenizer stream, reporting an
  * error if something goes wrong.
  */
 bool
-CSSParserImpl::ParseSingleTransform(bool aIsPrefixed,
-                                    bool aDisallowRelativeValues,
+CSSParserImpl::ParseSingleTransform(bool aDisallowRelativeValues,
                                     nsCSSValue& aValue)
 {
   if (!GetToken(true))
     return false;
 
   if (mToken.mType != eCSSToken_Function) {
     UngetToken();
     return false;
   }
 
   const uint32_t* variantMask;
   uint16_t minElems, maxElems;
   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
 
-  if (!GetFunctionParseInformation(keyword, aIsPrefixed,
+  if (!GetFunctionParseInformation(keyword,
                                    aDisallowRelativeValues,
                                    minElems, maxElems,
                                    variantMask))
     return false;
 
   return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
 }
 
@@ -16164,29 +16148,29 @@ CSSParserImpl::ParseSingleTransform(bool
  * and constructing a matrix from it.
  * aProperty can be transform or -moz-window-transform.
  * FIXME: For -moz-window-transform, it would be nice to reject non-2d
  * transforms at parse time, because the implementation only supports 2d
  * transforms. Instead, at the moment, non-2d transforms are treated as the
  * identity transform very late in the pipeline.
  */
 bool
-CSSParserImpl::ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
+CSSParserImpl::ParseTransform(nsCSSPropertyID aProperty,
                               bool aDisallowRelativeValues)
 {
   nsCSSValue value;
   // 'inherit', 'initial', 'unset' and 'none' must be alone
   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
                                nullptr)) {
     nsCSSValueSharedList* list = new nsCSSValueSharedList;
     value.SetSharedListValue(list);
     list->mHead = new nsCSSValueList;
     nsCSSValueList* cur = list->mHead;
     for (;;) {
-      if (!ParseSingleTransform(aIsPrefixed, aDisallowRelativeValues,
+      if (!ParseSingleTransform(aDisallowRelativeValues,
                                 cur->mValue)) {
         return false;
       }
       if (CheckEndProperty()) {
         break;
       }
       cur->mNext = new nsCSSValueList;
       cur = cur->mNext;
--- a/layout/style/nsCSSPropAliasList.h
+++ b/layout/style/nsCSSPropAliasList.h
@@ -39,16 +39,21 @@
 
  ******/
 
 CSS_PROP_ALIAS(word-wrap,
                word_wrap,
                overflow_wrap,
                WordWrap,
                "")
+CSS_PROP_ALIAS(-moz-transform,
+               _moz_transform,
+               transform,
+               MozTransform,
+               "layout.css.prefixes.transforms")
 CSS_PROP_ALIAS(-moz-transform-origin,
                _moz_transform_origin,
                transform_origin,
                MozTransformOrigin,
                "layout.css.prefixes.transforms")
 CSS_PROP_ALIAS(-moz-perspective-origin,
                _moz_perspective_origin,
                perspective_origin,
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -4277,25 +4277,16 @@ CSS_PROP_DISPLAY(
         CSS_PROPERTY_CREATES_STACKING_CONTEXT |
         CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR |
         CSS_PROPERTY_FIXPOS_CB,
     "",
     0,
     nullptr,
     offsetof(nsStyleDisplay, mSpecifiedTransform),
     eStyleAnimType_Custom)
-// This shorthand is essentially an alias, but it requires different
-// parsing rules, and it therefore implemented as a shorthand.
-CSS_PROP_SHORTHAND(
-    -moz-transform,
-    _moz_transform,
-    MozTransform,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_IS_ALIAS,
-    "layout.css.prefixes.transforms")
 CSS_PROP_DISPLAY(
     transform-box,
     transform_box,
     TransformBox,
     CSS_PROPERTY_PARSE_VALUE,
     "svg.transform-box.enabled",
     VARIANT_HK,
     kTransformBoxKTable,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -2986,23 +2986,16 @@ static const nsCSSPropertyID gPlaceItems
 };
 
 static const nsCSSPropertyID gPlaceSelfSubpropTable[] = {
   eCSSProperty_align_self,
   eCSSProperty_justify_self,
   eCSSProperty_UNKNOWN
 };
 
-// Subproperty tables for shorthands that are just aliases with
-// different parsing rules.
-static const nsCSSPropertyID gMozTransformSubpropTable[] = {
-  eCSSProperty_transform,
-  eCSSProperty_UNKNOWN
-};
-
 static const nsCSSPropertyID gOverscrollBehaviorSubpropTable[] = {
   eCSSProperty_overscroll_behavior_x,
   eCSSProperty_overscroll_behavior_y,
   eCSSProperty_UNKNOWN
 };
 
 static const nsCSSPropertyID gScrollSnapTypeSubpropTable[] = {
   eCSSProperty_scroll_snap_type_x,
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -2362,23 +2362,23 @@ nsStyleSet::SheetChanged(CSSStyleSheet& 
 
   mStylesHaveChanged = true;
   // If we need to restyle everything, no need to restyle individual
   // scoped style roots.
   mChangedScopeStyleRoots.Clear();
 }
 
 void
-nsStyleSet::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
+nsStyleSet::RecordShadowStyleChange(ShadowRoot& aShadowRoot)
 {
   if (mStylesHaveChanged) {
     return;
   }
 
-  mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement());
+  mChangedScopeStyleRoots.AppendElement(aShadowRoot.Host());
 }
 
 void
 nsStyleSet::InvalidateStyleForCSSRuleChanges()
 {
   MOZ_ASSERT(!mStylesHaveChanged || mChangedScopeStyleRoots.IsEmpty());
 
   AutoTArray<RefPtr<mozilla::dom::Element>, 1> scopeRoots;
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -362,17 +362,17 @@ class nsStyleSet final
                               mozilla::StyleSheet::ChangeType)
   {
     SheetChanged(*aSheet);
   }
 
   void SheetChanged(mozilla::CSSStyleSheet&);
 
   // Notes that style sheets have changed in a shadow root.
-  void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
+  void RecordShadowStyleChange(mozilla::dom::ShadowRoot& aShadowRoot);
 
   bool StyleSheetsHaveChanged() const
   {
     return mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty();
   }
 
   void InvalidateStyleForCSSRuleChanges();
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -5408,81 +5408,16 @@ var gCSSProperties = {
     subproperties: [ "overflow-wrap" ],
   },
   "-moz-transform": {
     domProp: "MozTransform",
     inherited: false,
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "transform",
     subproperties: [ "transform" ],
-    // NOTE: We specify other_values & invalid_values explicitly here (instead
-    // of deferring to "transform") because we accept some legacy syntax as
-    // valid for "-moz-transform" but not for "transform".
-    other_values: [ "translatex(1px)", "translatex(4em)",
-      "translatex(-4px)", "translatex(3px)",
-      "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)",
-      "translatey(4em)", "translate(3px)", "translate(10px, -3px)",
-      "rotate(45deg)", "rotate(45grad)", "rotate(45rad)",
-      "rotate(0.25turn)", "rotate(0)", "scalex(10)", "scaley(10)",
-      "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)",
-      "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewx(0.08turn)",
-      "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "skewy(0.08turn)",
-      "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)",
-      "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)",
-      "translatex(50%)", "translatey(50%)", "translate(50%)",
-      "translate(3%, 5px)", "translate(5px, 3%)",
-      "matrix(1, 2, 3, 4, 5, 6)",
-      /* valid calc() values */
-      "translatex(calc(5px + 10%))",
-      "translatey(calc(0.25 * 5px + 10% / 3))",
-      "translate(calc(5px - 10% * 3))",
-      "translate(calc(5px - 3 * 10%), 50px)",
-      "translate(-50px, calc(5px - 10% * 3))",
-      /* valid only when prefixed */
-      "matrix(1, 2, 3, 4, 5px, 6%)",
-      "matrix(1, 2, 3, 4, 5%, 6px)",
-      "matrix(1, 2, 3, 4, 5%, 6%)",
-      "matrix(1, 2, 3, 4, 5px, 6em)",
-      "matrix(1, 0, 0, 1, calc(5px * 3), calc(10% - 3px))",
-      "translatez(1px)", "translatez(4em)", "translatez(-4px)",
-      "translatez(0px)", "translatez(2px) translatez(5px)",
-      "translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)",
-      "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)",
-      "scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)",
-      "scalez(-6)", "rotate3d(2, 3, 4, 45deg)",
-      "rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)",
-      "rotatez(72rad)", "rotatex(0.125turn)",
-      "perspective(0px)", "perspective(1000px)",
-      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)",
-      /* valid only when prefixed */
-      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)",
-      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)",
-    ],
-    invalid_values: ["1px", "#0000ff", "red", "auto",
-      "translatex(1)", "translatey(1)", "translate(2)",
-      "translate(-3, -4)",
-      "translatex(1px 1px)", "translatex(translatex(1px))",
-      "translatex(#0000ff)", "translatex(red)", "translatey()",
-      "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)",
-      "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)",
-      "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)",
-      "matrix(0, 1, 2, 3%, 4%, 5%)",
-      /* invalid calc() values */
-      "translatey(-moz-min(5px,10%))",
-      "translatex(-moz-max(5px,10%))",
-      "translate(10px, calc(min(5px,10%)))",
-      "translate(calc(max(5px,10%)), 10%)",
-      "matrix(1, 0, 0, 1, max(5px * 3), calc(10% - 3px))",
-      "perspective(-10px)", "matrix3d(dinosaur)",
-      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)",
-      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)",
-      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15%, 16)",
-      "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16px)",
-      "rotatey(words)", "rotatex(7)", "translate3d(3px, 4px, 1px, 7px)",
-    ],
   },
   "-moz-transform-origin": {
     domProp: "MozTransformOrigin",
     inherited: false,
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "transform-origin",
     subproperties: [ "transform-origin" ],
   },
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -471,16 +471,17 @@ nsHostRecord::GetPriority(uint16_t aFlag
 
 // Returns true if the entry can be removed, or false if it should be left.
 // Sets mResolveAgain true for entries being resolved right now.
 bool
 nsHostRecord::RemoveOrRefresh()
 {
     // no need to flush TRRed names, they're not resolved "locally"
     Cancel();
+    MutexAutoLock lock(addr_info_lock);
     if (addr_info && addr_info->IsTRR()) {
         return false;
     }
     if (mNative) {
         if (!onQueue) {
             // The request has been passed to the OS resolver. The resultant DNS
             // record should be considered stale and not trusted; set a flag to
             // ensure it is called again.
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -600,26 +600,16 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_SelectorList_Drop(ptr: RawServoSelectorListOwned);
 }
 extern "C" {
     pub fn Servo_SourceSizeList_Drop(ptr: RawServoSourceSizeListOwned);
 }
 extern "C" {
-    pub fn Gecko_RecordTraversalStatistics(
-        total: u32,
-        parallel: u32,
-        total_t: u32,
-        parallel_t: u32,
-        total_s: u32,
-        parallel_s: u32,
-    );
-}
-extern "C" {
     pub fn Gecko_IsSignificantChild(
         node: RawGeckoNodeBorrowed,
         text_is_significant: bool,
         whitespace_is_significant: bool,
     ) -> bool;
 }
 extern "C" {
     pub fn Gecko_GetLastChild(node: RawGeckoNodeBorrowed) -> RawGeckoNodeBorrowedOrNull;
@@ -2313,16 +2303,23 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_AuthorStyles_Flush(
         self_: RawServoAuthorStylesBorrowedMut,
         document_styles: RawServoStyleSetBorrowed,
     );
 }
 extern "C" {
+    pub fn Servo_AuthorStyles_SizeOfIncludingThis(
+        malloc_size_of: MallocSizeOf,
+        malloc_enclosing_size_of: MallocSizeOf,
+        self_: RawServoAuthorStylesBorrowed,
+    ) -> usize;
+}
+extern "C" {
     pub fn Servo_StyleContext_AddRef(ctx: ServoStyleContextBorrowed);
 }
 extern "C" {
     pub fn Servo_StyleContext_Release(ctx: ServoStyleContextBorrowed);
 }
 extern "C" {
     pub fn Servo_StyleSet_MightHaveAttributeDependency(
         set: RawServoStyleSetBorrowed,
--- a/servo/components/style/gecko/generated/structs.rs
+++ b/servo/components/style/gecko/generated/structs.rs
@@ -3081,16 +3081,23 @@ pub mod root {
                 pub mAsNode: *mut root::nsINode,
                 pub mKind: root::mozilla::dom::DocumentOrShadowRoot_Kind,
             }
             pub const DocumentOrShadowRoot_Kind_Document:
                 root::mozilla::dom::DocumentOrShadowRoot_Kind = 0;
             pub const DocumentOrShadowRoot_Kind_ShadowRoot:
                 root::mozilla::dom::DocumentOrShadowRoot_Kind = 1;
             pub type DocumentOrShadowRoot_Kind = i32;
+            pub const DocumentOrShadowRoot_ElementsFromPointFlags_IGNORE_ROOT_SCROLL_FRAME:
+                root::mozilla::dom::DocumentOrShadowRoot_ElementsFromPointFlags = 1;
+            pub const DocumentOrShadowRoot_ElementsFromPointFlags_FLUSH_LAYOUT:
+                root::mozilla::dom::DocumentOrShadowRoot_ElementsFromPointFlags = 2;
+            pub const DocumentOrShadowRoot_ElementsFromPointFlags_IS_ELEMENT_FROM_POINT:
+                root::mozilla::dom::DocumentOrShadowRoot_ElementsFromPointFlags = 4;
+            pub type DocumentOrShadowRoot_ElementsFromPointFlags = u32;
             #[test]
             fn bindgen_test_layout_DocumentOrShadowRoot() {
                 assert_eq!(
                     ::std::mem::size_of::<DocumentOrShadowRoot>(),
                     64usize,
                     concat!("Size of: ", stringify!(DocumentOrShadowRoot))
                 );
                 assert_eq!(
@@ -6540,17 +6547,19 @@ pub mod root {
         pub const UseCounter_eUseCounter_LenientSetter: root::mozilla::UseCounter = 80;
         pub const UseCounter_eUseCounter_FileLastModifiedDate: root::mozilla::UseCounter = 81;
         pub const UseCounter_eUseCounter_ImageBitmapRenderingContext_TransferImageBitmap:
             root::mozilla::UseCounter = 82;
         pub const UseCounter_eUseCounter_URLCreateObjectURL_MediaStream: root::mozilla::UseCounter =
             83;
         pub const UseCounter_eUseCounter_XMLBaseAttribute: root::mozilla::UseCounter = 84;
         pub const UseCounter_eUseCounter_WindowContentUntrusted: root::mozilla::UseCounter = 85;
-        pub const UseCounter_eUseCounter_Count: root::mozilla::UseCounter = 86;
+        pub const UseCounter_eUseCounter_RegisterProtocolHandlerInsecure:
+            root::mozilla::UseCounter = 86;
+        pub const UseCounter_eUseCounter_Count: root::mozilla::UseCounter = 87;
         pub type UseCounter = i16;
         pub const LogLevel_Disabled: root::mozilla::LogLevel = 0;
         pub const LogLevel_Error: root::mozilla::LogLevel = 1;
         pub const LogLevel_Warning: root::mozilla::LogLevel = 2;
         pub const LogLevel_Info: root::mozilla::LogLevel = 3;
         pub const LogLevel_Debug: root::mozilla::LogLevel = 4;
         pub const LogLevel_Verbose: root::mozilla::LogLevel = 5;
         pub type LogLevel = i32;
@@ -8257,17 +8266,16 @@ pub mod root {
         pub const SERVO_PREF_ENABLED__webkit_text_stroke_width: bool = true;
         pub const SERVO_PREF_ENABLED_scale: bool = true;
         pub const SERVO_PREF_ENABLED_text_transform: bool = false;
         pub const SERVO_PREF_ENABLED__x_text_zoom: bool = false;
         pub const SERVO_PREF_ENABLED_top: bool = false;
         pub const SERVO_PREF_ENABLED__moz_top_layer: bool = false;
         pub const SERVO_PREF_ENABLED_touch_action: bool = true;
         pub const SERVO_PREF_ENABLED_transform: bool = false;
-        pub const SERVO_PREF_ENABLED__moz_transform: bool = true;
         pub const SERVO_PREF_ENABLED_transform_box: bool = true;
         pub const SERVO_PREF_ENABLED_transform_origin: bool = false;
         pub const SERVO_PREF_ENABLED_transform_style: bool = false;
         pub const SERVO_PREF_ENABLED_transition: bool = false;
         pub const SERVO_PREF_ENABLED_transition_delay: bool = false;
         pub const SERVO_PREF_ENABLED_transition_duration: bool = false;
         pub const SERVO_PREF_ENABLED_transition_property: bool = false;
         pub const SERVO_PREF_ENABLED_transition_timing_function: bool = false;
@@ -8289,16 +8297,17 @@ pub mod root {
         pub const SERVO_PREF_ENABLED__moz_window_transform: bool = false;
         pub const SERVO_PREF_ENABLED__moz_window_transform_origin: bool = false;
         pub const SERVO_PREF_ENABLED_word_break: bool = false;
         pub const SERVO_PREF_ENABLED_word_spacing: bool = false;
         pub const SERVO_PREF_ENABLED_overflow_wrap: bool = false;
         pub const SERVO_PREF_ENABLED_writing_mode: bool = false;
         pub const SERVO_PREF_ENABLED_z_index: bool = false;
         pub const SERVO_PREF_ENABLED_word_wrap: bool = false;
+        pub const SERVO_PREF_ENABLED__moz_transform: bool = true;
         pub const SERVO_PREF_ENABLED__moz_transform_origin: bool = true;
         pub const SERVO_PREF_ENABLED__moz_perspective_origin: bool = true;
         pub const SERVO_PREF_ENABLED__moz_perspective: bool = true;
         pub const SERVO_PREF_ENABLED__moz_transform_style: bool = true;
         pub const SERVO_PREF_ENABLED__moz_backface_visibility: bool = true;
         pub const SERVO_PREF_ENABLED__moz_border_image: bool = true;
         pub const SERVO_PREF_ENABLED__moz_transition: bool = true;
         pub const SERVO_PREF_ENABLED__moz_transition_delay: bool = true;
@@ -21512,19 +21521,19 @@ pub mod root {
         eCSSProperty_place_content = 366,
         eCSSProperty_place_items = 367,
         eCSSProperty_place_self = 368,
         eCSSProperty_overscroll_behavior = 369,
         eCSSProperty_scroll_snap_type = 370,
         eCSSProperty_text_decoration = 371,
         eCSSProperty_text_emphasis = 372,
         eCSSProperty__webkit_text_stroke = 373,
-        eCSSProperty__moz_transform = 374,
-        eCSSProperty_transition = 375,
-        eCSSPropertyAlias_WordWrap = 376,
+        eCSSProperty_transition = 374,
+        eCSSPropertyAlias_WordWrap = 375,
+        eCSSPropertyAlias_MozTransform = 376,
         eCSSPropertyAlias_MozTransformOrigin = 377,
         eCSSPropertyAlias_MozPerspectiveOrigin = 378,
         eCSSPropertyAlias_MozPerspective = 379,
         eCSSPropertyAlias_MozTransformStyle = 380,
         eCSSPropertyAlias_MozBackfaceVisibility = 381,
         eCSSPropertyAlias_MozBorderImage = 382,
         eCSSPropertyAlias_MozTransition = 383,
         eCSSPropertyAlias_MozTransitionDelay = 384,
@@ -22178,23 +22187,16 @@ pub mod root {
     pub type nsIDocument_nsSubDocEnumFunc = ::std::option::Option<
         unsafe extern "C" fn(aDocument: *mut root::nsIDocument, aData: *mut ::std::os::raw::c_void)
             -> bool,
     >;
     /// Collect all the descendant documents for which |aCalback| returns true.
     /// The callback function must not mutate any state for the given document.
     pub type nsIDocument_nsDocTestFunc =
         ::std::option::Option<unsafe extern "C" fn(aDocument: *const root::nsIDocument) -> bool>;
-    pub const nsIDocument_ElementsFromPointFlags_IGNORE_ROOT_SCROLL_FRAME:
-        root::nsIDocument_ElementsFromPointFlags = 1;
-    pub const nsIDocument_ElementsFromPointFlags_FLUSH_LAYOUT:
-        root::nsIDocument_ElementsFromPointFlags = 2;
-    pub const nsIDocument_ElementsFromPointFlags_IS_ELEMENT_FROM_POINT:
-        root::nsIDocument_ElementsFromPointFlags = 4;
-    pub type nsIDocument_ElementsFromPointFlags = u32;
     /// A class that represents an external resource load that has begun but
     /// doesn't have a document yet.  Observers can be registered on this object,
     /// and will be notified after the document is created.  Observers registered
     /// after the document has been created will NOT be notified.  When observers
     /// are notified, the subject will be the newly-created document, the topic
     /// will be "external-resource-document-created", and the data will be null.
     /// If document creation fails for some reason, observers will still be
     /// notified, with a null document pointer.
@@ -22304,18 +22306,20 @@ pub mod root {
     pub const nsIDocument_DeprecatedOperations_eImageBitmapRenderingContext_TransferImageBitmap:
         root::nsIDocument_DeprecatedOperations = 27;
     pub const nsIDocument_DeprecatedOperations_eURLCreateObjectURL_MediaStream:
         root::nsIDocument_DeprecatedOperations = 28;
     pub const nsIDocument_DeprecatedOperations_eXMLBaseAttribute:
         root::nsIDocument_DeprecatedOperations = 29;
     pub const nsIDocument_DeprecatedOperations_eWindowContentUntrusted:
         root::nsIDocument_DeprecatedOperations = 30;
+    pub const nsIDocument_DeprecatedOperations_eRegisterProtocolHandlerInsecure:
+        root::nsIDocument_DeprecatedOperations = 31;
     pub const nsIDocument_DeprecatedOperations_eDeprecatedOperationCount:
-        root::nsIDocument_DeprecatedOperations = 31;
+        root::nsIDocument_DeprecatedOperations = 32;
     pub type nsIDocument_DeprecatedOperations = u32;
     pub const nsIDocument_DocumentWarnings_eIgnoringWillChangeOverBudget:
         root::nsIDocument_DocumentWarnings = 0;
     pub const nsIDocument_DocumentWarnings_ePreventDefaultFromPassiveListener:
         root::nsIDocument_DocumentWarnings = 1;
     pub const nsIDocument_DocumentWarnings_eSVGRefLoop: root::nsIDocument_DocumentWarnings = 2;
     pub const nsIDocument_DocumentWarnings_eSVGRefChainLengthExceeded:
         root::nsIDocument_DocumentWarnings = 3;
@@ -26154,25 +26158,25 @@ pub mod root {
         pub static mut nsCSSProps_kAnimTypeTable: [root::nsStyleAnimType; 326usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps23kStyleStructOffsetTableE"]
         pub static mut nsCSSProps_kStyleStructOffsetTable: [isize; 326usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps11kFlagsTableE"]
-        pub static mut nsCSSProps_kFlagsTable: [u32; 376usize];
+        pub static mut nsCSSProps_kFlagsTable: [u32; 375usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps19kParserVariantTableE"]
         pub static mut nsCSSProps_kParserVariantTable: [u32; 326usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps17kSubpropertyTableE"]
-        pub static mut nsCSSProps_kSubpropertyTable: [*const root::nsCSSPropertyID; 50usize];
+        pub static mut nsCSSProps_kSubpropertyTable: [*const root::nsCSSPropertyID; 49usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps26gShorthandsContainingTableE"]
         pub static mut nsCSSProps_gShorthandsContainingTable:
             [*mut root::nsCSSPropertyID; 326usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps25gShorthandsContainingPoolE"]
@@ -26191,21 +26195,21 @@ pub mod root {
         pub static mut nsCSSProps_kLogicalGroupTable: [*const root::nsCSSPropertyID; 9usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps16gPropertyEnabledE"]
         pub static mut nsCSSProps_gPropertyEnabled: [bool; 485usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps13kIDLNameTableE"]
-        pub static mut nsCSSProps_kIDLNameTable: [*const ::std::os::raw::c_char; 376usize];
+        pub static mut nsCSSProps_kIDLNameTable: [*const ::std::os::raw::c_char; 375usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps25kIDLNameSortPositionTableE"]
-        pub static mut nsCSSProps_kIDLNameSortPositionTable: [i32; 376usize];
+        pub static mut nsCSSProps_kIDLNameSortPositionTable: [i32; 375usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps19gPropertyUseCounterE"]
         pub static mut nsCSSProps_gPropertyUseCounter: [root::mozilla::UseCounter; 326usize];
     }
     extern "C" {
         #[link_name = "\u{1}_ZN10nsCSSProps25kAnimationDirectionKTableE"]
         pub static mut nsCSSProps_kAnimationDirectionKTable: [root::nsCSSProps_KTableEntry; 0usize];
--- a/servo/components/style/invalidation/stylesheets.rs
+++ b/servo/components/style/invalidation/stylesheets.rs
@@ -20,18 +20,17 @@ use selectors::attr::CaseSensitivity;
 use selectors::parser::{Component, LocalName, Selector};
 use shared_lock::SharedRwLockReadGuard;
 use stylesheets::{CssRule, StylesheetInDocument};
 
 /// A style sheet invalidation represents a kind of element or subtree that may
 /// need to be restyled. Whether it represents a whole subtree or just a single
 /// element is determined by whether the invalidation is stored in the
 /// StylesheetInvalidationSet's invalid_scopes or invalid_elements table.
-#[derive(Debug, Eq, Hash, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
 enum Invalidation {
     /// An element with a given id.
     ID(Atom),
     /// An element with a given class name.
     Class(Atom),
     /// An element with a given local name.
     LocalName { name: SelectorLocalName, lower_name: SelectorLocalName },
 }
@@ -93,17 +92,17 @@ impl Invalidation {
         false
     }
 }
 
 /// A set of invalidations due to stylesheet additions.
 ///
 /// TODO(emilio): We might be able to do the same analysis for media query
 /// changes too (or even selector changes?).
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(MallocSizeOf)]
 pub struct StylesheetInvalidationSet {
     /// The subtrees we know we have to restyle so far.
     invalid_scopes: FnvHashSet<Invalidation>,
     /// The elements we know we have to restyle so far.
     invalid_elements: FnvHashSet<Invalidation>,
     /// Whether the whole document should be restyled.
     fully_invalid: bool,
 }
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -861,33 +861,23 @@ impl PropertyDeclarationBlock {
                         AppendableValue::Css {
                             css: CssStringBorrow::from(&v),
                             with_variables: false,
                         }
                     }
                 };
 
                 // Substeps 7 and 8
-                // We need to check the shorthand whether it's an alias property or not.
-                // If it's an alias property, it should be serialized like its longhand.
-                if shorthand.flags().contains(PropertyFlags::SHORTHAND_ALIAS_PROPERTY) {
-                    append_serialization::<Cloned<slice::Iter< _>>, _>(
-                         dest,
-                         &property,
-                         value,
-                         importance,
-                         &mut is_first_serialization)?;
-                } else {
-                    append_serialization::<Cloned<slice::Iter< _>>, _>(
-                         dest,
-                         &shorthand,
-                         value,
-                         importance,
-                         &mut is_first_serialization)?;
-                }
+                append_serialization::<Cloned<slice::Iter< _>>, _>(
+                    dest,
+                    &shorthand,
+                    value,
+                    importance,
+                    &mut is_first_serialization,
+                )?;
 
                 for current_longhand in &current_longhands {
                     // Substep 9
                     already_serialized.insert(current_longhand.id());
                 }
 
                 // FIXME(https://github.com/w3c/csswg-drafts/issues/1774)
                 // The specification does not include an instruction to abort
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -963,20 +963,17 @@ def set_gecko_property(ffi_name, expr):
             }
         }
     }
 </%def>
 
 <%
 transform_functions = [
     ("Matrix3D", "matrix3d", ["number"] * 16),
-    ("PrefixedMatrix3D", "matrix3d", ["number"] * 12 + ["lopon"] * 2
-                                        + ["lon"] + ["number"]),
     ("Matrix", "matrix", ["number"] * 6),
-    ("PrefixedMatrix", "matrix", ["number"] * 4 + ["lopon"] * 2),
     ("Translate", "translate", ["lop", "optional_lop"]),
     ("Translate3D", "translate3d", ["lop", "lop", "length"]),
     ("TranslateX", "translatex", ["lop"]),
     ("TranslateY", "translatey", ["lop"]),
     ("TranslateZ", "translatez", ["length"]),
     ("Scale3D", "scale3d", ["number"] * 3),
     ("Scale", "scale", ["number", "optional_number"]),
     ("ScaleX", "scalex", ["number"]),
@@ -1022,18 +1019,16 @@ transform_functions = [
         # %s substituted with the corresponding variable
         css_value_setters = {
             "length" : "bindings::Gecko_CSSValue_SetPixelLength(%s, %s.px())",
             "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s.0)",
             # Note: This is an integer type, but we use it as a percentage value in Gecko, so
             #       need to cast it to f32.
             "integer_to_percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s as f32)",
             "lop" : "%s.set_lop(%s)",
-            "lopon" : "set_lopon(%s, %s)",
-            "lon" : "set_lon(%s, %s)",
             "angle" : "%s.set_angle(%s)",
             "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
             # Note: We use nsCSSValueSharedList here, instead of nsCSSValueList_heap
             #       because this function is not called on the main thread and
             #       nsCSSValueList_heap is not thread safe.
             "list" : "%s.set_shared_list(%s.0.iter().map(&convert_to_ns_css_value));",
         }
     %>
@@ -1101,26 +1096,17 @@ transform_functions = [
             post_symbols = "})"
         field_names = None
         if keyword == "interpolatematrix":
             field_names = ["from_list", "to_list", "progress"]
         elif keyword == "accumulatematrix":
             field_names = ["from_list", "to_list", "count"]
 
     %>
-    <%
-
-        guard = ""
-        if name == "Matrix3D" or name == "Matrix":
-            guard = "if !needs_prefix "
-        elif name == "PrefixedMatrix3D" or name == "PrefixedMatrix":
-            guard = "if needs_prefix "
-
-    %>
-    structs::nsCSSKeyword::eCSSKeyword_${keyword} ${guard}=> {
+    structs::nsCSSKeyword::eCSSKeyword_${keyword} => {
         ::values::generics::transform::TransformOperation::${name}${pre_symbols}
         % for index, item in enumerate(items):
             % if keyword == "matrix3d":
                 m${index / 4 + 1}${index % 4 + 1}:
             % elif keyword == "matrix":
                 ${chr(ord('a') + index)}:
             % elif keyword == "interpolatematrix" or keyword == "accumulatematrix":
                 ${field_names[index]}:
@@ -1144,42 +1130,25 @@ transform_functions = [
         ${post_symbols}
     },
 </%def>
 
 fn set_single_transform_function(
     servo_value: &values::computed::TransformOperation,
     gecko_value: &mut structs::nsCSSValue /* output */
 ) {
-    use values::computed::{Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber};
     use values::computed::TransformOperation;
     use values::generics::transform::{Matrix, Matrix3D};
 
     let convert_to_ns_css_value = |item: &TransformOperation| -> structs::nsCSSValue {
         let mut value = structs::nsCSSValue::null();
         set_single_transform_function(item, &mut value);
         value
     };
 
-    unsafe fn set_lopon(css: &mut structs::nsCSSValue, lopon: LengthOrPercentageOrNumber) {
-        let lop = match lopon {
-            Either::First(number) => LengthOrPercentage::Length(Length::new(number)),
-            Either::Second(lop) => lop,
-        };
-        css.set_lop(lop);
-    }
-
-    unsafe fn set_lon(css: &mut structs::nsCSSValue, lopon: LengthOrNumber) {
-        let length = match lopon {
-            Either::Second(number) => Length::new(number),
-            Either::First(l) => l,
-        };
-        bindings::Gecko_CSSValue_SetPixelLength(css, length.px())
-    }
-
     unsafe {
         match *servo_value {
             % for servo, gecko, format in transform_functions:
                 ${transform_function_arm(servo, gecko, format)}
             % endfor
         }
     }
 }
@@ -1223,29 +1192,16 @@ fn clone_single_transform_function(
                             .map(|item| clone_single_transform_function(item))
                             .collect()
     };
 
     let transform_function = unsafe {
         bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0))
     };
 
-    let needs_prefix = if transform_function == structs::nsCSSKeyword::eCSSKeyword_matrix3d {
-        unsafe {
-            bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 13).mUnit
-                    != structs::nsCSSUnit::eCSSUnit_Number ||
-            bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 14).mUnit
-                    != structs::nsCSSUnit::eCSSUnit_Number ||
-            bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 15).mUnit
-                    != structs::nsCSSUnit::eCSSUnit_Number
-        }
-    } else {
-        false
-    };
-
     unsafe {
         match transform_function {
             % for servo, gecko, format in transform_functions:
                 ${computed_operation_arm(servo, gecko, format)}
             % endfor
             _ => panic!("unacceptable transform function"),
         }
     }
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -390,17 +390,17 @@
     gecko_pref="layout.css.scroll-snap.enabled",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-destination)",
     animation_value_type="discrete",
     allow_empty="NotInitial"
 )}
 
 ${helpers.predefined_type("transform", "Transform",
                           "generics::transform::Transform::none()",
-                          extra_prefixes="webkit",
+                          extra_prefixes="webkit moz",
                           animation_value_type="ComputedValue",
                           gecko_ffi_name="mSpecifiedTransform",
                           flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
                           spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
                           servo_restyle_damage = "reflow_out_of_flow")}
 
 ${helpers.predefined_type("rotate", "Rotate",
                           "generics::transform::Rotate::None",
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -788,24 +788,22 @@ bitflags! {
         /// This property requires a stacking context.
         const CREATES_STACKING_CONTEXT = 1 << 0;
         /// This property has values that can establish a containing block for
         /// fixed positioned and absolutely positioned elements.
         const FIXPOS_CB = 1 << 1;
         /// This property has values that can establish a containing block for
         /// absolutely positioned elements.
         const ABSPOS_CB = 1 << 2;
-        /// This shorthand property is an alias of another property.
-        const SHORTHAND_ALIAS_PROPERTY = 1 << 3;
         /// This longhand property applies to ::first-letter.
-        const APPLIES_TO_FIRST_LETTER = 1 << 4;
+        const APPLIES_TO_FIRST_LETTER = 1 << 3;
         /// This longhand property applies to ::first-line.
-        const APPLIES_TO_FIRST_LINE = 1 << 5;
+        const APPLIES_TO_FIRST_LINE = 1 << 4;
         /// This longhand property applies to ::placeholder.
-        const APPLIES_TO_PLACEHOLDER = 1 << 6;
+        const APPLIES_TO_PLACEHOLDER = 1 << 5;
     }
 }
 
 /// An identifier for a given longhand property.
 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq)]
 #[repr(u16)]
 pub enum LonghandId {
     % for i, property in enumerate(data.longhands):
@@ -1682,27 +1680,20 @@ pub struct VariableDeclaration {
 
 impl ToCss for VariableDeclaration {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
         // https://drafts.csswg.org/css-variables/#variables-in-shorthands
         match self.value.from_shorthand {
-            // Normally, we shouldn't be printing variables here if they came from
-            // shorthands. But we should allow properties that came from shorthand
-            // aliases. That also matches with the Gecko behavior.
-            // FIXME(emilio): This is just a hack for `-moz-transform`.
-            Some(shorthand) if shorthand.flags().contains(PropertyFlags::SHORTHAND_ALIAS_PROPERTY) => {
-                dest.write_str(&*self.value.css)?
-            }
             None => {
                 dest.write_str(&*self.value.css)?
             }
-            _ => {},
+            Some(..) => {},
         }
         Ok(())
     }
 }
 
 /// A custom property declaration with the property name and the declared value.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[derive(Clone, PartialEq)]
@@ -1763,30 +1754,21 @@ impl PropertyDeclaration {
             _ => id,
         });
         PropertyDeclarationId::Longhand(id)
     }
 
     fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> {
         match *self {
             PropertyDeclaration::WithVariables(ref declaration) => {
-                if let Some(s) = declaration.value.from_shorthand {
-                    if s == shorthand {
-                        Some(&*declaration.value.css)
-                    } else { None }
-                } else {
-                    // Normally, longhand property that doesn't come from a shorthand
-                    // should return None here. But we return Some to longhands if they
-                    // came from a shorthand alias. Because for example, we should be able to
-                    // get -moz-transform's value from transform.
-                    if shorthand.flags().contains(PropertyFlags::SHORTHAND_ALIAS_PROPERTY) {
-                        return Some(&*declaration.value.css);
-                    }
-                    None
+                let s = declaration.value.from_shorthand?;
+                if s != shorthand {
+                    return None;
                 }
+                Some(&*declaration.value.css)
             },
             _ => None,
         }
     }
 
     /// Returns a CSS-wide keyword if the declaration's value is one.
     pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
         match *self {
--- a/servo/components/style/properties/shorthand/box.mako.rs
+++ b/servo/components/style/properties/shorthand/box.mako.rs
@@ -387,17 +387,17 @@ macro_rules! try_parse_one {
 </%helpers:shorthand>
 
 <%helpers:shorthand name="overscroll-behavior" products="gecko"
                     gecko_pref="layout.css.overscroll-behavior.enabled"
                     sub_properties="overscroll-behavior-x overscroll-behavior-y"
                     spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties">
     pub fn parse_value<'i, 't>(
         _: &ParserContext,
-        input: &mut Parser<'i, 't>
+        input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         use values::specified::OverscrollBehavior;
         let behavior_x = OverscrollBehavior::parse(input)?;
         let behavior_y = input.try(OverscrollBehavior::parse).unwrap_or(behavior_x);
         Ok(expanded! {
             overscroll_behavior_x: behavior_x,
             overscroll_behavior_y: behavior_y,
         })
@@ -411,23 +411,8 @@ macro_rules! try_parse_one {
             if self.overscroll_behavior_y != self.overscroll_behavior_x {
                 dest.write_str(" ")?;
                 self.overscroll_behavior_y.to_css(dest)?;
             }
             Ok(())
         }
     }
 </%helpers:shorthand>
-
-<%helpers:shorthand name="-moz-transform" products="gecko"
-                    sub_properties="transform"
-                    gecko_pref="layout.css.prefixes.transforms"
-                    flags="SHORTHAND_ALIAS_PROPERTY"
-                    derive_serialize="True"
-                    spec="Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/transform">
-    pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-                               -> Result<Longhands, ParseError<'i>> {
-        use values::specified::transform::Transform;
-        Ok(expanded! {
-            transform: Transform::parse_prefixed(context, input)?,
-        })
-    }
-</%helpers:shorthand>
--- a/servo/components/style/stylesheet_set.rs
+++ b/servo/components/style/stylesheet_set.rs
@@ -8,17 +8,17 @@ use dom::TElement;
 use invalidation::stylesheets::StylesheetInvalidationSet;
 use media_queries::Device;
 use selector_parser::SnapshotMap;
 use shared_lock::SharedRwLockReadGuard;
 use std::{mem, slice};
 use stylesheets::{Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument};
 
 /// Entry for a StylesheetSet.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(MallocSizeOf)]
 struct StylesheetSetEntry<S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
 {
     /// The sheet.
     sheet: S,
 
     /// Whether this sheet has been part of at least one flush.
@@ -98,18 +98,17 @@ where
             }
 
             self.current = None;
         }
     }
 }
 
 /// The validity of the data in a given cascade origin.
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
 pub enum DataValidity {
     /// The origin is clean, all the data already there is valid, though we may
     /// have new sheets at the end.
     Valid = 0,
 
     /// The cascade data is invalid, but not the invalidation data (which is
     /// order-independent), and thus only the cascade data should be inserted.
     CascadeInvalid = 1,
@@ -226,17 +225,17 @@ where
                 DataValidity::FullyInvalid => SheetRebuildKind::Full,
             };
 
             return Some((&potential_sheet.sheet, rebuild_kind));
         }
     }
 }
 
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(MallocSizeOf)]
 struct SheetCollection<S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
 {
     /// The actual list of stylesheets.
     ///
     /// This is only a list of top-level stylesheets, and as such it doesn't
     /// include recursive `@import` rules.
@@ -553,16 +552,17 @@ where
             self.collections
                 .borrow_mut_for_origin(&origin)
                 .set_data_validity_at_least(DataValidity::FullyInvalid);
         }
     }
 }
 
 /// The set of stylesheets effective for a given XBL binding or Shadow Root.
+#[derive(MallocSizeOf)]
 pub struct AuthorStylesheetSet<S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
 {
     /// The actual style sheets.
     collection: SheetCollection<S>,
     /// The set of invalidations scheduled for this collection.
     invalidations: StylesheetInvalidationSet,
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1830,18 +1830,17 @@ impl<'a> SelectorVisitor for StylistSele
             _ => {},
         }
 
         true
     }
 }
 
 /// A set of rules for element and pseudo-elements.
-#[derive(Debug, Default)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(Debug, Default, MallocSizeOf)]
 struct ElementAndPseudoRules {
     /// Rules from stylesheets at this `CascadeData`'s origin.
     element_map: SelectorMap<Rule>,
 
     /// Rules from stylesheets at this `CascadeData`'s origin that correspond
     /// to a given pseudo-element.
     ///
     /// FIXME(emilio): There are a bunch of wasted entries here in practice.
@@ -1899,18 +1898,17 @@ impl ElementAndPseudoRules {
     }
 }
 
 /// Data resulting from performing the CSS cascade that is specific to a given
 /// origin.
 ///
 /// FIXME(emilio): Consider renaming and splitting in `CascadeData` and
 /// `InvalidationData`? That'd make `clear_cascade_data()` clearer.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[derive(Debug)]
+#[derive(Debug, MallocSizeOf)]
 pub struct CascadeData {
     /// The data coming from normal style rules that apply to elements at this
     /// cascade level.
     normal_rules: ElementAndPseudoRules,
 
     /// The data coming from ::slotted() pseudo-element rules.
     ///
     /// We need to store them separately because an element needs to match
@@ -1922,17 +1920,17 @@ pub struct CascadeData {
 
     /// The invalidation map for these rules.
     invalidation_map: InvalidationMap,
 
     /// The attribute local names that appear in attribute selectors.  Used
     /// to avoid taking element snapshots when an irrelevant attribute changes.
     /// (We don't bother storing the namespace, since namespaced attributes
     /// are rare.)
-    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "just an array")]
+    #[ignore_malloc_size_of = "just an array"]
     attribute_dependencies: NonCountingBloomFilter,
 
     /// Whether `"style"` appears in an attribute selector.  This is not common,
     /// and by tracking this explicitly, we can avoid taking an element snapshot
     /// in the common case of style=""` changing due to modifying
     /// `element.style`.  (We could track this in `attribute_dependencies`, like
     /// all other attributes, but we should probably not risk incorrectly
     /// returning `true` for `"style"` just due to a hash collision.)
@@ -1947,23 +1945,23 @@ pub struct CascadeData {
     /// to tell whether we need to restyle the entire document when a document
     /// state bit changes.
     document_state_dependencies: DocumentState,
 
     /// The ids that appear in the rightmost complex selector of selectors (and
     /// hence in our selector maps).  Used to determine when sharing styles is
     /// safe: we disallow style sharing for elements whose id matches this
     /// filter, and hence might be in one of our selector maps.
-    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "just an array")]
+    #[ignore_malloc_size_of = "just an array"]
     mapped_ids: NonCountingBloomFilter,
 
     /// Selectors that require explicit cache revalidation (i.e. which depend
     /// on state that is not otherwise visible to the cache, like attributes or
     /// tree-structural state like child index and pseudos).
-    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
+    #[ignore_malloc_size_of = "Arc"]
     selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
 
     /// A map with all the animations at this `CascadeData`'s origin, indexed
     /// by name.
     animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
 
     /// Effective media query results cached from the last rebuild.
     effective_media_query_results: EffectiveMediaQueryResults,
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -1,39 +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/. */
 
 //! Computed types for CSS values that are related to transformations.
 
 use euclid::{Transform3D, Vector3D};
 use num_traits::Zero;
-use super::{CSSFloat, Either};
+use super::CSSFloat;
 use values::animated::ToAnimatedZero;
 use values::computed::{Angle, Integer, Length, LengthOrPercentage, Number, Percentage};
-use values::computed::{LengthOrNumber, LengthOrPercentageOrNumber};
 use values::generics::transform::{self, Matrix as GenericMatrix, Matrix3D as GenericMatrix3D};
 use values::generics::transform::{Transform as GenericTransform, TransformOperation as GenericTransformOperation};
 use values::generics::transform::Rotate as GenericRotate;
 use values::generics::transform::Scale as GenericScale;
 use values::generics::transform::TimingFunction as GenericTimingFunction;
 use values::generics::transform::TransformOrigin as GenericTransformOrigin;
 use values::generics::transform::Translate as GenericTranslate;
 
 pub use values::generics::transform::TransformStyle;
 
 /// A single operation in a computed CSS `transform`
 pub type TransformOperation = GenericTransformOperation<
     Angle,
     Number,
     Length,
     Integer,
-    LengthOrNumber,
     LengthOrPercentage,
-    LengthOrPercentageOrNumber,
 >;
 /// A computed CSS `transform`
 pub type Transform = GenericTransform<TransformOperation>;
 
 /// The computed value of a CSS `<transform-origin>`
 pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
 
 /// A computed timing function.
@@ -51,22 +48,18 @@ impl TransformOrigin {
             LengthOrPercentage::Percentage(Percentage(0.5)),
             Length::new(0.),
         )
     }
 }
 
 /// computed value of matrix3d()
 pub type Matrix3D = GenericMatrix3D<Number>;
-/// computed value of matrix3d() in -moz-transform
-pub type PrefixedMatrix3D = GenericMatrix3D<Number, LengthOrPercentageOrNumber, LengthOrNumber>;
 /// computed value of matrix()
 pub type Matrix = GenericMatrix<Number>;
-/// computed value of matrix() in -moz-transform
-pub type PrefixedMatrix = GenericMatrix<Number, LengthOrPercentageOrNumber>;
 
 // we rustfmt_skip here because we want the matrices to look like
 // matrices instead of being split across lines
 #[cfg_attr(rustfmt, rustfmt_skip)]
 impl Matrix3D {
     #[inline]
     /// Get an identity matrix
     pub fn identity() -> Self {
@@ -91,31 +84,16 @@ impl Matrix3D {
             })
         } else {
             Err(())
         }
     }
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
-impl PrefixedMatrix3D {
-    #[inline]
-    /// Get an identity matrix
-    pub fn identity() -> Self {
-        Self {
-            m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0,
-            m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0,
-            m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
-            m41: Either::First(0.), m42: Either::First(0.),
-            m43: Either::First(Length::new(0.)), m44: 1.0
-        }
-    }
-}
-
-#[cfg_attr(rustfmt, rustfmt_skip)]
 impl Matrix {
     #[inline]
     /// Get an identity matrix
     pub fn identity() -> Self {
         Self {
             a: 1., c: 0., /* 0      0*/
             b: 0., d: 1., /* 0      0*/
             /* 0      0      1      0 */
@@ -132,30 +110,16 @@ impl From<Matrix> for Matrix3D {
             m21: m.c, m22: m.d, m23: 0.0, m24: 0.0,
             m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
             m41: m.e, m42: m.f, m43: 0.0, m44: 1.0
         }
     }
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
-impl PrefixedMatrix {
-    #[inline]
-    /// Get an identity matrix
-    pub fn identity() -> Self {
-        Self {
-            a: 1.,                    c: 0., /*            0      0 */
-            b: 0.,                    d: 1., /*            0      0 */
-            /* 0                      0                    1      0 */
-            e: Either::First(0.), f: Either::First(0.), /* 0      1 */
-        }
-    }
-}
-
-#[cfg_attr(rustfmt, rustfmt_skip)]
 impl From<Transform3D<CSSFloat>> for Matrix3D {
     #[inline]
     fn from(m: Transform3D<CSSFloat>) -> Self {
         Matrix3D {
             m11: m.m11, m12: m.m12, m13: m.m13, m14: m.m14,
             m21: m.m21, m22: m.m22, m23: m.m23, m24: m.m24,
             m31: m.m31, m32: m.m32, m33: m.m33, m34: m.m34,
             m41: m.m41, m42: m.m42, m43: m.m43, m44: m.m44
@@ -208,27 +172,17 @@ impl TransformOperation {
 
 /// Build an equivalent 'identity transform function list' based
 /// on an existing transform list.
 /// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
 impl ToAnimatedZero for TransformOperation {
     fn to_animated_zero(&self) -> Result<Self, ()> {
         match *self {
             GenericTransformOperation::Matrix3D(..) => Ok(GenericTransformOperation::Matrix3D(Matrix3D::identity())),
-            GenericTransformOperation::PrefixedMatrix3D(..) => {
-                Ok(GenericTransformOperation::PrefixedMatrix3D(
-                    PrefixedMatrix3D::identity(),
-                ))
-            },
             GenericTransformOperation::Matrix(..) => Ok(GenericTransformOperation::Matrix(Matrix::identity())),
-            GenericTransformOperation::PrefixedMatrix(..) => {
-                Ok(GenericTransformOperation::PrefixedMatrix(
-                    PrefixedMatrix::identity(),
-                ))
-            },
             GenericTransformOperation::Skew(sx, sy) => {
                 Ok(GenericTransformOperation::Skew(
                     sx.to_animated_zero()?,
                     sy.to_animated_zero()?,
                 ))
             },
             GenericTransformOperation::SkewX(s) => Ok(GenericTransformOperation::SkewX(s.to_animated_zero()?)),
             GenericTransformOperation::SkewY(s) => Ok(GenericTransformOperation::SkewY(s.to_animated_zero()?)),
--- a/servo/components/style/values/generics/font.rs
+++ b/servo/components/style/values/generics/font.rs
@@ -192,47 +192,37 @@ where
             kw: x,
             factor: 1.,
             offset: Au(0).into(),
         }
     }
 }
 
 /// CSS font keywords
-#[derive(Animate, ComputeSquaredDistance, MallocSizeOf, ToAnimatedValue, ToAnimatedZero)]
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
+#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero)]
 #[allow(missing_docs)]
 pub enum KeywordSize {
-    XXSmall = 1, // This is to enable the NonZero optimization
-                 // which simplifies the representation of Option<KeywordSize>
-                 // in bindgen
+    XXSmall,
     XSmall,
     Small,
     Medium,
     Large,
     XLarge,
     XXLarge,
     // This is not a real font keyword and will not parse
     // HTML font-size 7 corresponds to this value
     XXXLarge,
 }
 
 impl KeywordSize {
     /// Convert to an HTML <font size> value
-    pub fn html_size(&self) -> u8 {
-        match *self {
-            KeywordSize::XXSmall => 0,
-            KeywordSize::XSmall => 1,
-            KeywordSize::Small => 2,
-            KeywordSize::Medium => 3,
-            KeywordSize::Large => 4,
-            KeywordSize::XLarge => 5,
-            KeywordSize::XXLarge => 6,
-            KeywordSize::XXXLarge => 7,
-        }
+    #[inline]
+    pub fn html_size(self) -> u8 {
+        self as u8
     }
 }
 
 impl Default for KeywordSize {
     fn default() -> Self {
         KeywordSize::Medium
     }
 }
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -14,34 +14,34 @@ use values::computed::length::Length as 
 use values::computed::length::LengthOrPercentage as ComputedLengthOrPercentage;
 use values::specified::length::Length as SpecifiedLength;
 use values::specified::length::LengthOrPercentage as SpecifiedLengthOrPercentage;
 
 /// A generic 2D transformation matrix.
 #[allow(missing_docs)]
 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 #[css(comma, function)]
-pub struct Matrix<T, U = T> {
+pub struct Matrix<T> {
     pub a: T,
     pub b: T,
     pub c: T,
     pub d: T,
-    pub e: U,
-    pub f: U,
+    pub e: T,
+    pub f: T,
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(rustfmt, rustfmt_skip)]
 #[css(comma, function = "matrix3d")]
 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
-pub struct Matrix3D<T, U = T, V = T> {
+pub struct Matrix3D<T> {
     pub m11: T, pub m12: T, pub m13: T, pub m14: T,
     pub m21: T, pub m22: T, pub m23: T, pub m24: T,
     pub m31: T, pub m32: T, pub m33: T, pub m34: T,
-    pub m41: U, pub m42: U, pub m43: V, pub m44: T,
+    pub m41: T, pub m42: T, pub m43: T, pub m44: T,
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
 impl<T: Into<f64>> From<Matrix<T>> for Transform3D<f64> {
     #[inline]
     fn from(m: Matrix<T>) -> Self {
         Transform3D::row_major(
             m.a.into(), m.b.into(), 0.0, 0.0,
@@ -195,27 +195,21 @@ impl TimingKeyword {
             TimingKeyword::EaseOut => (0., 0., 0.58, 1.),
             TimingKeyword::EaseInOut => (0.42, 0., 0.58, 1.),
         }
     }
 }
 
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 /// A single operation in the list of a `transform` value
-pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> {
+pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage> {
     /// Represents a 2D 2x3 matrix.
     Matrix(Matrix<Number>),
-    /// Represents a 3D 4x4 matrix with percentage and length values.
-    /// For `moz-transform`.
-    PrefixedMatrix(Matrix<Number, LoPoNumber>),
     /// Represents a 3D 4x4 matrix.
     Matrix3D(Matrix3D<Number>),
-    /// Represents a 3D 4x4 matrix with percentage and length values.
-    /// For `moz-transform`.
-    PrefixedMatrix3D(Matrix3D<Number, LoPoNumber, LengthOrNumber>),
     /// A 2D skew.
     ///
     /// If the second angle is not provided it is assumed zero.
     ///
     /// Syntax can be skew(angle) or skew(angle, angle)
     #[css(comma, function)]
     Skew(Angle, Option<Angle>),
     /// skewX(angle)
@@ -295,32 +289,28 @@ pub enum TransformOperation<Angle, Numbe
         #[compute(ignore_bound)]
         #[css(ignore_bound)]
         from_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
-                LengthOrNumber,
                 LengthOrPercentage,
-                LoPoNumber,
             >,
         >,
         #[compute(ignore_bound)]
         #[css(ignore_bound)]
         to_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
-                LengthOrNumber,
                 LengthOrPercentage,
-                LoPoNumber,
             >,
         >,
         #[compute(clone)]
         progress: computed::Percentage,
     },
     /// A intermediate type for accumulation of mismatched transform lists.
     #[allow(missing_docs)]
     #[css(comma, function = "accumulatematrix")]
@@ -328,45 +318,41 @@ pub enum TransformOperation<Angle, Numbe
         #[compute(ignore_bound)]
         #[css(ignore_bound)]
         from_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
-                LengthOrNumber,
                 LengthOrPercentage,
-                LoPoNumber,
             >,
         >,
         #[compute(ignore_bound)]
         #[css(ignore_bound)]
         to_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
-                LengthOrNumber,
                 LengthOrPercentage,
-                LoPoNumber,
             >,
         >,
         count: Integer,
     },
 }
 
 #[derive(Animate, ToComputedValue)]
 #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
 /// A value of the `transform` property
 pub struct Transform<T>(pub Vec<T>);
 
-impl<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber>
-    TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> {
+impl<Angle, Number, Length, Integer, LengthOrPercentage>
+    TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage> {
     /// Check if it is any translate function
     pub fn is_translate(&self) -> bool {
         use self::TransformOperation::*;
         match *self {
             Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => true,
             _ => false,
         }
     }
@@ -445,32 +431,31 @@ impl ToAbsoluteLength for ComputedLength
 pub trait ToMatrix {
     /// Check if it is a 3d transform function.
     fn is_3d(&self) -> bool;
 
     /// Return the equivalent 3d matrix.
     fn to_3d_matrix(&self, reference_box: Option<&Rect<Au>>) -> Result<Transform3D<f64>, ()>;
 }
 
-impl<Angle, Number, Length, Integer, LoN, LoP, LoPoNumber> ToMatrix
-    for TransformOperation<Angle, Number, Length, Integer, LoN, LoP, LoPoNumber>
+impl<Angle, Number, Length, Integer, LoP> ToMatrix
+    for TransformOperation<Angle, Number, Length, Integer, LoP>
 where
     Angle: Copy + AsRef<computed::angle::Angle>,
     Number: Copy + Into<f32> + Into<f64>,
     Length: ToAbsoluteLength,
     LoP: ToAbsoluteLength,
 {
     #[inline]
     fn is_3d(&self) -> bool {
         use self::TransformOperation::*;
         match *self {
             Translate3D(..) | TranslateZ(..) |
             Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..) |
-            Scale3D(..) | ScaleZ(..) |
-            Perspective(..) | Matrix3D(..) | PrefixedMatrix3D(..) => true,
+            Scale3D(..) | ScaleZ(..) | Perspective(..) | Matrix3D(..) => true,
             _ => false,
         }
     }
 
     /// If |reference_box| is None, we will drop the percent part from translate because
     /// we cannot resolve it without the layout info, for computed TransformOperation.
     /// However, for specified TransformOperation, we will return Err(()) if there is any relative
     /// lengths because the only caller, DOMMatrix, doesn't accept relative lengths.
@@ -548,20 +533,16 @@ where
             SkewY(theta) => {
                 Transform3D::create_skew(
                     euclid::Angle::radians(0.),
                     euclid::Angle::radians(theta.as_ref().radians64()),
                 )
             },
             Matrix3D(m) => m.into(),
             Matrix(m) => m.into(),
-            PrefixedMatrix3D(_) | PrefixedMatrix(_) => {
-                unreachable!("-moz-transform` is not implemented in Servo yet, and DOMMatrix \
-                              doesn't support this")
-            },
             InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
                 // TODO: Convert InterpolateMatrix/AccumulateMatrix into a valid Transform3D by
                 // the reference box and do interpolation on these two Transform3D matrices.
                 // Both Gecko and Servo don't support this for computing distance, and Servo
                 // doesn't support animations on InterpolateMatrix/AccumulateMatrix, so
                 // return an identity matrix.
                 // Note: DOMMatrix doesn't go into this arm.
                 Transform3D::identity()
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -13,48 +13,44 @@ use values::computed::{Percentage as Com
 use values::computed::transform::TimingFunction as ComputedTimingFunction;
 use values::generics::transform::{Matrix3D, Transform as GenericTransform};
 use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction, Matrix};
 use values::generics::transform::{TimingKeyword, TransformOrigin as GenericTransformOrigin};
 use values::generics::transform::Rotate as GenericRotate;
 use values::generics::transform::Scale as GenericScale;
 use values::generics::transform::TransformOperation as GenericTransformOperation;
 use values::generics::transform::Translate as GenericTranslate;
-use values::specified::{self, Angle, Number, Length, Integer};
-use values::specified::{LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber};
+use values::specified::{self, Angle, Number, Length, Integer, LengthOrPercentage};
 use values::specified::position::{Side, X, Y};
 
 pub use values::generics::transform::TransformStyle;
 
 /// A single operation in a specified CSS `transform`
 pub type TransformOperation = GenericTransformOperation<
     Angle,
     Number,
     Length,
     Integer,
-    LengthOrNumber,
     LengthOrPercentage,
-    LengthOrPercentageOrNumber,
 >;
 
 /// A specified CSS `transform`
 pub type Transform = GenericTransform<TransformOperation>;
 
 /// The specified value of a CSS `<transform-origin>`
 pub type TransformOrigin = GenericTransformOrigin<OriginComponent<X>, OriginComponent<Y>, Length>;
 
 impl Transform {
     /// Internal parse function for deciding if we wish to accept prefixed values or not
     ///
     /// `transform` allows unitless zero angles as an exception, see:
     /// https://github.com/w3c/csswg-drafts/issues/1162
     fn parse_internal<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
-        prefixed: bool,
     ) -> Result<Self, ParseError<'i>> {
         use style_traits::{Separator, Space};
 
         if input
             .try(|input| input.expect_ident_matching("none"))
             .is_ok()
         {
             return Ok(GenericTransform(Vec::new()));
@@ -70,29 +66,21 @@ impl Transform {
                         let a = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let b = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let c = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let d = Number::parse(context, input)?;
                         input.expect_comma()?;
-                        if !prefixed {
-                            // Standard matrix parsing.
-                            let e = Number::parse(context, input)?;
-                            input.expect_comma()?;
-                            let f = Number::parse(context, input)?;
-                            Ok(GenericTransformOperation::Matrix(Matrix { a, b, c, d, e, f }))
-                        } else {
-                            // Non-standard prefixed matrix parsing for -moz-transform.
-                            let e = LengthOrPercentageOrNumber::parse(context, input)?;
-                            input.expect_comma()?;
-                            let f = LengthOrPercentageOrNumber::parse(context, input)?;
-                            Ok(GenericTransformOperation::PrefixedMatrix(Matrix { a, b, c, d, e, f }))
-                        }
+                        // Standard matrix parsing.
+                        let e = Number::parse(context, input)?;
+                        input.expect_comma()?;
+                        let f = Number::parse(context, input)?;
+                        Ok(GenericTransformOperation::Matrix(Matrix { a, b, c, d, e, f }))
                     },
                     "matrix3d" => {
                         let m11 = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let m12 = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let m13 = Number::parse(context, input)?;
                         input.expect_comma()?;
@@ -109,47 +97,30 @@ impl Transform {
                         let m31 = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let m32 = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let m33 = Number::parse(context, input)?;
                         input.expect_comma()?;
                         let m34 = Number::parse(context, input)?;
                         input.expect_comma()?;
-                        if !prefixed {
-                            // Standard matrix3d parsing.
-                            let m41 = Number::parse(context, input)?;
-                            input.expect_comma()?;
-                            let m42 = Number::parse(context, input)?;
-                            input.expect_comma()?;
-                            let m43 = Number::parse(context, input)?;
-                            input.expect_comma()?;
-                            let m44 = Number::parse(context, input)?;
-                            Ok(GenericTransformOperation::Matrix3D(Matrix3D {
-                                m11, m12, m13, m14,
-                                m21, m22, m23, m24,
-                                m31, m32, m33, m34,
-                                m41, m42, m43, m44,
-                            }))
-                        } else {
-                            // Non-standard prefixed matrix parsing for -moz-transform.
-                            let m41 = LengthOrPercentageOrNumber::parse(context, input)?;
-                            input.expect_comma()?;
-                            let m42 = LengthOrPercentageOrNumber::parse(context, input)?;
-                            input.expect_comma()?;
-                            let m43 = LengthOrNumber::parse(context, input)?;
-                            input.expect_comma()?;
-                            let m44 = Number::parse(context, input)?;
-                            Ok(GenericTransformOperation::PrefixedMatrix3D(Matrix3D {
-                                m11, m12, m13, m14,
-                                m21, m22, m23, m24,
-                                m31, m32, m33, m34,
-                                m41, m42, m43, m44,
-                            }))
-                        }
+                        // Standard matrix3d parsing.
+                        let m41 = Number::parse(context, input)?;
+                        input.expect_comma()?;
+                        let m42 = Number::parse(context, input)?;
+                        input.expect_comma()?;
+                        let m43 = Number::parse(context, input)?;
+                        input.expect_comma()?;
+                        let m44 = Number::parse(context, input)?;
+                        Ok(GenericTransformOperation::Matrix3D(Matrix3D {
+                            m11, m12, m13, m14,
+                            m21, m22, m23, m24,
+                            m31, m32, m33, m34,
+                            m41, m42, m43, m44,
+                        }))
                     },
                     "translate" => {
                         let sx = specified::LengthOrPercentage::parse(context, input)?;
                         if input.try(|input| input.expect_comma()).is_ok() {
                             let sy = specified::LengthOrPercentage::parse(context, input)?;
                             Ok(GenericTransformOperation::Translate(sx, Some(sy)))
                         } else {
                             Ok(GenericTransformOperation::Translate(sx, None))
@@ -254,34 +225,24 @@ impl Transform {
                     },
                     _ => Err(()),
                 };
                 result
                     .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone())))
             })
         })?))
     }
-
-    /// Parses `-moz-transform` property. This prefixed property also accepts LengthOrPercentage
-    /// in the nondiagonal homogeneous components of matrix and matrix3d.
-    #[inline]
-    pub fn parse_prefixed<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>> {
-        Transform::parse_internal(context, input, true)
-    }
 }
 
 impl Parse for Transform {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<Self, ParseError<'i>> {
-        Transform::parse_internal(context, input, false)
+        Transform::parse_internal(context, input)
     }
 }
 
 /// The specified value of a component of a CSS `<transform-origin>`.
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
 pub enum OriginComponent<S> {
     /// `center`
     Center,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -10,16 +10,17 @@ use selectors::NthIndexCache;
 use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
 use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
 use smallvec::SmallVec;
 use std::cell::RefCell;
 use std::env;
 use std::fmt::Write;
 use std::iter;
 use std::mem;
+use std::os::raw::c_void;
 use std::ptr;
 use style::applicable_declarations::ApplicableDeclarationBlock;
 use style::author_styles::AuthorStyles;
 use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
 use style::context::ThreadLocalStyleContext;
 use style::counter_style;
 use style::data::{ElementStyles, self};
 use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
@@ -1180,16 +1181,36 @@ pub unsafe extern "C" fn Servo_AuthorSty
     styles.flush::<GeckoElement>(
         stylist.device(),
         stylist.quirks_mode(),
         &guard,
     );
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn Servo_AuthorStyles_SizeOfIncludingThis(
+    malloc_size_of: GeckoMallocSizeOf,
+    malloc_enclosing_size_of: GeckoMallocSizeOf,
+    styles: RawServoAuthorStylesBorrowed,
+) -> usize {
+    // We cannot `use` MallocSizeOf at the top level, otherwise the compiler
+    // would complain in `Servo_StyleSheet_SizeOfIncludingThis` for `size_of`
+    // there.
+    use malloc_size_of::MallocSizeOf;
+    let malloc_size_of = malloc_size_of.unwrap();
+    let malloc_size_of_this = malloc_size_of(styles as *const RawServoAuthorStyles as *const c_void);
+
+    let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(styles);
+    let mut ops = MallocSizeOfOps::new(malloc_size_of,
+                                       Some(malloc_enclosing_size_of.unwrap()),
+                                       None);
+    malloc_size_of_this + styles.size_of(&mut ops)
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
     document_set: RawServoStyleSetBorrowed,
     non_document_styles: *mut nsTArray<RawServoAuthorStylesBorrowedMut>,
     may_affect_default_style: bool,
 ) -> structs::MediumFeaturesChangedResult {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
 
--- a/testing/geckodriver/CHANGES.md
+++ b/testing/geckodriver/CHANGES.md
@@ -4,33 +4,37 @@ Change log
 All notable changes to this program is documented in this file.
 
 Unreleased
 ----------
 
 ### Added
 
 - New `--jsdebugger` flag to open the Browser Toolbox when Firefox
-  launches.  This is useful for debugging Marionette internals.
+  launches.  This is useful for debugging Marionette internals
 
 - Introduced the temporary, boolean capability
   `moz:useNonSpecCompliantPointerOrigin` to disable the WebDriver
-  conforming behavior of calculating the Pointer Origin.
+  conforming behavior of calculating the Pointer Origin
 
 ### Changed
 
 - HTTP status code for the [`StaleElementReference`] error changed
   from 400 (Bad Request) to 404 (Not Found)
 
 - Backtraces from geckodriver no longer substitute for missing
   Marionette stacktraces
 
 - `Delete Session` now allows Firefox to safely shutdown within 70s before
   force-killing the process
 
+### Fixed
+
+- Improved error messages for malformed capabilities
+
 
 0.19.1 (2017-10-30)
 -------------------
 
 ### Changed
 
 - Search suggestions in the location bar turned off as not to
   trigger network connections
--- a/testing/geckodriver/Cargo.lock
+++ b/testing/geckodriver/Cargo.lock
@@ -1,8 +1,13 @@
+[[package]]
+name = "adler32"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "aho-corasick"
 version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -21,16 +26,21 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitflags"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "build_const"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "byteorder"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bzip2"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -39,23 +49,23 @@ dependencies = [
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "bzip2-sys"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cc"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "cfg-if"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -83,22 +93,30 @@ dependencies = [
 name = "cookie"
 version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "crc"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "flate2"
-version = "0.2.20"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miniz_oxide_c_api 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "fuchsia-zircon"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -123,17 +141,17 @@ dependencies = [
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozprofile 0.3.0",
  "mozrunner 0.5.0",
  "mozversion 0.1.2",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "webdriver 0.34.0",
- "zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "httparse"
 version = "1.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -223,36 +241,47 @@ dependencies = [
 name = "mime"
 version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
-name = "miniz-sys"
-version = "0.1.10"
+name = "miniz_oxide"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "miniz_oxide_c_api"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "mozprofile"
 version = "0.3.0"
 dependencies = [
  "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mozrunner"
 version = "0.5.0"
 dependencies = [
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozprofile 0.3.0",
  "winreg 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mozversion"
 version = "0.1.2"
 dependencies = [
@@ -560,56 +589,60 @@ name = "winreg"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "zip"
-version = "0.1.19"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [metadata]
+"checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45"
 "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
 "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
 "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
+"checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9"
 "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
 "checksum bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3eafc42c44e0d827de6b1c131175098fe7fb53b8ce8a47e65cb3ea94688be24"
 "checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
-"checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719"
+"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"
 "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
 "checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
 "checksum clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "110d43e343eb29f4f51c1db31beb879d546db27998577e5715270a54bcf41d3f"
 "checksum cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "746858cae4eae40fff37e1998320068df317bc247dc91a67c6cfa053afdc2abb"
-"checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423"
+"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
+"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
 "checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159"
 "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
 "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
 "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
 "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
 "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
 "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
 "checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2"
 "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
 "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
 "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
 "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
 "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
-"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4"
+"checksum miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aaa2d3ad070f428fffbd7d3ca2ea20bb0d8cffe9024405c44e1840bc1418b398"
+"checksum miniz_oxide_c_api 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92d98fdbd6145645828069b37ea92ca3de225e000d80702da25c20d3584b38a5"
 "checksum msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "65ba9d75bcea84e07812618fedf284a64776c2f2ea0cad6bca7f69739695a958"
 "checksum num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525"
 "checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
 "checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
 "checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
 "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
 "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
 "checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
@@ -641,9 +674,9 @@ dependencies = [
 "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
 "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
 "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 "checksum winreg 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9338067aba07889a38beaad4dbb77fa2e62e87c423b770824b3bdf412874bd2c"
-"checksum zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c0deac03fc7d43abcf19f2c2db6bd9289f9ea3d31f350e26eb0ed8b4117983c1"
+"checksum zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "10931e278527cea65682696481e6d840371d581079df529ebfee186e0eaad719"
--- a/testing/geckodriver/Cargo.toml
+++ b/testing/geckodriver/Cargo.toml
@@ -17,12 +17,12 @@ lazy_static = "1.0"
 log = { version = "0.4", features = ["std"] }
 mozprofile = { path = "../mozbase/rust/mozprofile" }
 mozrunner = { path = "../mozbase/rust/mozrunner" }
 mozversion = { path = "../mozbase/rust/mozversion" }
 regex = "0.2"
 rustc-serialize = "0.3"
 uuid = "0.1.18"
 webdriver = { path = "../webdriver" }
-zip = "0.1"
+zip = "0.3"
 
 [[bin]]
 name = "geckodriver"
--- a/testing/marionette/event.js
+++ b/testing/marionette/event.js
@@ -1012,117 +1012,16 @@ event.synthesizeKeyExpectEvent = functio
   checkExpectedEvent_(
       expectedTarget,
       expectedEvent,
       eventHandler,
       testName);
 };
 
 /**
- * Synthesize a composition event.
- *
- * @param {DOMEvent} ev
- *     The composition event information.  This must have |type|
- *     member.  The value must be "compositionstart", "compositionend" or
- *     "compositionupdate".  And also this may have |data| and |locale|
- *     which would be used for the value of each property of the
- *     composition event.  Note that the data would be ignored if the
- *     event type were "compositionstart".
- * @param {Window=} window
- *     Window object.  Defaults to the current window.
- */
-event.synthesizeComposition = function(ev, window = undefined) {
-  let domutils = getDOMWindowUtils(window);
-  domutils.sendCompositionEvent(ev.type, ev.data || "", ev.locale || "");
-};
-
-/**
- * Synthesize a text event.
- *
- * The text event's information, this has |composition| and |caret|
- * members.  |composition| has |string| and |clauses| members. |clauses|
- * must be array object.  Each object has |length| and |attr|.
- * And |caret| has |start| and |length|.  See the following tree image.
- *
- *     ev
- *      +-- composition
- *      |     +-- string
- *      |     +-- clauses[]
- *      |           +-- length
- *      |           +-- attr
- *      +-- caret
- *            +-- start
- *            +-- length
- *
- * Set the composition string to |composition.string|.  Set its clauses
- * information to the |clauses| array.
- *
- * When it's composing, set the each clauses' length
- * to the |composition.clauses[n].length|.  The sum
- * of the all length values must be same as the length of
- * |composition.string|. Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
- * |composition.clauses[n].attr|.
- *
- * When it's not composing, set 0 to the |composition.clauses[0].length|
- * and |composition.clauses[0].attr|.
- *
- * Set caret position to the |caret.start|. Its offset from the start of
- * the composition string.  Set caret length to |caret.length|.  If it's
- * larger than 0, it should be wide caret.  However, current nsEditor
- * doesn't support wide caret, therefore, you should always set 0 now.
- *
- * @param {Object.<string, ?>} ev
- *     The text event's information,
- * @param {Window=} window
- *     Window object.  Defaults to the current window.
- */
-event.synthesizeText = function(ev, window = undefined) {
-  let domutils = getDOMWindowUtils(window);
-
-  if (!ev.composition ||
-      !ev.composition.clauses ||
-      !ev.composition.clauses[0]) {
-    return;
-  }
-
-  let firstClauseLength = ev.composition.clauses[0].length;
-  let firstClauseAttr   = ev.composition.clauses[0].attr;
-  let secondClauseLength = 0;
-  let secondClauseAttr = 0;
-  let thirdClauseLength = 0;
-  let thirdClauseAttr = 0;
-  if (ev.composition.clauses[1]) {
-    secondClauseLength = ev.composition.clauses[1].length;
-    secondClauseAttr   = ev.composition.clauses[1].attr;
-    if (event.composition.clauses[2]) {
-      thirdClauseLength = ev.composition.clauses[2].length;
-      thirdClauseAttr   = ev.composition.clauses[2].attr;
-    }
-  }
-
-  let caretStart = -1;
-  let caretLength = 0;
-  if (event.caret) {
-    caretStart = ev.caret.start;
-    caretLength = ev.caret.length;
-  }
-
-  domutils.sendTextEvent(
-      ev.composition.string,
-      firstClauseLength,
-      firstClauseAttr,
-      secondClauseLength,
-      secondClauseAttr,
-      thirdClauseLength,
-      thirdClauseAttr,
-      caretStart,
-      caretLength);
-};
-
-/**
  * Synthesize a query selected text event.
  *
  * @param {Window=}
  *     Window object.  Defaults to the current window.
  *
  * @return {(nsIQueryContentEventResult|null)}
  *     Event's result, or null if it failed.
  */
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py
@@ -1,19 +1,35 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 
+import urllib
+
 from marionette_driver.by import By
 
 from marionette_harness import MarionetteTestCase
 
 
+TEST_SIZE = """
+    <?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+  <head>
+    <title>Test page for element size</title>
+  </head>
+  <body>
+    <p>Let's get the size of <a href='#' id='linkId'>some really cool link</a></p>
+  </body>
+</html>
+"""
+
+def inline(doc):
+    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
 class TestElementSize(MarionetteTestCase):
     def testShouldReturnTheSizeOfALink(self):
-        test_html = self.marionette.absolute_url("testSize.html")
-        self.marionette.navigate(test_html)
+        self.marionette.navigate(inline(TEST_SIZE))
         shrinko = self.marionette.find_element(By.ID, 'linkId')
         size = shrinko.rect
         self.assertTrue(size['width'] > 0)
         self.assertTrue(size['height'] > 0)
deleted file mode 100644
--- a/testing/marionette/harness/marionette_harness/www/testSize.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0"?>
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-  <head>
-    <title>Test page for element size</title>
-  </head>
-  <body>
-    <p>Let's get the size of <a href='#' id='linkId'>some really cool link</a></p>
-  </body>
-</html>
--- a/testing/mozbase/rust/mozrunner/Cargo.toml
+++ b/testing/mozbase/rust/mozrunner/Cargo.toml
@@ -2,16 +2,16 @@
 name = "mozrunner"
 version = "0.5.0"
 authors = ["Mozilla Tools and Automation <auto-tools@mozilla.com>"]
 description = "Library for starting Firefox binaries."
 repository = "https://hg.mozilla.org/mozilla-central/file/tip/testing/mozbase/rust/mozrunner"
 license = "MPL-2.0"
 
 [dependencies]
-log = "0.3"
+log = "0.4"
 mozprofile = { path = "../mozprofile" }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 winreg = "0.5"
 
 [[bin]]
 name = "firefox-default-path"
deleted file mode 100644
--- a/testing/web-platform/meta/shadow-dom/untriaged/styles/test-005.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[test-005.html]
-  [A_06_00_06_T01]
-    expected:
-      if stylo: FAIL
-
--- a/testing/webdriver/src/capabilities.rs
+++ b/testing/webdriver/src/capabilities.rs
@@ -82,40 +82,40 @@ impl SpecNewSessionParameters {
         for key in null_entries {
             capabilities.remove(&key);
         }
 
         for (key, value) in capabilities.iter() {
             match &**key {
                 "acceptInsecureCerts" => if !value.is_boolean() {
                         return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                       "acceptInsecureCerts is not a boolean"))
+                                                       format!("acceptInsecureCerts is not boolean: {}", value)))
                     },
                 x @ "browserName" |
                 x @ "browserVersion" |
                 x @ "platformName" => if !value.is_string() {
                         return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                       format!("{} is not a string", x)))
+                                                       format!("{} is not a string: {}", x, value)))
                     },
                 "pageLoadStrategy" => {
                     try!(SpecNewSessionParameters::validate_page_load_strategy(value))
                 }
                 "proxy" => {
                     try!(SpecNewSessionParameters::validate_proxy(value))
                 },
                 "timeouts" => {
                     try!(SpecNewSessionParameters::validate_timeouts(value))
                 },
                 "unhandledPromptBehavior" => {
                     try!(SpecNewSessionParameters::validate_unhandled_prompt_behaviour(value))
                 }
                 x => {
                     if !x.contains(":") {
                         return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                       format!("{} is not the name of a known capability or a valid extension capability", x)))
+                                                       format!("{} is not the name of a known capability or extension capability", x)))
                     } else {
                         try!(browser_capabilities.validate_custom(x, value));
                     }
                 }
             }
         }
         Ok(capabilities)
     }
@@ -125,203 +125,242 @@ impl SpecNewSessionParameters {
             &Json::String(ref x) => {
                 match &**x {
                     "normal" |
                     "eager" |
                     "none" => {},
                     x => {
                         return Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
-                            format!("\"{}\" is not a valid page load strategy", x)))
+                            format!("Invalid page load strategy: {}", x)))
                     }
                 }
             }
             _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                                 "pageLoadStrategy is not a string"))
         }
         Ok(())
     }
 
     fn validate_proxy(proxy_value: &Json) -> WebDriverResult<()> {
         let obj = try_opt!(proxy_value.as_object(),
                            ErrorStatus::InvalidArgument,
                            "proxy is not an object");
+
         for (key, value) in obj.iter() {
             match &**key {
                 "proxyType" => match value.as_string() {
                     Some("pac") |
                     Some("direct") |
                     Some("autodetect") |
                     Some("system") |
                     Some("manual") => {},
                     Some(x) => return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
-                        format!("{} is not a valid proxyType value", x))),
+                        format!("Invalid proxyType value: {}", x))),
                     None => return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
-                        "proxyType value is not a string")),
+                        format!("proxyType is not a string: {}", value))),
                 },
+
                 "proxyAutoconfigUrl" => match value.as_string() {
                     Some(x) => {
-                        try!(Url::parse(x).or(Err(WebDriverError::new(
+                        Url::parse(x).or(Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
-                            "proxyAutoconfigUrl is not a valid url"))));
+                            format!("proxyAutoconfigUrl is not a valid URL: {}", x))))?;
                     },
                     None => return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         "proxyAutoconfigUrl is not a string"
                     ))
                 },
-                "ftpProxy" => try!(SpecNewSessionParameters::validate_host(value)),
-                "httpProxy" => try!(SpecNewSessionParameters::validate_host(value)),
-                "noProxy" => try!(SpecNewSessionParameters::validate_no_proxy(value)),
-                "sslProxy" => try!(SpecNewSessionParameters::validate_host(value)),
-                "socksProxy" => try!(SpecNewSessionParameters::validate_host(value)),
+
+                "ftpProxy" => SpecNewSessionParameters::validate_host(value, "ftpProxy")?,
+                "httpProxy" => SpecNewSessionParameters::validate_host(value, "httpProxy")?,
+                "noProxy" => SpecNewSessionParameters::validate_no_proxy(value)?,
+                "sslProxy" => SpecNewSessionParameters::validate_host(value, "sslProxy")?,
+                "socksProxy" => SpecNewSessionParameters::validate_host(value, "socksProxy")?,
                 "socksVersion" => if !value.is_number() {
-                    return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                   "socksVersion is not a number"))
+                    return Err(WebDriverError::new(
+                        ErrorStatus::InvalidArgument,
+                        format!("socksVersion is not a number: {}", value)
+                    ))
                 },
+
                 x => return Err(WebDriverError::new(
                     ErrorStatus::InvalidArgument,
-                    format!("{} is not a valid proxy configuration capability", x)))
+                    format!("Invalid proxy configuration entry: {}", x)))
             }
         }
+
         Ok(())
     }
 
     fn validate_no_proxy(value: &Json) -> WebDriverResult<()> {
         match value.as_array() {
             Some(hosts) => {
                 for host in hosts {
                     match host.as_string() {
                         Some(_) => {},
                         None => return Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
-                            format!("{} is not a string", host)
+                            format!("noProxy item is not a string: {}", host)
                         ))
                     }
                 }
             },
             None => return Err(WebDriverError::new(
                 ErrorStatus::InvalidArgument,
-                format!("{} is not an array", value)
+                format!("noProxy is not an array: {}", value)
             ))
         }
 
         Ok(())
     }
 
     /// Validate whether a named capability is JSON value is a string containing a host
     /// and possible port
-    fn validate_host(value: &Json) -> WebDriverResult<()> {
+    fn validate_host(value: &Json, entry: &str) -> WebDriverResult<()> {
         match value.as_string() {
             Some(host) => {
                 if host.contains("://") {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
-                        format!("{} contains a scheme", host)));
+                        format!("{} must not contain a scheme: {}", entry, host)));
                 }
 
                 // Temporarily add a scheme so the host can be parsed as URL
                 let s = String::from(format!("http://{}", host));
-                let url = try!(Url::parse(s.as_str()).or(Err(WebDriverError::new(
+                let url = Url::parse(s.as_str()).or(Err(WebDriverError::new(
                     ErrorStatus::InvalidArgument,
-                    format!("{} is not a valid host", host)))));
+                    format!("{} is not a valid URL: {}", entry, host))))?;
 
                 if url.username() != "" ||
                     url.password() != None ||
                     url.path() != "/" ||
                     url.query() != None ||
                     url.fragment() != None {
                         return Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
-                            format!("{} is not of the form host[:port]", host)));
+                            format!("{} is not of the form host[:port]: {}", entry, host)));
                     }
             },
+
             None => return Err(WebDriverError::new(
                 ErrorStatus::InvalidArgument,
-                format!("{} is not a string", value)
+                format!("{} is not a string: {}", entry, value)
             ))
         }
+
         Ok(())
     }
 
     fn validate_timeouts(value: &Json) -> WebDriverResult<()> {
-        let obj = try_opt!(value.as_object(),
-                           ErrorStatus::InvalidArgument,
-                           "timeouts capability is not an object");
+        let obj = try_opt!(
+            value.as_object(),
+            ErrorStatus::InvalidArgument,
+            "timeouts capability is not an object"
+        );
+
         for (key, value) in obj.iter() {
             match &**key {
-                x @ "script" |
-                x @ "pageLoad" |
-                x @ "implicit" => {
-                    let timeout = try_opt!(value.as_i64(),
-                                           ErrorStatus::InvalidArgument,
-                                           format!("{} timeouts value is not an integer", x));
+                x @ "script" | x @ "pageLoad" | x @ "implicit" => {
+                    let timeout = try_opt!(
+                        value.as_i64(),
+                        ErrorStatus::InvalidArgument,
+                        format!("{} timeouts value is not an integer: {}", x, value)
+                    );
                     if timeout < 0 {
-                        return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                       format!("{} timeouts value is negative", x)))
+                        return Err(WebDriverError::new(
+                            ErrorStatus::InvalidArgument,
+                            format!("{} timeouts value is negative: {}", x, timeout),
+                        ));
                     }
-                },
-                x => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                    format!("{} is not a valid timeouts capability", x)))
+                }
+
+                x => {
+                    return Err(WebDriverError::new(
+                        ErrorStatus::InvalidArgument,
+                        format!("Invalid timeouts capability entry: {}", x),
+                    ))
+                }
             }
         }
+
         Ok(())
     }
 
     fn validate_unhandled_prompt_behaviour(value: &Json) -> WebDriverResult<()> {
-        let behaviour = try_opt!(value.as_string(),
-                                 ErrorStatus::InvalidArgument,
-                                 "unhandledPromptBehavior capability is not a string");
+        let behaviour = try_opt!(
+            value.as_string(),
+            ErrorStatus::InvalidArgument,
+            format!("unhandledPromptBehavior is not a string: {}", value)
+        );
+
         match behaviour {
-            "dismiss" |
-            "accept" => {},
-            x => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                format!("{} is not a valid unhandledPromptBehavior value", x)))        }
+            "dismiss" | "accept" => {}
+            x => {
+                return Err(WebDriverError::new(
+                    ErrorStatus::InvalidArgument,
+                    format!("Invalid unhandledPromptBehavior value: {}", x),
+                ))
+            }
+        }
+
         Ok(())
     }
 }
 
 impl Parameters for SpecNewSessionParameters {
     fn from_json(body: &Json) -> WebDriverResult<SpecNewSessionParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::UnknownError,
-                            "Message body is not an object");
+        let data = try_opt!(
+            body.as_object(),
+            ErrorStatus::UnknownError,
+            format!("Malformed capabilities, message body is not an object: {}", body)
+        );
 
         let capabilities = try_opt!(
-            try_opt!(data.get("capabilities"),
-                     ErrorStatus::InvalidArgument,
-                     "Missing 'capabilities' parameter").as_object(),
+            try_opt!(
+                data.get("capabilities"),
+                ErrorStatus::InvalidArgument,
+                "Malformed capabilities, missing \"capabilities\" field"
+            ).as_object(),
             ErrorStatus::InvalidArgument,
-                     "'capabilities' parameter is not an object");
+            "Malformed capabilities, \"capabilities\" field is not an object}"
+        );
 
         let default_always_match = Json::Object(Capabilities::new());
-        let always_match = try_opt!(capabilities.get("alwaysMatch")
-                                   .unwrap_or(&default_always_match)
-                                   .as_object(),
-                                   ErrorStatus::InvalidArgument,
-                                   "'alwaysMatch' parameter is not an object");
+        let always_match = try_opt!(
+            capabilities
+                .get("alwaysMatch")
+                .unwrap_or(&default_always_match)
+                .as_object(),
+            ErrorStatus::InvalidArgument,
+            "Malformed capabilities, alwaysMatch field is not an object"
+        );
         let default_first_matches = Json::Array(vec![]);
-        let first_matches = try!(
-            try_opt!(capabilities.get("firstMatch")
-                     .unwrap_or(&default_first_matches)
-                     .as_array(),
-                     ErrorStatus::InvalidArgument,
-                     "'firstMatch' parameter is not an array")
-                .iter()
-                .map(|x| x.as_object()
-                     .map(|x| x.clone())
-                     .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                "'firstMatch' entry is not an object")))
-                .collect::<WebDriverResult<Vec<Capabilities>>>());
+        let first_matches = try_opt!(
+            capabilities
+                .get("firstMatch")
+                .unwrap_or(&default_first_matches)
+                .as_array(),
+            ErrorStatus::InvalidArgument,
+            "Malformed capabilities, firstMatch field is not an array"
+        ).iter()
+            .map(|x| {
+                x.as_object().map(|x| x.clone()).ok_or(WebDriverError::new(
+                    ErrorStatus::InvalidArgument,
+                    "Malformed capabilities, firstMatch entry is not an object",
+                ))
+            })
+            .collect::<WebDriverResult<Vec<Capabilities>>>()?;
 
         return Ok(SpecNewSessionParameters {
             alwaysMatch: always_match.clone(),
-            firstMatch: first_matches
+            firstMatch: first_matches,
         });
     }
 }
 
 impl ToJson for SpecNewSessionParameters {
     fn to_json(&self) -> Json {
         let mut body = BTreeMap::new();
         let mut capabilities = BTreeMap::new();
@@ -342,23 +381,20 @@ impl CapabilitiesMatching for SpecNewSes
             &self.firstMatch
         } else {
             &default
         };
 
         let merged_capabilities = capabilities_list
             .iter()
             .map(|first_match_entry| {
-                if first_match_entry.keys().any(
-                    |k| self.alwaysMatch.contains_key(k),
-                )
-                {
+                if first_match_entry.keys().any(|k| self.alwaysMatch.contains_key(k)) {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
-                        "'firstMatch' key shadowed a value in 'alwaysMatch'",
+                        "firstMatch key shadowed a value in alwaysMatch",
                     ));
                 }
                 let mut merged = self.alwaysMatch.clone();
                 merged.append(&mut first_match_entry.clone());
                 Ok(merged)
             })
             .map(|merged| {
                 merged.and_then(|x| self.validate(x, browser_capabilities))
@@ -477,58 +513,59 @@ impl CapabilitiesMatching for LegacyNewS
         );
         browser_capabilities.init(&capabilities);
         Ok(Some(capabilities))
     }
 }
 
 impl Parameters for LegacyNewSessionParameters {
     fn from_json(body: &Json) -> WebDriverResult<LegacyNewSessionParameters> {
-        let data = try_opt!(body.as_object(),
-                            ErrorStatus::UnknownError,
-                            "Message body is not an object");
-
-        let desired_capabilities =
-            if let Some(capabilities) = data.get("desiredCapabilities") {
-                try_opt!(capabilities.as_object(),
-                         ErrorStatus::InvalidArgument,
-                         "'desiredCapabilities' parameter is not an object").clone()
-            } else {
-                BTreeMap::new()
-            };
+        let data = try_opt!(
+            body.as_object(),
+            ErrorStatus::UnknownError,
+            format!("Malformed legacy capabilities, message body is not an object: {}", body)
+        );
 
-        let required_capabilities =
-            if let Some(capabilities) = data.get("requiredCapabilities") {
-                try_opt!(capabilities.as_object(),
-                         ErrorStatus::InvalidArgument,
-                         "'requiredCapabilities' parameter is not an object").clone()
-            } else {
-                BTreeMap::new()
-            };
+        let desired = if let Some(capabilities) = data.get("desiredCapabilities") {
+            try_opt!(
+                capabilities.as_object(),
+                ErrorStatus::InvalidArgument,
+                "Malformed legacy capabilities, desiredCapabilities field is not an object"
+            ).clone()
+        } else {
+            BTreeMap::new()
+        };
 
-        Ok(LegacyNewSessionParameters {
-            desired: desired_capabilities,
-            required: required_capabilities
-        })
+        let required = if let Some(capabilities) = data.get("requiredCapabilities") {
+            try_opt!(
+                capabilities.as_object(),
+                ErrorStatus::InvalidArgument,
+                "Malformed legacy capabilities, requiredCapabilities field is not an object"
+            ).clone()
+        } else {
+            BTreeMap::new()
+        };
+
+        Ok(LegacyNewSessionParameters { desired, required })
     }
 }
 
 impl ToJson for LegacyNewSessionParameters {
     fn to_json(&self) -> Json {
         let mut data = BTreeMap::new();
         data.insert("desiredCapabilities".to_owned(), self.desired.to_json());
         data.insert("requiredCapabilities".to_owned(), self.required.to_json());
         Json::Object(data)
     }
 }
 
 #[cfg(test)]
 mod tests {
     use rustc_serialize::json::Json;
-    use super::{WebDriverResult, SpecNewSessionParameters};
+    use super::{SpecNewSessionParameters, WebDriverResult};
 
     fn validate_proxy(value: &str) -> WebDriverResult<()> {
         let data = Json::from_str(value).unwrap();
         SpecNewSessionParameters::validate_proxy(&data)
     }
 
     #[test]
     fn test_validate_proxy() {
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -348,17 +348,17 @@ impl<U: WebDriverExtensionRoute> WebDriv
         if requires_body {
             match Json::from_str(body) {
                 Ok(x @ Json::Object(_)) => Ok(x),
                 Ok(_) => {
                     Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                             "Body was not a JSON Object"))
                 }
                 Err(json::ParserError::SyntaxError(_, line, col)) => {
-                    let msg = format!("Failed to decode request as JSON: {}", body);
+                    let msg = format!("Failed to decode request as JSON: \"{}\"", body);
                     let stack = format!("Syntax error at :{}:{}", line, col);
                     Err(WebDriverError::new_with_stack(ErrorStatus::InvalidArgument, msg, stack))
                 }
                 Err(json::ParserError::IoError(e)) => {
                     Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                             format!("I/O error whilst decoding body: {}", e)))
                 }
             }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/adler32/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{".travis.yml":"4b265390d238dcd79664069258603bbdfa3ef0012f3289cdfdf2eb656888b9cc","Cargo.toml":"6cba738dddcd24c99d2bd68ea564a8db7b6e1c8e135ad45078fdd45f8fc976fa","LICENSE":"485b7d77429d023aaf97d8dd79f1b2b122f9d529e23b25aee284622f98e2400a","README.md":"3c3150b973b16d5a8d0f6edc6d6d29a039c623c985c269c5d79ef3314f0cc6dd","appveyor.yml":"4873092bae0713890497e5ceae761af359d680e6cce5ce003bf38bc5c45cde44","src/lib.rs":"24e7e7923d2460c98746f91fd77c65793374e7c58ba842f1a3bbd713a2c27996"},"package":"6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/adler32/.travis.yml
@@ -0,0 +1,18 @@
+language: rust
+rust:
+  - stable
+  - beta
+  - nightly
+sudo: false
+script:
+  - cargo build --verbose
+  - cargo test --verbose
+  - cargo doc
+after_success: |
+  [ $TRAVIS_BRANCH = master ] &&
+  [ $TRAVIS_RUST_VERSION = nightly ] &&
+  [ $TRAVIS_PULL_REQUEST = false ] &&
+  echo '<meta http-equiv=refresh content=0;url=adler32/index.html>' > target/doc/index.html &&
+  pip install --user ghp-import &&
+  $HOME/.local/bin/ghp-import -n target/doc &&
+  git push -qf https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
new file mode 100644
--- /dev/null
+++ b/third_party/rust/adler32/Cargo.toml
@@ -0,0 +1,24 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "adler32"
+version = "1.0.2"
+authors = ["Remi Rampin <remirampin@gmail.com>"]
+description = "Minimal Adler32 implementation for Rust."
+documentation = "https://remram44.github.io/adler32-rs/index.html"
+readme = "README.md"
+keywords = ["adler32", "hash", "rolling"]
+license = "BSD-3-Clause"
+repository = "https://github.com/remram44/adler32-rs"
+[dev-dependencies.rand]
+version = "0.3"
new file mode 100644
--- /dev/null
+++ b/third_party/rust/adler32/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Remi Rampin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
new file mode 100644
--- /dev/null
+++ b/third_party/rust/adler32/README.md
@@ -0,0 +1,10 @@
+[![Build Status](https://travis-ci.org/remram44/adler32-rs.svg?branch=master)](https://travis-ci.org/remram44/adler32-rs/builds)
+[![Win Build](https://ci.appveyor.com/api/projects/status/ekyg20rd6rwrus64/branch/master?svg=true)](https://ci.appveyor.com/project/remram44/adler32-rs)
+[![Crates.io](https://img.shields.io/crates/v/adler32.svg)](https://crates.io/crates/adler32)
+
+What is this?
+=============
+
+It is an implementation of the [Adler32 rolling hash algorithm](https://en.wikipedia.org/wiki/Adler-32) in the [Rust programming language](https://www.rust-lang.org/).
+
+[Generated documentation](https://remram44.github.io/adler32-rs/index.html)
new file mode 100644
--- /dev/null
+++ b/third_party/rust/adler32/appveyor.yml
@@ -0,0 +1,12 @@
+install:
+  - ps: Start-FileDownload 'https://static.rust-lang.org/dist/rust-nightly-i686-pc-windows-gnu.exe'
+  - rust-nightly-i686-pc-windows-gnu.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
+  - set PATH=%PATH%;C:\Program Files (x86)\Rust\bin
+  - rustc -V
+  - cargo -V
+
+build: false
+
+test_script:
+  - cargo build --verbose
+  - cargo test --verbose
new file mode 100644
--- /dev/null
+++ b/third_party/rust/adler32/src/lib.rs
@@ -0,0 +1,301 @@
+//! A minimal implementation of Adler32 for Rust.
+//!
+//! This provides the simple method adler32(), that exhausts a Read and
+//! computes the Adler32 hash, as well as the RollingAdler32 struct, that can
+//! build a hash byte-by-byte, allowing to 'forget' past bytes in a rolling
+//! fashion.
+//!
+//! The adler32 code has been translated (as accurately as I could manage) from
+//! the zlib implementation.
+
+#[cfg(test)]
+extern crate rand;
+
+use std::io;
+
+// adler32 algorithm and implementation taken from zlib; http://www.zlib.net/
+// It was translated into Rust as accurately as I could manage
+// The (slow) reference was taken from Wikipedia; https://en.wikipedia.org/
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.2.8, April 28th, 2013
+
+  Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+*/
+
+// largest prime smaller than 65536
+const BASE: u32 = 65521;
+
+// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
+const NMAX: usize = 5552;
+
+#[inline(always)]
+fn do1(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
+    *adler += buf[0] as u32;
+    *sum2 += *adler;
+}
+
+#[inline(always)]
+fn do2(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
+    do1(adler, sum2, &buf[0..1]);
+    do1(adler, sum2, &buf[1..2]);
+}
+
+#[inline(always)]
+fn do4(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
+    do2(adler, sum2, &buf[0..2]);
+    do2(adler, sum2, &buf[2..4]);
+}
+
+#[inline(always)]
+fn do8(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
+    do4(adler, sum2, &buf[0..4]);
+    do4(adler, sum2, &buf[4..8]);
+}
+
+#[inline(always)]
+fn do16(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
+    do8(adler, sum2, &buf[0..8]);
+    do8(adler, sum2, &buf[8..16]);
+}
+
+/// A rolling version of the Adler32 hash, which can 'forget' past bytes.
+///
+/// Calling remove() will update the hash to the value it would have if that
+/// past byte had never been fed to the algorithm. This allows you to get the
+/// hash of a rolling window very efficiently.
+pub struct RollingAdler32 {
+    a: u32,
+    b: u32,
+}
+
+impl RollingAdler32 {
+    /// Creates an empty Adler32 context (with hash 1).
+    pub fn new() -> RollingAdler32 {
+        Self::from_value(1)
+    }
+
+    /// Creates an Adler32 context with the given initial value.
+    pub fn from_value(adler32: u32) -> RollingAdler32 {
+        let a = adler32 & 0xFFFF;
+        let b = adler32 >> 16;
+        RollingAdler32 { a: a, b: b }
+    }
+
+    /// Convenience function initializing a context from the hash of a buffer.
+    pub fn from_buffer(buffer: &[u8]) -> RollingAdler32 {
+        let mut hash = RollingAdler32::new();
+        hash.update_buffer(buffer);
+        hash
+    }
+
+    /// Returns the current hash.
+    pub fn hash(&self) -> u32 {
+        (self.b << 16) | self.a
+    }
+
+    /// Removes the given `byte` that was fed to the algorithm `size` bytes ago.
+    pub fn remove(&mut self, size: usize, byte: u8) {
+        let byte = byte as u32;
+        self.a = (self.a + BASE - byte) % BASE;
+        self.b = ((self.b + BASE - 1)
+                      .wrapping_add(BASE.wrapping_sub(size as u32)
+                                        .wrapping_mul(byte))) % BASE;
+    }
+
+    /// Feeds a new `byte` to the algorithm to update the hash.
+    pub fn update(&mut self, byte: u8) {
+        let byte = byte as u32;
+        self.a = (self.a + byte) % BASE;
+        self.b = (self.b + self.a) % BASE;
+    }
+
+    /// Feeds a vector of bytes to the algorithm to update the hash.
+    pub fn update_buffer(&mut self, buffer: &[u8]) {
+        let len = buffer.len();
+
+        // in case user likes doing a byte at a time, keep it fast
+        if len == 1 {
+            self.update(buffer[0]);
+            return;
+        }
+
+        // in case short lengths are provided, keep it somewhat fast
+        if len < 16 {
+            for pos in 0..len {
+                self.a += buffer[pos] as u32;
+                self.b += self.a;
+            }
+            if self.a >= BASE {
+                self.a -= BASE;
+            }
+            self.b %= BASE;
+            return;
+        }
+
+        let mut pos = 0;
+
+        // do length NMAX blocks -- requires just one modulo operation;
+        while pos + NMAX <= len {
+            let end = pos + NMAX;
+            while pos < end {
+                // 16 sums unrolled
+                do16(&mut self.a, &mut self.b, &buffer[pos..pos + 16]);
+                pos += 16;
+            }
+            self.a %= BASE;
+            self.b %= BASE;
+        }
+
+        // do remaining bytes (less than NMAX, still just one modulo)
+        if pos < len { // avoid modulos if none remaining
+            while len - pos >= 16 {
+                do16(&mut self.a, &mut self.b, &buffer[pos..pos + 16]);
+                pos += 16;
+            }
+            while len - pos > 0 {
+                self.a += buffer[pos] as u32;
+                self.b += self.a;
+                pos += 1;
+            }
+            self.a %= BASE;
+            self.b %= BASE;
+        }
+    }
+}
+
+/// Consume a Read object and returns the Adler32 hash.
+pub fn adler32<R: io::Read>(mut reader: R) -> io::Result<u32> {
+    let mut hash = RollingAdler32::new();
+    let mut buffer = [0u8; NMAX];
+    let mut read = try!(reader.read(&mut buffer));
+    while read > 0 {
+        hash.update_buffer(&buffer[..read]);
+        read = try!(reader.read(&mut buffer));
+    }
+    Ok(hash.hash())
+}
+
+#[cfg(test)]
+mod test {
+    use rand;
+    use rand::Rng;
+    use std::io;
+
+    use super::{BASE, adler32, RollingAdler32};
+
+    fn adler32_slow<R: io::Read>(reader: R) -> io::Result<u32> {
+        let mut a: u32 = 1;
+        let mut b: u32 = 0;
+
+        for byte in reader.bytes() {
+            let byte = try!(byte) as u32;
+            a = (a + byte) % BASE;
+            b = (b + a) % BASE;
+        }
+
+        Ok((b << 16) | a)
+    }
+
+    #[test]
+    fn testvectors() {
+        fn do_test(v: u32, bytes: &[u8]) {
+            let mut hash = RollingAdler32::new();
+            hash.update_buffer(&bytes);
+            assert_eq!(hash.hash(), v);
+
+            let r = io::Cursor::new(bytes);
+            assert_eq!(adler32(r).unwrap(), v);
+        }
+        do_test(0x00000001, b"");
+        do_test(0x00620062, b"a");
+        do_test(0x024d0127, b"abc");
+        do_test(0x29750586, b"message digest");
+        do_test(0x90860b20, b"abcdefghijklmnopqrstuvwxyz");
+        do_test(0x8adb150c, b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+                              abcdefghijklmnopqrstuvwxyz\
+                              0123456789");
+        do_test(0x97b61069, b"1234567890123456789012345678901234567890\
+                              1234567890123456789012345678901234567890");
+        do_test(0xD6251498, &[255; 64000]);
+    }
+
+    #[test]
+    fn compare() {
+        let mut rng = rand::thread_rng();
+        let mut data = vec![0u8; 5589];
+        for size in [0, 1, 3, 4, 5, 31, 32, 33, 67,
+                     5550, 5552, 5553, 5568, 5584, 5589].iter().cloned() {
+            rng.fill_bytes(&mut data[..size]);
+            let r1 = io::Cursor::new(&data[..size]);
+            let r2 = r1.clone();
+            if adler32_slow(r1).unwrap() != adler32(r2).unwrap() {
+                panic!("Comparison failed, size={}", size);
+            }
+        }
+    }
+
+    #[test]
+    fn rolling() {
+        assert_eq!(RollingAdler32::from_value(0x01020304).hash(), 0x01020304);
+
+        fn do_test(a: &[u8], b: &[u8]) {
+            let mut total = Vec::with_capacity(a.len() + b.len());
+            total.extend(a);
+            total.extend(b);
+            let mut h = RollingAdler32::from_buffer(&total[..(b.len())]);
+            for i in 0..(a.len()) {
+                h.remove(b.len(), a[i]);
+                h.update(total[b.len() + i]);
+            }
+            assert_eq!(h.hash(), adler32(b).unwrap());
+        }
+        do_test(b"a", b"b");
+        do_test(b"", b"this a test");
+        do_test(b"th", b"is a test");
+        do_test(b"this a ", b"test");
+    }
+
+    #[test]
+    fn long_window_remove() {
+        let mut hash = RollingAdler32::new();
+        let w = 65536;
+        assert!(w as u32 > BASE);
+
+        let mut bytes = vec![0; w*3];
+        for (i, b) in bytes.iter_mut().enumerate() {
+            *b = i as u8;
+        }
+
+        for (i, b) in bytes.iter().enumerate() {
+            if i >= w {
+                hash.remove(w, bytes[i - w]);
+            }
+            hash.update(*b);
+            if i > 0 && i % w == 0 {
+                assert_eq!(hash.hash(), 0x433a8772);
+            }
+        }
+        assert_eq!(hash.hash(), 0xbbba8772);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/build_const/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{"Cargo.toml":"74daf7bd386ce92aeaaf78642366eb3c9fac62c00e1902317630aa56a99a6150","LICENSE.txt":"7a31ff1d8a253fdbf2084fb6854375b1e98722d1a4ec043226c7c2f8c91437c1","README.md":"6e2ed8b0c11176190dcbb0ffd57e9e41ce99057d68b146d9a9f0aeb7439bbf7b","justfile":"3c1e5b6be00f7100043862b9180cd39351a85e7903d84c045cf69934c35579c5","src/lib.rs":"b80d3ab5c18d324e77d239a0945bc57e96ae8693c147b55532c1b10951fc1493","src/writer.rs":"88aff16820f80683cd547169115bb29336c5d09710a304a166dfb4a79860bd06"},"package":"e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/build_const/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "build_const"
+authors = ["Garrett Berg <vitiral@gmail.com>"]
+description = "library for creating importable constants from build.rs or a script"
+keywords = ["embedded", "no_std", "build", "const", "static"]
+license = "MIT"
+repository = "https://github.com/vitiral/build_const"
+documentation = "https://docs.rs/build_const"
+version = "0.2.0"
+
+[features]
+default = ["std"]
+std = []
new file mode 100644
--- /dev/null
+++ b/third_party/rust/build_const/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Garrett Berg, vitiral@gmail.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/build_const/README.md
@@ -0,0 +1,6 @@
+# `build_const`: crate for creating constants in your build script
+
+Rust library for creating importable constants from build.rs or a script
+
+See the [crate documentation](https://docs.rs/build_const)
+and the crate on [crates.io](https://crates.io/crates/build_const)
new file mode 100644
--- /dev/null
+++ b/third_party/rust/build_const/justfile
@@ -0,0 +1,5 @@
+
+test:
+	cd test_crates/example && rm -f Cargo.lock && cargo test && cd ..
+	cd test_crates/no_std && rm -f Cargo.lock && cargo test && cd ..
+
new file mode 100644
--- /dev/null
+++ b/third_party/rust/build_const/src/lib.rs
@@ -0,0 +1,88 @@
+//! `build_const`: crate for creating constants in your build script
+//!
+//! The build_const crate exists to help create rust constant files at compile time or in a
+//! generating script. It is ultra simple and lightweight, making constant creation a simple
+//! matter.
+//! 
+//! Recommended use: when developing make your constants in `build.rs`. Once your constants are
+//! fairly stable create a script instead and have your constants file be generated in either a
+//! single file or an external crate that you can bring in as a dependency.
+//! 
+//! # Example
+//! 
+//! Include `build_const = VERSION` in your `Cargo.toml` file. For `no_std` support (macros only)
+//! use `default-features = false`.
+//! 
+//! See `ConstWriter` for how to use in a build.rs or script. To then import a "constants.rs" file
+//! created in `build.rs` use:
+//! 
+//! ```c
+//! #[macro_use]
+//! extern crate build_const;
+//! 
+//! build_const!("constants");
+//! println!("VALUE: {}", VALUE);
+//! println!("VALUE: {}", ARRAY);
+//! ```
+//! 
+//! For writing constants in a script, the macro `src_file!` is also provided.
+//! ```c
+//! // will write files to `/src/constants.rs`
+//! let mut consts = ConstWriter::from_path(&Path::from(src_file!("constants.rs"))).unwrap();
+//! // ... use consts
+//! ```
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(feature = "std")]
+mod writer;
+
+#[cfg(feature = "std")]
+pub use writer::{
+    ConstWriter, 
+    ConstValueWriter, 
+    write_array,
+    write_array_raw,
+};
+
+/// Shortcut macro which expands to the same module path used in
+/// `ConstWriter::for_build(mod_name)`.
+/// 
+/// If you don't want to include macros, this is simply a one liner:
+/// ```
+/// include!(concat!(env!("OUT_DIR"), concat!("/", $mod_name))
+/// ```
+#[macro_export]
+macro_rules! build_const {
+    ( $mod_name:expr ) => {
+        include!(
+            concat!(
+                env!("OUT_DIR"), 
+                concat!("/", concat!($mod_name, ".rs"))
+            )
+        );
+    };
+}
+
+/// Macro which returns the path to file in your `src/` directory.
+/// 
+/// Example:
+/// ```
+/// src_file!("constants.rs")
+/// ```
+/// returns `/path/to/project/src/constants.rs`
+/// 
+/// If you need a more custom path, the basic implementation is:
+/// ```
+/// concat!(env!("CARGO_MANIFEST_DIR"), "/src/path/to/file")
+/// ```
+#[macro_export]
+macro_rules! src_file {
+    ( $file_name:expr) => {
+        concat!(
+            env!("CARGO_MANIFEST_DIR"), 
+            concat!("/", concat!("src", concat!("/", $file_name)))
+        )
+    };
+}
+
new file mode 100644
--- /dev/null
+++ b/third_party/rust/build_const/src/writer.rs
@@ -0,0 +1,216 @@
+use std::env;
+use std::fs;
+use std::fmt::Debug;
+use std::io;
+use std::io::Write;
+use std::path::Path;
+use std::str;
+
+
+/// Primary object used to write constant files.
+/// 
+/// # Example
+/// ```no_run
+/// # use std::path::Path;
+/// # #[derive(Debug)]
+/// # struct Point { x: u8, y: u8 }
+/// use build_const::ConstWriter;
+/// 
+/// // use `for_build` in `build.rs`
+/// let mut consts = ConstWriter::from_path(
+///     &Path::new("/tmp/constants.rs")
+/// ).unwrap();
+/// 
+/// // add an external dependency (`use xyz::Point`)
+/// consts.add_dependency("xyz::Point");
+/// 
+/// // finish dependencies and starting writing constants
+/// let mut consts = consts.finish_dependencies();
+///
+/// // add an array of values
+/// let values: Vec<u8> = vec![1, 2, 3, 36];
+/// consts.add_array("ARRAY", "u8", &values);
+///
+/// // Add a value that is a result of "complex" calculations
+/// consts.add_value("VALUE", "u8", values.iter().sum::<u8>());
+/// 
+/// // Add a value from an external crate (must implement `Debug`)
+/// consts.add_value("VALUE", "Point", &Point { x: 3, y: 7});
+/// ```
+pub struct ConstWriter {
+    f: fs::File,
+}
+
+/// Created from `ConstWriter::finish_dependencies`. See
+/// documentation for `ConstWriter`.
+pub struct ConstValueWriter {
+    f: fs::File,
+}
+
+impl ConstWriter {
+    /// Create a ConstWriter to be used for your crate's `build.rs`
+    pub fn for_build(mod_name: &str) -> io::Result<ConstWriter> {
+        let out_dir = env::var("OUT_DIR").unwrap();
+        let mod_name = format!("{}.rs", mod_name);
+        let dest_path = Path::new(&out_dir).join(mod_name);
+
+        Ok(ConstWriter {
+            f: fs::File::create(&dest_path)?
+        })
+    }
+
+    /// Create a new ConstWriter to write to an path. If a file
+    /// already exists at the path then it will be deleted.
+    pub fn from_path(path: &Path) -> io::Result<ConstWriter> {
+        let f = fs::OpenOptions::new()
+            .write(true)
+            .truncate(true)
+            .open(path)?;
+        Ok(ConstWriter {
+            f: f,
+        })
+    }
+
+    /// finish writing dependencies and start writing constants
+    pub fn finish_dependencies(self) -> ConstValueWriter {
+        ConstValueWriter { f: self.f }
+    }
+
+    /// Add a dependency to your constants file.
+    pub fn add_dependency(&mut self, lib: &str) {
+        write!(self.f, "pub use {};\n", lib).unwrap();
+    }
+
+    /// Add a raw string to the constants file.
+    /// 
+    /// This method only changes `raw` by adding a `\n` at the end.
+    pub fn add_raw(&mut self, raw: &str) {
+        write!(self.f, "{}\n", raw).unwrap();
+    }
+
+}
+
+impl ConstValueWriter {
+    /// Add a value to the constants file.
+    /// 
+    /// You have to manually specify the `name`, type (`ty`) and `value`
+    /// of the constant you want to add.
+    /// 
+    /// The `value` uses the `Debug` trait to determine the formating of
+    /// the value being added. If `Debug` is not accurate or will not work,
+    /// you must use `add_value_raw` instead and format it yourself.
+    pub fn add_value<T: Debug>(&mut self, name: &str, ty: &str, value: T) {
+        self.add_value_raw(name, ty, &format!("{:?}", value));
+    }
+
+    /// Add a pre-formatted value to the constants file.
+    /// 
+    /// `add_value` depends on `Debug` being implemented in such a way
+    /// that it accurately represents the type's creation. Sometimes that
+    /// cannot be relied on and `add_value_raw` has to be used instead.
+    pub fn add_value_raw(&mut self, name: &str, ty: &str, raw_value: &str) {
+        write!(
+            self.f, "pub const {}: {} = {};\n", 
+            name, 
+            ty,
+            raw_value,
+        ).unwrap();
+    }
+
+    /// Add an array of len > 0 to the constants
+    /// 
+    /// You have to manually specify the `name`, type (`ty`) of the **items** and 
+    /// `values` of the array constant you want to add. The length of the array
+    /// is determined automatically.
+    /// 
+    /// Example: `const.add_array("foo", "u16", &[1,2,3])`
+    /// 
+    /// The `value` of each item uses the `Debug` trait to determine the 
+    /// formatting of the value being added. If `Debug` is not accurate or will 
+    /// not work, you must use `add_array_raw` instead and format it yourself.
+    pub fn add_array<T: Debug>(&mut self, name: &str, ty: &str, values: &[T]) {
+        write_array(&mut self.f, name, ty, values);
+    }
+
+    /// Add an array of pre-formatted values to the constants file. The length of the array is
+    /// determined automatically.
+    /// 
+    /// `add_array` depends on `Debug` being implemented for each item in such a way that it
+    /// accurately represents the item's creation. Sometimes that cannot be relied on and
+    /// `add_array_raw` has to be used instead.
+    pub fn add_array_raw(&mut self, name: &str, ty: &str, raw_values: &[&str]) {
+        write_array_raw(&mut self.f, name, ty, raw_values);
+    }
+
+    /// Add a raw string to the constants file.
+    /// 
+    /// This method only changes `raw` by adding a `\n` at the end.
+    pub fn add_raw(&mut self, raw: &str) {
+        write!(self.f, "{}\n", raw).unwrap();
+    }
+
+    /// Finish writing to the constants file and consume self.
+    pub fn finish(&mut self) {
+        self.f.flush().unwrap();
+    }
+
+}
+
+// Public Functions
+
+/// Write an array and return the array's full type representation.
+/// 
+/// This can be used to create nested array constant types.
+pub fn write_array<T: Debug, W: Write>(w: &mut W, name: &str, ty: &str, values: &[T]) 
+    -> String
+{
+    assert!(
+        !values.is_empty(), 
+        "attempting to add an array of len zero. If this is intentional, use \
+        add_value_raw instead."
+    );
+    let full_ty = write_array_header(w, name, ty, values.len());
+    for v in values.iter() {
+        write_array_item_raw(w, &format!("{:?}", v));
+    }
+    write_array_end(w);
+    full_ty
+}
+
+/// Write an array of raw values and return the array's full type representation.
+/// 
+/// This can be used to create nested array constant types.
+pub fn write_array_raw<W: Write>(
+        w: &mut W, name: &str, ty: &str, raw_values: &[&str]
+    ) 
+    -> String
+{
+    assert!(
+        !raw_values.is_empty(), 
+        "attempting to add an array of len zero. If this is intentional, use \
+        add_value_raw instead."
+    );
+    let full_ty = write_array_header(w, name, ty, raw_values.len());
+    for &v in raw_values {
+        write_array_item_raw(w, v);
+    }
+    write_array_end(w);
+    full_ty
+}
+
+// Helpers
+
+/// Write the array header and return the array's full type.
+fn write_array_header<W: Write>(w: &mut W, name: &str, ty: &str, len: usize) -> String {
+    let full_ty = format!("[{}; {}]", ty, len);
+    write!(w, "pub const {}: {} = [\n", name, &full_ty).unwrap();
+    full_ty
+}
+
+fn write_array_item_raw<W: Write>(w: &mut W, raw_item: &str) {
+    write!(w, "    {},\n", raw_item).unwrap()
+}
+
+fn write_array_end<W: Write>(w: &mut W) {
+    write!(w, "];\n").unwrap();
+}
--- a/third_party/rust/cc/.cargo-checksum.json
+++ b/third_party/rust/cc/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".travis.yml":"3ca887c3f17391bd915265fbb6015b403263fbb13b9dedc8ea9ab8aa5c5ef163","Cargo.toml":"abe25a5fc35c2fe24c2f93b00f9488886310a3b7a25975984cc7030e826bb2e5","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"d73ba5602263665e2d3987db4378787f6aa1e449b699a2530cee478acc7586dc","appveyor.yml":"ab45bfdcf2596f357225a54e730c34d518a8f3ad56c2ed33af682cfd45bddc02","src/bin/gcc-shim.rs":"d6be9137cb48b86891e7b263adbf492e1193ffe682db9ba4a88eb1079b874b58","src/com.rs":"0cb06f5db0fb70d27db0e5917ca337de6e7032119e6aabfea1bad9c719f5f34b","src/lib.rs":"164a360f504c7614be1f51dd9c76ef84bf8195e94ea5f1a914a90c1a036db6b8","src/registry.rs":"3876ef9573e3bbc050aef41a684b9a510cc1a91b15ae874fe032cf4377b4d116","src/setup_config.rs":"1a3eeb11c6847c31f2a4685b62ab35c76f0b6d5a17f7ed99e9df164283a771f7","src/winapi.rs":"cb5e6cab3eb570b0f97c660ca448ccfb5024262c0c7b245c181daad91a79f211","src/windows_registry.rs":"6de548aa94215e449f0e58e9a3b1702939d7c2f7b63a9040901c948bf138201d","tests/cc_env.rs":"7402315eea7ffa23b29b393c1de8e236294ede9de562ff0a562704a157135341","tests/support/mod.rs":"092551f9f6e3a999fa0aa02f93314aac0bda2b09268f948c423df56a43575e0b","tests/test.rs":"b1164258714e13173f3861126e97bedf1e29aa24618993c4eb0edd57c431dcc7"},"package":"a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719"}
\ No newline at end of file
+{"files":{".travis.yml":"1a4a3f7f90349924378e93acbb524b9127e37c02cfbc6dc59fd904bbdc1c8d0b","Cargo.toml":"623dd06a83bcbf2f292ab51af93e9b79b689e3be06a62968b79f4e36f1bb769f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"186c5c8a62520cb7a3d90d77161c954b52ae8456fca0e0669bc3a5b889592a43","appveyor.yml":"ab45bfdcf2596f357225a54e730c34d518a8f3ad56c2ed33af682cfd45bddc02","src/bin/gcc-shim.rs":"d6be9137cb48b86891e7b263adbf492e1193ffe682db9ba4a88eb1079b874b58","src/com.rs":"0cb06f5db0fb70d27db0e5917ca337de6e7032119e6aabfea1bad9c719f5f34b","src/lib.rs":"996b650e19d5ccd6e64e741789427017c913644e980862a7286ec4ed53c14a17","src/registry.rs":"3876ef9573e3bbc050aef41a684b9a510cc1a91b15ae874fe032cf4377b4d116","src/setup_config.rs":"1a3eeb11c6847c31f2a4685b62ab35c76f0b6d5a17f7ed99e9df164283a771f7","src/winapi.rs":"cb5e6cab3eb570b0f97c660ca448ccfb5024262c0c7b245c181daad91a79f211","src/windows_registry.rs":"6de548aa94215e449f0e58e9a3b1702939d7c2f7b63a9040901c948bf138201d","tests/cc_env.rs":"7402315eea7ffa23b29b393c1de8e236294ede9de562ff0a562704a157135341","tests/support/mod.rs":"092551f9f6e3a999fa0aa02f93314aac0bda2b09268f948c423df56a43575e0b","tests/test.rs":"b1164258714e13173f3861126e97bedf1e29aa24618993c4eb0edd57c431dcc7"},"package":"deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"}
\ No newline at end of file
--- a/third_party/rust/cc/.travis.yml
+++ b/third_party/rust/cc/.travis.yml
@@ -1,48 +1,53 @@
 language: rust
-rust:
-  - stable
-  - beta
-  - nightly
+sudo: false
+
 matrix:
   include:
-    # Minimum version supported
     - rust: 1.13.0
       install:
       script: cargo build
+    - rust: stable
+      env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1
+    - rust: stable
+      env: TARGET=i686-unknown-linux-gnu
+    - os: osx
+      env: TARGET=x86_64-apple-darwin NO_ADD=1
+    - rust: beta
+      env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1
+    - rust: nightly
+      env: TARGET=x86_64-unknown-linux-gnu NO_ADD=1
 
-sudo: false
+    - rust: nightly
+      before_script:
+        - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
+      install:
+      script:
+        - cargo doc --no-deps --all-features
+      after_success:
+        - travis-cargo --only nightly doc-upload
+
 install:
-  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi
-  - export TARGET=$ARCH-$OS
   - if [ -z "$NO_ADD" ]; then rustup target add $TARGET; fi
-before_script:
-  - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
+
 script:
   - cargo build --verbose
   - cargo test --verbose
   - cargo test --verbose --features parallel
   - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET
   - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --features parallel
   - cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --release
   - cargo doc
   - cargo clean && cargo build
   - rustdoc --test README.md -L target/debug -L target/debug/deps
-after_success:
-  - travis-cargo --only nightly doc-upload
+
 env:
   global:
     secure: "CBtqrudgE0PS8x3kTr44jKbC2D4nfnmdYVecooNm0qnER4B4TSvZpZSQoCgKK6k4BYQuOSyFTOwYx6M79w39ZMOgyCP9ytB+tyMWL0/+ZuUQL04yVg4M5vd3oJMkOaXbvG56ncgPyFrseY+FPDg+mXAzvJk/nily37YXjkQj2D0="
 
-  matrix:
-    - ARCH=x86_64 NO_ADD=1
-    - ARCH=i686
 notifications:
   email:
     on_success: never
-os:
-  - linux
-  - osx
 addons:
   apt:
     packages:
       - g++-multilib
--- a/third_party/rust/cc/Cargo.toml
+++ b/third_party/rust/cc/Cargo.toml
@@ -7,31 +7,31 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cc"
-version = "1.0.3"
+version = "1.0.4"
 authors = ["Alex Crichton <alex@alexcrichton.com>"]
 description = "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n"
 homepage = "https://github.com/alexcrichton/cc-rs"
 documentation = "https://docs.rs/cc"
 readme = "README.md"
 keywords = ["build-dependencies"]
 categories = ["development-tools"]
 license = "MIT/Apache-2.0"
 repository = "https://github.com/alexcrichton/cc-rs"
 [dependencies.rayon]
-version = "0.8"
+version = "0.9"
 optional = true
 [dev-dependencies.tempdir]
 version = "0.3"
 
 [features]
 parallel = ["rayon"]
-[badges.travis-ci]
+[badges.appveyor]
 repository = "alexcrichton/cc-rs"
 
-[badges.appveyor]
+[badges.travis-ci]
 repository = "alexcrichton/cc-rs"
--- a/third_party/rust/cc/README.md
+++ b/third_party/rust/cc/README.md
@@ -152,15 +152,51 @@ fn main() {
         .compile("libfoo.a");
 }
 ```
 
 When using C++ library compilation switch, the `CXX` and `CXXFLAGS` env
 variables are used instead of `CC` and `CFLAGS` and the C++ standard library is
 linked to the crate target.
 
+## CUDA C++ support
+
+`cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method
+on `Build` (currently for GNU/Clang toolchains only):
+
+```rust,no_run
+extern crate cc;
+
+fn main() {
+    cc::Build::new()
+        // Switch to CUDA C++ library compilation using NVCC.
+        .cuda(true)
+        // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X).
+        .flag("-gencode").flag("arch=compute_52,code=sm_52")
+        // Generate code for Maxwell (Jetson TX1).
+        .flag("-gencode").flag("arch=compute_53,code=sm_53")
+        // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp).
+        .flag("-gencode").flag("arch=compute_61,code=sm_61")
+        // Generate code for Pascal (Tesla P100).
+        .flag("-gencode").flag("arch=compute_60,code=sm_60")
+        // Generate code for Pascal (Jetson TX2).
+        .flag("-gencode").flag("arch=compute_62,code=sm_62")
+        .file("bar.cu")
+        .compile("libbar.a");
+}
+```
+
 ## License
 
-`cc-rs` is primarily distributed under the terms of both the MIT license and
-the Apache License (Version 2.0), with portions covered by various BSD-like
-licenses.
+This project is licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+   http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
+   http://opensource.org/licenses/MIT)
 
-See LICENSE-APACHE, and LICENSE-MIT for details.
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in Serde by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
--- a/third_party/rust/cc/src/lib.rs
+++ b/third_party/rust/cc/src/lib.rs
@@ -96,16 +96,17 @@ pub struct Build {
     definitions: Vec<(String, Option<String>)>,
     objects: Vec<PathBuf>,
     flags: Vec<String>,
     flags_supported: Vec<String>,
     files: Vec<PathBuf>,
     cpp: bool,
     cpp_link_stdlib: Option<Option<String>>,
     cpp_set_stdlib: Option<String>,
+    cuda: bool,
     target: Option<String>,
     host: Option<String>,
     out_dir: Option<PathBuf>,
     opt_level: Option<String>,
     debug: Option<bool>,
     env: Vec<(OsString, OsString)>,
     compiler: Option<PathBuf>,
     archiver: Option<PathBuf>,
@@ -128,22 +129,22 @@ enum ErrorKind {
     /// Environment variable not found, with the var in question as extra info.
     EnvVarNotFound,
     /// Error occurred while using external tools (ie: invocation of compiler).
     ToolExecError,
     /// Error occurred due to missing external tools.
     ToolNotFound,
 }
 
-/// Represents an internal error that occurred, with an explaination.
+/// Represents an internal error that occurred, with an explanation.
 #[derive(Clone, Debug)]
 pub struct Error {
     /// Describes the kind of error that occurred.
     kind: ErrorKind,
-    /// More explaination of error that occurred.
+    /// More explanation of error that occurred.
     message: String,
 }
 
 impl Error {
     fn new(kind: ErrorKind, message: &str) -> Error {
         Error {
             kind: kind,
             message: message.to_owned(),
@@ -167,16 +168,17 @@ impl From<io::Error> for Error {
 #[derive(Clone, Debug)]
 pub struct Tool {
     path: PathBuf,
     cc_wrapper_path: Option<PathBuf>,
     cc_wrapper_args: Vec<OsString>,
     args: Vec<OsString>,
     env: Vec<(OsString, OsString)>,
     family: ToolFamily,
+    cuda: bool,
 }
 
 /// Represents the family of tools this tool belongs to.
 ///
 /// Each family of tools differs in how and what arguments they accept.
 ///
 /// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -228,16 +230,55 @@ impl ToolFamily {
 
     /// What the flag to turn warning into errors
     fn warnings_to_errors_flag(&self) -> &'static str {
         match *self {
             ToolFamily::Msvc => "/WX",
             ToolFamily::Gnu | ToolFamily::Clang => "-Werror",
         }
     }
+
+    /// NVCC-specific. Device code debug info flag. This is separate from the
+    /// debug info flag passed to the C++ compiler.
+    fn nvcc_debug_flag(&self) -> &'static str {
+        match *self {
+            ToolFamily::Msvc => unimplemented!(),
+            ToolFamily::Gnu |
+            ToolFamily::Clang => "-G",
+        }
+    }
+
+    /// NVCC-specific. Redirect the following flag to the underlying C++
+    /// compiler.
+    fn nvcc_redirect_flag(&self) -> &'static str {
+        match *self {
+            ToolFamily::Msvc => unimplemented!(),
+            ToolFamily::Gnu |
+            ToolFamily::Clang => "-Xcompiler",
+        }
+    }
+}
+
+/// Represents an object.
+///
+/// This is a source file -> object file pair.
+#[derive(Clone, Debug)]
+struct Object {
+    src: PathBuf,
+    dst: PathBuf,
+}
+
+impl Object {
+    /// Create a new source file -> object file pair.
+    fn new(src: PathBuf, dst: PathBuf) -> Object {
+        Object {
+            src: src,
+            dst: dst,
+        }
+    }
 }
 
 impl Build {
     /// Construct a new instance of a blank set of configuration.
     ///
     /// This builder is finished with the [`compile`] function.
     ///
     /// [`compile`]: struct.Build.html#method.compile
@@ -249,16 +290,17 @@ impl Build {
             flags: Vec::new(),
             flags_supported: Vec::new(),
             files: Vec::new(),
             shared_flag: None,
             static_flag: None,
             cpp: false,
             cpp_link_stdlib: None,
             cpp_set_stdlib: None,
+            cuda: false,
             target: None,
             host: None,
             out_dir: None,
             opt_level: None,
             debug: None,
             env: Vec::new(),
             compiler: None,
             archiver: None,
@@ -327,42 +369,53 @@ impl Build {
     /// ```
     pub fn flag(&mut self, flag: &str) -> &mut Build {
         self.flags.push(flag.to_string());
         self
     }
 
     fn ensure_check_file(&self) -> Result<PathBuf, Error> {
         let out_dir = self.get_out_dir()?;
-        let src = if self.cpp {
+        let src = if self.cuda {
+            assert!(self.cpp);
+            out_dir.join("flag_check.cu")
+        } else if self.cpp {
             out_dir.join("flag_check.cpp")
         } else {
             out_dir.join("flag_check.c")
         };
 
         if !src.exists() {
             let mut f = fs::File::create(&src)?;
             write!(f, "int main(void) {{ return 0; }}")?;
         }
 
         Ok(src)
     }
 
-    fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> {
+    /// Run the compiler to test if it accepts the given flag.
+    ///
+    /// For a convenience method for setting flags conditionally,
+    /// see `flag_if_supported()`.
+    ///
+    /// It may return error if it's unable to run the compilier with a test file
+    /// (e.g. the compiler is missing or a write to the `out_dir` failed).
+    pub fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> {
         let out_dir = self.get_out_dir()?;
         let src = self.ensure_check_file()?;
         let obj = out_dir.join("flag_check");
         let target = self.get_target()?;
         let mut cfg = Build::new();
         cfg.flag(flag)
             .target(&target)
             .opt_level(0)
             .host(&target)
             .debug(false)
-            .cpp(self.cpp);
+            .cpp(self.cpp)
+            .cuda(self.cuda);
         let compiler = cfg.try_get_compiler()?;
         let mut cmd = compiler.to_command();
         command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false);
 
         // We need to explicitly tell msvc not to link and create an exe
         // in the root directory of the crate
         if target.contains("msvc") {
             cmd.arg("/c");
@@ -450,16 +503,32 @@ impl Build {
     ///
     /// The other `cpp_*` options will only become active if this is set to
     /// `true`.
     pub fn cpp(&mut self, cpp: bool) -> &mut Build {
         self.cpp = cpp;
         self
     }
 
+    /// Set CUDA C++ support.
+    ///
+    /// Enabling CUDA will pass the detected C/C++ toolchain as an argument to
+    /// the CUDA compiler, NVCC. NVCC itself accepts some limited GNU-like args;
+    /// any other arguments for the C/C++ toolchain will be redirected using
+    /// "-Xcompiler" flags.
+    ///
+    /// If enabled, this also implicitly enables C++ support.
+    pub fn cuda(&mut self, cuda: bool) -> &mut Build {
+        self.cuda = cuda;
+        if cuda {
+            self.cpp = true;
+        }
+        self
+    }
+
     /// Set warnings into errors flag.
     ///
     /// Disabled by default.
     ///
     /// Warning: turning warnings into errors only make sense
     /// if you are a developer of the crate using cc-rs.
     /// Some warnings only appear on some architecture or
     /// specific version of the compiler. Any user of this crate,
@@ -725,17 +794,16 @@ impl Build {
             gnu.push_str("lib");
             gnu.push_str(&output);
             gnu.push_str(".a");
             (output, gnu)
         };
         let dst = self.get_out_dir()?;
 
         let mut objects = Vec::new();
-        let mut src_dst = Vec::new();
         for file in self.files.iter() {
             let obj = dst.join(file).with_extension("o");
             let obj = if !obj.starts_with(&dst) {
                 dst.join(obj.file_name().ok_or_else(|| {
                     Error::new(ErrorKind::IOError, "Getting object file details failed.")
                 })?)
             } else {
                 obj
@@ -746,20 +814,19 @@ impl Build {
                 None => {
                     return Err(Error::new(
                         ErrorKind::IOError,
                         "Getting object file details failed.",
                     ))
                 }
             };
 
-            src_dst.push((file.to_path_buf(), obj.clone()));
-            objects.push(obj);
+            objects.push(Object::new(file.to_path_buf(), obj));
         }
-        self.compile_objects(&src_dst)?;
+        self.compile_objects(&objects)?;
         self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?;
 
         if self.get_target()?.contains("msvc") {
             let compiler = self.get_base_compiler()?;
             let atlmfc_lib = compiler
                 .env()
                 .iter()
                 .find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB"))
@@ -805,56 +872,56 @@ impl Build {
     /// or creating directories.
     pub fn compile(&self, output: &str) {
         if let Err(e) = self.try_compile(output) {
             fail(&e.message);
         }
     }
 
     #[cfg(feature = "parallel")]
-    fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) -> Result<(), Error> {
+    fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
         use self::rayon::prelude::*;
 
         let mut cfg = rayon::Configuration::new();
         if let Ok(amt) = env::var("NUM_JOBS") {
             if let Ok(amt) = amt.parse() {
                 cfg = cfg.num_threads(amt);
             }
         }
         drop(rayon::initialize(cfg));
 
         let results: Mutex<Vec<Result<(), Error>>> = Mutex::new(Vec::new());
 
         objs.par_iter().with_max_len(1).for_each(
-            |&(ref src, ref dst)| {
-                let res = self.compile_object(src, dst);
+            |obj| {
+                let res = self.compile_object(obj);
                 results.lock().unwrap().push(res)
             },
         );
 
         // Check for any errors and return the first one found.
         for result in results.into_inner().unwrap().iter() {
             if result.is_err() {
                 return result.clone();
             }
         }
 
         Ok(())
     }
 
     #[cfg(not(feature = "parallel"))]
-    fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) -> Result<(), Error> {
-        for &(ref src, ref dst) in objs {
-            self.compile_object(src, dst)?;
+    fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
+        for obj in objs {
+            self.compile_object(obj)?;
         }
         Ok(())
     }
 
-    fn compile_object(&self, file: &Path, dst: &Path) -> Result<(), Error> {
-        let is_asm = file.extension().and_then(|s| s.to_str()) == Some("asm");
+    fn compile_object(&self, obj: &Object) -> Result<(), Error> {
+        let is_asm = obj.src.extension().and_then(|s| s.to_str()) == Some("asm");
         let msvc = self.get_target()?.contains("msvc");
         let (mut cmd, name) = if msvc && is_asm {
             self.msvc_macro_assembler()?
         } else {
             let compiler = self.try_get_compiler()?;
             let mut cmd = compiler.to_command();
             for &(ref a, ref b) in self.env.iter() {
                 cmd.env(a, b);
@@ -866,19 +933,19 @@ impl Build {
                     .file_name()
                     .ok_or_else(|| {
                         Error::new(ErrorKind::IOError, "Failed to get compiler path.")
                     })?
                     .to_string_lossy()
                     .into_owned(),
             )
         };
-        command_add_output_file(&mut cmd, dst, msvc, is_asm);
+        command_add_output_file(&mut cmd, &obj.dst, msvc, is_asm);
         cmd.arg(if msvc { "/c" } else { "-c" });
-        cmd.arg(file);
+        cmd.arg(&obj.src);
 
         run(&mut cmd, &name)?;
         Ok(())
     }
 
     /// This will return a result instead of panicing; see expand() for the complete description.
     pub fn try_expand(&self) -> Result<Vec<u8>, Error> {
         let compiler = self.try_get_compiler()?;
@@ -956,26 +1023,24 @@ impl Build {
     /// Get the compiler that's in use for this configuration.
     ///
     /// This will return a result instead of panicing; see get_compiler() for the complete description.
     pub fn try_get_compiler(&self) -> Result<Tool, Error> {
         let opt_level = self.get_opt_level()?;
         let target = self.get_target()?;
 
         let mut cmd = self.get_base_compiler()?;
-        let nvcc = cmd.path
-            .file_name()
-            .and_then(|p| p.to_str())
-            .map(|p| p.contains("nvcc"))
-            .unwrap_or(false);
 
         // Non-target flags
         // If the flag is not conditioned on target variable, it belongs here :)
         match cmd.family {
             ToolFamily::Msvc => {
+                assert!(!self.cuda,
+                    "CUDA C++ compilation not supported for MSVC, yet... but you are welcome to implement it :)");
+
                 cmd.args.push("/nologo".into());
 
                 let crt_flag = match self.static_crt {
                     Some(true) => "/MT",
                     Some(false) => "/MD",
                     None => {
                         let features =
                             env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or(String::new());
@@ -1000,34 +1065,34 @@ impl Build {
                 // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does
                 // not support '-Oz'
                 if opt_level == "z" && cmd.family != ToolFamily::Clang {
                     cmd.args.push("-Os".into());
                 } else {
                     cmd.args.push(format!("-O{}", opt_level).into());
                 }
 
-                if !nvcc {
-                    cmd.args.push("-ffunction-sections".into());
-                    cmd.args.push("-fdata-sections".into());
-                    if self.pic.unwrap_or(!target.contains("windows-gnu")) {
-                        cmd.args.push("-fPIC".into());
-                    }
-                } else if self.pic.unwrap_or(false) {
-                    cmd.args.push("-Xcompiler".into());
-                    cmd.args.push("\'-fPIC\'".into());
+                cmd.push_cc_arg("-ffunction-sections".into());
+                cmd.push_cc_arg("-fdata-sections".into());
+                if self.pic.unwrap_or(!target.contains("windows-gnu")) {
+                    cmd.push_cc_arg("-fPIC".into());
                 }
             }
         }
         for arg in self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }) {
             cmd.args.push(arg.into());
         }
 
         if self.get_debug() {
-            cmd.args.push(cmd.family.debug_flag().into());
+            if self.cuda {
+                let nvcc_debug_flag = cmd.family.nvcc_debug_flag().into();
+                cmd.args.push(nvcc_debug_flag);
+            }
+            let debug_flag = cmd.family.debug_flag().into();
+            cmd.push_cc_arg(debug_flag);
         }
 
         // Target flags
         match cmd.family {
             ToolFamily::Clang => {
                 cmd.args.push(format!("--target={}", target).into());
             }
             ToolFamily::Msvc => {
@@ -1057,16 +1122,28 @@ impl Build {
                 // (specified in the android spec online)
                 if target.starts_with("armv7-linux-androideabi") {
                     cmd.args.push("-march=armv7-a".into());
                     cmd.args.push("-mthumb".into());
                     cmd.args.push("-mfpu=vfpv3-d16".into());
                     cmd.args.push("-mfloat-abi=softfp".into());
                 }
 
+                if target.starts_with("armv4t-unknown-linux-") {
+                    cmd.args.push("-march=armv4t".into());
+                    cmd.args.push("-marm".into());
+                    cmd.args.push("-mfloat-abi=soft".into());
+                }
+
+                if target.starts_with("armv5te-unknown-linux-") {
+                    cmd.args.push("-march=armv5te".into());
+                    cmd.args.push("-marm".into());
+                    cmd.args.push("-mfloat-abi=soft".into());
+                }
+
                 // For us arm == armv6 by default
                 if target.starts_with("arm-unknown-linux-") {
                     cmd.args.push("-march=armv6".into());
                     cmd.args.push("-marm".into());
                 }
 
                 // We can guarantee some settings for FRC
                 if target.starts_with("arm-frc-") {
@@ -1132,17 +1209,17 @@ impl Build {
             cmd.args.push("-shared".into());
         }
 
         if self.cpp {
             match (self.cpp_set_stdlib.as_ref(), cmd.family) {
                 (None, _) => {}
                 (Some(stdlib), ToolFamily::Gnu) |
                 (Some(stdlib), ToolFamily::Clang) => {
-                    cmd.args.push(format!("-stdlib=lib{}", stdlib).into());
+                    cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
                 }
                 _ => {
                     println!(
                         "cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
                               does not support this option, ignored",
                         cmd.family
                     );
                 }
@@ -1151,27 +1228,27 @@ impl Build {
 
         for directory in self.include_directories.iter() {
             cmd.args.push(cmd.family.include_flag().into());
             cmd.args.push(directory.into());
         }
 
         if self.warnings {
             for flag in cmd.family.warnings_flags().iter() {
-                cmd.args.push(flag.into());
+                cmd.push_cc_arg(flag.into());
             }
         }
 
         for flag in self.flags.iter() {
             cmd.args.push(flag.into());
         }
 
         for flag in self.flags_supported.iter() {
             if self.is_flag_supported(flag).unwrap_or(false) {
-                cmd.args.push(flag.into());
+                cmd.push_cc_arg(flag.into());
             }
         }
 
         for &(ref key, ref value) in self.definitions.iter() {
             let lead = if let ToolFamily::Msvc = cmd.family {
                 "/"
             } else {
                 "-"
@@ -1179,17 +1256,18 @@ impl Build {
             if let Some(ref value) = *value {
                 cmd.args.push(format!("{}D{}={}", lead, key, value).into());
             } else {
                 cmd.args.push(format!("{}D{}", lead, key).into());
             }
         }
 
         if self.warnings_into_errors {
-            cmd.args.push(cmd.family.warnings_to_errors_flag().into());
+            let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into();
+            cmd.push_cc_arg(warnings_to_errors_flag);
         }
 
         Ok(cmd)
     }
 
     fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> {
         let target = self.get_target()?;
         let tool = if target.contains("x86_64") {
@@ -1214,39 +1292,38 @@ impl Build {
         }
         for flag in self.flags.iter() {
             cmd.arg(flag);
         }
 
         Ok((cmd, tool.to_string()))
     }
 
-    fn assemble(&self, lib_name: &str, dst: &Path, objects: &[PathBuf]) -> Result<(), Error> {
+    fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> {
         // Delete the destination if it exists as the `ar` tool at least on Unix
         // appends to it, which we don't want.
         let _ = fs::remove_file(&dst);
 
+        let objects: Vec<_> = objs.iter().map(|obj| obj.dst.clone()).collect();
         let target = self.get_target()?;
         if target.contains("msvc") {
             let mut cmd = match self.archiver {
                 Some(ref s) => self.cmd(s),
                 None => {
                     windows_registry::find(&target, "lib.exe").unwrap_or_else(
                         || {
                             self.cmd("lib.exe")
                         },
                     )
                 }
             };
             let mut out = OsString::from("/OUT:");
             out.push(dst);
             run(
-                cmd.arg(out).arg("/nologo").args(objects).args(
-                    &self.objects,
-                ),
+                cmd.arg(out).arg("/nologo").args(&objects).args(&self.objects),
                 "lib.exe",
             )?;
 
             // The Rust compiler will look for libfoo.a and foo.lib, but the
             // MSVC linker will also be passed foo.lib, so be sure that both
             // exist for now.
             let lib_dst = dst.with_file_name(format!("{}.lib", lib_name));
             let _ = fs::remove_file(&lib_dst);
@@ -1260,17 +1337,17 @@ impl Build {
                         ErrorKind::IOError,
                         "Could not copy or create a hard-link to the generated lib file.",
                     ))
                 }
             };
         } else {
             let (mut ar, cmd) = self.get_ar()?;
             run(
-                ar.arg("crs").arg(dst).args(objects).args(&self.objects),
+                ar.arg("crs").arg(dst).args(&objects).args(&self.objects),
                 &cmd,
             )?;
         }
 
         Ok(())
     }
 
     fn ios_flags(&self, cmd: &mut Tool) -> Result<(), Error> {
@@ -1348,30 +1425,24 @@ impl Build {
     }
 
     fn get_base_compiler(&self) -> Result<Tool, Error> {
         if let Some(ref c) = self.compiler {
             return Ok(Tool::new(c.clone()));
         }
         let host = self.get_host()?;
         let target = self.get_target()?;
-        let (env, msvc, gnu) = if self.cpp {
-            ("CXX", "cl.exe", "g++")
+        let (env, msvc, gnu, traditional) = if self.cpp {
+            ("CXX", "cl.exe", "g++", "c++")
         } else {
-            ("CC", "cl.exe", "gcc")
+            ("CC", "cl.exe", "gcc", "cc")
         };
 
-        let default = if host.contains("solaris") {
-            // In this case, c++/cc unlikely to exist or be correct.
-            gnu
-        } else if self.cpp {
-            "c++"
-        } else {
-            "cc"
-        };
+        // On Solaris, c++/cc unlikely to exist or be correct.
+        let default = if host.contains("solaris") { gnu } else { traditional };
 
         let tool_opt: Option<Tool> =
             self.env_tool(env)
                 .map(|(tool, cc, args)| {
                     let mut t = Tool::new(PathBuf::from(tool));
                     if let Some(cc) = cc {
                         t.cc_wrapper_path = Some(PathBuf::from(cc));
                     }
@@ -1404,23 +1475,28 @@ impl Build {
                 let compiler = if host.contains("windows") && target.contains("windows") {
                     if target.contains("msvc") {
                         msvc.to_string()
                     } else {
                         format!("{}.exe", gnu)
                     }
                 } else if target.contains("android") {
                     format!("{}-{}", target.replace("armv7", "arm"), gnu)
+                } else if target.contains("cloudabi") {
+                    format!("{}-{}", target, traditional)
                 } else if self.get_host()? != target {
                     // CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
                     let cc_env = self.getenv("CROSS_COMPILE");
                     let cross_compile = cc_env.as_ref().map(|s| s.trim_right_matches('-'));
                     let prefix = cross_compile.or(match &target[..] {
                         "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
+                        "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"),
                         "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
+                        "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
+                        "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
                         "arm-frc-linux-gnueabi" => Some("arm-frc-linux-gnueabi"),
                         "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
                         "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
                         "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
                         "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"),
                         "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"),
                         "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
                         "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
@@ -1456,16 +1532,30 @@ impl Build {
                     }
                 } else {
                     default.to_string()
                 };
                 Tool::new(PathBuf::from(compiler))
             }
         };
 
+        let tool = if self.cuda {
+            assert!(tool.args.is_empty(),
+                "CUDA compilation currently assumes empty pre-existing args");
+            let nvcc = match self.get_var("NVCC") {
+                Err(_) => "nvcc".into(),
+                Ok(nvcc) => nvcc,
+            };
+            let mut nvcc_tool = Tool::with_features(PathBuf::from(nvcc), self.cuda);
+            nvcc_tool.args.push(format!("-ccbin={}", tool.path.display()).into());
+            nvcc_tool
+        } else {
+            tool
+        };
+
         Ok(tool)
     }
 
     fn get_var(&self, var_base: &str) -> Result<String, Error> {
         let target = self.get_target()?;
         let host = self.get_host()?;
         let kind = if host == target { "HOST" } else { "TARGET" };
         let target_u = target.replace("-", "_");
@@ -1520,16 +1610,18 @@ impl Build {
             None => {
                 let target = self.get_target()?;
                 if target.contains("msvc") {
                     Ok(None)
                 } else if target.contains("darwin") {
                     Ok(Some("c++".to_string()))
                 } else if target.contains("freebsd") {
                     Ok(Some("c++".to_string()))
+                } else if target.contains("openbsd") {
+                    Ok(Some("c++".to_string()))
                 } else {
                     Ok(Some("stdc++".to_string()))
                 }
             }
         }
     }
 
     fn get_ar(&self) -> Result<(Command, String), Error> {
@@ -1626,38 +1718,56 @@ impl Build {
 impl Default for Build {
     fn default() -> Build {
         Build::new()
     }
 }
 
 impl Tool {
     fn new(path: PathBuf) -> Tool {
+        Tool::with_features(path, false)
+    }
+
+    fn with_features(path: PathBuf, cuda: bool) -> Tool {
         // Try to detect family of the tool from its name, falling back to Gnu.
         let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
             if fname.contains("clang") {
                 ToolFamily::Clang
-            } else if fname.contains("cl") && !fname.contains("uclibc") {
+            } else if fname.contains("cl") && !fname.contains("cloudabi") &&
+                      !fname.contains("uclibc") {
                 ToolFamily::Msvc
             } else {
                 ToolFamily::Gnu
             }
         } else {
             ToolFamily::Gnu
         };
         Tool {
             path: path,
             cc_wrapper_path: None,
             cc_wrapper_args: Vec::new(),
             args: Vec::new(),
             env: Vec::new(),
             family: family,
+            cuda: cuda,
         }
     }
 
+    /// Add a flag, and optionally prepend the NVCC wrapper flag "-Xcompiler".
+    ///
+    /// Currently this is only used for compiling CUDA sources, since NVCC only
+    /// accepts a limited set of GNU-like flags, and the rest must be prefixed
+    /// with a "-Xcompiler" flag to get passed to the underlying C++ compiler.
+    fn push_cc_arg(&mut self, flag: OsString) {
+        if self.cuda {
+            self.args.push(self.family.nvcc_redirect_flag().into());
+        }
+        self.args.push(flag);
+    }
+
     /// Converts this compiler into a `Command` that's ready to be run.
     ///
     /// This is useful for when the compiler needs to be executed and the
     /// command returned will already have the initial arguments and environment
     /// variables configured.
     pub fn to_command(&self) -> Command {
         let mut cmd = match self.cc_wrapper_path {
             Some(ref cc_wrapper_path) => {
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{".travis.yml":"ad9cdfee6e8874c60215726e7376df90f4e090ea380c1a5b35e060374f4fc10d","Cargo.toml":"2ebfb76e56845aac102a40a9e01943d67fcb9201ca4af38c7f360f5bb88a3f73","LICENSE-APACHE":"470355a7eed93fcc4281ec2e0f82ca3b94e7af1e4d83629f91de8cfac34d750e","LICENSE-MIT":"3488679340a49ecc34d342c4009d2dabf76f4a21f12aec2ca99b15805d656544","README.md":"23f7a2d1effb924c6ad6f35f4dbcb83c0219d264edbc9f1b7c49670cf90e145a","benches/bench.rs":"6e129032ad3c596840d0a88f369b0a86efe560b2da9c6db338e80e17876741e5","build.rs":"b5fe83c7ef8f8796d17ba44ba8b2279e4066ffcc81b65454c15545bc8af25ae4","src/crc32.rs":"509b3b4138521c8481863a0a8421f6ffccf97d1aa9a7cd5c9221b7ebda26da66","src/crc64.rs":"fd59ca0336d798bc4b14224858ca27ce372274f21951950b38eacf36c59bd863","src/lib.rs":"f4a043d46d67edfcc9551d303deecd97c5df90199e362a2d8992f40f9dc7d879","src/util.rs":"65f5e50f0b31440ec65f695ed4c6794fa08a2bf27ef2045121bd816e68f88b9d","tests/crc.rs":"f787a4a14c52dd1794d4297a3d9cf81f9b2fda45b3538294f6f34587d1298fd4","tests/hash.rs":"d3b314a4e50dd7669c603c0c2fdc51c47f04293c0747406dbe79c0f72a58be7d"},"package":"bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/.travis.yml
@@ -0,0 +1,4 @@
+language: rust
+rust:
+    - nightly
+    - stable
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/Cargo.toml
@@ -0,0 +1,27 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "crc"
+version = "1.7.0"
+authors = ["Rui Hu <code@mrhooray.com>"]
+description = "Rust implementation of CRC(32, 64) with support of various standards"
+readme = "README.md"
+keywords = ["crc", "crc32", "crc64", "hash", "checksum"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/mrhooray/crc-rs.git"
+[build-dependencies.build_const]
+version = "0.2"
+
+[features]
+default = ["std"]
+std = []
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0 January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/LICENSE-MIT
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 crc-rs Developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/README.md
@@ -0,0 +1,95 @@
+# crc [![Build Status](https://travis-ci.org/mrhooray/crc-rs.svg?branch=master)](https://travis-ci.org/mrhooray/crc-rs)
+> Rust implementation of CRC(32, 64) with support of various standards
+
+* [Crate](https://crates.io/crates/crc)
+* [Documentation](https://docs.rs/crc/)
+* [Usage](#usage)
+* [Benchmark](#benchmark)
+* [License](#license)
+
+## Usage
+Add `crc` to `Cargo.toml`
+```toml
+[dependencies]
+crc = "^1.0.0"
+```
+or
+```toml
+[dependencies.crc]
+git = "https://github.com/mrhooray/crc-rs"
+```
+
+Add this to crate root
+```rust
+extern crate crc;
+```
+
+### Compute CRC32
+```rust
+use crc::{crc32, Hasher32};
+
+// CRC-32-IEEE being the most commonly used one
+assert_eq!(crc32::checksum_ieee(b"123456789"), 0xcbf43926);
+assert_eq!(crc32::checksum_castagnoli(b"123456789"), 0xe3069283);
+assert_eq!(crc32::checksum_koopman(b"123456789"), 0x2d3dd0ae);
+
+// use provided or custom polynomial
+let mut digest = crc32::Digest::new(crc32::IEEE);
+digest.write(b"123456789");
+assert_eq!(digest.sum32(), 0xcbf43926);
+
+// with initial
+let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, 0u32);
+digest.write(b"123456789");
+assert_eq!(digest.sum32(), 0xcbf43926);
+```
+
+### Compute CRC64
+```rust
+use crc::{crc64, Hasher64};
+
+assert_eq!(crc64::checksum_ecma(b"123456789"), 0x995dc9bbdf1939fa);
+assert_eq!(crc64::checksum_iso(b"123456789"), 0xb90956c775a41001);
+
+// use provided or custom polynomial
+let mut digest = crc64::Digest::new(crc64::ECMA);
+digest.write(b"123456789");
+assert_eq!(digest.sum64(), 0x995dc9bbdf1939fa);
+
+// with initial
+let mut digest = crc64::Digest::new_with_initial(crc64::ECMA, 0u64);
+digest.write(b"123456789");
+assert_eq!(digest.sum64(), 0x995dc9bbdf1939fa);
+```
+
+## Benchmark
+> Bencher is currently not available in Rust stable releases.
+
+`cargo bench` with 2.3 GHz Intel Core i7 results ~430MB/s throughput. [Comparison](http://create.stephan-brumme.com/crc32/)
+```
+cargo bench
+     Running target/release/bench-5c82e94dab3e9c79
+
+running 4 tests
+test bench_crc32_make_table       ... bench:       439 ns/iter (+/- 82)
+test bench_crc32_update_megabytes ... bench:   2327803 ns/iter (+/- 138845)
+test bench_crc64_make_table       ... bench:      1200 ns/iter (+/- 223)
+test bench_crc64_update_megabytes ... bench:   2322472 ns/iter (+/- 92870)
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured
+```
+
+## License
+
+Licensed under either of
+
+ * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
+additional terms or conditions.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/benches/bench.rs
@@ -0,0 +1,30 @@
+#![feature(test)]
+extern crate crc;
+extern crate test;
+
+use crc::{crc32, crc64};
+use test::Bencher;
+
+#[bench]
+fn bench_crc32_make_table(b: &mut Bencher) {
+    b.iter(|| crc32::make_table(crc32::IEEE));
+}
+
+#[bench]
+fn bench_crc32_update_megabytes(b: &mut Bencher) {
+    let table = crc32::make_table(crc32::IEEE);
+    let bytes = Box::new([0u8; 1_000_000]);
+    b.iter(|| crc32::update(0, &table, &*bytes));
+}
+
+#[bench]
+fn bench_crc64_make_table(b: &mut Bencher) {
+    b.iter(|| crc64::make_table(crc64::ECMA));
+}
+
+#[bench]
+fn bench_crc64_update_megabytes(b: &mut Bencher) {
+    let table = crc64::make_table(crc64::ECMA);
+    let bytes = Box::new([0u8; 1_000_000]);
+    b.iter(|| crc64::update(0, &table, &*bytes));
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/build.rs
@@ -0,0 +1,41 @@
+extern crate build_const;
+
+include!("src/util.rs");
+
+#[allow(non_snake_case)]
+fn create_constants() {
+    let mut crc32 = build_const::ConstWriter::for_build("crc32_constants")
+        .unwrap()
+        .finish_dependencies();
+    let CASTAGNOLI: u32 = 0x82f63b78;
+    crc32.add_value("CASTAGNOLI", "u32", CASTAGNOLI);
+    crc32.add_array("CASTAGNOLI_TABLE", "u32", &make_table_crc32(CASTAGNOLI));
+
+    let IEEE: u32 = 0xedb88320;
+    crc32.add_value("IEEE", "u32", IEEE);
+    crc32.add_array("IEEE_TABLE", "u32", &make_table_crc32(IEEE));
+
+    let KOOPMAN: u32 = 0xeb31d82e;
+    crc32.add_value("KOOPMAN", "u32", KOOPMAN);
+    crc32.add_array("KOOPMAN_TABLE", "u32", &make_table_crc32(KOOPMAN));
+
+    crc32.finish();
+
+    let mut crc64 = build_const::ConstWriter::for_build("crc64_constants")
+        .unwrap()
+        .finish_dependencies();
+
+    let ECMA: u64 = 0xc96c5795d7870f42;
+    crc64.add_value("ECMA", "u64", ECMA);
+    crc64.add_array("ECMA_TABLE", "u64", &make_table_crc64(ECMA));
+
+    let ISO: u64 = 0xd800000000000000;
+    crc64.add_value("ISO", "u64", ISO);
+    crc64.add_array("ISO_TABLE", "u64", &make_table_crc64(ISO));
+
+    crc64.finish();
+}
+
+fn main() {
+    create_constants();
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/src/crc32.rs
@@ -0,0 +1,81 @@
+#[cfg(feature = "std")]
+use std::hash::Hasher;
+#[cfg(not(feature = "std"))]
+use core::hash::Hasher;
+
+pub use util::make_table_crc32 as make_table;
+
+include!(concat!(env!("OUT_DIR"), "/crc32_constants.rs"));
+
+pub struct Digest {
+    table: [u32; 256],
+    initial: u32,
+    value: u32
+}
+
+pub trait Hasher32 {
+    fn reset(&mut self);
+    fn write(&mut self, bytes: &[u8]);
+    fn sum32(&self) -> u32;
+}
+
+pub fn update(mut value: u32, table: &[u32; 256], bytes: &[u8]) -> u32 {
+    value = !value;
+    for &i in bytes.iter() {
+        value = table[((value as u8) ^ i) as usize] ^ (value >> 8)
+    }
+    !value
+}
+
+pub fn checksum_ieee(bytes: &[u8]) -> u32 {
+    return update(0, &IEEE_TABLE, bytes);
+}
+
+pub fn checksum_castagnoli(bytes: &[u8]) -> u32 {
+    return update(0, &CASTAGNOLI_TABLE, bytes);
+}
+
+pub fn checksum_koopman(bytes: &[u8]) -> u32 {
+    return update(0, &KOOPMAN_TABLE, bytes);
+}
+
+impl Digest {
+    pub fn new(poly: u32) -> Digest {
+        Digest {
+            table: make_table(poly),
+            initial: 0,
+            value: 0
+        }
+    }
+
+    pub fn new_with_initial(poly: u32, initial: u32) -> Digest {
+        Digest {
+            table: make_table(poly),
+            initial: initial,
+            value: initial
+        }
+    }
+}
+
+impl Hasher32 for Digest {
+    fn reset(&mut self) {
+        self.value = self.initial;
+    }
+    fn write(&mut self, bytes: &[u8]) {
+        self.value = update(self.value, &self.table, bytes);
+    }
+    fn sum32(&self) -> u32 {
+        self.value
+    }
+}
+
+/// Implementation of std::hash::Hasher so that types which #[derive(Hash)] can hash with Digest.
+impl Hasher for Digest {
+    fn write(&mut self, bytes: &[u8]) {
+        Hasher32::write(self, bytes);
+    }
+
+    fn finish(&self) -> u64 {
+        self.sum32() as u64
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/src/crc64.rs
@@ -0,0 +1,77 @@
+#[cfg(feature = "std")]
+use std::hash::Hasher;
+#[cfg(not(feature = "std"))]
+use core::hash::Hasher;
+
+pub use util::make_table_crc64 as make_table;
+
+include!(concat!(env!("OUT_DIR"), "/crc64_constants.rs"));
+
+pub struct Digest {
+    table: [u64; 256],
+    initial: u64,
+    value: u64
+}
+
+pub trait Hasher64 {
+    fn reset(&mut self);
+    fn write(&mut self, bytes: &[u8]);
+    fn sum64(&self) -> u64;
+}
+
+pub fn update(mut value: u64, table: &[u64; 256], bytes: &[u8]) -> u64 {
+    value = !value;
+    for &i in bytes.iter() {
+        value = table[((value as u8) ^ i) as usize] ^ (value >> 8)
+    }
+    !value
+}
+
+pub fn checksum_ecma(bytes: &[u8]) -> u64 {
+    return update(0, &ECMA_TABLE, bytes);
+}
+
+pub fn checksum_iso(bytes: &[u8]) -> u64 {
+    return update(0, &ISO_TABLE, bytes);
+}
+
+impl Digest {
+    pub fn new(poly: u64) -> Digest {
+        Digest {
+            table: make_table(poly),
+            initial: 0,
+            value: 0
+        }
+    }
+
+    pub fn new_with_initial(poly: u64, initial: u64) -> Digest {
+        Digest {
+            table: make_table(poly),
+            initial: initial,
+            value: initial
+        }
+    }
+}
+
+impl Hasher64 for Digest {
+    fn reset(&mut self) {
+        self.value = self.initial;
+    }
+    fn write(&mut self, bytes: &[u8]) {
+        self.value = update(self.value, &self.table, bytes);
+    }
+    fn sum64(&self) -> u64 {
+        self.value
+    }
+}
+
+/// Implementation of std::hash::Hasher so that types which #[derive(Hash)] can hash with Digest.
+impl Hasher for Digest {
+    fn write(&mut self, bytes: &[u8]) {
+        Hasher64::write(self, bytes);
+    }
+
+    fn finish(&self) -> u64 {
+        self.sum64()
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/src/lib.rs
@@ -0,0 +1,50 @@
+//! # crc
+//! Rust implementation of CRC(32, 64)
+//!
+//! ## Usage
+//! ### Compute CRC32
+//! ```rust
+//! use crc::{crc32, Hasher32};
+//!
+//! // CRC-32-IEEE being the most commonly used one
+//! assert_eq!(crc32::checksum_ieee(b"123456789"), 0xcbf43926);
+//! assert_eq!(crc32::checksum_castagnoli(b"123456789"), 0xe3069283);
+//! assert_eq!(crc32::checksum_koopman(b"123456789"), 0x2d3dd0ae);
+//!
+//! // use provided or custom polynomial
+//! let mut digest = crc32::Digest::new(crc32::IEEE);
+//! digest.write(b"123456789");
+//! assert_eq!(digest.sum32(), 0xcbf43926);
+//!
+//! // with initial
+//! let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, 0u32);
+//! digest.write(b"123456789");
+//! assert_eq!(digest.sum32(), 0xcbf43926);
+//! ```
+//!
+//! ### Compute CRC64
+//! ```rust
+//! use crc::{crc64, Hasher64};
+//!
+//! assert_eq!(crc64::checksum_ecma(b"123456789"), 0x995dc9bbdf1939fa);
+//! assert_eq!(crc64::checksum_iso(b"123456789"), 0xb90956c775a41001);
+//!
+//! // use provided or custom polynomial
+//! let mut digest = crc64::Digest::new(crc64::ECMA);
+//! digest.write(b"123456789");
+//! assert_eq!(digest.sum64(), 0x995dc9bbdf1939fa);
+//!
+//! // with initial
+//! let mut digest = crc64::Digest::new_with_initial(crc64::ECMA, 0u64);
+//! digest.write(b"123456789");
+//! assert_eq!(digest.sum64(), 0x995dc9bbdf1939fa);
+//! ```
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub mod crc32;
+pub mod crc64;
+mod util;
+
+pub use self::crc32::Hasher32;
+pub use self::crc64::Hasher64;
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/src/util.rs
@@ -0,0 +1,31 @@
+pub fn make_table_crc32(poly: u32) -> [u32; 256] {
+    let mut table = [0u32; 256];
+    for i in 0..256 {
+        let mut value = i as u32;
+        for _ in 0..8 {
+            value = if (value & 1) == 1 {
+                (value >> 1) ^ poly
+            } else {
+                value >> 1
+            }
+        }
+        table[i] = value;
+    }
+    table
+}
+
+pub fn make_table_crc64(poly: u64) -> [u64; 256] {
+    let mut table = [0u64; 256];
+    for i in 0..256 {
+        let mut value = i as u64;
+        for _ in 0..8 {
+            value = if (value & 1) == 1 {
+                (value >> 1) ^ poly
+            } else {
+                value >> 1
+            }
+        }
+        table[i] = value;
+    }
+    table
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/tests/crc.rs
@@ -0,0 +1,88 @@
+extern crate crc;
+
+mod crc32 {
+    use crc::{crc32, Hasher32};
+
+    const CASTAGNOLI_CHECK_VALUE: u32 = 0xe3069283;
+    const IEEE_CHECK_VALUE: u32 = 0xcbf43926;
+    const KOOPMAN_CHECK_VALUE: u32 = 0x2d3dd0ae;
+
+    #[test]
+    fn checksum_castagnoli() {
+        assert_eq!(crc32::checksum_castagnoli(b"123456789"), CASTAGNOLI_CHECK_VALUE)
+    }
+
+    #[test]
+    fn checksum_ieee() {
+        assert_eq!(crc32::checksum_ieee(b"123456789"), IEEE_CHECK_VALUE)
+    }
+
+    #[test]
+    fn checksum_koopman() {
+        assert_eq!(crc32::checksum_koopman(b"123456789"), KOOPMAN_CHECK_VALUE)
+    }
+
+    #[test]
+    fn digest_castagnoli() {
+        verify_checksum(crc32::CASTAGNOLI, CASTAGNOLI_CHECK_VALUE);
+    }
+
+    #[test]
+    fn digest_ieee() {
+        verify_checksum(crc32::IEEE, IEEE_CHECK_VALUE);
+    }
+
+    #[test]
+    fn digest_koopman() {
+        verify_checksum(crc32::KOOPMAN, KOOPMAN_CHECK_VALUE);
+    }
+
+    fn verify_checksum(poly: u32, check_value: u32) {
+        let mut digest = crc32::Digest::new(poly);
+        digest.write(b"123456789");
+        assert_eq!(digest.sum32(), check_value);
+        digest.reset();
+        for i in 1..10 {
+            digest.write(i.to_string().as_bytes());
+        }
+        assert_eq!(digest.sum32(), check_value);
+    }
+}
+
+mod crc64 {
+    use crc::{crc64, Hasher64};
+
+    const ECMA_CHECK_VALUE: u64 = 0x995dc9bbdf1939fa;
+    const ISO_CHECK_VALUE: u64 = 0xb90956c775a41001;
+
+    #[test]
+    fn checksum_ecma() {
+        assert_eq!(crc64::checksum_ecma(b"123456789"), ECMA_CHECK_VALUE)
+    }
+
+    #[test]
+    fn checksum_iso() {
+        assert_eq!(crc64::checksum_iso(b"123456789"), ISO_CHECK_VALUE)
+    }
+
+    #[test]
+    fn digest_ecma() {
+        verify_checksum(crc64::ECMA, ECMA_CHECK_VALUE);
+    }
+
+    #[test]
+    fn digest_iso() {
+        verify_checksum(crc64::ISO, ISO_CHECK_VALUE);
+    }
+
+    fn verify_checksum(poly: u64, check_value: u64) {
+        let mut digest = crc64::Digest::new(poly);
+        digest.write(b"123456789");
+        assert_eq!(digest.sum64(), check_value);
+        digest.reset();
+        for i in 1..10 {
+            digest.write(i.to_string().as_bytes());
+        }
+        assert_eq!(digest.sum64(), check_value);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/crc/tests/hash.rs
@@ -0,0 +1,26 @@
+extern crate crc;
+
+mod hasher {
+    use crc::{crc32, crc64};
+    use std::hash::{Hash, Hasher};
+
+    #[derive(Hash)]
+    struct Person(&'static str, u8);
+
+    #[test]
+    fn checksum_hashcrc32() {
+        let person = Person("John Smith", 34);
+        let mut hasher = crc32::Digest::new(crc32::IEEE);
+        person.hash(&mut hasher);
+        assert_eq!(467823795u64, hasher.finish());
+    }
+
+    #[test]
+    fn checksum_hashcrc64() {
+        let person = Person("John Smith", 34);
+        let mut hasher = crc64::Digest::new(crc64::ECMA);
+        person.hash(&mut hasher);
+        assert_eq!(3567258626315136489u64, hasher.finish());
+    }
+
+}
--- a/third_party/rust/flate2/.cargo-checksum.json
+++ b/third_party/rust/flate2/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".travis.yml":"98cf8c8df11438fb438cfe8226d1123bce6680832a4accf91f83614a10363466","Cargo.toml":"b143aa0bd29dbdd83e67e0d28f91f398e0a006d41a899a088c1b560af2bad5a8","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"2accc2c811f3cfa17753c9cbe1cb9856f6a5f52705dafc355e1d5494d2a81e2f","appveyor.yml":"7293e690b6b1fb75cbab1f72ffc5077dd145c806282969b595cc1a82ce1a2e62","examples/deflatedecoder-bufread.rs":"9066719a7b91c57792c1e305a808c12727c93938e936ac2ae16b0e174b82138f","examples/deflatedecoder-read.rs":"39ee43782a5bee7f808da7d32e1b5cd8b51e8be5e344ac11ad9be8a300bc7c85","examples/deflatedecoder-write.rs":"fd084e864a6f6ea6597b8baa2c6f2ab59c2d07da1c3aeb094ec4391c0266d0ad","examples/deflateencoder-bufread.rs":"9c1bd6d215095e338c5a6fc2b3c3b4685f836cae8f102a229d8571c17e2ece6a","examples/deflateencoder-read.rs":"805b677b584c3209ba46e7b4e9706e93d15ebe5f26e086760931fdecef14aeff","examples/deflateencoder-write.rs":"fa402135fa3d009aba4d492ff1287dc2a7cfd745c6fff37d3eb12fef924bf7f4","examples/flatereadext.rs":"656b9bc5352d30a4e6ba7d33a187bc4c1adc28ccb4bd7bffd67fa74c75da3a95","examples/gzbuilder.rs":"2bccaf7f3fb81d1251f410323a3f9a98d838754ce3d370e16ed15c97be5d91bc","examples/gzdecoder-bufread.rs":"76b208873eb2461d213cdf8b20c4062c8fc5dd17a874df12f120415b43d81da3","examples/gzdecoder-read.rs":"e90f4b05312341bdd3b9995b919903efd5be75a7815473d472c1a494446acac2","examples/gzencoder-bufread.rs":"d403cb34700a0c654e11d111e4f862b7a0ae88d05df39a2c8ecd163fe3bcf2f0","examples/gzencoder-read.rs":"c97c87b059da9c46e0d0ddf06ea4db7c22a3dac9e699ab81dbb2d4a8b8681d3a","examples/gzencoder-write.rs":"5ff4f9ea1f9facad50d08ef1687bb785b2f657d6897b7253cd1af32e912e6284","examples/gzmultidecoder-bufread.rs":"23a92037081675fbab811b1d1ac1037130f4f8a4f2f0642c0bf063da835c0c94","examples/gzmultidecoder-read.rs":"d34e50a1fcd5d6c8c25cb203561fd44f698679ba624fe1ecfbfe7b67adff40ba","examples/hello_world.txt":"d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26","examples/zlibdecoder-bufread.rs":"b0d5646607d29a3b6d1add6ce002fab781a55463e9bb749ffff22492e2be0618","examples/zlibdecoder-read.rs":"5f5497f7534750229c182a90e17f2522f46ded904ebf942169fcc86fc2503abd","examples/zlibdecoder-write.rs":"755f6beec5d25874611101d6785b4245f405364e21e45f54ff6915d11a563f53","examples/zlibencoder-bufread.rs":"e7ab4059b98c583382a9d8ec21210329dd150c534af864d23e6beed58bd94ac1","examples/zlibencoder-read.rs":"61853ef3c41e94f45eecedbb90964175ce13390e1a5a01c13326af474714e962","examples/zlibencoder-write.rs":"ec2b7bdacc5b517029c1bd7a3ce6fd399a8f760503a98e9d31dea50f69296e0a","src/bufreader.rs":"ebfbaf104b292a64e8b46076c1bbb680c2941753c02928e85cb5215897891d4a","src/crc.rs":"fc9147392c4b87255950e9bb65e75f1ddebe3d45bfc985980a3937e80f345aab","src/deflate/bufread.rs":"9bbf8b1111f112db2a6bed9b797b2acf055611f605294be29010b8fb7cb67ada","src/deflate/mod.rs":"1f8280ea4ab35c91f886b0373eacbc89da89d53876d4473aa4ab32a07dd098aa","src/deflate/read.rs":"88c2ac0e393fbd8604639b4730f377eeb8797366dd729d52e14236c964af2b02","src/deflate/write.rs":"db6a4782fb4d46fbc87344e59fe8b4155522a76f8ea9d9bdea8bb1398b639b49","src/ffi.rs":"0cab9ad8d3c0c2558cc2b3e9d0faa9c7fc94a0518896fb1a130e573f72aad05e","src/gz/bufread.rs":"b9f038d1ace807e97c4408ea0d1e5bc615697b7fc87fe4dc4183d99b6270b9c2","src/gz/mod.rs":"8a59e00a571f59bab43722000de3b128d443ff869c25e8d7a7f0eaa180055be9","src/gz/read.rs":"4e0c3560de39d7481484f4df6a6f012979421e257eebce247dab5f54a49407e7","src/gz/write.rs":"f5558769630a7f8d8d61a29f8625570ee50fa898b1aa3f3b7d24fcf50ecb2736","src/lib.rs":"aca4a46ac726cce6c67bf06719722b96006faf5330a8596099a9135fa54cfde9","src/mem.rs":"d8bf64e13e0a0bce49d4e3ee2522283f2d4541e2049aa928774d964398358ea6","src/zio.rs":"1076ceab3cee8a455ef8d199f66c0d0f19b1ae512421a776aaa50c9f6bacace5","src/zlib/bufread.rs":"464083100ca54e20643c7acfb6cd6ea96c928dca42667593997aa29d9d0de823","src/zlib/mod.rs":"6c959d5f7ba78ce42a2337327e1f730b0c529b853a13fbd8a59e9ba150763fa2","src/zlib/read.rs":"e963274f6013cb81b52510e7074f5c6554954a44b0391491cb831fff8c190290","src/zlib/write.rs":"0ce320af2aad71fe7ac91ba258ca348641dd7627bb51756dfb30e2e9ca9d9ad8","tests/corrupt-file.gz":"083dd284aa1621916a2d0f66ea048c8d3ba7a722b22d0d618722633f51e7d39c","tests/early-flush.rs":"6ff08e3e16722a61d141fc503a438bbb84c5beb3bdd28bbac9dfa03d3562cc3d","tests/good-file.gz":"87296963e53024a74752179ce7e54087565d358a85d3e65c3b37ef36eaa3d4a6","tests/good-file.txt":"bc4e03658a441fe2ad2df7cd2197144b87e41696f01e327b380e869cd9b485a0","tests/gunzip.rs":"3d2e0a80756474dc2b08f20071685117083765c4f1763456c676f1feeaff35e9","tests/multi.gz":"efa3341da052f95056314cc6920e02a3da15bdef30234b2552fb407812db5cc6","tests/multi.txt":"dbea9325179efe46ea2add94f7b6b745ca983fabb208dc6d34aa064623d7ee23","tests/tokio.rs":"d19defd6c369877f406ed8bd74683a257fde7de51f4161004cea4260faf4464d","tests/zero-write.rs":"027330824a7141963657efa26d99d7503a16492ed8779d7e316e22bba0e7f190"},"package":"e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423"}
\ No newline at end of file
+{"files":{".travis.yml":"3a1869337e8341e30dc40221d23bd832185a87bd7d81849ca10dcf0ceea7d84c","Cargo.toml":"7024cb49649732a718af25d83cf4734dc376455f86c81f8c77bb3232683b74cf","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"6bd103720c4870cb162e21819a319322ef7da8aab4a1ea62594135d45f8ed466","appveyor.yml":"7293e690b6b1fb75cbab1f72ffc5077dd145c806282969b595cc1a82ce1a2e62","examples/deflatedecoder-bufread.rs":"897f245252460b7611696afb51174d829be77a3ff54faddb6e9f8f505a8041df","examples/deflatedecoder-read.rs":"751070ee3bbbbb50ccee19d8d3b5a0126f17901d2056026ccb9ff285147ba86f","examples/deflatedecoder-write.rs":"4a3663de0af773509a57ec5a95288a6461530f2ac75f7afddf82fa5f9a8bdb5f","examples/deflateencoder-bufread.rs":"c9a798336ed81c104fc01c3620380bb3369af90a710a89b19c2402f25c25d375","examples/deflateencoder-read.rs":"e2714d74aae10e28b99e47f31a0ff59c41879e9dd7c07d0a71cf254591a13d28","examples/deflateencoder-write.rs":"c15264846926339ad025f1bd385dcb9131f5503e6a558aa0443c989d63dc0b66","examples/gzbuilder.rs":"56c779c33a088dc4e9d5e77d7093ee49fda8210d722d0dd7f9681b0bdcbd0a0c","examples/gzdecoder-bufread.rs":"f3220e9f07d067a80667ec8caa2977588f867b5ee84eb2e3057969ed2b8421d2","examples/gzdecoder-read.rs":"5dccd8c2d5454a60b39f13fde234c499fbc51cf039e638451480ab16d13dffe3","examples/gzencoder-bufread.rs":"ad79e9f8630359ed21b74a1431d49fdb08c0673df7b21e98d0cad05b29357460","examples/gzencoder-read.rs":"5f4a5cb995254d0f5c4558142b1a55f8cddabb5f0411a1527bf33b80fa09dbb8","examples/gzencoder-write.rs":"40eccdce2be8b2c794e26989d72444bb8c05fae7c752c676dff8ac0c4fe7e433","examples/gzmultidecoder-bufread.rs":"68a3db0a2a032a02be1101261accdc5488f1a9e20691a531675460ec42a51d3a","examples/gzmultidecoder-read.rs":"ca85e260dc558b848aa9cad2111e8493b4aa30df23d9be9ca1314e606758b892","examples/hello_world.txt":"d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26","examples/zlibdecoder-bufread.rs":"373956fb2890a5757597740ca37e854d3a10fc58bdf37efe4ae810ec72d94920","examples/zlibdecoder-read.rs":"ead477d8e687c1fe343da895ad5d6897e8ba316072691dccaf4aeed8bb83ee9b","examples/zlibdecoder-write.rs":"b80f3ec163a2227166bdfefd61ab232942967f34841ebb6a63b31ce37feecb8a","examples/zlibencoder-bufread.rs":"667d3bdc83878e97d4faae6c820693339799989db1031e976dc52d7bc1e119ce","examples/zlibencoder-read.rs":"89767e7bb3aa786d813a6c31d1770aadc966eecc0d80f4e5263f82198c8baa21","examples/zlibencoder-write.rs":"05fd187dba9b2a67db8c97e3550050b08cf65c46e0b425529c1982c6c35922c6","src/bufreader.rs":"ebfbaf104b292a64e8b46076c1bbb680c2941753c02928e85cb5215897891d4a","src/crc.rs":"308fd636ccc2e304e90713a775aedf012205823aa2b956d36eaa9a64ce4d881d","src/deflate/bufread.rs":"a01791da80ff6552a0dba9cebac5c2dd071eed732468f2dc87d723ca26207815","src/deflate/mod.rs":"76e9b0081be3771853650e592e48d87e005a92f7f78eb67f5ecf6dd7890437d3","src/deflate/read.rs":"9a65e8452c8b5f8055121659f080ff4760d789a4cf58e722390ae2bef3bd14f1","src/deflate/write.rs":"9f99ae1160516481087a4b47457814bd4c60dcf7373fc25ed882b0055da7ef2f","src/ffi.rs":"10babc755e572f35f811fdbf6988dad890a8f0ef8bcb04e238ae0a19ea5c73fa","src/gz/bufread.rs":"8e23ba1d4b5b56c299d04f3fad4687af2dde046c0945388bb7ef65de84f03c79","src/gz/mod.rs":"27d46e013fce8f229f90394a1d5bd3f984c1bce424329ed4dfc38fa471587000","src/gz/read.rs":"a85bf61cc17804a7f0e611e565edc75d04f1c548a716c065e0eeb78a93018e57","src/gz/write.rs":"649bf7e3e6cfe47702b75490d8250f3558558e9648b4189db1ac69a32929b3a2","src/lib.rs":"a37282ec3b70bfeae346fe66f6225a2b7ba2e1c47f71d4b990dd918898e73b88","src/mem.rs":"8990d45d9a66be9dc5bc0aa1d9e7c71d846e2d1515bced6347c4d3c0c7bc5b1b","src/zio.rs":"14b62753f4ce593f7e4e9e4dcf40d66e03dba7e592f5411f381196ebb7be6456","src/zlib/bufread.rs":"6ffadc4f80b7c83218b9c0ddbe6493c1ce8e31f7ae0b7a1c2d6b63185b4f6ea4","src/zlib/mod.rs":"e48bd4c4b21339bfc1078fd3db54a6b7151fe02068c2fe5166b941874cf1ee26","src/zlib/read.rs":"bd293915d7c66a61c2286030c3bdf88100958af571fc587916ce477005d9ba7c","src/zlib/write.rs":"177f0650e299a7942ee60d63ba032039fe0cf410416ac9772283bbfe5c59b5f4","tests/corrupt-file.gz":"083dd284aa1621916a2d0f66ea048c8d3ba7a722b22d0d618722633f51e7d39c","tests/early-flush.rs":"29b73a15f6cdb6834ace208590caff1cbc38ebac56193f11caa719caca411491","tests/good-file.gz":"87296963e53024a74752179ce7e54087565d358a85d3e65c3b37ef36eaa3d4a6","tests/good-file.txt":"bc4e03658a441fe2ad2df7cd2197144b87e41696f01e327b380e869cd9b485a0","tests/gunzip.rs":"63d36eb0bbaad0906d12c223202590fb28a56565088567cf5b1c474acb48b538","tests/multi.gz":"efa3341da052f95056314cc6920e02a3da15bdef30234b2552fb407812db5cc6","tests/multi.txt":"dbea9325179efe46ea2add94f7b6b745ca983fabb208dc6d34aa064623d7ee23","tests/tokio.rs":"5fb574754bded91009e71bd64e7adf391f4328343aab3246657e2b09c381212b","tests/zero-write.rs":"ff8d0349a540b54363c55807c5fd7fbbdc363d08a536d35a3a40f0ce92c16489"},"package":"9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"}
\ No newline at end of file
--- a/third_party/rust/flate2/.travis.yml
+++ b/third_party/rust/flate2/.travis.yml
@@ -1,32 +1,41 @@
 language: rust
-rust:
-  - stable
-  - beta
-  - nightly
 sudo: false
-before_script:
-  - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
+
+matrix:
+  include:
+    - rust: 1.21.0
+    - rust: stable
+    - os: osx
+    - rust: beta
+    - rust: nightly
+
+    - rust: nightly
+      before_script:
+        - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
+      script:
+        - cargo doc --no-deps --all-features
+      after_success:
+        - travis-cargo --only nightly doc-upload
+
 script:
   - export CARGO_TARGET_DIR=`pwd`/target
   - cargo build --verbose
   - rustdoc --test README.md -L target/debug/deps --extern flate2=target/debug/libflate2.rlib
   - cargo test --verbose
   - cargo test --verbose --features zlib
   - cargo test --verbose --features tokio
   - cargo test --verbose --features 'tokio zlib'
   - cargo test --verbose --features zlib --no-default-features
+  - cargo test --verbose --features rust_backend
+  - cargo test --verbose --features rust_backend --no-default-features
   - cargo clean && cargo build
   - cargo doc --no-deps
   - cargo doc --no-deps --manifest-path=miniz-sys/Cargo.toml
-after_success:
-  - travis-cargo --only nightly doc-upload
+
 env:
   global:
     secure: "PHVT7IaeP5nQQVwGHKwqCYBDp0QyetSlER7se2j2Xgfx+lw3Bu6VWH6VF04B636Gb0tHPN/sUCXSgGRcvDuy6XFOev4LfynoYxNKgHJYg2E34EP2QLwsFfnvE4iujaG3GJk3o935Y7OYGv2OP1HeG4Mv6JhQK0GLnNDBZQ65kWI="
 
 notifications:
   email:
     on_success: never
-os:
-  - linux
-  - osx
--- a/third_party/rust/flate2/Cargo.toml
+++ b/third_party/rust/flate2/Cargo.toml
@@ -7,55 +7,60 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "flate2"
-version = "0.2.20"
+version = "1.0.1"
 authors = ["Alex Crichton <alex@alexcrichton.com>"]
 description = "Bindings to miniz.c for DEFLATE compression and decompression exposed as\nReader/Writer streams. Contains bindings for zlib, deflate, and gzip-based\nstreams.\n"
 homepage = "https://github.com/alexcrichton/flate2-rs"
 documentation = "https://docs.rs/flate2"
 readme = "README.md"
 keywords = ["gzip", "flate", "zlib", "encoding"]
 categories = ["compression", "api-bindings"]
 license = "MIT/Apache-2.0"
 repository = "https://github.com/alexcrichton/flate2-rs"
+[dependencies.futures]
+version = "0.1"
+optional = true
+
 [dependencies.libc]
 version = "0.2"
 
 [dependencies.libz-sys]
 version = "1.0"
 optional = true
 
 [dependencies.miniz-sys]
 version = "0.1.7"
 optional = true
 
-[dependencies.futures]
+[dependencies.miniz_oxide_c_api]
 version = "0.1"
 optional = true
 
 [dependencies.tokio-io]
 version = "0.1"
 optional = true
-[dev-dependencies.rand]
-version = "0.3"
-
 [dev-dependencies.quickcheck]
 version = "0.4"
 default-features = false
 
+[dev-dependencies.rand]
+version = "0.3"
+
 [dev-dependencies.tokio-core]
 version = "0.1"
 
 [features]
 default = ["miniz-sys"]
+rust_backend = ["miniz_oxide_c_api"]
+tokio = ["tokio-io", "futures"]
 zlib = ["libz-sys"]
-tokio = ["tokio-io", "futures"]
+[badges.appveyor]
+repository = "alexcrichton/flate2-rs"
+
 [badges.travis-ci]
 repository = "alexcrichton/flate2-rs"
-
-[badges.appveyor]
-repository = "alexcrichton/flate2-rs"
--- a/third_party/rust/flate2/README.md
+++ b/third_party/rust/flate2/README.md
@@ -1,19 +1,23 @@
 # flate2
 
 [![Build Status](https://travis-ci.org/alexcrichton/flate2-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/flate2-rs)
 [![Build status](https://ci.appveyor.com/api/projects/status/9tatexq47i3ee13k?svg=true)](https://ci.appveyor.com/project/alexcrichton/flate2-rs)
 [![Crates.io](https://img.shields.io/crates/v/flate2.svg?maxAge=2592000)](https://crates.io/crates/flate2)
 [![Documentation](https://docs.rs/flate2/badge.svg)](https://docs.rs/flate2)
 
 A streaming compression/decompression library for Rust. The underlying
-implementation by default uses [`miniz`](https://code.google.com/p/miniz/) but
+implementation by default uses [`miniz`](https://github.com/richgel999/miniz) but
 can optionally be configured to use the system zlib, if available.
 
+There is also an experimental rust backend that uses the
+[`miniz_oxide`](https://crates.io/crates/miniz_oxide) crate. This avoids the need
+to build C code, but hasn't gone through as much testing as the other backends.
+
 Supported formats:
 
 * deflate
 * zlib
 * gzip
 
 ```toml
 # Cargo.toml
@@ -23,48 +27,64 @@ flate2 = "0.2"
 
 Using zlib instead of miniz:
 
 ```toml
 [dependencies]
 flate2 = { version = "0.2", features = ["zlib"], default-features = false }
 ```
 
+Using the rust back-end:
+
+```toml
+[dependencies]
+flate2 = { version = "0.2", features = ["rust_backend"], default-features = false }
+```
+
 ## Compression
 
 ```rust
 extern crate flate2;
 
 use std::io::prelude::*;
 use flate2::Compression;
 use flate2::write::ZlibEncoder;
 
 fn main() {
-    let mut e = ZlibEncoder::new(Vec::new(), Compression::Default);
+    let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
     e.write(b"foo");
     e.write(b"bar");
     let compressed_bytes = e.finish();
 }
 ```