Merge mozilla-central to inbound. a=merge CLOSED TREE
authorCosmin Sabou <csabou@mozilla.com>
Fri, 01 Jun 2018 12:44:57 +0300
changeset 420831 e99ff79303ea48b856b93e66ccc808d0aac8a68b
parent 420758 4b57c07f8c2cd55c94a766b769808ccba71868bf (current diff)
parent 420830 9900cebb1f9000bd05731ba67736b7c51f7eb812 (diff)
child 420832 f22e28ed197c45e29c5df80e3c91036a976e266d
push id103894
push usercsabou@mozilla.com
push dateFri, 01 Jun 2018 09:46:36 +0000
treeherdermozilla-inbound@e99ff79303ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.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
dom/base/nsDOMWindowUtils.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsFrameMessageManager.cpp
dom/events/EventDispatcher.cpp
dom/events/EventListenerManager.cpp
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsParent.cpp
dom/ipc/ContentChild.cpp
dom/ipc/TabChild.cpp
dom/ipc/TabParent.cpp
dom/workers/WorkerPrivate.cpp
gfx/layers/PaintThread.cpp
gfx/thebes/gfxWindowsPlatform.cpp
js/src/gc/GC.cpp
js/xpconnect/loader/ScriptPreloader.cpp
js/xpconnect/src/XPCJSRuntime.cpp
layout/base/PresShell.cpp
layout/base/nsRefreshDriver.cpp
layout/generic/nsFloatManager.cpp
layout/generic/nsFrame.cpp
layout/painting/FrameLayerBuilder.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-039-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-039.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-040.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-045-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-045.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-046.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-018-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-018.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-019-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-019.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-026-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-026.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-027-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-027.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-028.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-029.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-030-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-030.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-031-ref.html
layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-031.html
media/webrtc/signaling/gtest/sdp_unittests.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
testing/mochitest/Makefile.in
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-040.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-046.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-028.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-029.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031-ref.html
testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031.html
tools/docs/requirements.txt
tools/profiler/core/platform.cpp
tools/profiler/tests/gtest/GeckoProfiler.cpp
widget/android/nsAppShell.cpp
widget/cocoa/nsChildView.mm
widget/gtk/nsWindow.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/threads/Scheduler.cpp
xpcom/threads/ThreadEventQueue.cpp
xpcom/threads/nsThreadPool.cpp
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -395,39 +395,16 @@ html|input.urlbar-input {
 }
 
 #editBookmarkPanelRows > vbox > textbox[focused="true"],
 #editBookmarkPanelRows > vbox > hbox > textbox[focused="true"] {
   border-color: -moz-mac-focusring !important;
   box-shadow: var(--focus-ring-box-shadow);
 }
 
-.editBookmarkPanelBottomButton {
-  @hudButton@
-  margin: 0;
-  min-width: 82px;
-  min-height: 22px;
-}
-
-.editBookmarkPanelBottomButton:hover:active {
-  @hudButtonPressed@
-}
-
-.editBookmarkPanelBottomButton:-moz-focusring {
-  box-shadow: var(--focus-ring-box-shadow);
-}
-
-.editBookmarkPanelBottomButton[default="true"] {
-  background-color: #666;
-}
-
-.editBookmarkPanelBottomButton:last-child {
-  margin-inline-start: 8px;
-}
-
 /* The following elements come from editBookmarkPanel.inc.xul. Styling that's
    specific to the editBookmarkPanel should be in browser.css. Styling that
    should be shared by all editBookmarkPanel.inc.xul consumers should be in
    editBookmark.css. */
 
 #editBMPanel_newFolderBox {
   background: linear-gradient(#fff, #f2f2f2);
   background-origin: padding-box;
--- a/browser/themes/shared/places/editBookmarkPanel.inc.css
+++ b/browser/themes/shared/places/editBookmarkPanel.inc.css
@@ -3,17 +3,55 @@
  * 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/. */
 %endif
 
 #editBookmarkPanel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
-#editBookmarkPanelBottomButtons,
 #editBookmarkPanelRows {
   padding: var(--arrowpanel-padding);
 }
 
 /* Implements editBookmarkPanel resizing on folderTree un-collapse. */
 #editBMPanel_folderTree {
   min-width: 27em;
 }
+
+#editBookmarkPanelBottomButtons {
+  display: flex;
+}
+
+.editBookmarkPanelBottomButton {
+  flex: 1;
+  -moz-appearance: none;
+  margin: 0;
+  padding: .8em;
+  color: inherit;
+  background-color: var(--arrowpanel-dimmed);
+  border-top: 1px solid var(--panel-separator-color);
+}
+
+.editBookmarkPanelBottomButton:not(:last-child) {
+  border-inline-end: 1px solid var(--panel-separator-color);
+}
+
+.editBookmarkPanelBottomButton:hover {
+  background-color: var(--arrowpanel-dimmed-further);
+}
+
+.editBookmarkPanelBottomButton:hover:active {
+  background-color: var(--arrowpanel-dimmed-even-further);
+}
+
+.editBookmarkPanelBottomButton[default] {
+  color: white;
+  background-color: #0996f8;
+}
+
+.editBookmarkPanelBottomButton[default]:hover {
+  background-color: #0675d3;
+}
+
+.editBookmarkPanelBottomButton[default]:hover:active {
+  background-color: #0568ba;
+}
--- a/devtools/client/framework/components/ToolboxTabs.js
+++ b/devtools/client/framework/components/ToolboxTabs.js
@@ -105,21 +105,26 @@ class ToolboxTabs extends Component {
     return !this.equalToolIdArray(prevPanels, nextPanels);
   }
 
   /**
    * Update the Map of tool id and tool tab width.
    */
   updateCachedToolTabsWidthMap() {
     let thisNode = findDOMNode(this);
+    let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+        .getInterface(Ci.nsIDOMWindowUtils);
+    // Force a reflow before calling getBoundingWithoutFlushing on each tab.
+    thisNode.clientWidth;
+
     for (let tab of thisNode.querySelectorAll(".devtools-tab")) {
       let tabId = tab.id.replace("toolbox-tab-", "");
       if (!this._cachedToolTabsWidthMap.has(tabId)) {
-        let cs = getComputedStyle(tab);
-        this._cachedToolTabsWidthMap.set(tabId, parseInt(cs.width, 10));
+        let rect = utils.getBoundsWithoutFlushing(tab);
+        this._cachedToolTabsWidthMap.set(tabId, rect.width);
       }
     }
   }
 
   /**
    * Update the overflowed tab array from currently displayed tool tab.
    * If calculated result is the same as the current overflowed tab array, this
    * function will not update state.
@@ -155,34 +160,35 @@ class ToolboxTabs extends Component {
         enabledTabs.includes(currentToolId)) {
       let selectedToolWidth = this._cachedToolTabsWidthMap.get(currentToolId);
       while ((sumWidth + selectedToolWidth) > toolboxWidth &&
              visibleTabs.length > 0) {
         let removingToolId  = visibleTabs.pop();
         let removingToolWidth = this._cachedToolTabsWidthMap.get(removingToolId);
         sumWidth -= removingToolWidth;
       }
-      visibleTabs.push(currentToolId);
-    }
 
-    if (visibleTabs.length === 0) {
-      visibleTabs = [enabledTabs[0]];
+      // If toolbox width is narrow, toolbox display only chevron menu.
+      // i.e. All tool tabs will overflow.
+      if ((sumWidth + selectedToolWidth) <= toolboxWidth) {
+        visibleTabs.push(currentToolId);
+      }
     }
 
     let willOverflowTabs = enabledTabs.filter(id => !visibleTabs.includes(id));
     if (!this.equalToolIdArray(this.state.overflowedTabIds, willOverflowTabs)) {
       this.setState({ overflowedTabIds: willOverflowTabs });
     }
   }
 
   resizeHandler(evt) {
     window.cancelIdleCallback(this._resizeTimerId);
     this._resizeTimerId = window.requestIdleCallback(() => {
       this.updateOverflowedTabs();
-    }, { timeout: 300 });
+    }, { timeout: 100 });
   }
 
   /**
    * Render a button to access overflowed tools, displayed only when the toolbar
    * presents an overflow.
    */
   renderToolsChevronButton() {
     let {
--- a/devtools/client/framework/components/ToolboxToolbar.js
+++ b/devtools/client/framework/components/ToolboxToolbar.js
@@ -83,28 +83,40 @@ class ToolboxToolbar extends Component {
     };
   }
 
   /**
    * The render function is kept fairly short for maintainability. See the individual
    * render functions for how each of the sections is rendered.
    */
   render() {
-    const containerProps = {className: "devtools-tabbar"};
+    let classnames = ["devtools-tabbar"];
+    let startButtons = renderToolboxButtonsStart(this.props);
+    let endButtons = renderToolboxButtonsEnd(this.props);
+
+    if (!startButtons) {
+      classnames.push("devtools-tabbar-has-start");
+    }
+    if (!endButtons) {
+      classnames.push("devtools-tabbar-has-end");
+    }
+
     return this.props.canRender
       ? (
         div(
-          containerProps,
-          renderToolboxButtonsStart(this.props),
+          {
+            className: classnames.join(" ")
+          },
+          startButtons,
           ToolboxTabs(this.props),
-          renderToolboxButtonsEnd(this.props),
+          endButtons,
           renderToolboxControls(this.props)
         )
       )
-      : div(containerProps);
+      : div({ className: classnames.join(" ") });
   }
 }
 
 module.exports = ToolboxToolbar;
 
 /**
  * A little helper function to call renderToolboxButtons for buttons at the start
  * of the toolbox.
--- a/devtools/client/responsive.html/index.css
+++ b/devtools/client/responsive.html/index.css
@@ -201,16 +201,17 @@ select > option.divider {
 
 #global-toolbar > .toolbar-button:first-of-type {
   margin-inline-start: 8px;
 }
 
 #global-toolbar > .toolbar-button::before {
   width: 12px;
   height: 12px;
+  background-size: cover;
 }
 
 #global-toolbar .toolbar-dropdown {
   background-position-x: right 5px;
   border-right: 1px solid var(--theme-splitter-color);
   padding-right: 15px;
   /* padding-left: 0; */
 }
--- a/devtools/client/themes/devtools-browser.css
+++ b/devtools/client/themes/devtools-browser.css
@@ -1,16 +1,18 @@
 /* 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/. */
 
 @import url("resource://devtools/client/themes/splitters.css");
 
 .devtools-toolbox-side-iframe {
-  min-width: 250px;
+  /* Toolbar should display the chevron and meatball (and close) button.
+    This size is sum of chevron and meatball and close button. */
+  min-width: 74px;
 }
 
 /* Eyedropper Widget */
 /* <panel> added to mainPopupSet */
 
 .devtools-eyedropper-panel {
   pointer-events: none;
   -moz-appearance: none;
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -18,29 +18,54 @@
   --command-measure-image: url(images/command-measure.svg);
   --command-chevron-image: url(images/command-chevron.svg);
 }
 
 /* Toolbox tabbar */
 
 .devtools-tabbar {
   -moz-appearance: none;
-  display: flex;
+  /* For narrow devtool width, we define the each column width of tabbar.
+    Defined layout is as follow:
+
+    -------------------------------------------------
+    | Picker |  tooltabs  |  commands |   controls  |
+    |  auto  | 26px ~ 1fr |    auto   |  max-content|
+    -------------------------------------------------
+  */
+  display: grid;
+  grid-template-columns: auto minmax(26px, 1fr) auto max-content;
   background: var(--theme-tab-toolbar-background);
   border-bottom: 1px solid var(--theme-splitter-color);
   box-sizing: border-box;
   min-height: 29px;
 }
 
 .toolbox-tabs-wrapper {
   position: relative;
   display: flex;
   flex: 1;
 }
 
+/* These classes use to stretch the tool tabs wrapper width if toolbox does'n
+  have start buttons or end buttons element. */
+
+.devtools-tabbar .toolbox-tabs-wrapper {
+  grid-column-start: 2;
+  grid-column-end: 3;
+}
+
+.devtools-tabbar-has-start .toolbox-tabs-wrapper {
+  grid-column-start: 1;
+}
+
+.devtools-tabbar-has-end .toolbox-tabs-wrapper {
+  grid-column-end: 4;
+}
+
 .toolbox-tabs-wrapper .tools-chevron-menu {
   border-top-width: 0;
   border-bottom-width: 0;
 }
 
 .toolbox-tabs {
   position: absolute;
   top: 0;
@@ -55,16 +80,17 @@
 
 /* Set flex attribute to Toolbox buttons and Picker container so,
    they don't overlap with the tab bar */
 #toolbox-buttons-start,
 #toolbox-buttons-end,
 #toolbox-controls {
   display: flex;
   align-items: stretch;
+  overflow: hidden;
 }
 
 /* Toolbox tabs */
 
 .devtools-tab {
   position: relative;
   display: flex;
   align-items: center;
@@ -170,21 +196,29 @@
   margin-inline-end: 3px;
 }
 
 #toolbox-buttons-end > .devtools-separator {
   margin-inline-start: 5px;
   margin-inline-end: 5px;
 }
 
+#toolbox-close {
+  min-width: 24px;
+}
+
 #toolbox-close::before {
   fill: var(--theme-toolbar-photon-icon-color);
   background-image: var(--close-button-image);
 }
 
+#toolbox-meatball-menu-button {
+  min-width: 24px;
+}
+
 #toolbox-meatball-menu-button::before {
   fill: var(--theme-toolbar-photon-icon-color);
   background-image: var(--more-button-image);
 }
 
 /* Command buttons */
 
 .command-button,
--- a/dom/animation/test/mozilla/file_deferred_start.html
+++ b/dom/animation/test/mozilla/file_deferred_start.html
@@ -11,156 +11,158 @@
   background-color: white;
 }
 </style>
 <body>
 <script>
 'use strict';
 
 function waitForDocLoad() {
-  return new Promise(function(resolve, reject) {
+  return new Promise((resolve, reject) => {
     if (document.readyState === 'complete') {
       resolve();
     } else {
       window.addEventListener('load', resolve);
     }
   });
 }
 
 function waitForPaints() {
-  return new Promise(function(resolve, reject) {
+  return new Promise((resolve, reject) => {
     waitForAllPaintsFlushed(resolve);
   });
 }
 
-promise_test(function(t) {
+promise_test(async t => {
   // Test that empty animations actually start.
   //
   // Normally we tie the start of animations to when their first frame of
   // the animation is rendered. However, for animations that don't actually
   // trigger a paint (e.g. because they are empty, or are animating something
   // that doesn't render or is offscreen) we want to make sure they still
   // start.
   //
   // Before we start, wait for the document to finish loading, then create
   // div element, and wait for painting. This is because during loading we will
   // have other paint events taking place which might, by luck, happen to
   // trigger animations that otherwise would not have been triggered, leading to
   // false positives.
   //
   // As a result, it's better to wait until we have a more stable state before
   // continuing.
-  var div;
-  var promiseCallbackDone = false;
-  return waitForDocLoad().then(function() {
-    div = addDiv(t);
+  await waitForDocLoad();
+
+  const div = addDiv(t);
 
-    return waitForPaints();
-  }).then(function() {
-    div.style.animation = 'empty 1000s';
-    var animation = div.getAnimations()[0];
+  await waitForPaints();
+
+  div.style.animation = 'empty 1000s';
+  const animation = div.getAnimations()[0];
 
-    animation.ready.then(function() {
-      promiseCallbackDone = true;
-    }).catch(function() {
-      assert_unreached('ready promise was rejected');
-    });
+  let promiseCallbackDone = false;
+  animation.ready.then(() => {
+    promiseCallbackDone = true;
+  }).catch(() => {
+    assert_unreached('ready promise was rejected');
+  });
 
-    // We need to wait for up to three frames. This is because in some
-    // cases it can take up to two frames for the initial layout
-    // to take place. Even after that happens we don't actually resolve the
-    // ready promise until the following tick.
-    return waitForAnimationFrames(3);
-  }).then(function() {
-    assert_true(promiseCallbackDone,
-                'ready promise for an empty animation was resolved'
-                + ' within three animation frames');
-  });
+  // We need to wait for up to three frames. This is because in some
+  // cases it can take up to two frames for the initial layout
+  // to take place. Even after that happens we don't actually resolve the
+  // ready promise until the following tick.
+  await waitForAnimationFrames(3);
+
+  assert_true(promiseCallbackDone,
+              'ready promise for an empty animation was resolved'
+              + ' within three animation frames');
 }, 'Animation.ready is resolved for an empty animation');
 
 // Test that compositor animations with delays get synced correctly
 //
 // NOTE: It is important that we DON'T use
 // SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes
 // us through a different code path.
-promise_test(function(t) {
+promise_test(async t => {
   assert_false(SpecialPowers.DOMWindowUtils.isTestControllingRefreshes,
                'Test should run without the refresh driver being under'
                + ' test control');
 
   // This test only applies to compositor animations
   if (!isOMTAEnabled()) {
     return;
   }
 
   const div = addDiv(t, { class: 'target' });
 
   // As with the above test, any stray paints can cause this test to produce
   // a false negative (that is, pass when it should fail). To avoid this we
   // wait for paints and only then do we commence the test.
-  return waitForPaints().then(() => {
+  await waitForPaints();
+
+  const animation =
     div.animate({ transform: [ 'translate(0px)', 'translate(100px)' ] },
                 { duration: 400 * MS_PER_SEC,
                   delay: -200 * MS_PER_SEC });
 
-      return waitForPaints();
-  }).then(() => {
-    const transformStr =
-      SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
-    const translateX = getTranslateXFromTransform(transformStr);
+  await waitForAnimationReadyToRestyle(animation);
+
+  await waitForPaints();
 
-    // If the delay has been applied we should be about half-way through
-    // the animation. However, if we applied it twice we will be at the
-    // end of the animation already so check that we are roughly half way
-    // through.
-    assert_between_inclusive(translateX, 40, 75,
-        'Animation is about half-way through on the compositor');
-  });
+  const transformStr =
+    SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
+  const translateX = getTranslateXFromTransform(transformStr);
+
+  // If the delay has been applied we should be about half-way through
+  // the animation. However, if we applied it twice we will be at the
+  // end of the animation already so check that we are roughly half way
+  // through.
+  assert_between_inclusive(translateX, 40, 75,
+      'Animation is about half-way through on the compositor');
 }, 'Starting an animation with a delay starts from the correct point');
 
 // Test that compositor animations with a playback rate start at the
 // appropriate point.
 //
 // NOTE: As with the previous test, it is important that we DON'T use
 // SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes
 // us through a different code path.
-promise_test(function(t) {
+promise_test(async t => {
   assert_false(SpecialPowers.DOMWindowUtils.isTestControllingRefreshes,
                'Test should run without the refresh driver being under'
                + ' test control');
 
   // This test only applies to compositor animations
   if (!isOMTAEnabled()) {
     return;
   }
 
   const div = addDiv(t, { class: 'target' });
 
   // Wait for the document to load and painting (see notes in previous test).
-  return waitForPaints().then(() => {
-    const animation =
-      div.animate({ transform: [ 'translate(0px)', 'translate(100px)' ] },
-                  200 * MS_PER_SEC);
-    animation.currentTime = 100 * MS_PER_SEC;
-    animation.playbackRate = 0.1;
+  await waitForPaints();
+
+  const animation =
+    div.animate({ transform: [ 'translate(0px)', 'translate(100px)' ] },
+                200 * MS_PER_SEC);
+  animation.currentTime = 100 * MS_PER_SEC;
+  animation.playbackRate = 0.1;
+
+  await waitForPaints();
 
-    return waitForPaints();
-  }).then(() => {
-    const transformStr =
-      SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
-    const translateX = getTranslateXFromTransform(transformStr);
+  const transformStr =
+    SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
+  const translateX = getTranslateXFromTransform(transformStr);
 
-    // We pass the playback rate to the compositor independently and we have
-    // tests to ensure that it is correctly applied there. However, if, when
-    // we resolve the start time of the pending animation, we fail to
-    // incorporate the playback rate, we will end up starting from the wrong
-    // point and the current time calculated on the compositor will be wrong.
-    assert_between_inclusive(translateX, 25, 75,
-        'Animation is about half-way through on the compositor');
-  });
+  // We pass the playback rate to the compositor independently and we have
+  // tests to ensure that it is correctly applied there. However, if, when
+  // we resolve the start time of the pending animation, we fail to
+  // incorporate the playback rate, we will end up starting from the wrong
+  // point and the current time calculated on the compositor will be wrong.
+  assert_between_inclusive(translateX, 25, 75,
+      'Animation is about half-way through on the compositor');
 }, 'Starting an animation with a playbackRate starts from the correct point');
 
 function getTranslateXFromTransform(transformStr) {
   const matrixComponents =
     transformStr.startsWith('matrix(')
     ? transformStr.substring('matrix('.length, transformStr.length-1)
                   .split(',')
                   .map(component => Number(component))
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -127,28 +127,16 @@ function waitForWheelEvent(aTarget) {
 
     sendWheelAndPaintNoFlush(aTarget, centerX, centerY,
                              { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                                deltaY: targetRect.height },
                              resolve);
   });
 }
 
-async function waitForAnimationReadyToRestyle(aAnimation) {
-  await aAnimation.ready;
-  // If |aAnimation| begins at the current timeline time, we will not process
-  // restyling in the initial frame because of aligning with the refresh driver,
-  // the animation frame in which the ready promise is resolved happens to
-  // coincide perfectly with the start time of the animation.  In this case no
-  // restyling is needed in the frame so we have to wait one more frame.
-  if (animationStartsRightNow(aAnimation)) {
-    await waitForNextFrame();
-  }
-}
-
 var omtaEnabled = isOMTAEnabled();
 
 const isWebRender =
   SpecialPowers.DOMWindowUtils.layerManagerType == 'WebRender';
 
 function add_task_if_omta_enabled(test) {
   if (!omtaEnabled) {
     info(test.name + " is skipped because OMTA is disabled");
--- a/dom/animation/test/testcommon.js
+++ b/dom/animation/test/testcommon.js
@@ -418,8 +418,20 @@ function waitForPaints() {
 // asynchronously (e.g. using play()) and then ended up running the next frame
 // at precisely the time the animation started (due to aligning with vsync
 // refresh rate) then we won't end up restyling in that frame.
 function animationStartsRightNow(aAnimation) {
   return aAnimation.startTime === aAnimation.timeline.currentTime &&
          aAnimation.currentTime === 0;
 }
 
+// Waits for a given animation being ready to restyle.
+async function waitForAnimationReadyToRestyle(aAnimation) {
+  await aAnimation.ready;
+  // If |aAnimation| begins at the current timeline time, we will not process
+  // restyling in the initial frame because of aligning with the refresh driver,
+  // the animation frame in which the ready promise is resolved happens to
+  // coincide perfectly with the start time of the animation.  In this case no
+  // restyling is needed in the frame so we have to wait one more frame.
+  if (animationStartsRightNow(aAnimation)) {
+    await waitForNextFrame();
+  }
+}
--- a/layout/base/ShapeUtils.cpp
+++ b/layout/base/ShapeUtils.cpp
@@ -119,25 +119,39 @@ ShapeUtils::ComputeInsetRect(const Uniqu
                              const nsRect& aRefBox)
 {
   MOZ_ASSERT(aBasicShape->GetShapeType() == StyleBasicShapeType::Inset,
              "The basic shape must be inset()!");
 
   const nsTArray<nsStyleCoord>& coords = aBasicShape->Coordinates();
   MOZ_ASSERT(coords.Length() == 4, "wrong number of arguments");
 
-  nsMargin inset(coords[0].ComputeCoordPercentCalc(aRefBox.height),
-                 coords[1].ComputeCoordPercentCalc(aRefBox.width),
-                 coords[2].ComputeCoordPercentCalc(aRefBox.height),
-                 coords[3].ComputeCoordPercentCalc(aRefBox.width));
+  nsMargin inset(coords[0].ComputeCoordPercentCalc(aRefBox.Height()),
+                 coords[1].ComputeCoordPercentCalc(aRefBox.Width()),
+                 coords[2].ComputeCoordPercentCalc(aRefBox.Height()),
+                 coords[3].ComputeCoordPercentCalc(aRefBox.Width()));
+
+  nscoord x = aRefBox.X() + inset.left;
+  nscoord width = aRefBox.Width() - inset.LeftRight();
+  nscoord y = aRefBox.Y() + inset.top;
+  nscoord height = aRefBox.Height() - inset.TopBottom();
 
-  nsRect insetRect(aRefBox);
-  insetRect.Deflate(inset);
+  // Invert left and right, if necessary.
+  if (width < 0) {
+    width *= -1;
+    x -= width;
+  }
 
-  return insetRect;
+  // Invert top and bottom, if necessary.
+  if (height < 0) {
+    height *= -1;
+    y -= height;
+  }
+
+  return nsRect(x, y, width, height);
 }
 
 /* static */ bool
 ShapeUtils::ComputeInsetRadii(const UniquePtr<StyleBasicShape>& aBasicShape,
                               const nsRect& aInsetRect,
                               const nsRect& aRefBox,
                               nscoord aRadii[8])
 {
--- a/layout/base/ShapeUtils.h
+++ b/layout/base/ShapeUtils.h
@@ -52,17 +52,26 @@ struct ShapeUtils final
   // @param aCenter the center of the ellipse.
   // @param aRefBox the reference box of the ellipse.
   // @return The radii of the ellipse in app units. The width and height
   // represent the x-axis and y-axis radii of the ellipse.
   static nsSize ComputeEllipseRadii(
     const UniquePtr<StyleBasicShape>& aBasicShape,
     const nsPoint& aCenter, const nsRect& aRefBox);
 
-  // Compute the rect for an inset.
+  // Compute the rect for an inset. If the inset amount is larger than
+  // aRefBox itself, this will return a rect the same shape as the inverse
+  // rect that would be created by insetting aRefBox by the inset amount.
+  // This process is *not* what is called for by the current spec at
+  // https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes.
+  // The spec currently treats empty shapes, including overly-inset rects, as
+  // defining 'empty float areas' that don't affect layout. However, it is
+  // practically useful to treat empty shapes as having edges for purposes of
+  // affecting layout, and there is growing momentum for the approach we
+  // are taking here.
   // @param aRefBox the reference box of the inset.
   // @return The inset rect in app units.
   static nsRect ComputeInsetRect(
     const UniquePtr<StyleBasicShape>& aBasicShape,
     const nsRect& aRefBox);
 
   // Compute the radii for an inset.
   // @param aRefBox the reference box of the inset.
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1464737.html
@@ -0,0 +1,7 @@
+<style>
+.cl { -webkit-transform-style: preserve-3d }
+:not(mask) { -webkit-perspective: 0px }
+:root { columns: 0px }
+</style>
+<textarea class="cl"></textarea>
+<p class="cl">z
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -533,8 +533,9 @@ load 1452839.html
 load 1453196.html
 load 1453342.html
 load 1453702.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1461749.html
 load 1461812.html
 load 1462412.html
 load 1463940.html
 pref(dom.webcomponents.shadowdom.enabled,true) HTTP load 1464641.html
+load 1464737.html
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -725,17 +725,20 @@ public:
                     const nscoord aBEnd) const override;
   nscoord BStart() const override {
     return mCenter.y - mRadii.height - mShapeMargin;
   }
   nscoord BEnd() const override {
     return mCenter.y + mRadii.height + mShapeMargin;
   }
   bool IsEmpty() const override {
-    return mRadii.IsEmpty();
+    // An EllipseShapeInfo is never empty, because an ellipse or circle with
+    // a zero radius acts like a point, and an ellipse with one zero radius
+    // acts like a line.
+    return false;
   }
 
   void Translate(nscoord aLineLeft, nscoord aBlockStart) override
   {
     mCenter.MoveBy(aLineLeft, aBlockStart);
 
     for (nsRect& interval : mIntervals) {
       interval.MoveBy(aLineLeft, aBlockStart);
@@ -861,38 +864,46 @@ nsFloatManager::EllipseShapeInfo::Ellips
     bool bIsInExpandedRegion(b < bExpand);
     nscoord bInAppUnits = (b - bExpand) * aAppUnitsPerDevPixel;
     bool bIsMoreThanEllipseBEnd(bInAppUnits > mRadii.height);
 
     // Find the i intercept of the ellipse edge for this block row, and
     // adjust it to compensate for the expansion of the inline dimension.
     // If we're in the expanded region, or if we're using a b that's more
     // than the bEnd of the ellipse, the intercept is nscoord_MIN.
-    const int32_t iIntercept = (bIsInExpandedRegion ||
-                                bIsMoreThanEllipseBEnd) ? nscoord_MIN :
+    // We have one other special case to consider: when the ellipse has no
+    // height. In that case we treat the bInAppUnits == 0 case as
+    // intercepting at the width of the ellipse. All other cases solve
+    // the intersection mathematically.
+    const int32_t iIntercept =
+      (bIsInExpandedRegion || bIsMoreThanEllipseBEnd) ? nscoord_MIN :
       iExpand + NSAppUnitsToIntPixels(
-        XInterceptAtY(bInAppUnits, mRadii.width, mRadii.height),
+        (!!mRadii.height || bInAppUnits) ?
+        XInterceptAtY(bInAppUnits, mRadii.width, mRadii.height) :
+        mRadii.width,
         aAppUnitsPerDevPixel);
 
     // Set iMax in preparation for this block row.
     int32_t iMax = iIntercept;
 
     for (uint32_t i = 0; i < iSize; ++i) {
       const uint32_t index = i + b * iSize;
       MOZ_ASSERT(index < (iSize * bSize),
                  "Our distance field index should be in-bounds.");
 
       // Handle our three cases, in order.
       if (i < iExpand ||
           bIsInExpandedRegion) {
         // Case 1: Expanded reqion pixel.
         df[index] = MAX_MARGIN_5X;
       } else if ((int32_t)i <= iIntercept) {
-        // Case 2: Pixel within the ellipse.
-        df[index] = 0;
+        // Case 2: Pixel within the ellipse, or just outside the edge of it.
+        // Having a positive height indicates that there's an area we can
+        // be inside of.
+        df[index] = (!!mRadii.height) ? 0 : 5;
       } else {
         // Case 3: Other pixel.
 
         // Backward-looking neighborhood distance from target pixel X
         // with chamfer 5-7-11 looks like:
         //
         // +--+--+--+
         // |  |11|  |
@@ -1068,17 +1079,23 @@ public:
                       int32_t aAppUnitsPerDevPixel);
 
   nscoord LineLeft(const nscoord aBStart,
                    const nscoord aBEnd) const override;
   nscoord LineRight(const nscoord aBStart,
                     const nscoord aBEnd) const override;
   nscoord BStart() const override { return mRect.y; }
   nscoord BEnd() const override { return mRect.YMost(); }
-  bool IsEmpty() const override { return mRect.IsEmpty(); }
+  bool IsEmpty() const override {
+    // A RoundedBoxShapeInfo is never empty, because if it is collapsed to
+    // zero area, it acts like a point. If it is collapsed further, to become
+    // inside-out, it acts like a rect in the same shape as the inside-out
+    // rect.
+    return false;
+  }
 
   void Translate(nscoord aLineLeft, nscoord aBlockStart) override
   {
     mRect.MoveBy(aLineLeft, aBlockStart);
 
     if (mShapeMargin > 0) {
       MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalTopRightCorner &&
                  mLogicalBottomLeftCorner && mLogicalBottomRightCorner,
@@ -1255,24 +1272,30 @@ public:
                    const nsRect& aMarginRect);
 
   nscoord LineLeft(const nscoord aBStart,
                    const nscoord aBEnd) const override;
   nscoord LineRight(const nscoord aBStart,
                     const nscoord aBEnd) const override;
   nscoord BStart() const override { return mBStart; }
   nscoord BEnd() const override { return mBEnd; }
-  bool IsEmpty() const override { return mEmpty; }
+  bool IsEmpty() const override {
+    // A PolygonShapeInfo is never empty, because the parser prevents us from
+    // creating a shape with no vertices. If we only have 1 vertex, the
+    // shape acts like a point. With 2 non-coincident vertices, the shape
+    // acts like a line.
+    return false;
+  }
 
   void Translate(nscoord aLineLeft, nscoord aBlockStart) override;
 
 private:
-  // Helper method for determining if the vertices define a float area at
-  // all, and to set mBStart and mBEnd based on the vertices' y extent.
-  void ComputeEmptinessAndExtent();
+  // Helper method for determining the mBStart and mBEnd based on the
+  // vertices' y extent.
+  void ComputeExtent();
 
   // Helper method for implementing LineLeft() and LineRight().
   nscoord ComputeLineIntercept(
     const nscoord aBStart,
     const nscoord aBEnd,
     nscoord (*aCompareOp) (std::initializer_list<nscoord>),
     const nscoord aLineInterceptInitialValue) const;
 
@@ -1290,52 +1313,41 @@ private:
   // These are only generated and used in float area calculations for
   // shape-margin > 0. Each interval is a rectangle that is one device pixel
   // deep in the block axis. The values are stored as block edges in the y
   // coordinates, and inline edges as the x coordinates.
 
   // The intervals are stored in ascending order on y.
   nsTArray<nsRect> mIntervals;
 
-  // If mEmpty is true, that means the polygon encloses no area.
-  bool mEmpty = false;
-
-  // Computed block start and block end value of the polygon shape.
-  //
-  // If mEmpty is false, their initial values nscoord_MAX and nscoord_MIN
-  // are used as sentinels for computing min() and max() in the
-  // constructor, and mBStart is guaranteed to be less than or equal to
-  // mBEnd. If mEmpty is true, their values do not matter.
+  // Computed block start and block end value of the polygon shape. These
+  // initial values are set to correct values in ComputeExtent(), which is
+  // called from all constructors. Afterwards, mBStart is guaranteed to be
+  // less than or equal to mBEnd.
   nscoord mBStart = nscoord_MAX;
   nscoord mBEnd = nscoord_MIN;
 };
 
 nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(nsTArray<nsPoint>&& aVertices)
   : mVertices(aVertices)
 {
-  ComputeEmptinessAndExtent();
+  ComputeExtent();
 }
 
 nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(
   nsTArray<nsPoint>&& aVertices,
   nscoord aShapeMargin,
   int32_t aAppUnitsPerDevPixel,
   const nsRect& aMarginRect)
   : mVertices(aVertices)
 {
   MOZ_ASSERT(aShapeMargin > 0, "This constructor should only be used for a "
                                "polygon with a positive shape-margin.");
 
-  ComputeEmptinessAndExtent();
-
-  // If we're empty, then the float area stays empty, even with a positive
-  // shape-margin.
-  if (mEmpty) {
-    return;
-  }
+  ComputeExtent();
 
   // With a positive aShapeMargin, we have to calculate a distance
   // field from the opaque pixels, then build intervals based on
   // them being within aShapeMargin distance to an opaque pixel.
 
   // Roughly: for each pixel in the margin box, we need to determine the
   // distance to the nearest opaque image-pixel.  If that distance is less
   // than aShapeMargin, we consider this margin-box pixel as being part of
@@ -1414,17 +1426,17 @@ nsFloatManager::PolygonShapeInfo::Polygo
     // the right edge of the polygon at one-dev-pixel-thick strip of b. We
     // have a ComputeLineIntercept function that takes and returns app unit
     // coordinates in the space of aMarginRect. So to pass in b values, we
     // first have to add the aMarginRect.y value. And for the values that we
     // get out, we have to subtract away the aMarginRect.x value before
     // converting the app units to dev pixels.
     nscoord bInAppUnitsMarginRect = bInAppUnits + aMarginRect.y;
     bool bIsLessThanPolygonBStart(bInAppUnitsMarginRect < mBStart);
-    bool bIsMoreThanPolygonBEnd(bInAppUnitsMarginRect >= mBEnd);
+    bool bIsMoreThanPolygonBEnd(bInAppUnitsMarginRect > mBEnd);
 
     const int32_t iLeftEdge = (bIsInExpandedRegion ||
                                bIsLessThanPolygonBStart ||
                                bIsMoreThanPolygonBEnd) ? nscoord_MAX :
       kiExpansionPerSide + NSAppUnitsToIntPixels(
         ComputeLineIntercept(bInAppUnitsMarginRect,
                              bInAppUnitsMarginRect + aAppUnitsPerDevPixel,
                              std::min<nscoord>, nscoord_MAX) - aMarginRect.x,
@@ -1445,19 +1457,21 @@ nsFloatManager::PolygonShapeInfo::Polygo
                  "Our distance field index should be in-bounds.");
 
       // Handle our three cases, in order.
       if (i < kiExpansionPerSide ||
           i >= iSize - kiExpansionPerSide ||
           bIsInExpandedRegion) {
         // Case 1: Expanded pixel.
         df[index] = MAX_MARGIN_5X;
-      } else if ((int32_t)i >= iLeftEdge && (int32_t)i < iRightEdge) {
-        // Case 2: Polygon pixel.
-        df[index] = 0;
+      } else if ((int32_t)i >= iLeftEdge && (int32_t)i <= iRightEdge) {
+        // Case 2: Polygon pixel, either inside or just adjacent to the right
+        // edge. We need this special distinction to detect a space between
+        // edges that is less than one dev pixel.
+        df[index] = (int32_t)i < iRightEdge ? 0 : 5;
       } else {
         // Case 3: Other pixel.
 
         // Backward-looking neighborhood distance from target pixel X
         // with chamfer 5-7-11 looks like:
         //
         // +--+--+--+--+--+
         // |  |11|  |11|  |
@@ -1600,18 +1614,16 @@ nsFloatManager::PolygonShapeInfo::Polygo
   mBStart = std::min(mBStart, mBStart - aShapeMargin);
   mBEnd = std::max(mBEnd, mBEnd + aShapeMargin);
 }
 
 nscoord
 nsFloatManager::PolygonShapeInfo::LineLeft(const nscoord aBStart,
                                            const nscoord aBEnd) const
 {
-  MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
-
   // Use intervals if we have them.
   if (!mIntervals.IsEmpty()) {
     return LineEdge(mIntervals, aBStart, aBEnd, true);
   }
 
   // We want the line-left-most inline-axis coordinate where the
   // (block-axis) aBStart/aBEnd band crosses a line segment of the polygon.
   // To get that, we start as line-right as possible (at nscoord_MAX). Then
@@ -1623,120 +1635,117 @@ nsFloatManager::PolygonShapeInfo::LineLe
   // parameter nscoord, not the minimum value of nscoord.
   return ComputeLineIntercept(aBStart, aBEnd, std::min<nscoord>, nscoord_MAX);
 }
 
 nscoord
 nsFloatManager::PolygonShapeInfo::LineRight(const nscoord aBStart,
                                             const nscoord aBEnd) const
 {
-  MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
-
   // Use intervals if we have them.
   if (!mIntervals.IsEmpty()) {
     return LineEdge(mIntervals, aBStart, aBEnd, false);
   }
 
   // Similar to LineLeft(). Though here, we want the line-right-most
   // inline-axis coordinate, so we instead start at nscoord_MIN and use
   // std::max() to get the biggest inline-coordinate among those
   // intersection points.
   return ComputeLineIntercept(aBStart, aBEnd, std::max<nscoord>, nscoord_MIN);
 }
 
 void
-nsFloatManager::PolygonShapeInfo::ComputeEmptinessAndExtent()
+nsFloatManager::PolygonShapeInfo::ComputeExtent()
 {
-  // Polygons with fewer than three vertices result in an empty area.
-  // https://drafts.csswg.org/css-shapes/#funcdef-polygon
-  if (mVertices.Length() < 3) {
-    mEmpty = true;
-    return;
-  }
-
-  auto Determinant = [] (const nsPoint& aP0, const nsPoint& aP1) {
-    // Returns the determinant of the 2x2 matrix [aP0 aP1].
-    // https://en.wikipedia.org/wiki/Determinant#2_.C3.97_2_matrices
-    return aP0.x * aP1.y - aP0.y * aP1.x;
-  };
-
-  // See if we have any vertices that are non-collinear with the first two.
-  // (If a polygon's vertices are all collinear, it encloses no area.)
-  bool isEntirelyCollinear = true;
-  const nsPoint& p0 = mVertices[0];
-  const nsPoint& p1 = mVertices[1];
-  for (size_t i = 2; i < mVertices.Length(); ++i) {
-    const nsPoint& p2 = mVertices[i];
-
-    // If the determinant of the matrix formed by two points is 0, that
-    // means they're collinear with respect to the origin. Here, if it's
-    // nonzero, then p1 and p2 are non-collinear with respect to p0, i.e.
-    // the three points are non-collinear.
-    if (Determinant(p2 - p0, p1 - p0) != 0) {
-      isEntirelyCollinear = false;
-      break;
-    }
-  }
-
-  if (isEntirelyCollinear) {
-    mEmpty = true;
-    return;
-  }
-
   // mBStart and mBEnd are the lower and the upper bounds of all the
   // vertex.y, respectively. The vertex.y is actually on the block-axis of
   // the float manager's writing mode.
   for (const nsPoint& vertex : mVertices) {
     mBStart = std::min(mBStart, vertex.y);
     mBEnd = std::max(mBEnd, vertex.y);
   }
+
+  MOZ_ASSERT(mBStart <= mBEnd, "Start of float area should be less than "
+                               "or equal to the end.");
 }
 
 nscoord
 nsFloatManager::PolygonShapeInfo::ComputeLineIntercept(
   const nscoord aBStart,
   const nscoord aBEnd,
   nscoord (*aCompareOp) (std::initializer_list<nscoord>),
   const nscoord aLineInterceptInitialValue) const
 {
   MOZ_ASSERT(aBStart <= aBEnd,
              "The band's block start is greater than its block end?");
 
   const size_t len = mVertices.Length();
   nscoord lineIntercept = aLineInterceptInitialValue;
 
+  // We have some special treatment of horizontal lines between vertices.
+  // Generally, we can ignore the impact of the horizontal lines since their
+  // endpoints will be included in the lines preceeding or following them.
+  // However, it's possible the polygon is entirely a horizontal line,
+  // possibly built from more than one horizontal segment. In such a case,
+  // we need to have the horizontal line(s) contribute to the line intercepts.
+  // We do this by accepting horizontal lines until we find a non-horizontal
+  // line, after which all further horizontal lines are ignored.
+  bool canIgnoreHorizontalLines = false;
+
   // Iterate each line segment {p0, p1}, {p1, p2}, ..., {pn, p0}.
   for (size_t i = 0; i < len; ++i) {
     const nsPoint* smallYVertex = &mVertices[i];
     const nsPoint* bigYVertex = &mVertices[(i + 1) % len];
 
     // Swap the two points to satisfy the requirement for calling
     // XInterceptAtY.
     if (smallYVertex->y > bigYVertex->y) {
       std::swap(smallYVertex, bigYVertex);
     }
 
-    if (aBStart >= bigYVertex->y || aBEnd <= smallYVertex->y ||
-        smallYVertex->y == bigYVertex->y) {
-      // Skip computing the intercept if a) the band doesn't intersect the
-      // line segment (even if it crosses one of two the vertices); or b)
-      // the line segment is horizontal. It's OK because the two end points
-      // forming this horizontal segment will still be considered if each of
-      // them is forming another non-horizontal segment with other points.
+    // Generally, we need to ignore line segments that either don't intersect
+    // the band, or merely touch it. However, if the polygon has no block extent
+    // (it is a point, or a horizontal line), and the band touches the line
+    // segment, we let that line segment through.
+    if ((aBStart >= bigYVertex->y || aBEnd <= smallYVertex->y) &&
+        !(mBStart == mBEnd && aBStart == bigYVertex->y)) {
+      // Skip computing the intercept if the band doesn't intersect the
+      // line segment.
       continue;
     }
 
-    nscoord bStartLineIntercept =
-      aBStart <= smallYVertex->y
-        ? smallYVertex->x
-        : XInterceptAtY(aBStart, *smallYVertex, *bigYVertex);
-    nscoord bEndLineIntercept =
-      aBEnd >= bigYVertex->y
-        ? bigYVertex->x
-        : XInterceptAtY(aBEnd, *smallYVertex, *bigYVertex);
+    nscoord bStartLineIntercept;
+    nscoord bEndLineIntercept;
+
+    if (smallYVertex->y == bigYVertex->y) {
+      // The line is horizontal; see if we can ignore it.
+      if (canIgnoreHorizontalLines) {
+        continue;
+      }
+
+      // For a horizontal line that we can't ignore, we treat the two x value
+      // ends as the bStartLineIntercept and bEndLineIntercept. It doesn't
+      // matter which is applied to which, because they'll both be applied
+      // to aCompareOp.
+      bStartLineIntercept = smallYVertex->x;
+      bEndLineIntercept = bigYVertex->x;
+    } else {
+      // This is not a horizontal line. We can now ignore all future
+      // horizontal lines.
+      canIgnoreHorizontalLines = true;
+
+      bStartLineIntercept =
+        aBStart <= smallYVertex->y
+          ? smallYVertex->x
+          : XInterceptAtY(aBStart, *smallYVertex, *bigYVertex);
+      bEndLineIntercept =
+        aBEnd >= bigYVertex->y
+          ? bigYVertex->x
+          : XInterceptAtY(aBEnd, *smallYVertex, *bigYVertex);
+    }
 
     // If either new intercept is more extreme than lineIntercept (per
     // aCompareOp), then update lineIntercept to that value.
     lineIntercept =
       aCompareOp({lineIntercept, bStartLineIntercept, bEndLineIntercept});
   }
 
   return lineIntercept;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2664,20 +2664,19 @@ FrameParticipatesIn3DContext(nsIFrame* a
   MOZ_ASSERT(frame == aAncestor);
   return true;
 }
 
 static bool
 ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
 {
   nsIFrame* transformFrame;
-  if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
+  if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM ||
+      aItem->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
     transformFrame = aItem->Frame();
-  } else if (aItem->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
-    transformFrame = static_cast<nsDisplayPerspective*>(aItem)->TransformFrame();
   } else {
     return false;
   }
   if (aAncestor == transformFrame) {
     return true;
   }
   return FrameParticipatesIn3DContext(aAncestor, transformFrame);
 }
@@ -2861,17 +2860,16 @@ nsIFrame::BuildDisplayListForStackingCon
   nsRect visibleRect = aBuilder->GetVisibleRect();
   nsRect dirtyRect = aBuilder->GetDirtyRect();
 
   const bool isTransformed = IsTransformed(disp);
   const bool hasPerspective = isTransformed && HasPerspective(disp);
   const bool extend3DContext = Extend3DContext(disp, effectSet);
   const bool combines3DTransformWithAncestors =
     (extend3DContext || isTransformed) && Combines3DTransformWithAncestors(disp);
-  const bool childrenHavePerspective = ChildrenHavePerspective(disp);
 
   Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
   if (extend3DContext && !combines3DTransformWithAncestors) {
     // Start a new preserves3d context to keep informations on
     // nsDisplayListBuilder.
     autoPreserves3DContext.emplace(aBuilder);
     // Save dirty rect on the builder to avoid being distorted for
     // multiple transforms along the chain.
@@ -2880,25 +2878,16 @@ nsIFrame::BuildDisplayListForStackingCon
     // We rebuild everything within preserve-3d and don't try
     // to retain, so override the dirty rect now.
     if (aBuilder->IsRetainingDisplayList()) {
       dirtyRect = visibleRect;
       aBuilder->SetDisablePartialUpdates(true);
     }
   }
 
-  // nsDisplayPerspective items use an index to keep their PerFrameKey unique.
-  // We need to make sure we build all of them for them to be consistent, so
-  // rebuild all items if we have perspective. Bug 1431249 should remove
-  // this requirement.
-  if (aBuilder->IsRetainingDisplayList() && childrenHavePerspective) {
-    dirtyRect = visibleRect;
-    aBuilder->SetDisablePartialUpdates(true);
-  }
-
   // reset blend mode so we can keep track if this stacking context needs have
   // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
   // so we keep track if the parent stacking context needs a container too.
   AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
   aBuilder->SetContainsBlendMode(false);
 
   nsRect visibleRectOutsideTransform = visibleRect;
   bool allowAsyncAnimation = false;
@@ -3059,18 +3048,16 @@ nsIFrame::BuildDisplayListForStackingCon
     clipForMask = ComputeClipForMaskItem(aBuilder, this, !useOpacity);
   }
 
   nsDisplayListCollection set(aBuilder);
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     nsDisplayListBuilder::AutoInTransformSetter
       inTransformSetter(aBuilder, inTransform);
-    nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex
-      perspectiveIndex(aBuilder, childrenHavePerspective);
     nsDisplayListBuilder::AutoFilterASRSetter
       filterASRSetter(aBuilder, usingFilter);
 
     CheckForApzAwareEventHandlers(aBuilder, this);
 
     Maybe<nsRect> contentClip =
       GetClipPropClipRect(disp, effects, GetSize());
 
@@ -3377,19 +3364,17 @@ nsIFrame::BuildDisplayListForStackingCon
     resultList.AppendToTop(transformItem);
 
     if (hasPerspective) {
       if (clipCapturedBy == ContainerItemType::ePerspective) {
         clipState.Restore();
       }
       resultList.AppendToTop(
         MakeDisplayItem<nsDisplayPerspective>(
-          aBuilder, this,
-          GetContainingBlock(0, disp)->GetContent()->GetPrimaryFrame(),
-          &resultList));
+          aBuilder, this, &resultList));
     }
 
     if (aCreatedContainerItem) {
       *aCreatedContainerItem = true;
     }
   }
 
   if (clipCapturedBy == ContainerItemType::eOwnLayerForTransformWithRoundedClip) {
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -4630,17 +4630,17 @@ ContainerState::ProcessDisplayItems(nsDi
         // Perspective items have a single child item, an nsDisplayTransform.
         // If the perspective item is scrolled, but the perspective-inducing
         // frame is outside the scroll frame (indicated by item->Frame()
         // being outside that scroll frame), we have to take special care to
         // make APZ scrolling work properly. APZ needs us to put the scroll
         // frame's FrameMetrics on our child transform ContainerLayer instead.
         // It's worth investigating whether this ASR adjustment can be done at
         // display item creation time.
-        scrollMetadataASR = GetASRForPerspective(scrollMetadataASR, item->Frame());
+        scrollMetadataASR = GetASRForPerspective(scrollMetadataASR, item->Frame()->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME));
         params.mScrollMetadataASR = scrollMetadataASR;
         itemASR = scrollMetadataASR;
       }
 
       // Just use its layer.
       // Set layerContentsVisibleRect.width/height to -1 to indicate we
       // currently don't know. If BuildContainerLayerFor gets called by
       // item->BuildLayer, this will be set to a proper rect.
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -993,17 +993,16 @@ nsDisplayListBuilder::nsDisplayListBuild
       mUsedAGRBudget(0),
       mDirtyRect(-1,-1,-1,-1),
       mGlassDisplayItem(nullptr),
       mScrollInfoItemsForHoisting(nullptr),
       mActiveScrolledRootForRootScrollframe(nullptr),
       mMode(aMode),
       mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
       mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
-      mPerspectiveItemIndex(0),
       mSVGEffectsBuildingDepth(0),
       mFilterASR(nullptr),
       mContainsBlendMode(false),
       mIsBuildingScrollbar(false),
       mCurrentScrollbarWillHaveLayer(false),
       mBuildCaret(aBuildCaret),
       mRetainingDisplayList(aRetainingDisplayList),
       mPartialUpdate(false),
@@ -2966,17 +2965,17 @@ void nsDisplayList::HitTest(nsDisplayLis
 
     bool snap;
     nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
     auto itemType = item->GetType();
     bool same3DContext =
       (itemType == DisplayItemType::TYPE_TRANSFORM &&
        static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
       (itemType == DisplayItemType::TYPE_PERSPECTIVE &&
-       static_cast<nsDisplayPerspective*>(item)->TransformFrame()->Extend3DContext());
+       item->Frame()->Extend3DContext());
     if (same3DContext &&
         (itemType != DisplayItemType::TYPE_TRANSFORM ||
          !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) {
       if (!item->GetClip().MayIntersect(aRect)) {
         continue;
       }
       AutoTArray<nsIFrame*, 1> neverUsed;
       // Start gethering leaves of the 3D rendering context, and
@@ -9166,42 +9165,36 @@ nsDisplayTransform::WriteDebugInfo(std::
     aStream << " extends-3d-context";
   }
   if (mFrame->Combines3DTransformWithAncestors()) {
     aStream << " combines-3d-with-ancestors";
   }
 }
 
 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder,
-                                           nsIFrame* aTransformFrame,
-                                           nsIFrame* aPerspectiveFrame,
+                                           nsIFrame* aFrame,
                                            nsDisplayList* aList)
-  : nsDisplayItem(aBuilder, aPerspectiveFrame)
-  , mList(aBuilder, aPerspectiveFrame, aList, true)
-  , mTransformFrame(aTransformFrame)
-  , mIndex(aBuilder->AllocatePerspectiveItemIndex())
+  : nsDisplayItem(aBuilder, aFrame)
+  , mList(aBuilder, aFrame, aList, true)
 {
   MOZ_ASSERT(mList.GetChildren()->Count() == 1);
   MOZ_ASSERT(mList.GetChildren()->GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM);
-
-  if (aBuilder->IsRetainingDisplayList()) {
-    mTransformFrame->AddDisplayItem(this);
-  }
+  mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(mFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME));
 }
 
 already_AddRefed<Layer>
 nsDisplayPerspective::BuildLayer(nsDisplayListBuilder *aBuilder,
                                  LayerManager *aManager,
                                  const ContainerLayerParameters& aContainerParameters)
 {
   float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 
   Matrix4x4 perspectiveMatrix;
   DebugOnly<bool> hasPerspective =
-    nsDisplayTransform::ComputePerspectiveMatrix(mTransformFrame, appUnitsPerPixel,
+    nsDisplayTransform::ComputePerspectiveMatrix(mFrame, appUnitsPerPixel,
                                                  perspectiveMatrix);
   MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
 
   /*
    * ClipListToRange can remove our child after we were created.
    */
   if (!mList.GetChildren()->GetTop()) {
     return nullptr;
@@ -9254,17 +9247,17 @@ nsDisplayPerspective::CreateWebRenderCom
                                               mozilla::wr::IpcResourceUpdateQueue& aResources,
                                               const StackingContextHelper& aSc,
                                               WebRenderLayerManager* aManager,
                                               nsDisplayListBuilder* aDisplayListBuilder)
 {
   float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   Matrix4x4 perspectiveMatrix;
   DebugOnly<bool> hasPerspective =
-    nsDisplayTransform::ComputePerspectiveMatrix(mTransformFrame, appUnitsPerPixel,
+    nsDisplayTransform::ComputePerspectiveMatrix(mFrame, appUnitsPerPixel,
                                                  perspectiveMatrix);
   MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?");
 
   /*
    * ClipListToRange can remove our child after we were created.
    */
   if (!mList.GetChildren()->GetTop()) {
     return false;
@@ -9300,22 +9293,16 @@ nsDisplayPerspective::CreateWebRenderCom
                            gfx::CompositionOp::OP_OVER,
                            !BackfaceIsHidden(),
                            true);
 
   return mList.CreateWebRenderCommands(aBuilder, aResources, sc,
                                        aManager, aDisplayListBuilder);
 }
 
-int32_t
-nsDisplayPerspective::ZIndex() const
-{
-  return ZIndexForFrame(mTransformFrame);
-}
-
 nsDisplayItemGeometry*
 nsCharClipDisplayItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
 {
   return new nsCharClipGeometry(this, aBuilder);
 }
 
 void
 nsCharClipDisplayItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1189,41 +1189,16 @@ public:
     ~AutoFilterASRSetter() {
       mBuilder->mFilterASR = mOldValue;
     }
   private:
     nsDisplayListBuilder* mBuilder;
     const ActiveScrolledRoot* mOldValue;
   };
 
-  class AutoSaveRestorePerspectiveIndex {
-  public:
-    AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder,
-                                    const bool aChildrenHavePerspective)
-      : mBuilder(nullptr)
-    {
-      if (aChildrenHavePerspective) {
-        mBuilder = aBuilder;
-        mCachedItemIndex = aBuilder->mPerspectiveItemIndex;
-        aBuilder->mPerspectiveItemIndex = 0;
-      }
-    }
-
-    ~AutoSaveRestorePerspectiveIndex()
-    {
-      if (mBuilder) {
-        mBuilder->mPerspectiveItemIndex = mCachedItemIndex;
-      }
-    }
-
-  private:
-    nsDisplayListBuilder* mBuilder;
-    uint32_t mCachedItemIndex;
-  };
-
   /**
    * A helper class to temporarily set the value of mCurrentScrollParentId.
    */
   class AutoCurrentScrollParentIdSetter {
   public:
     AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollId)
       : mBuilder(aBuilder)
       , mOldValue(aBuilder->mCurrentScrollParentId)
@@ -1636,18 +1611,16 @@ public:
   /**
    * mContainsBlendMode is true if we processed a display item that
    * has a blend mode attached. We do this so we can insert a
    * nsDisplayBlendContainer in the parent stacking context.
    */
   void SetContainsBlendMode(bool aContainsBlendMode) { mContainsBlendMode = aContainsBlendMode; }
   bool ContainsBlendMode() const { return mContainsBlendMode; }
 
-  uint32_t AllocatePerspectiveItemIndex() { return mPerspectiveItemIndex++; }
-
   DisplayListClipState& ClipState() { return mClipState; }
   const ActiveScrolledRoot* CurrentActiveScrolledRoot() { return mCurrentActiveScrolledRoot; }
   const ActiveScrolledRoot* CurrentAncestorASRStackingContextContents() { return mCurrentContainerASR; }
 
   /**
    * Add the current frame to the will-change budget if possible and
    * remeber the outcome. Subsequent calls to IsInWillChangeBudget
    * will return the same value as return here.
@@ -1817,16 +1790,17 @@ private:
    */
   bool ShouldBuildCompositorHitTestInfo(const nsIFrame* aFrame,
                                         const mozilla::gfx::CompositorHitTestInfo& aInfo,
                                         const bool aBuildNew) const;
 
   friend class nsDisplayCanvasBackgroundImage;
   friend class nsDisplayBackgroundImage;
   friend class nsDisplayFixedPosition;
+  friend class nsDisplayPerspective;
   AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsDisplayItem* aItem);
 
   friend class nsDisplayItem;
   friend class nsDisplayOwnLayer;
   friend struct RetainedDisplayListBuilder;
   AnimatedGeometryRoot* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
 
   AnimatedGeometryRoot* WrapAGRForFrame(nsIFrame* aAnimatedGeometryRoot,
@@ -1958,17 +1932,16 @@ private:
   std::list<DisplayItemClipChain*> mClipChainsToDestroy;
   nsTArray<nsDisplayItem*> mTemporaryItems;
   const ActiveScrolledRoot*      mActiveScrolledRootForRootScrollframe;
   nsDisplayListBuilderMode       mMode;
   ViewID                         mCurrentScrollParentId;
   ViewID                         mCurrentScrollbarTarget;
   MaybeScrollDirection           mCurrentScrollbarDirection;
   Preserves3DContext             mPreserves3DCtx;
-  uint32_t                       mPerspectiveItemIndex;
   int32_t                        mSVGEffectsBuildingDepth;
   // When we are inside a filter, the current ASR at the time we entered the
   // filter. Otherwise nullptr.
   const ActiveScrolledRoot*      mFilterASR;
   bool                           mContainsBlendMode;
   bool                           mIsBuildingScrollbar;
   bool                           mCurrentScrollbarWillHaveLayer;
   bool                           mBuildCaret;
@@ -6884,28 +6857,20 @@ private:
  */
 class nsDisplayPerspective : public nsDisplayItem
 {
   typedef mozilla::gfx::Point3D Point3D;
 
 public:
   NS_DISPLAY_DECL_NAME("nsDisplayPerspective", TYPE_PERSPECTIVE)
 
-  nsDisplayPerspective(nsDisplayListBuilder* aBuilder, nsIFrame* aTransformFrame,
-                       nsIFrame* aPerspectiveFrame,
+  nsDisplayPerspective(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                        nsDisplayList* aList);
   ~nsDisplayPerspective()
   {
-    if (mTransformFrame) {
-      mTransformFrame->RemoveDisplayItem(this);
-    }
-  }
-
-  virtual uint32_t GetPerFrameKey() const override {
-    return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey();
   }
 
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override
   {
     return mList.HitTest(aBuilder, aRect, aState, aOutFrames);
   }
 
@@ -6980,50 +6945,37 @@ public:
     mList.SetActiveScrolledRoot(aActiveScrolledRoot);
   }
 
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override
   {
     return mList.GetComponentAlphaBounds(aBuilder);
   }
 
-  nsIFrame* TransformFrame() { return mTransformFrame; }
-
-  virtual nsIFrame* FrameForInvalidation() const override { return mTransformFrame; }
-
-  virtual int32_t ZIndex() const override;
-
   virtual void
   DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
     if (mList.GetChildren()->GetTop()) {
       static_cast<nsDisplayTransform*>(mList.GetChildren()->GetTop())->DoUpdateBoundsPreserves3D(aBuilder);
     }
   }
 
   virtual void Destroy(nsDisplayListBuilder* aBuilder) override
   {
     mList.GetChildren()->DeleteAll(aBuilder);
     nsDisplayItem::Destroy(aBuilder);
   }
 
-  virtual bool HasDeletedFrame() const override { return !mTransformFrame || nsDisplayItem::HasDeletedFrame(); }
-
   virtual void RemoveFrame(nsIFrame* aFrame) override
   {
-    if (aFrame == mTransformFrame) {
-      mTransformFrame = nullptr;
-    }
     nsDisplayItem::RemoveFrame(aFrame);
     mList.RemoveFrame(aFrame);
   }
 
 private:
   nsDisplayWrapList mList;
-  nsIFrame* mTransformFrame;
-  uint32_t mIndex;
 };
 
 /**
  * This class adds basic support for limiting the rendering (in the inline axis
  * of the writing mode) to the part inside the specified edges.  It's a base
  * class for the display item classes that do the actual work.
  * The two members, mVisIStartEdge and mVisIEndEdge, are relative to the edges
  * of the frame's scrollable overflow rectangle and are the amount to suppress
--- a/layout/reftests/css-shapes/reftest.list
+++ b/layout/reftests/css-shapes/reftest.list
@@ -1,1 +1,33 @@
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),16,4) pref(layout.css.shape-outside.enabled,true) == dynamic-shape-outside-1.html dynamic-shape-outside-1-ref.html
+
+== shape-outside-empty-circle-1.html shape-outside-empty-point-ref.html
+== shape-outside-empty-circle-2.html shape-outside-empty-circle-ref.html
+== shape-outside-empty-circle-3.html shape-outside-empty-nothing-ref.html
+
+== shape-outside-empty-ellipse-1.html shape-outside-empty-point-ref.html
+== shape-outside-empty-ellipse-2.html shape-outside-empty-circle-ref.html
+== shape-outside-empty-ellipse-3.html shape-outside-empty-point-ref.html
+# The next test needs fuzzy due to chamfer aberration
+fuzzy(255,520) == shape-outside-empty-ellipse-4.html shape-outside-empty-circle-ref.html
+== shape-outside-empty-ellipse-5.html shape-outside-empty-line-ref.html
+== shape-outside-empty-ellipse-6.html shape-outside-empty-line-ref.html
+== shape-outside-empty-ellipse-7.html shape-outside-empty-nothing-ref.html
+== shape-outside-empty-ellipse-8.html shape-outside-empty-nothing-ref.html
+
+== shape-outside-empty-inset-1.html shape-outside-empty-point-ref.html
+== shape-outside-empty-inset-2.html shape-outside-empty-circle-ref.html
+== shape-outside-empty-inset-3.html shape-outside-empty-point-ref.html
+== shape-outside-empty-inset-4.html shape-outside-empty-circle-ref.html
+== shape-outside-empty-inset-5.html shape-outside-empty-line-ref.html
+== shape-outside-empty-inset-6.html shape-outside-empty-line-ref.html
+== shape-outside-empty-inset-7.html shape-outside-empty-nothing-ref.html
+== shape-outside-empty-inset-8.html shape-outside-empty-nothing-ref.html
+
+== shape-outside-empty-polygon-1.html shape-outside-empty-point-ref.html
+# The next test needs fuzzy due to chamfer aberration
+fuzzy(255,520) == shape-outside-empty-polygon-2.html shape-outside-empty-circle-ref.html
+== shape-outside-empty-polygon-3.html shape-outside-empty-line-ref.html
+== shape-outside-empty-polygon-4.html shape-outside-empty-line-ref.html
+== shape-outside-empty-polygon-5.html shape-outside-empty-point-ref.html
+== shape-outside-empty-polygon-6.html shape-outside-empty-nothing-ref.html
+== shape-outside-empty-polygon-7.html shape-outside-empty-nothing-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-circle-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty circle, acts like a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: circle(0px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-circle-2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty circle, with shape-margin acts like a circle</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: circle(0px);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-circle-3.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty circle, positioned between elements, acts like nothing</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: circle(0px at 100px 20px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-circle-ref.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Reference: Shape-outside empty area, float elements around a circle</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: circle(90px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty point ellipse, acts like a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(0px 0px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty point ellipse, with shape-margin acts like a circle</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(0px 0px);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-3.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty horizontal ellipse, acts like a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(50px 0px at 50px 90px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-4.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty horizontal ellipse, with shape-margin acts like a circle (with some aberration)</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(50px 0px at 50px 90px);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-5.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty vertical ellipse, acts like a line</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(0px 100px at 190px 100px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-6.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty vertical ellipse, with shape-margin acts like a capsule (with rounded endpoints at top and bottom)</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(0px 100px at 100px 100px);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-7.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty point ellipse, positioned between elements, acts like nothing</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(0px 0px at 100px 20px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-ellipse-8.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty horizontal ellipse, positioned between elements, acts like nothing</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: ellipse(50px 0px at 50px 20px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty zero-sized inset, acts like a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(50% 50%);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty zero-sized inset, with shape-margin acts like a circle</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(50% 50%);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-3.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty horizontal flat inset, acts like a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(50% 50% 50% 0%);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-4.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty horizontal flat inset, with shape-margin acts like a circle</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(50% 50% 50% 0%);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-5.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty vertical flat inset, acts like a line</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(0% 5% 0% 95%);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-6.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside inside-out vertical flat inset, acts like a line</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(0% 100% 0% 95%);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-7.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty zero-sized inset, positioned between elements, acts like nothing</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(20px 100px 160px 100px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-inset-8.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside empty horizontal inset, positioned between elements, acts like nothing</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: inset(20px 0px 160px 50px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-line-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Reference: Shape-outside empty area, float text around a vertical line</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+    margin-left: 190px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-nothing-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Reference: Shape-outside empty area, no float impact</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-point-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Reference: Shape-outside empty area, float text around a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box style="margin-left: 100px"></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-polygon-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside polygon with 1 vertex, acts like a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: polygon(100px 90px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-polygon-2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside polygon with 1 vertex, with shape-margin acts like a circle (with some aberration)</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: polygon(100px 90px);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-polygon-3.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside polygon with 2 vertices, acts like a line</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: polygon(190px 0px, 190px 200px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-polygon-4.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside polygon with 2 vertices, with shape-margin acts like a line with rounded endpoints</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: polygon(100px 0px, 100px 200px);
+    shape-margin: 90px;
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-polygon-5.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside polygon with 3 coincident vertices, acts like a point</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: polygon(100px 90px, 100px 90px, 100px 90px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-polygon-6.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside polygon with 1 vertex, positioned between elements, acts like nothing</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: polygon(100px 20px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-shapes/shape-outside-empty-polygon-7.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Shape-outside polygon with 2 vertices, positioned between elements, acts like nothing</title>
+<style>
+  body {
+    line-height: 0;
+  }
+  .container {
+    width: 600px;
+  }
+  box {
+    display: inline-block;
+    background-color: blue;
+    width: 100px;
+    height: 20px;
+  }
+  .shape {
+    float: left;
+    width: 200px;
+    height: 180px;
+    shape-outside: polygon(0px 20px, 100px 20px);
+  }
+</style>
+</head>
+<body>
+<div class="container">
+<div class="shape"></div>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+<box></box><br>
+</div>
+</body>
+</html>
--- a/layout/reftests/w3c-css/submitted/shapes1/reftest.list
+++ b/layout/reftests/w3c-css/submitted/shapes1/reftest.list
@@ -39,24 +39,20 @@ fails == shape-outside-border-box-border
 # Basic shape: circle()
 == shape-outside-circle-032.html shape-outside-circle-032-ref.html
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),7,6) == shape-outside-circle-033.html shape-outside-circle-033-ref.html
 == shape-outside-circle-034.html shape-outside-circle-034-ref.html
 == shape-outside-circle-035.html shape-outside-circle-035-ref.html
 == shape-outside-circle-036.html shape-outside-circle-036-ref.html
 == shape-outside-circle-037.html shape-outside-circle-036-ref.html
 == shape-outside-circle-038.html shape-outside-circle-036-ref.html
-== shape-outside-circle-039.html shape-outside-circle-039-ref.html
-== shape-outside-circle-040.html shape-outside-circle-039-ref.html
 == shape-outside-circle-041.html shape-outside-circle-041-ref.html
 == shape-outside-circle-042.html shape-outside-circle-042-ref.html
 == shape-outside-circle-043.html shape-outside-circle-042-ref.html
 == shape-outside-circle-044.html shape-outside-circle-042-ref.html
-== shape-outside-circle-045.html shape-outside-circle-045-ref.html
-== shape-outside-circle-046.html shape-outside-circle-045-ref.html
 == shape-outside-circle-047.html shape-outside-circle-047-ref.html
 == shape-outside-circle-048.html shape-outside-circle-048-ref.html
 == shape-outside-circle-049.html shape-outside-circle-049-ref.html
 == shape-outside-circle-050.html shape-outside-circle-050-ref.html
 == shape-outside-circle-051.html shape-outside-circle-051-ref.html
 == shape-outside-circle-052.html shape-outside-circle-052-ref.html
 == shape-outside-circle-053.html shape-outside-circle-053-ref.html
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),16,4) == shape-outside-circle-054.html shape-outside-circle-054-ref.html
@@ -84,18 +80,16 @@ fuzzy(108,81) == shape-outside-circle-05
 == shape-outside-ellipse-049.html shape-outside-ellipse-049-ref.html
 == shape-outside-ellipse-050.html shape-outside-ellipse-050-ref.html
 == shape-outside-ellipse-051.html shape-outside-ellipse-051-ref.html
 fuzzy(108,81) == shape-outside-ellipse-052.html shape-outside-ellipse-052-ref.html
 
 # Basic shape: inset()
 == shape-outside-inset-016.html shape-outside-inset-016-ref.html
 == shape-outside-inset-017.html shape-outside-inset-017-ref.html
-== shape-outside-inset-018.html shape-outside-inset-018-ref.html
-== shape-outside-inset-019.html shape-outside-inset-019-ref.html
 == shape-outside-inset-020.html shape-outside-inset-020-ref.html
 == shape-outside-inset-021.html shape-outside-inset-021-ref.html
 == shape-outside-inset-022.html shape-outside-inset-022-ref.html
 == shape-outside-inset-023.html shape-outside-inset-023-ref.html
 == shape-outside-inset-024.html shape-outside-inset-024-ref.html
 == shape-outside-inset-025.html shape-outside-inset-025-ref.html
 == shape-outside-inset-026.html shape-outside-inset-026-ref.html
 == shape-outside-inset-027.html shape-outside-inset-027-ref.html
@@ -104,15 +98,9 @@ fuzzy(108,81) == shape-outside-ellipse-0
 == shape-outside-polygon-018.html shape-outside-polygon-018-ref.html
 == shape-outside-polygon-019.html shape-outside-polygon-019-ref.html
 == shape-outside-polygon-020.html shape-outside-polygon-020-ref.html
 == shape-outside-polygon-021.html shape-outside-polygon-021-ref.html
 == shape-outside-polygon-022.html shape-outside-polygon-022-ref.html
 == shape-outside-polygon-023.html shape-outside-polygon-023-ref.html
 == shape-outside-polygon-024.html shape-outside-polygon-024-ref.html
 == shape-outside-polygon-025.html shape-outside-polygon-025-ref.html
-== shape-outside-polygon-026.html shape-outside-polygon-026-ref.html
-== shape-outside-polygon-027.html shape-outside-polygon-027-ref.html
-== shape-outside-polygon-028.html shape-outside-polygon-026-ref.html
-== shape-outside-polygon-029.html shape-outside-polygon-027-ref.html
-== shape-outside-polygon-030.html shape-outside-polygon-030-ref.html
-== shape-outside-polygon-031.html shape-outside-polygon-031-ref.html
 fuzzy(101,2263) == shape-outside-polygon-032.html shape-outside-polygon-032-ref.html
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-039-ref.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, circle(0%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 20px;
-    width: 20px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px; top: 0px; left: 0px;"></div>
-    <div class="box" style="height: 12px; top: 12px; left: 0px;"></div>
-    <div class="box" style="height: 36px; top: 24px; left: 0px;"></div>
-    <div class="box" style="height: 36px; top: 60px; left: 0px;"></div>
-    <div class="box" style="height: 12px; top: 96px; left: 0px;"></div>
-    <div class="box" style="height: 12px; top: 108px; left: 0px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-039.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, circle(0%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-039-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the basic shape circle(0%) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    shape-outside: circle(0%) border-box;
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-040.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, circle(closest-side at left center) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-039-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the basic shape circle(closest-side at left center) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    shape-outside: circle(closest-side at left center) border-box;
-    clip-path: circle(closest-side at left center) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-045-ref.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, circle(0%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    direction: rtl;
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 20px;
-    width: 20px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px; top: 0px; right: 0px;"></div>
-    <div class="box" style="height: 12px; top: 12px; right: 0px;"></div>
-    <div class="box" style="height: 36px; top: 24px; right: 0px;"></div>
-    <div class="box" style="height: 36px; top: 60px; right: 0px;"></div>
-    <div class="box" style="height: 12px; top: 96px; right: 0px;"></div>
-    <div class="box" style="height: 12px; top: 108px; right: 0px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-045.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, circle(0%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-045-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the basic shape circle(0%) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    shape-outside: circle(0%) border-box;
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-046.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, circle(closest-side at right center) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-045-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the basic shape circle(closest-side at right center) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    shape-outside: circle(closest-side at right center) border-box;
-    clip-path: circle(closest-side at right center) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-018-ref.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, inset(50%) reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px; top: 0; left: 0;"></div>
-    <div class="box" style="height: 36px; top: 24px; left: 0;"></div>
-    <div class="box" style="height: 36px; top: 60px; left: 0;"></div>
-    <div class="box" style="height: 24px; top: 96px; left: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-018.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, inset(50%)</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-inset-018-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the basic shape inset(50%) value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    shape-outside: inset(50%);
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 24px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-019-ref.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, inset(50%) reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    direction: rtl;
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px; top: 0; right: 0;"></div>
-    <div class="box" style="height: 36px; top: 24px; right: 0;"></div>
-    <div class="box" style="height: 36px; top: 60px; right: 0;"></div>
-    <div class="box" style="height: 24px; top: 96px; right: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-019.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, inset(50%)</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-inset-019-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the basic shape inset(50%) value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    shape-outside: inset(50%);
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 24px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-026-ref.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(60px 20px, 100px 60px) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px; top: 0; left: 0;"></div>
-    <div class="long box" style="height: 30px; top: 30px; left: 0;"></div>
-    <div class="long box" style="height: 20px; top: 60px; left: 0;"></div>
-    <div class="long box" style="height: 20px; top: 80px; left: 0;"></div>
-    <div class="long box" style="height: 30px; top: 100px; left: 0;"></div>
-    <div class="long box" style="height: 30px; top: 130px; left: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-026.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(60px 20px, 100px 60px) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-026-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the polygon(60px 20px, 100px 60px) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Less than three vertices, resulting an empty area. */
-    shape-outside: polygon(60px 20px, 100px 60px) border-box;
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-027-ref.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(60px 20px, 100px 60px) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px; top: 0; right: 0;"></div>
-    <div class="long box" style="height: 30px; top: 30px; right: 0;"></div>
-    <div class="long box" style="height: 20px; top: 60px; right: 0;"></div>
-    <div class="long box" style="height: 20px; top: 80px; right: 0;"></div>
-    <div class="long box" style="height: 30px; top: 100px; right: 0;"></div>
-    <div class="long box" style="height: 30px; top: 130px; right: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-027.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(60px 20px, 100px 60px) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-027-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the polygon(60px 20px, 100px 60px) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Less than three vertices, resulting an empty area. */
-    shape-outside: polygon(60px 20px, 100px 60px) border-box;
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-028.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-026-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* All vertices are collinear, resulting an empty area. */
-    shape-outside: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    clip-path: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-029.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-027-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* All vertices are collinear, resulting an empty area. */
-    shape-outside: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    clip-path: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-030-ref.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px; top: 0; left: 80px;"></div>
-    <div class="box" style="height: 30px; top: 30px; left: 80px;"></div>
-    <div class="box" style="height: 20px; top: 60px; left: 80px;"></div>
-    <div class="box" style="height: 20px; top: 80px; left: 80px;"></div>
-    <div class="box" style="height: 30px; top: 100px; left: 80px;"></div>
-    <div class="box" style="height: 30px; top: 130px; left: 80px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-030.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-030-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* polygon contains horizontal lines. */
-    shape-outside: polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box;
-    clip-path: polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-031-ref.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    direction: rtl;
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px; top: 0; right: 80px;"></div>
-    <div class="box" style="height: 30px; top: 30px; right: 80px;"></div>
-    <div class="box" style="height: 20px; top: 60px; right: 80px;"></div>
-    <div class="box" style="height: 20px; top: 80px; right: 80px;"></div>
-    <div class="box" style="height: 30px; top: 100px; right: 80px;"></div>
-    <div class="box" style="height: 30px; top: 130px; right: 80px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-031.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-031-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* polygon contains horizontal lines. */
-    shape-outside: polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box;
-    clip-path: polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-  </body>
-</html>
--- a/media/webrtc/signaling/gtest/sdp_unittests.cpp
+++ b/media/webrtc/signaling/gtest/sdp_unittests.cpp
@@ -3044,24 +3044,22 @@ const std::string kBasicAudioVideoDataOf
 "a=setup:actpass" CRLF
 "a=rtcp-mux" CRLF
 "m=application 9 DTLS/SCTP 5000" CRLF
 "c=IN IP4 0.0.0.0" CRLF
 "a=sctpmap:5000 webrtc-datachannel 16" CRLF
 "a=setup:actpass" CRLF;
 
 TEST_P(NewSdpTest, BasicAudioVideoDataSdpParse) {
-  SKIP_TEST_WITH_RUST_PARSER; // See Bug 1432922
   ParseSdp(kBasicAudioVideoDataOffer);
   ASSERT_EQ(0U, mSdpErrorHolder->GetParseErrors().size()) <<
     "Got parse errors: " << GetParseErrors();
 }
 
 TEST_P(NewSdpTest, CheckApplicationParameters) {
-  SKIP_TEST_WITH_RUST_PARSER; // See Bug 1432922
   ParseSdp(kBasicAudioVideoDataOffer);
   ASSERT_TRUE(!!mSdp);
   ASSERT_EQ(3U, mSdp->GetMediaSectionCount()) << "Wrong number of media sections";
   ASSERT_EQ(SdpMediaSection::kAudio, mSdp->GetMediaSection(0).GetMediaType())
     << "Wrong type for first media section";
   ASSERT_EQ(SdpMediaSection::kVideo, mSdp->GetMediaSection(1).GetMediaType())
     << "Wrong type for second media section";
   ASSERT_EQ(SdpMediaSection::kApplication, mSdp->GetMediaSection(2).GetMediaType())
@@ -3082,17 +3080,16 @@ TEST_P(NewSdpTest, CheckApplicationParam
 
   ASSERT_TRUE(mSdp->GetMediaSection(2).GetAttributeList().HasAttribute(
       SdpAttribute::kSetupAttribute));
   ASSERT_EQ(SdpSetupAttribute::kActpass,
       mSdp->GetMediaSection(2).GetAttributeList().GetSetup().mRole);
 }
 
 TEST_P(NewSdpTest, CheckExtmap) {
-  SKIP_TEST_WITH_RUST_PARSER; // See Bug 1432922
   ParseSdp(kBasicAudioVideoDataOffer);
   ASSERT_TRUE(!!mSdp);
   ASSERT_EQ(3U, mSdp->GetMediaSectionCount()) << "Wrong number of media sections";
 
   ASSERT_TRUE(mSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
         SdpAttribute::kExtmapAttribute));
 
   auto extmaps =
@@ -3118,17 +3115,16 @@ TEST_P(NewSdpTest, CheckExtmap) {
   ASSERT_FALSE(extmaps[2].direction_specified);
   ASSERT_EQ("some_other_extension",
       extmaps[2].extensionname);
   ASSERT_EQ("some_params some more params",
       extmaps[2].extensionattributes);
 }
 
 TEST_P(NewSdpTest, CheckRtcpFb) {
-  SKIP_TEST_WITH_RUST_PARSER; // See Bug 1432922
   ParseSdp(kBasicAudioVideoDataOffer);
   ASSERT_TRUE(!!mSdp);
   ASSERT_EQ(3U, mSdp->GetMediaSectionCount()) << "Wrong number of media sections";
 
   auto& video_attrs = mSdp->GetMediaSection(1).GetAttributeList();
   ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute));
   auto& rtcpfbs = video_attrs.GetRtcpFb().mFeedbacks;
   ASSERT_EQ(20U, rtcpfbs.size());
--- a/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
@@ -8,16 +8,18 @@
 
 #include "signaling/src/sdp/RsdparsaSdpAttributeList.h"
 #include "signaling/src/sdp/RsdparsaSdpInc.h"
 #include "signaling/src/sdp/RsdparsaSdpGlue.h"
 
 #include <ostream>
 #include "mozilla/Assertions.h"
 
+#include <limits>
+
 namespace mozilla
 {
 
 const std::string RsdparsaSdpAttributeList::kEmptyString = "";
 
 RsdparsaSdpAttributeList::~RsdparsaSdpAttributeList()
 {
   for (size_t i = 0; i < kNumAttributeTypes; ++i) {
@@ -450,16 +452,19 @@ RsdparsaSdpAttributeList::LoadAttribute(
         LoadMsidSemantics(attributeList);
         return;
       case SdpAttribute::kGroupAttribute:
         LoadGroup(attributeList);
         return;
       case SdpAttribute::kRtcpAttribute:
         LoadRtcp(attributeList);
         return;
+      case SdpAttribute::kRtcpFbAttribute:
+        LoadRtcpFb(attributeList);
+        return;
       case SdpAttribute::kImageattrAttribute:
         LoadImageattr(attributeList);
         return;
       case SdpAttribute::kSctpmapAttribute:
         LoadSctpmaps(attributeList);
         return;
       case SdpAttribute::kDirectionAttribute:
         LoadDirection(attributeList);
@@ -468,22 +473,22 @@ RsdparsaSdpAttributeList::LoadAttribute(
         LoadRemoteCandidates(attributeList);
         return;
       case SdpAttribute::kRidAttribute:
         LoadRids(attributeList);
         return;
       case SdpAttribute::kExtmapAttribute:
         LoadExtmap(attributeList);
         return;
+
       case SdpAttribute::kDtlsMessageAttribute:
       case SdpAttribute::kLabelAttribute:
       case SdpAttribute::kMaxptimeAttribute:
       case SdpAttribute::kSsrcGroupAttribute:
       case SdpAttribute::kMaxMessageSizeAttribute:
-      case SdpAttribute::kRtcpFbAttribute:
       case SdpAttribute::kRtcpRsizeAttribute:
       case SdpAttribute::kSctpPortAttribute:
       case SdpAttribute::kCandidateAttribute:
       case SdpAttribute::kSimulcastAttribute:
       case SdpAttribute::kConnectionAttribute:
       case SdpAttribute::kIceMismatchAttribute:
         // TODO: Not implemented, or not applicable.
         // Sort this out in Bug 1437165.
@@ -842,16 +847,49 @@ RsdparsaSdpAttributeList::LoadRtcp(RustA
       std::string addr(rtcp.unicastAddr.unicastAddr);
       SetAttribute(new SdpRtcpAttribute(rtcp.port, sdp::kInternet,
                                         addrType, addr));
     }
   }
 }
 
 void
+RsdparsaSdpAttributeList::LoadRtcpFb(RustAttributeList* attributeList)
+{
+  auto rtcpfbsCount = sdp_get_rtcpfb_count(attributeList);
+  if (!rtcpfbsCount) {
+    return;
+  }
+
+  auto rustRtcpfbs = MakeUnique<RustSdpAttributeRtcpFb[]>(rtcpfbsCount);
+  sdp_get_rtcpfbs(attributeList, rtcpfbsCount, rustRtcpfbs.get());
+
+  auto rtcpfbList = MakeUnique<SdpRtcpFbAttributeList>();
+  for(size_t i = 0; i < rtcpfbsCount; i++) {
+    RustSdpAttributeRtcpFb& rtcpfb = rustRtcpfbs[i];
+    uint32_t payloadTypeU32 = rtcpfb.payloadType;
+
+    std::stringstream ss;
+    if(payloadTypeU32 == std::numeric_limits<uint32_t>::max()){
+      ss << "*";
+    } else {
+      ss << payloadTypeU32;
+    }
+
+    uint32_t feedbackType = rtcpfb.feedbackType;
+    std::string parameter = convertStringView(rtcpfb.parameter);
+    std::string extra = convertStringView(rtcpfb.extra);
+
+    rtcpfbList->PushEntry(ss.str(), static_cast<SdpRtcpFbAttributeList::Type>(feedbackType), parameter, extra);
+  }
+
+  SetAttribute(rtcpfbList.release());
+}
+
+void
 RsdparsaSdpAttributeList::LoadImageattr(RustAttributeList* attributeList)
 {
   size_t numImageattrs = sdp_get_imageattr_count(attributeList);
   if (numImageattrs == 0) {
     return;
   }
   auto rustImageattrs = MakeUnique<StringView[]>(numImageattrs);
   sdp_get_imageattrs(attributeList, numImageattrs, rustImageattrs.get());
--- a/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
@@ -130,16 +130,17 @@ private:
   void LoadFmtp(RustAttributeList* attributeList);
   void LoadPtime(RustAttributeList* attributeList);
   void LoadFlags(RustAttributeList* attributeList);
   void LoadMid(RustAttributeList* attributeList);
   void LoadMsid(RustAttributeList* attributeList);
   void LoadMsidSemantics(RustAttributeList* attributeList);
   void LoadGroup(RustAttributeList* attributeList);
   void LoadRtcp(RustAttributeList* attributeList);
+  void LoadRtcpFb(RustAttributeList* attributeList);
   void LoadImageattr(RustAttributeList* attributeList);
   void LoadSctpmaps(RustAttributeList* attributeList);
   void LoadDirection(RustAttributeList* attributeList);
   void LoadRemoteCandidates(RustAttributeList* attributeList);
   void LoadRids(RustAttributeList* attributeList);
   void LoadExtmap(RustAttributeList* attributeList);
 
   void WarnAboutMisplacedAttribute(SdpAttribute::AttributeType type,
--- a/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h
@@ -89,16 +89,23 @@ struct RustSdpAttributeSsrc {
 
 struct RustSdpAttributeRtpmap {
   uint8_t payloadType;
   StringView codecName;
   uint32_t frequency;
   uint32_t channels;
 };
 
+struct RustSdpAttributeRtcpFb {
+  uint32_t payloadType;
+  uint32_t feedbackType;
+  StringView parameter;
+  StringView extra;
+};
+
 struct RustSdpAttributeFmtp {
   uint8_t payloadType;
   StringView codecName;
   StringVec* tokens;
 };
 
 struct RustSdpAttributeFlags {
   bool iceLite;
@@ -254,16 +261,21 @@ void sdp_get_msid_semantics(const RustAt
 
 size_t sdp_get_group_count(const RustAttributeList* aList);
 nsresult sdp_get_groups(const RustAttributeList* aList, size_t listSize,
                         RustSdpAttributeGroup* ret);
 
 nsresult sdp_get_rtcp(const RustAttributeList* aList,
                       RustSdpAttributeRtcp* ret);
 
+
+size_t sdp_get_rtcpfb_count(const RustAttributeList* aList);
+void sdp_get_rtcpfbs(const RustAttributeList* aList, size_t listSize,
+                     RustSdpAttributeRtcpFb* ret);
+
 size_t sdp_get_imageattr_count(const RustAttributeList* aList);
 void sdp_get_imageattrs(const RustAttributeList* aList, size_t listSize,
                         StringView* ret);
 
 size_t sdp_get_sctpmap_count(const RustAttributeList* aList);
 void sdp_get_sctpmaps(const RustAttributeList* aList, size_t listSize,
                       RustSdpAttributeSctpmap* ret);
 
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
@@ -1095,16 +1095,17 @@ const char* SdpRtcpFbAttributeList::sli 
 const char* SdpRtcpFbAttributeList::rpsi = "rpsi";
 const char* SdpRtcpFbAttributeList::app = "app";
 
 const char* SdpRtcpFbAttributeList::fir = "fir";
 const char* SdpRtcpFbAttributeList::tmmbr = "tmmbr";
 const char* SdpRtcpFbAttributeList::tstr = "tstr";
 const char* SdpRtcpFbAttributeList::vbcm = "vbcm";
 
+
 void
 SdpRtcpFbAttributeList::Serialize(std::ostream& os) const
 {
   for (auto i = mFeedbacks.begin(); i != mFeedbacks.end(); ++i) {
     os << "a=" << mType << ":" << i->pt << " " << i->type;
     if (i->parameter.length()) {
       os << " " << i->parameter;
       if (i->extra.length()) {
--- a/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
@@ -1,16 +1,23 @@
 use std::net::IpAddr;
 use std::str::FromStr;
 use std::fmt;
 
 use SdpType;
 use error::SdpParserInternalError;
 use network::{parse_nettype, parse_addrtype, parse_unicast_addr};
 
+
+#[derive(Clone)]
+pub enum SdpAttributePayloadType {
+    PayloadType(u8),
+    Wildcard, // Wildcard means "*",
+}
+
 #[derive(Clone)]
 pub enum SdpAttributeCandidateTransport {
     Udp,
     Tcp,
 }
 
 #[derive(Clone)]
 pub enum SdpAttributeCandidateType {
@@ -178,20 +185,32 @@ impl SdpAttributeRtcp {
     }
 
     fn set_addr(&mut self, addr: IpAddr) {
         self.unicast_addr = Some(addr)
     }
 }
 
 #[derive(Clone)]
+pub enum SdpAttributeRtcpFbType {
+    Ack = 0,
+    Ccm = 2, // This is explicitly 2 to make the conversion to the
+             // enum used in the glue-code possible. The glue code has "app"
+             // in the place of 1
+    Nack,
+    TrrInt,
+    Remb
+}
+
+#[derive(Clone)]
 pub struct SdpAttributeRtcpFb {
-    pub payload_type: u32,
-    // TODO parse this and use an enum instead?
-    pub feedback_type: String,
+    pub payload_type: SdpAttributePayloadType,
+    pub feedback_type: SdpAttributeRtcpFbType,
+    pub parameter: String,
+    pub extra: String,
 }
 
 #[derive(Clone)]
 pub enum SdpAttributeDirection {
     Recvonly,
     Sendonly,
     Sendrecv,
 }
@@ -565,16 +584,24 @@ fn string_or_empty(to_parse: &str) -> Re
     if to_parse.is_empty() {
         Err(SdpParserInternalError::Generic("This attribute is required to have a value"
                                                 .to_string()))
     } else {
         Ok(to_parse.to_string())
     }
 }
 
+fn parse_payload_type(to_parse: &str) -> Result<SdpAttributePayloadType, SdpParserInternalError>
+{
+    Ok(match to_parse {
+             "*" => SdpAttributePayloadType::Wildcard,
+             _ => SdpAttributePayloadType::PayloadType(to_parse.parse::<u8>()?)
+         })
+}
+
 fn parse_sctp_port(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
     let port = to_parse.parse()?;
     if port > 65535 {
         return Err(SdpParserInternalError::Generic(format!("Sctpport port {} can only be a bit 16bit number",
                                                            port)));
     }
     Ok(SdpAttribute::SctpPort(port))
 }
@@ -936,27 +963,116 @@ fn parse_rtcp(to_parse: &str) -> Result<
                 }
             };
         }
     };
     Ok(SdpAttribute::Rtcp(rtcp))
 }
 
 fn parse_rtcp_fb(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
-    let tokens: Vec<&str> = to_parse.splitn(2, ' ').collect();
+    let tokens: Vec<&str> = to_parse.splitn(4,' ').collect();
+
+    // Parse this in advance to use it later in the parameter switch
+    let feedback_type = match tokens.get(1) {
+        Some(x) => match x.as_ref(){
+            "ack" => SdpAttributeRtcpFbType::Ack,
+            "ccm" => SdpAttributeRtcpFbType::Ccm,
+            "nack" => SdpAttributeRtcpFbType::Nack,
+            "trr-int" => SdpAttributeRtcpFbType::TrrInt,
+            "goog-remb" => SdpAttributeRtcpFbType::Remb,
+            _ => {
+                return Err(SdpParserInternalError::Unsupported(
+                    format!("Unknown rtcpfb feedback type: {:?}",x).to_string()
+                ))
+            }
+        },
+        None => {
+            return Err(SdpParserInternalError::Generic(
+                           "Error parsing rtcpfb: no feedback type".to_string(),
+                       ))
+        }
+    };
+
+    // Parse this in advance to make the initilization block below better readable
+    let parameter = match &feedback_type {
+        &SdpAttributeRtcpFbType::Ack => match tokens.get(2) {
+            Some(x) => match x.as_ref() {
+                "rpsi" | "app"  => x.to_string(),
+                _ => {
+                    return Err(SdpParserInternalError::Unsupported(
+                        format!("Unknown rtcpfb ack parameter: {:?}",x).to_string()
+                    ))
+                },
+            }
+            None => {
+                return Err(SdpParserInternalError::Unsupported(
+                    format!("The rtcpfb ack feeback type needs a parameter:").to_string()
+                ))
+            }
+        },
+        &SdpAttributeRtcpFbType::Ccm => match tokens.get(2) {
+            Some(x) => match x.as_ref() {
+                "fir" | "tmmbr" | "tstr" | "vbcm"  => x.to_string(),
+                _ => {
+                    return Err(SdpParserInternalError::Unsupported(
+                        format!("Unknown rtcpfb ccm parameter: {:?}",x).to_string()
+                    ))
+                },
+            }
+            None => "".to_string(),
+        },
+        &SdpAttributeRtcpFbType::Nack => match tokens.get(2) {
+            Some(x) => match x.as_ref() {
+                "sli" | "pli" | "rpsi" | "app"  => x.to_string(),
+                _ => {
+                    return Err(SdpParserInternalError::Unsupported(
+                        format!("Unknown rtcpfb nack parameter: {:?}",x).to_string()
+                    ))
+                },
+            }
+            None => "".to_string(),
+        },
+        &SdpAttributeRtcpFbType::TrrInt => match tokens.get(2) {
+            Some(x) => match x {
+                _ if x.parse::<u32>().is_ok() => x.to_string(),
+                _ => {
+                    return Err(SdpParserInternalError::Generic(
+                        format!("Unknown rtcpfb trr-int parameter: {:?}",x).to_string()
+                    ))
+                },
+            }
+            None => {
+                    return Err(SdpParserInternalError::Generic(
+                        format!("The rtcpfb trr-int feedback type needs a parameter").to_string()
+                    ))
+            }
+        },
+        &SdpAttributeRtcpFbType::Remb => match tokens.get(2) {
+            Some(x) => match x {
+                _ => {
+                    return Err(SdpParserInternalError::Unsupported(
+                        format!("Unknown rtcpfb remb parameter: {:?}",x).to_string()
+                    ))
+                },
+            }
+            None => "".to_string(),
+        }
+    };
+
+
     Ok(SdpAttribute::Rtcpfb(SdpAttributeRtcpFb {
-                                // TODO limit this to dymaic PTs
-                                payload_type: tokens[0].parse::<u32>()?,
-                                feedback_type: match tokens.get(1) {
+                                payload_type: parse_payload_type(tokens[0])?,
+
+                                feedback_type: feedback_type,
+
+                                parameter: parameter,
+
+                                extra: match tokens.get(3) {
                                     Some(x) => x.to_string(),
-                                    None => {
-                                        return Err(SdpParserInternalError::Generic(
-                                                       "Error parsing rtcpfb".to_string(),
-                                                   ))
-                                    }
+                                    None => "".to_string(),
                                 },
                             }))
 }
 
 fn parse_sctpmap(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
     let tokens: Vec<&str> = to_parse.split_whitespace().collect();
     if tokens.len() != 3 {
         return Err(SdpParserInternalError::Generic("Sctpmap needs to have three tokens"
--- a/media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs
@@ -355,16 +355,18 @@ pub fn parse_media_vector(lines: &[SdpLi
         _ => {
             return Err(SdpParserError::Sequence {
                            message: "first line in media section needs to be a media line"
                                .to_string(),
                            line_number: lines[0].line_number,
                        })
         }
     };
+
+
     for line in lines.iter().skip(1) {
         match line.sdp_type {
             SdpType::Connection(ref c) => {
                 sdp_media
                     .set_connection(c)
                     .map_err(|e: SdpParserInternalError| {
                                  SdpParserError::Sequence {
                                      message: format!("{}", e),
@@ -403,12 +405,14 @@ pub fn parse_media_vector(lines: &[SdpLi
                            })
             }
 
             // the line parsers throw unsupported errors for these already
             SdpType::Information(_) |
             SdpType::Key(_) => (),
         };
     }
+
     media_sections.push(sdp_media);
+
     Ok(media_sections)
 }
 // TODO add unit tests for parse_media_vector
--- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs
@@ -1,18 +1,19 @@
 use std::slice;
 use libc::{size_t, uint8_t, uint16_t, uint32_t, int64_t};
 
 use rsdparsa::SdpSession;
-use rsdparsa::attribute_type::{SdpAttribute, SdpAttributeFingerprint, SdpAttributeSetup, SdpAttributeSsrc, SdpAttributeRtpmap, SdpAttributeMsid, SdpAttributeMsidSemantic, SdpAttributeGroupSemantic, SdpAttributeGroup, SdpAttributeRtcp, SdpAttributeSctpmap, SdpAttributeRemoteCandidate, SdpAttributeExtmap, SdpAttributeDirection};
+use rsdparsa::attribute_type::{SdpAttribute, SdpAttributePayloadType, SdpAttributeFingerprint, SdpAttributeSetup, SdpAttributeSsrc, SdpAttributeRtpmap, SdpAttributeMsid, SdpAttributeMsidSemantic, SdpAttributeGroupSemantic, SdpAttributeGroup, SdpAttributeRtcp, SdpAttributeRtcpFb, SdpAttributeSctpmap, SdpAttributeRemoteCandidate, SdpAttributeExtmap, SdpAttributeDirection};
 use nserror::{nsresult, NS_OK, NS_ERROR_INVALID_ARG};
 
 use types::StringView;
 use network::RustIpAddr;
 
+
 #[repr(C)]
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub enum RustSdpAttributeType {
     BundleOnly,
     Candidate,
     EndOfCandidates,
     Extmap,
     Fingerprint,
@@ -565,16 +566,57 @@ pub unsafe extern "C" fn sdp_get_rtcp(at
     let attr = get_attribute((*attributes).as_slice(), RustSdpAttributeType::Rtcp);
     if let Some(&SdpAttribute::Rtcp(ref data)) = attr {
         *ret = RustSdpAttributeRtcp::from(data);
         return NS_OK;
     }
     NS_ERROR_INVALID_ARG
 }
 
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeRtcpFb {
+    pub payload_type: u32,
+    pub feedback_type: u32,
+    pub parameter: StringView,
+    pub extra: StringView
+}
+
+impl<'a> From<&'a SdpAttributeRtcpFb> for RustSdpAttributeRtcpFb {
+    fn from(other: &SdpAttributeRtcpFb) -> Self {
+        RustSdpAttributeRtcpFb {
+            payload_type: match other.payload_type{
+                SdpAttributePayloadType::Wildcard => u32::max_value(),
+                SdpAttributePayloadType::PayloadType(x) => x as u32,
+            },
+            feedback_type: other.feedback_type.clone() as u32,
+            parameter: StringView::from(other.parameter.as_str()),
+            extra: StringView::from(other.extra.as_str()),
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rtcpfb_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+    count_attribute((*attributes).as_slice(), RustSdpAttributeType::Rtcpfb)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rtcpfbs(attributes: *const Vec<SdpAttribute>, ret_size: size_t, ret_rtcpfbs: *mut RustSdpAttributeRtcpFb) {
+    let attrs: Vec<_> = (*attributes).iter().filter_map(|x| if let SdpAttribute::Rtcpfb(ref data) = *x {
+        Some(RustSdpAttributeRtcpFb::from(data))
+    } else {
+        None
+    }).collect();
+    let rtcpfbs = slice::from_raw_parts_mut(ret_rtcpfbs, ret_size);
+    rtcpfbs.clone_from_slice(attrs.as_slice());
+}
+
+
 #[no_mangle]
 pub unsafe extern "C" fn sdp_get_imageattr_count(attributes: *const Vec<SdpAttribute>) -> size_t {
     count_attribute((*attributes).as_slice(), RustSdpAttributeType::ImageAttr)
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn sdp_get_imageattrs(attributes: *const Vec<SdpAttribute>, ret_size: size_t, ret_groups: *mut StringView) {
     let attrs: Vec<_> = (*attributes).iter().filter_map(|x| if let SdpAttribute::ImageAttr(ref string) = *x {
--- a/python/mozbuild/mozbuild/action/test_archive.py
+++ b/python/mozbuild/mozbuild/action/test_archive.py
@@ -357,17 +357,29 @@ ARCHIVE_FILES = {
             'base': '',
             'pattern': 'mochitest/**',
         },
         {
             'source': buildconfig.topobjdir,
             'base': '',
             'pattern': 'mozinfo.json',
             'dest': 'mochitest'
-        }
+        },
+        {
+            'source': buildconfig.topobjdir,
+            'base': 'dist/xpi-stage',
+            'pattern': 'mochijar/**',
+            'dest': 'mochitest'
+        },
+        {
+            'source': buildconfig.topobjdir,
+            'base': 'dist/xpi-stage',
+            'pattern': 'specialpowers/**',
+            'dest': 'mochitest/extensions'
+        },
     ],
     'mozharness': [
         {
             'source': buildconfig.topsrcdir,
             'base': 'testing',
             'pattern': 'mozharness/**',
         },
     ],
--- a/python/mozbuild/mozbuild/backend/tup.py
+++ b/python/mozbuild/mozbuild/backend/tup.py
@@ -502,23 +502,27 @@ class TupBackend(CommonBackend):
                 if any(mozpath.match(f, p) for p in skip_files):
                     return False
 
             if backend_file.requires_delay(obj.inputs):
                 backend_file.delayed_generated_files.append(obj)
             else:
                 self._process_generated_file(backend_file, obj)
         elif (isinstance(obj, ChromeManifestEntry) and
-              obj.install_target.startswith('dist/bin')):
-            top_level = mozpath.join(obj.install_target, 'chrome.manifest')
-            if obj.path != top_level:
-                entry = 'manifest %s' % mozpath.relpath(obj.path,
-                                                        obj.install_target)
-                self._manifest_entries[top_level].add(entry)
-            self._manifest_entries[obj.path].add(str(obj.entry))
+              obj.install_target.startswith(('dist/bin', 'dist/xpi-stage'))):
+            # The quitter extension specifies its chrome.manifest as a
+            # FINAL_TARGET_FILE, which conflicts with the manifest generation
+            # we do here, so skip it for now.
+            if obj.install_target != 'dist/xpi-stage/quitter':
+                top_level = mozpath.join(obj.install_target, 'chrome.manifest')
+                if obj.path != top_level:
+                    entry = 'manifest %s' % mozpath.relpath(obj.path,
+                                                            obj.install_target)
+                    self._manifest_entries[top_level].add(entry)
+                self._manifest_entries[obj.path].add(str(obj.entry))
         elif isinstance(obj, Defines):
             self._process_defines(backend_file, obj)
         elif isinstance(obj, HostDefines):
             self._process_defines(backend_file, obj, host=True)
         elif isinstance(obj, FinalTargetFiles):
             self._process_final_target_files(obj)
         elif isinstance(obj, FinalTargetPreprocessedFiles):
             self._process_final_target_pp_files(obj, backend_file)
--- a/python/mozbuild/mozbuild/controller/building.py
+++ b/python/mozbuild/mozbuild/controller/building.py
@@ -988,16 +988,29 @@ class BuildDriver(MozbuildObject):
                         if e.errno == errno.ENOENT:
                             return True
                         raise
                     if dep_mtime > mtime:
                         return True
                 return False
 
             def backend_out_of_date(backend_file):
+                if not os.path.isfile(backend_file):
+                    return True
+
+                # Check if any of our output files have been removed since
+                # we last built the backend, re-generate the backend if
+                # so.
+                outputs = []
+                with open(backend_file, 'r') as fh:
+                    outputs = fh.read().splitlines()
+                for output in outputs:
+                    if not os.path.isfile(mozpath.join(self.topobjdir, output)):
+                        return True
+
                 dep_file = '%s.in' % backend_file
                 return build_out_of_date(backend_file, dep_file)
 
             def maybe_invoke_backend(active_backend):
                 # Attempt to bypass the make-oriented logic below. Note this
                 # will only succeed in case we're building with a non-make
                 # backend (Tup), and otherwise be harmless.
                 if active_backend:
--- a/storage/mozStorageConnection.cpp
+++ b/storage/mozStorageConnection.cpp
@@ -1161,16 +1161,20 @@ Connection::getFilename()
   }
   return leafname;
 }
 
 int
 Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
 {
   MOZ_ASSERT(aStatement);
+
+  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("Connection::stepStatement", OTHER,
+                                   ::sqlite3_sql(aStatement));
+
   bool checkedMainThread = false;
   TimeStamp startTime = TimeStamp::Now();
 
   // The connection may have been closed if the executing statement has been
   // created and cached after a call to asyncClose() but before the actual
   // sqlite3_close().  This usually happens when other tasks using cached
   // statements are asynchronously scheduled for execution and any of them ends
   // up after asyncClose. See bug 728653 for details.
@@ -1274,16 +1278,18 @@ Connection::prepareStatement(sqlite3 *aN
 
 
 int
 Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
 {
   if (!isConnectionReadyOnThisThread())
     return SQLITE_MISUSE;
 
+  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("Connection::executeSql", OTHER, aSqlString);
+
   TimeStamp startTime = TimeStamp::Now();
   int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
                            nullptr);
 
   // Report very slow SQL statements to Telemetry
   TimeDuration duration = TimeStamp::Now() - startTime;
   const uint32_t threshold =
     NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
--- a/taskcluster/ci/artifact-build/kind.yml
+++ b/taskcluster/ci/artifact-build/kind.yml
@@ -4,16 +4,17 @@
 
 loader: taskgraph.loader.transform:loader
 
 kind-dependencies:
     - toolchain
 
 transforms:
     - taskgraph.transforms.build_attrs:transforms
+    - taskgraph.transforms.build_lints:transforms
     - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.task:transforms
 
 jobs:
     linux64-artifact/opt:
         description: "Linux64 Opt Artifact Build"
         index:
@@ -23,16 +24,18 @@ jobs:
             platform: linux64/opt
             kind: build
             symbol: AB
             tier: 2
         run-on-projects: ['trunk', 'try']
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
             max-run-time: 36000
+            env:
+                PERFHERDER_EXTRA_OPTIONS: artifact
         run:
             using: mozharness
             actions: [get-secrets build]
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_sub_linux_configs/64_artifact.py
             script: "mozharness/scripts/fx_desktop_build.py"
             secrets: true
--- a/taskcluster/ci/hazard/kind.yml
+++ b/taskcluster/ci/hazard/kind.yml
@@ -4,16 +4,17 @@
 
 loader: taskgraph.loader.transform:loader
 
 kind-dependencies:
     - toolchain
 
 transforms:
     - taskgraph.transforms.build_attrs:transforms
+    - taskgraph.transforms.build_lints:transforms
     - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.task:transforms
 
 job-defaults:
     treeherder:
         kind: build
         tier: 1
--- a/taskcluster/ci/release-notify-promote/kind.yml
+++ b/taskcluster/ci/release-notify-promote/kind.yml
@@ -6,16 +6,17 @@ loader: taskgraph.loader.transform:loade
 
 transforms:
    - taskgraph.transforms.release_deps:transforms
    - taskgraph.transforms.release_notifications:transforms
    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
    - post-beetmover-dummy
+   - release-generate-checksums-beetmover
    - release-bouncer-sub
 
 job-defaults:
    name: notify-release-drivers-promote
    description: Sends email to release-drivers telling release was promoted.
    run-on-projects: []
    shipping-phase: promote
    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
--- a/taskcluster/ci/searchfox/kind.yml
+++ b/taskcluster/ci/searchfox/kind.yml
@@ -4,16 +4,17 @@
 
 loader: taskgraph.loader.transform:loader
 
 kind-dependencies:
     - toolchain
 
 transforms:
     - taskgraph.transforms.build_attrs:transforms
+    - taskgraph.transforms.build_lints:transforms
     - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.task:transforms
 
 job-defaults:
     index:
         product: firefox
     treeherder:
@@ -27,16 +28,18 @@ jobs:
         description: "Linux64 Debug Searchfox"
         index:
             job-name: linux64-searchfox-debug
         treeherder:
             platform: linux64/debug
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
             max-run-time: 36000
+            env:
+                PERFHERDER_EXTRA_OPTIONS: searchfox
         run:
             using: mozharness
             actions: [build]
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_sub_linux_configs/64_searchfox_and_debug.py
             script: "mozharness/scripts/fx_desktop_build.py"
             tooltool-downloads: public
@@ -51,16 +54,17 @@ jobs:
             job-name: macosx64-searchfox-debug
         treeherder:
             platform: osx-cross/debug
         worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
         worker:
             max-run-time: 36000
             env:
                 TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
+                PERFHERDER_EXTRA_OPTIONS: searchfox
         run:
             using: mozharness
             actions: [get-secrets build update]
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_base_mac_64_cross_builds.py
             script: "mozharness/scripts/fx_desktop_build.py"
             custom-build-variant-cfg: cross-debug-searchfox
@@ -84,16 +88,17 @@ jobs:
             job-name: win64-searchfox-debug
         treeherder:
             platform: windows2012-64/debug
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
             max-run-time: 7200
             env:
                 TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
+                PERFHERDER_EXTRA_OPTIONS: searchfox
         run:
             using: mozharness
             options: [append-env-variables-from-configs]
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/releng_base_firefox.py
                 - builds/taskcluster_base_windows.py
                 - builds/taskcluster_base_win64.py
--- a/taskcluster/ci/source-test/jsshell.yml
+++ b/taskcluster/ci/source-test/jsshell.yml
@@ -1,44 +1,46 @@
 job-defaults:
     platform: linux64/opt
     require-build: true
     worker-type:
         by-platform:
-            linux64.*: aws-provisioner-v1/gecko-t-linux-xlarge
+            linux64.*: releng-hardware/gecko-t-linux-talos
     worker:
         by-platform:
             linux64.*:
-                docker-image: {in-tree: "desktop1604-test"}
+                env:
+                    SHELL: /bin/bash
                 max-run-time: 1800
     treeherder:
         kind: test
         tier: 2
     run:
         using: run-task
         use-artifacts:
             build:
                 - target.jsshell.zip
+        workdir: /home/cltbld
     run-on-projects: ['mozilla-central', 'try']
 
 bench-ares6:
     description: Ares6 JavaScript shell benchmark suite
     treeherder:
         symbol: js-bench(ares6)
     run:
         command: >
             cd $USE_ARTIFACT_PATH/build &&
             unzip -q -d jsshell target.jsshell.zip &&
             export JSSHELL=$USE_ARTIFACT_PATH/build/jsshell/js &&
-            cd /builds/worker/checkouts/gecko &&
+            cd $GECKO_PATH &&
             ./mach jsshell-bench --binary $JSSHELL --perfherder ares6
 
 bench-sixspeed:
     description: Six-Speed JavaScript shell benchmark suite
     treeherder:
         symbol: js-bench(6speed)
     run:
         command: >
             cd $USE_ARTIFACT_PATH/build &&
             unzip -q -d jsshell target.jsshell.zip &&
             export JSSHELL=$USE_ARTIFACT_PATH/build/jsshell/js &&
-            cd /builds/worker/checkouts/gecko &&
+            cd $GECKO_PATH &&
             ./mach jsshell-bench --binary $JSSHELL --perfherder six-speed
--- a/taskcluster/ci/spidermonkey/kind.yml
+++ b/taskcluster/ci/spidermonkey/kind.yml
@@ -5,16 +5,17 @@
 loader: taskgraph.loader.transform:loader
 
 kind-dependencies:
     - toolchain
 
 transforms:
     - taskgraph.transforms.spidermonkey:transforms
     - taskgraph.transforms.build_attrs:transforms
+    - taskgraph.transforms.build_lints:transforms
     - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.task:transforms
 
 job-defaults:
     treeherder:
         kind: build
         tier: 1
--- a/taskcluster/ci/static-analysis-autotest/kind.yml
+++ b/taskcluster/ci/static-analysis-autotest/kind.yml
@@ -4,16 +4,17 @@
 
 loader: taskgraph.loader.transform:loader
 
 kind-dependencies:
     - toolchain
 
 transforms:
     - taskgraph.transforms.build_attrs:transforms
+    - taskgraph.transforms.build_lints:transforms
     - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.task:transforms
 
 job-defaults:
     index:
         product: firefox
     treeherder:
@@ -26,16 +27,18 @@ jobs:
         description: "Linux64 Debug Static Analysis Autotest"
         index:
             job-name: linux64-st-autotest-debug
         treeherder:
             platform: linux64/debug
         worker-type: aws-provisioner-v1/gecko-t-linux-large
         worker:
             max-run-time: 3600
+            env:
+                PERFHERDER_EXTRA_OPTIONS: static-analysis-autotest
         run:
             using: mozharness
             actions: [static-analysis-autotest]
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_sub_linux_configs/64_stat_and_debug.py
             script: "mozharness/scripts/fx_desktop_build.py"
             tooltool-downloads: public
--- a/taskcluster/ci/static-analysis/kind.yml
+++ b/taskcluster/ci/static-analysis/kind.yml
@@ -4,16 +4,17 @@
 
 loader: taskgraph.loader.transform:loader
 
 kind-dependencies:
     - toolchain
 
 transforms:
     - taskgraph.transforms.build_attrs:transforms
+    - taskgraph.transforms.build_lints:transforms
     - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.task:transforms
 
 job-defaults:
     index:
         product: firefox
     treeherder:
@@ -26,16 +27,18 @@ jobs:
         description: "Linux64 Debug Static Analysis"
         index:
             job-name: linux64-st-an-debug
         treeherder:
             platform: linux64/debug
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
             max-run-time: 36000
+            env:
+                PERFHERDER_EXTRA_OPTIONS: static-analysis
         run:
             using: mozharness
             actions: [build]
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_sub_linux_configs/64_stat_and_debug.py
             script: "mozharness/scripts/fx_desktop_build.py"
             tooltool-downloads: public
@@ -49,16 +52,18 @@ jobs:
         description: "Linux64 Opt Static Analysis"
         index:
             job-name: linux64-st-an-opt
         treeherder:
             platform: linux64/opt
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
             max-run-time: 36000
+            env:
+                PERFHERDER_EXTRA_OPTIONS: static-analysis
         run:
             using: mozharness
             actions: [build]
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_sub_linux_configs/64_stat_and_opt.py
             script: "mozharness/scripts/fx_desktop_build.py"
             tooltool-downloads: public
@@ -77,16 +82,17 @@ jobs:
             platform: windows2012-32/debug
             symbol: S
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
             max-run-time: 7200
             env:
                 TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/releng.manifest"
+                PERFHERDER_EXTRA_OPTIONS: static-analysis
         run:
             using: mozharness
             options: [append-env-variables-from-configs]
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/releng_base_firefox.py
                 - builds/taskcluster_base_windows.py
                 - builds/taskcluster_base_win32.py
@@ -105,16 +111,17 @@ jobs:
             platform: windows2012-32/opt
             symbol: S
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
             max-run-time: 7200
             env:
                 TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/releng.manifest"
+                PERFHERDER_EXTRA_OPTIONS: static-analysis
         run:
             using: mozharness
             options: [append-env-variables-from-configs]
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/releng_base_firefox.py
                 - builds/taskcluster_base_windows.py
                 - builds/taskcluster_base_win32.py
@@ -133,16 +140,17 @@ jobs:
             platform: windows2012-64/debug
             symbol: S
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
             max-run-time: 7200
             env:
                 TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
+                PERFHERDER_EXTRA_OPTIONS: static-analysis
         run:
             using: mozharness
             options: [append-env-variables-from-configs]
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/releng_base_firefox.py
                 - builds/taskcluster_base_windows.py
                 - builds/taskcluster_base_win64.py
@@ -161,16 +169,17 @@ jobs:
             platform: windows2012-64/opt
             symbol: S
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
             max-run-time: 7200
             env:
                 TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
+                PERFHERDER_EXTRA_OPTIONS: static-analysis
         run:
             using: mozharness
             options: [append-env-variables-from-configs]
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/releng_base_firefox.py
                 - builds/taskcluster_base_windows.py
                 - builds/taskcluster_base_win64.py
--- a/taskcluster/ci/valgrind/kind.yml
+++ b/taskcluster/ci/valgrind/kind.yml
@@ -4,16 +4,17 @@
 
 loader: taskgraph.loader.transform:loader
 
 kind-dependencies:
     - toolchain
 
 transforms:
     - taskgraph.transforms.build_attrs:transforms
+    - taskgraph.transforms.build_lints:transforms
     - taskgraph.transforms.use_toolchains:transforms
     - taskgraph.transforms.job:transforms
     - taskgraph.transforms.task:transforms
 
 jobs:
     linux64-valgrind/opt:
         description: "Linux64 Valgrind Opt"
         index:
@@ -23,16 +24,18 @@ jobs:
             platform: linux64/opt
             symbol: V
             kind: build
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
             docker-image: {in-tree: valgrind-build}
             max-run-time: 72000
+            env:
+                PERFHERDER_EXTRA_OPTIONS: valgrind
         run:
             using: mozharness
             actions: [get-secrets build valgrind-test]
             custom-build-variant-cfg: valgrind
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_base_linux_64_builds.py
             script: "mozharness/scripts/fx_desktop_build.py"
--- a/taskcluster/scripts/run-task
+++ b/taskcluster/scripts/run-task
@@ -463,17 +463,17 @@ def vcs_checkout(source_repo, dest, stor
                                      repo_name=source_repo.split('/')[-1]))
 
     print_line(b'vcs', msg.encode('utf-8'))
 
     return revision
 
 
 def main(args):
-    print_line(b'setup', b'run-task started\n')
+    print_line(b'setup', b'run-task started in %s\n' % os.getcwd().encode('utf-8'))
     running_as_root = IS_POSIX and os.getuid() == 0
 
     # Arguments up to '--' are ours. After are for the main task
     # to be executed.
     try:
         i = args.index('--')
         our_args = args[0:i]
         task_args = args[i + 1:]
@@ -501,28 +501,23 @@ def main(args):
     # expand ~ in some paths
     if args.vcs_checkout:
         args.vcs_checkout = os.path.expanduser(args.vcs_checkout)
     if args.tools_checkout:
         args.tools_checkout = os.path.expanduser(args.tools_checkout)
     if 'HG_STORE_PATH' in os.environ:
         os.environ['HG_STORE_PATH'] = os.path.expanduser(os.environ['HG_STORE_PATH'])
 
-    if IS_POSIX:
-        if running_as_root:
-            user, group, gids = get_posix_user_group(args.user, args.group)
-            uid = user.pw_uid
-            gid = group.gr_gid
-        else:
-            print('error: run-task must be run as root on POSIX platforms')
-            return 1
-    else:
-        uid = gid = gids = None
+    uid = gid = gids = None
+    if IS_POSIX and running_as_root:
+        user, group, gids = get_posix_user_group(args.user, args.group)
+        uid = user.pw_uid
+        gid = group.gr_gid
 
-    if os.path.exists("/dev/kvm"):
+    if running_as_root and os.path.exists("/dev/kvm"):
         # Ensure kvm permissions for worker, required for Android x86
         st = os.stat("/dev/kvm")
         os.chmod("/dev/kvm", st.st_mode | 0o666)
 
     # Validate caches.
     #
     # Taskgraph should pass in a list of paths that are caches via an
     # environment variable (which we don't want to pass down to child
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -75,16 +75,28 @@ PER_PROJECT_PARAMETERS = {
     },
 
     'mozilla-esr60': {
         'target_tasks_method': 'mozilla_esr60_tasks',
         'optimize_target_tasks': True,
         'include_nightly': True,
     },
 
+    'comm-beta': {
+        'target_tasks_method': 'mozilla_beta_tasks',
+        'optimize_target_tasks': True,
+        'include_nightly': True,
+    },
+
+    'comm-esr60': {
+        'target_tasks_method': 'mozilla_beta_tasks',
+        'optimize_target_tasks': True,
+        'include_nightly': True,
+    },
+
     'pine': {
         'target_tasks_method': 'pine_tasks',
         'optimize_target_tasks': True,
         'include_nightly': False,
     },
 
     # the default parameters are used for projects that do not match above.
     'default': {
--- a/taskcluster/taskgraph/transforms/build_lints.py
+++ b/taskcluster/taskgraph/transforms/build_lints.py
@@ -7,31 +7,32 @@ kind.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 
 transforms = TransformSequence()
 
+SEEN_CONFIGS = {}
+
 
 @transforms.add
 def check_mozharness_perfherder_options(config, jobs):
     """Verify that multiple jobs don't use the same perfherder bucket.
 
     Build jobs record perfherder metrics by default. Perfherder metrics go
     to a bucket derived by the platform by default. The name can further be
     customized by the presence of "extra options" either defined in
     mozharness sub-configs or in an environment variable.
 
     This linter tries to verify that no 2 jobs will send Perfherder metrics
     to the same bucket by looking for jobs not defining extra options when
     their platform or mozharness config are otherwise similar.
     """
-    seen_configs = {}
 
     for job in jobs:
         if job['run']['using'] != 'mozharness':
             yield job
             continue
 
         worker = job.get('worker', {})
 
@@ -42,17 +43,18 @@ def check_mozharness_perfherder_options(
 
         # This isn't strictly necessary. But the Perfherder code looking at the
         # values we care about is only active on builds. So it doesn't make
         # sense to run this linter elsewhere.
         assert primary_config.startswith('builds/')
 
         key = (platform, primary_config, nightly, options)
 
-        if key in seen_configs:
-            raise Exception('Non-unique Perfherder data collection for jobs '
-                            '%s and %s: set PERFHERDER_EXTRA_OPTIONS in worker '
-                            'environment variables or use different mozconfigs'
-                            % (job['name'], seen_configs[key]))
+        if key in SEEN_CONFIGS:
+            raise Exception(
+                'Non-unique Perfherder data collection for jobs %s-%s and %s: '
+                'set PERFHERDER_EXTRA_OPTIONS in worker environment variables '
+                'or use different mozconfigs'
+                % (config.kind, job['name'], SEEN_CONFIGS[key]))
 
-        seen_configs[key] = job['name']
+        SEEN_CONFIGS[key] = '{}-{}'.format(config.kind, job['name'])
 
         yield job
--- a/taskcluster/taskgraph/transforms/job/__init__.py
+++ b/taskcluster/taskgraph/transforms/job/__init__.py
@@ -77,16 +77,19 @@ job_description_schema = Schema({
         Optional('files-changed'): [basestring],
     }),
 
     # A description of how to run this job.
     'run': {
         # The key to a job implementation in a peer module to this one
         'using': basestring,
 
+        # Base work directory used to set up the task.
+        Optional('workdir'): basestring,
+
         # Any remaining content is verified against that job implementation's
         # own schema.
         Extra: object,
     },
 
     Required('worker-type'): task_description_schema['worker-type'],
 
     # This object will be passed through to the task description, with additions
@@ -144,16 +147,18 @@ def make_task_description(config, jobs):
         if impl:
             job.setdefault('tags', {})['worker-implementation'] = impl
         worker = job.setdefault('worker', {})
         assert 'implementation' not in worker
         worker['implementation'] = impl
         if os:
             worker['os'] = os
 
+        job['run'].setdefault('workdir', '/builds/worker')
+
         taskdesc = copy.deepcopy(job)
 
         # fill in some empty defaults to make run implementations easier
         taskdesc.setdefault('attributes', {})
         taskdesc.setdefault('dependencies', {})
         taskdesc.setdefault('routes', [])
         taskdesc.setdefault('scopes', [])
         taskdesc.setdefault('extra', {})
--- a/taskcluster/taskgraph/transforms/job/common.py
+++ b/taskcluster/taskgraph/transforms/job/common.py
@@ -22,17 +22,17 @@ def docker_worker_add_workspace_cache(co
     key name to avoid undesired conflicts with other caches."""
     taskdesc['worker'].setdefault('caches', []).append({
         'type': 'persistent',
         'name': 'level-{}-{}-build-{}-{}-workspace'.format(
             config.params['level'], config.params['project'],
             taskdesc['attributes']['build_platform'],
             taskdesc['attributes']['build_type'],
         ),
-        'mount-point': "/builds/worker/workspace",
+        'mount-point': "{workdir}/workspace".format(**job['run']),
         # Don't enable the workspace cache when we can't guarantee its
         # behavior, like on Try.
         'skip-untrusted': True,
     })
     if extra:
         taskdesc['worker']['caches'][-1]['name'] += '-{}'.format(
             extra
         )
@@ -43,17 +43,17 @@ def add_artifacts(config, job, taskdesc,
         'name': get_artifact_prefix(taskdesc),
         'path': path,
         'type': 'directory',
     })
 
 
 def docker_worker_add_artifacts(config, job, taskdesc):
     """ Adds an artifact directory to the task """
-    add_artifacts(config, job, taskdesc, path='/builds/worker/artifacts/')
+    add_artifacts(config, job, taskdesc, path='{workdir}/artifacts/'.format(**job['run']))
 
 
 def generic_worker_add_artifacts(config, job, taskdesc):
     """ Adds an artifact directory to the task """
     # The path is the location on disk; it doesn't necessarily
     # mean the artifacts will be public or private; that is set via the name
     # attribute in add_artifacts.
     add_artifacts(config, job, taskdesc, path=get_artifact_prefix(taskdesc))
@@ -80,24 +80,25 @@ def support_vcs_checkout(config, job, ta
         # Sparse checkouts need their own cache because they can interfere
         # with clients that aren't sparse aware.
         if sparse:
             name += '-sparse'
 
         taskdesc['worker'].setdefault('caches', []).append({
             'type': 'persistent',
             'name': name,
-            'mount-point': '/builds/worker/checkouts',
+            'mount-point': '{workdir}/checkouts'.format(**job['run']),
         })
 
     taskdesc['worker'].setdefault('env', {}).update({
         'GECKO_BASE_REPOSITORY': config.params['base_repository'],
         'GECKO_HEAD_REPOSITORY': config.params['head_repository'],
         'GECKO_HEAD_REV': config.params['head_rev'],
-        'HG_STORE_PATH': '/builds/worker/checkouts/hg-store',
+        'GECKO_PATH': '{workdir}/checkouts/gecko'.format(**job['run']),
+        'HG_STORE_PATH': '{workdir}/checkouts/hg-store'.format(**job['run']),
     })
 
     if 'comm_base_repository' in config.params:
         taskdesc['worker']['env'].update({
             'COMM_BASE_REPOSITORY': config.params['comm_base_repository'],
             'COMM_HEAD_REPOSITORY': config.params['comm_head_repository'],
             'COMM_HEAD_REV': config.params['comm_head_rev'],
         })
@@ -171,35 +172,35 @@ def docker_worker_add_tooltool(config, j
 
     assert job['worker']['implementation'] in ('docker-worker', 'docker-engine')
 
     level = config.params['level']
 
     taskdesc['worker'].setdefault('caches', []).append({
         'type': 'persistent',
         'name': 'level-%s-tooltool-cache' % level,
-        'mount-point': '/builds/worker/tooltool-cache',
+        'mount-point': '{workdir}/tooltool-cache'.format(**job['run']),
     })
 
     taskdesc['worker'].setdefault('env', {}).update({
-        'TOOLTOOL_CACHE': '/builds/worker/tooltool-cache',
+        'TOOLTOOL_CACHE': '{workdir}/tooltool-cache'.format(**job['run']),
     })
 
     taskdesc['worker']['relengapi-proxy'] = True
     taskdesc['scopes'].extend([
         'docker-worker:relengapi-proxy:tooltool.download.public',
     ])
 
     if internal:
         taskdesc['scopes'].extend([
             'docker-worker:relengapi-proxy:tooltool.download.internal',
         ])
 
 
-def docker_worker_use_artifacts(config, job, taskdesc, use_artifacts):
+def support_use_artifacts(config, job, taskdesc, use_artifacts):
     """Set a JSON object of artifact URLs in an environment variable.
 
     This will tell the run-task script to download the artifacts.
     """
     urls = {}
     prefix = get_artifact_prefix(taskdesc)
     for kind, artifacts in use_artifacts.items():
         if kind not in taskdesc['dependencies']:
@@ -209,9 +210,9 @@ def docker_worker_use_artifacts(config, 
         urls[kind] = []
 
         for artifact in artifacts:
             path = '/'.join([prefix, artifact])
             urls[kind].append(get_artifact_url(task_id, path))
 
     env = taskdesc['worker'].setdefault('env', {})
     env['USE_ARTIFACT_URLS'] = {'task-reference': json.dumps(urls)}
-    env['USE_ARTIFACT_PATH'] = '/builds/worker/use-artifacts'
+    env['USE_ARTIFACT_PATH'] = '{workdir}/use-artifacts'.format(**job['run'])
--- a/taskcluster/taskgraph/transforms/job/debian_package.py
+++ b/taskcluster/taskgraph/transforms/job/debian_package.py
@@ -51,16 +51,19 @@ run_schema = Schema({
     # List of package tasks to get build dependencies from.
     Optional('packages'): [basestring],
 
     # What resolver to use to install build dependencies. The default
     # (apt-get) is good in most cases, but in subtle cases involving
     # a *-backports archive, its solver might not be able to find a
     # solution that satisfies the build dependencies.
     Optional('resolver'): Any('apt-get', 'aptitude'),
+
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 @run_job_using("docker-worker", "debian-package", schema=run_schema)
 def docker_worker_debian_package(config, job, taskdesc):
     run = job['run']
 
     name = taskdesc['label'].replace('{}-'.format(config.kind), '', 1)
--- a/taskcluster/taskgraph/transforms/job/hazard.py
+++ b/taskcluster/taskgraph/transforms/job/hazard.py
@@ -29,16 +29,19 @@ haz_run_schema = Schema({
     Optional('mozconfig'): basestring,
 
     # The set of secret names to which the task has access; these are prefixed
     # with `project/releng/gecko/{treeherder.kind}/level-{level}/`.   Setting
     # this will enable any worker features required and set the task's scopes
     # appropriately.  `true` here means ['*'], all secrets.  Not supported on
     # Windows
     Required('secrets', default=False): Any(bool, [basestring]),
+
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 @run_job_using("docker-worker", "hazard", schema=haz_run_schema)
 def docker_worker_hazard(config, job, taskdesc):
     run = job['run']
 
     worker = taskdesc['worker']
@@ -57,16 +60,16 @@ def docker_worker_hazard(config, job, ta
     })
 
     # script parameters
     if run.get('mozconfig'):
         env['MOZCONFIG'] = run['mozconfig']
 
     # build-haz-linux.sh needs this otherwise it assumes the checkout is in
     # the workspace.
-    env['GECKO_DIR'] = '/builds/worker/checkouts/gecko'
+    env['GECKO_DIR'] = '{workdir}/checkouts/gecko'.format(**run)
 
     worker['command'] = [
-        '/builds/worker/bin/run-task',
-        '--vcs-checkout', '/builds/worker/checkouts/gecko',
+        '{workdir}/bin/run-task'.format(**run),
+        '--vcs-checkout', '{workdir}/checkouts/gecko'.format(**run),
         '--',
         '/bin/bash', '-c', run['command']
     ]
--- a/taskcluster/taskgraph/transforms/job/mach.py
+++ b/taskcluster/taskgraph/transforms/job/mach.py
@@ -15,21 +15,24 @@ mach_schema = Schema({
     Required('using'): 'mach',
 
     # The mach command (omitting `./mach`) to run
     Required('mach'): basestring,
 
     # if true, perform a checkout of a comm-central based branch inside the
     # gecko checkout
     Required('comm-checkout'): bool,
+
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 @run_job_using("docker-worker", "mach", schema=mach_schema, defaults={'comm-checkout': False})
 @run_job_using("native-engine", "mach", schema=mach_schema, defaults={'comm-checkout': False})
 def docker_worker_mach(config, job, taskdesc):
     run = job['run']
 
     # defer to the run_task implementation
-    run['command'] = 'cd /builds/worker/checkouts/gecko && ./mach ' + run['mach']
+    run['command'] = 'cd {workdir}/checkouts/gecko && ./mach {mach}'.format(**run)
     run['using'] = 'run-task'
     del run['mach']
     configure_taskdesc_for_run(config, job, taskdesc, job['worker']['implementation'])
--- a/taskcluster/taskgraph/transforms/job/mozharness.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness.py
@@ -96,16 +96,19 @@ mozharness_run_schema = Schema({
 
     # If false don't pass --branch mozharness script
     # Only disableable on windows
     Required('use-magic-mh-args'): bool,
 
     # if true, perform a checkout of a comm-central based branch inside the
     # gecko checkout
     Required('comm-checkout'): bool,
+
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 mozharness_defaults = {
     'tooltool-downloads': False,
     'secrets': False,
     'taskcluster-proxy': False,
     'need-xvfb': False,
@@ -194,27 +197,28 @@ def mozharness_on_docker_worker_setup(co
         docker_worker_add_tooltool(config, job, taskdesc, internal=internal)
 
     # Retry if mozharness returns TBPL_RETRY
     worker['retry-exit-status'] = [4]
 
     docker_worker_setup_secrets(config, job, taskdesc)
 
     command = [
-        '/builds/worker/bin/run-task',
-        '--vcs-checkout', '/builds/worker/workspace/build/src',
-        '--tools-checkout', '/builds/worker/workspace/build/tools',
+        '{workdir}/bin/run-task'.format(**run),
+        '--vcs-checkout', '{workdir}/workspace/build/src'.format(**run),
+        '--tools-checkout', '{workdir}/workspace/build/tools'.format(**run),
     ]
     if run['comm-checkout']:
-        command.append('--comm-checkout=/builds/worker/workspace/build/src/comm')
+        command.append('--comm-checkout={workdir}/workspace/build/src/comm'.format(**run))
 
     command += [
         '--',
-        '/builds/worker/workspace/build/src/{}'.format(
-            run.get('job-script', 'taskcluster/scripts/builder/build-linux.sh')
+        '{workdir}/workspace/build/src/{script}'.format(
+            workdir=run['workdir'],
+            script=run.get('job-script', 'taskcluster/scripts/builder/build-linux.sh'),
         ),
     ]
 
     worker['command'] = command
 
 
 @run_job_using("generic-worker", "mozharness", schema=mozharness_run_schema,
                defaults=mozharness_defaults)
--- a/taskcluster/taskgraph/transforms/job/mozharness_test.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py
@@ -78,61 +78,64 @@ def get_variant(test_platform):
     return ''
 
 
 test_description_schema = {str(k): v for k, v in test_description_schema.schema.iteritems()}
 
 mozharness_test_run_schema = Schema({
     Required('using'): 'mozharness-test',
     Required('test'): test_description_schema,
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 def test_packages_url(taskdesc):
     """Account for different platforms that name their test packages differently"""
     return get_artifact_url('<build>', get_artifact_path(taskdesc, 'target.test_packages.json'))
 
 
 @run_job_using('docker-engine', 'mozharness-test', schema=mozharness_test_run_schema)
 @run_job_using('docker-worker', 'mozharness-test', schema=mozharness_test_run_schema)
 def mozharness_test_on_docker(config, job, taskdesc):
+    run = job['run']
     test = taskdesc['run']['test']
     mozharness = test['mozharness']
     worker = taskdesc['worker']
 
     # apply some defaults
     worker['docker-image'] = test['docker-image']
     worker['allow-ptrace'] = True  # required for all tests, for crashreporter
     worker['loopback-video'] = test['loopback-video']
     worker['loopback-audio'] = test['loopback-audio']
     worker['max-run-time'] = test['max-run-time']
     worker['retry-exit-status'] = test['retry-exit-status']
 
     artifacts = [
         # (artifact name prefix, in-image path)
-        ("public/logs/", "/builds/worker/workspace/build/upload/logs/"),
-        ("public/test", "/builds/worker/artifacts/"),
-        ("public/test_info/", "/builds/worker/workspace/build/blobber_upload_dir/"),
+        ("public/logs/", "{workdir}/workspace/build/upload/logs/".format(**run)),
+        ("public/test", "{workdir}/artifacts/".format(**run)),
+        ("public/test_info/", "{workdir}/workspace/build/blobber_upload_dir/".format(**run)),
     ]
 
     installer_url = get_artifact_url('<build>', mozharness['build-artifact-name'])
     mozharness_url = get_artifact_url('<build>',
                                       get_artifact_path(taskdesc, 'mozharness.zip'))
 
     worker['artifacts'] = [{
         'name': prefix,
-        'path': os.path.join('/builds/worker/workspace', path),
+        'path': os.path.join('{workdir}/workspace'.format(**run), path),
         'type': 'directory',
     } for (prefix, path) in artifacts]
 
     worker['caches'] = [{
         'type': 'persistent',
         'name': 'level-{}-{}-test-workspace'.format(
             config.params['level'], config.params['project']),
-        'mount-point': "/builds/worker/workspace",
+        'mount-point': "{workdir}/workspace".format(**run),
     }]
 
     env = worker['env'] = {
         'MOZHARNESS_CONFIG': ' '.join(mozharness['config']),
         'MOZHARNESS_SCRIPT': mozharness['script'],
         'MOZILLA_BUILD_URL': {'task-reference': installer_url},
         'NEED_PULSEAUDIO': 'true',
         'NEED_WINDOW_MANAGER': 'true',
@@ -157,34 +160,34 @@ def mozharness_test_on_docker(config, jo
     if mozharness['tooltool-downloads']:
         docker_worker_add_tooltool(config, job, taskdesc, internal=True)
 
     if test['reboot']:
         raise Exception('reboot: {} not supported on generic-worker'.format(test['reboot']))
 
     # assemble the command line
     command = [
-        '/builds/worker/bin/run-task',
+        '{workdir}/bin/run-task'.format(**run),
     ]
 
     # Support vcs checkouts regardless of whether the task runs from
     # source or not in case it is needed on an interactive loaner.
     support_vcs_checkout(config, job, taskdesc)
 
     # If we have a source checkout, run mozharness from it instead of
     # downloading a zip file with the same content.
     if test['checkout']:
-        command.extend(['--vcs-checkout', '/builds/worker/checkouts/gecko'])
-        env['MOZHARNESS_PATH'] = '/builds/worker/checkouts/gecko/testing/mozharness'
+        command.extend(['--vcs-checkout', '{workdir}/checkouts/gecko'.format(**run)])
+        env['MOZHARNESS_PATH'] = '{workdir}/checkouts/gecko/testing/mozharness'.format(**run)
     else:
         env['MOZHARNESS_URL'] = {'task-reference': mozharness_url}
 
     command.extend([
         '--',
-        '/builds/worker/bin/test-linux.sh',
+        '{workdir}/bin/test-linux.sh'.format(**run),
     ])
 
     command.extend([
         {"task-reference": "--installer-url=" + installer_url},
         {"task-reference": "--test-packages-url=" + test_packages_url(taskdesc)},
     ])
     command.extend(mozharness.get('extra-options', []))
 
--- a/taskcluster/taskgraph/transforms/job/run_task.py
+++ b/taskcluster/taskgraph/transforms/job/run_task.py
@@ -4,27 +4,27 @@
 """
 Support for running jobs that are invoked via the `run-task` script.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.job import run_job_using
 from taskgraph.util.schema import Schema
-from taskgraph.transforms.job.common import support_vcs_checkout, docker_worker_use_artifacts
+from taskgraph.transforms.job.common import support_use_artifacts, support_vcs_checkout
 from voluptuous import Required, Any
 
 run_task_schema = Schema({
     Required('using'): 'run-task',
 
     # if true, add a cache at ~worker/.cache, which is where things like pip
     # tend to hide their caches.  This cache is never added for level-1 jobs.
     Required('cache-dotcache'): bool,
 
-    # if true (the default), perform a checkout in /builds/worker/checkouts/gecko
+    # if true (the default), perform a checkout in {workdir}/checkouts/gecko
     Required('checkout'): bool,
 
     # The sparse checkout profile to use. Value is the filename relative to the
     # directory where sparse profiles are defined (build/sparse-profiles/).
     Required('sparse-profile'): Any(basestring, None),
 
     # if true, perform a checkout of a comm-central based branch inside the
     # gecko checkout
@@ -37,83 +37,86 @@ run_task_schema = Schema({
     Required('use-artifacts'): Any(None, {
         basestring: [basestring],
     }),
 
     # The command arguments to pass to the `run-task` script, after the
     # checkout arguments.  If a list, it will be passed directly; otherwise
     # it will be included in a single argument to `bash -cx`.
     Required('command'): Any([basestring], basestring),
+
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 def common_setup(config, job, taskdesc):
     run = job['run']
     if run['checkout']:
         support_vcs_checkout(config, job, taskdesc,
                              sparse=bool(run['sparse-profile']))
 
+    if run['use-artifacts']:
+        support_use_artifacts(config, job, taskdesc, run['use-artifacts'])
+
     taskdesc['worker'].setdefault('env', {})['MOZ_SCM_LEVEL'] = config.params['level']
 
 
 def add_checkout_to_command(run, command):
     if not run['checkout']:
         return
 
-    command.append('--vcs-checkout=/builds/worker/checkouts/gecko')
+    command.append('--vcs-checkout={workdir}/checkouts/gecko'.format(**run))
 
     if run['sparse-profile']:
         command.append('--sparse-profile=build/sparse-profiles/%s' %
                        run['sparse-profile'])
 
 
-docker_defaults = {
+defaults = {
     'cache-dotcache': False,
     'checkout': True,
     'comm-checkout': False,
     'sparse-profile': None,
     'use-artifacts': None,
 }
 
 
-@run_job_using("docker-worker", "run-task", schema=run_task_schema, defaults=docker_defaults)
+@run_job_using("docker-worker", "run-task", schema=run_task_schema, defaults=defaults)
 def docker_worker_run_task(config, job, taskdesc):
     run = job['run']
     worker = taskdesc['worker'] = job['worker']
     common_setup(config, job, taskdesc)
 
-    if run['use-artifacts']:
-        docker_worker_use_artifacts(config, job, taskdesc, run['use-artifacts'])
-
     if run.get('cache-dotcache'):
         worker['caches'].append({
             'type': 'persistent',
             'name': 'level-{level}-{project}-dotcache'.format(**config.params),
-            'mount-point': '/builds/worker/.cache',
+            'mount-point': '{workdir}/.cache'.format(**run),
             'skip-untrusted': True,
         })
 
     # This must match EXIT_PURGE_CACHES in taskcluster/scripts/run-task
     worker.setdefault('retry-exit-status', []).append(72)
     worker.setdefault('purge-caches-exit-status', []).append(72)
 
     run_command = run['command']
     if isinstance(run_command, basestring):
         run_command = ['bash', '-cx', run_command]
-    command = ['/builds/worker/bin/run-task']
+    command = ['{workdir}/bin/run-task'.format(**run)]
     add_checkout_to_command(run, command)
     if run['comm-checkout']:
-        command.append('--comm-checkout=/builds/worker/checkouts/gecko/comm')
+        command.append('--comm-checkout={workdir}/checkouts/gecko/comm'.format(**run))
     command.append('--fetch-hgfingerprint')
     command.append('--')
     command.extend(run_command)
     worker['command'] = command
 
 
-@run_job_using("native-engine", "run-task", schema=run_task_schema)
+@run_job_using("native-engine", "run-task", schema=run_task_schema, defaults=defaults)
 def native_engine_run_task(config, job, taskdesc):
     run = job['run']
     worker = taskdesc['worker'] = job['worker']
     common_setup(config, job, taskdesc)
 
     worker['context'] = '{}/raw-file/{}/taskcluster/scripts/run-task'.format(
         config.params['head_repository'], config.params['head_rev']
     )
--- a/taskcluster/taskgraph/transforms/job/spidermonkey.py
+++ b/taskcluster/taskgraph/transforms/job/spidermonkey.py
@@ -20,16 +20,19 @@ from taskgraph.transforms.job.common imp
 )
 
 sm_run_schema = Schema({
     Required('using'): Any('spidermonkey', 'spidermonkey-package', 'spidermonkey-mozjs-crate',
                            'spidermonkey-rust-bindings'),
 
     # The SPIDERMONKEY_VARIANT
     Required('spidermonkey-variant'): basestring,
+
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 @run_job_using("docker-worker", "spidermonkey", schema=sm_run_schema)
 @run_job_using("docker-worker", "spidermonkey-package", schema=sm_run_schema)
 @run_job_using("docker-worker", "spidermonkey-mozjs-crate",
                schema=sm_run_schema)
 @run_job_using("docker-worker", "spidermonkey-rust-bindings",
@@ -38,17 +41,17 @@ def docker_worker_spidermonkey(config, j
     run = job['run']
 
     worker = taskdesc['worker']
     worker['artifacts'] = []
     worker.setdefault('caches', []).append({
         'type': 'persistent',
         'name': 'level-{}-{}-build-spidermonkey-workspace'.format(
             config.params['level'], config.params['project']),
-        'mount-point': "/builds/worker/workspace",
+        'mount-point': "{workdir}/workspace".format(**run),
         'skip-untrusted': True,
     })
 
     docker_worker_add_artifacts(config, job, taskdesc)
     docker_worker_add_tooltool(config, job, taskdesc)
 
     env = worker.setdefault('env', {})
     env.update({
@@ -64,22 +67,23 @@ def docker_worker_spidermonkey(config, j
     if run['using'] == 'spidermonkey-package':
         script = "build-sm-package.sh"
     elif run['using'] == 'spidermonkey-mozjs-crate':
         script = "build-sm-mozjs-crate.sh"
     elif run['using'] == 'spidermonkey-rust-bindings':
         script = "build-sm-rust-bindings.sh"
 
     worker['command'] = [
-        '/builds/worker/bin/run-task',
-        '--vcs-checkout', '/builds/worker/workspace/build/src',
+        '{workdir}/bin/run-task'.format(**run),
+        '--vcs-checkout', '{workdir}/workspace/build/src'.format(**run),
         '--',
         '/bin/bash',
         '-c',
-        'cd /builds/worker && workspace/build/src/taskcluster/scripts/builder/%s' % script
+        'cd {workdir} && workspace/build/src/taskcluster/scripts/builder/{script}'.format(
+            workdir=run['workdir'], script=script)
     ]
 
 
 @run_job_using("generic-worker", "spidermonkey", schema=sm_run_schema)
 def generic_worker_spidermonkey(config, job, taskdesc):
     assert job['worker']['os'] == 'windows', 'only supports windows right now'
 
     run = job['run']
--- a/taskcluster/taskgraph/transforms/job/toolchain.py
+++ b/taskcluster/taskgraph/transforms/job/toolchain.py
@@ -58,16 +58,19 @@ toolchain_run_schema = Schema({
     Optional('resources'): [basestring],
 
     # Path to the artifact produced by the toolchain job
     Required('toolchain-artifact'): basestring,
 
     # An alias that can be used instead of the real toolchain job name in
     # the toolchains list for build jobs.
     Optional('toolchain-alias'): basestring,
+
+    # Base work directory used to set up the task.
+    Required('workdir'): basestring,
 })
 
 
 def get_digest_data(config, run, taskdesc):
     files = list(run.get('resources', []))
     # This file
     files.append('taskcluster/taskgraph/transforms/job/toolchain.py')
     # The script
@@ -151,25 +154,25 @@ def docker_worker_toolchain(config, job,
         args = ' ' + shell_quote(*args)
 
     sparse_profile = []
     if run.get('sparse-profile'):
         sparse_profile = ['--sparse-profile',
                           'build/sparse-profiles/{}'.format(run['sparse-profile'])]
 
     worker['command'] = [
-        '/builds/worker/bin/run-task',
-        '--vcs-checkout=/builds/worker/workspace/build/src',
+        '{workdir}/bin/run-task'.format(**run),
+        '--vcs-checkout={workdir}/workspace/build/src'.format(**run),
     ] + sparse_profile + [
         '--',
         'bash',
         '-c',
-        'cd /builds/worker && '
+        'cd {} && '
         '{}workspace/build/src/taskcluster/scripts/misc/{}{}'.format(
-            wrapper, run['script'], args)
+            run['workdir'], wrapper, run['script'], args)
     ]
 
     attributes = taskdesc.setdefault('attributes', {})
     attributes['toolchain-artifact'] = run['toolchain-artifact']
     if 'toolchain-alias' in run:
         attributes['toolchain-alias'] = run['toolchain-alias']
 
     if not taskgraph.fast:
deleted file mode 100644
--- a/testing/mochitest/Makefile.in
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-
-_DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir)
-
-include $(topsrcdir)/config/rules.mk
-
-libs:: 
-	(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - mochijar) | (cd $(_DEST_DIR) && tar -xf -)
-
-$(_DEST_DIR):
-	$(NSINSTALL) -D $@
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -748,17 +748,28 @@ class MochitestArguments(ArgumentContain
             if build_obj:
                 possible.insert(0, os.path.join(build_obj.topobjdir, '_tests', 'modules'))
 
             for p in possible:
                 if os.path.isdir(p):
                     options.testingModulesDir = p
                     break
 
+        # Paths to specialpowers and mochijar from the tests zip.
+        options.stagedAddons = [
+            os.path.join(here, 'extensions', 'specialpowers'),
+            os.path.join(here, 'mochijar'),
+        ]
         if build_obj:
+            objdir_xpi_stage = os.path.join(build_obj.distdir, 'xpi-stage')
+            if os.path.isdir(objdir_xpi_stage):
+                options.stagedAddons = [
+                    os.path.join(objdir_xpi_stage, 'specialpowers'),
+                    os.path.join(objdir_xpi_stage, 'mochijar'),
+                ]
             plugins_dir = os.path.join(build_obj.distdir, 'plugins')
             if os.path.isdir(plugins_dir) and plugins_dir not in options.extraProfileFiles:
                 options.extraProfileFiles.append(plugins_dir)
 
         # Even if buildbot is updated, we still want this, as the path we pass in
         # to the app must be absolute and have proper slashes.
         if options.testingModulesDir is not None:
             options.testingModulesDir = os.path.normpath(
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -833,17 +833,16 @@ def update_mozinfo():
     mozinfo.find_and_update_from_json(*dirs)
 
 
 class MochitestDesktop(object):
     """
     Mochitest class for desktop firefox.
     """
     oldcwd = os.getcwd()
-    mochijar = os.path.join(SCRIPT_DIR, 'mochijar')
 
     # Path to the test script on the server
     TEST_PATH = "tests"
     NESTED_OOP_TEST_PATH = "nested_oop"
     CHROME_PATH = "redirect.html"
 
     certdbNew = False
     sslTunnel = None
@@ -851,19 +850,20 @@ class MochitestDesktop(object):
     mediaDevices = None
 
     patternFiles = {}
 
     # XXX use automation.py for test name to avoid breaking legacy
     # TODO: replace this with 'runtests.py' or 'mochitest' or the like
     test_name = 'automation.py'
 
-    def __init__(self, flavor, logger_options, quiet=False):
+    def __init__(self, flavor, logger_options, staged_addons=None, quiet=False):
         update_mozinfo()
         self.flavor = flavor
+        self.staged_addons = staged_addons
         self.server = None
         self.wsserver = None
         self.websocketProcessBridge = None
         self.sslTunnel = None
         self.manifest = None
         self.tests_by_manifest = defaultdict(list)
         self.prefs_by_manifest = defaultdict(set)
         self._active_tests = None
@@ -1316,21 +1316,16 @@ toolbar#nav-bar {
   background-image: none !important;
 }
 """
         with open(os.path.join(options.profilePath, "userChrome.css"), "a") as chromeFile:
             chromeFile.write(chrome)
 
         manifest = self.writeChromeManifest(options)
 
-        if not os.path.isdir(self.mochijar):
-            self.log.error(
-                "TEST-UNEXPECTED-FAIL | invalid setup: missing mochikit extension")
-            return None
-
         return manifest
 
     def getExtensionsToInstall(self, options):
         "Return a list of extensions to install in the profile"
         extensions = []
         appDir = options.app[
             :options.app.rfind(
                 os.sep)] if options.app else options.utilityPath
@@ -2253,20 +2248,24 @@ toolbar#nav-bar {
                 # start marionette and kick off the tests
                 marionette_args = marionette_args or {}
                 self.marionette = Marionette(**marionette_args)
                 self.marionette.start_session()
 
                 # install specialpowers and mochikit addons
                 addons = Addons(self.marionette)
 
-                addons.install(create_zip(
-                    os.path.join(here, 'extensions', 'specialpowers')
-                ))
-                addons.install(create_zip(self.mochijar))
+                if self.staged_addons:
+                    for addon_path in self.staged_addons:
+                        if not os.path.isdir(addon_path):
+                            self.log.error(
+                                "TEST-UNEXPECTED-FAIL | invalid setup: missing extension at %s" %
+                                addon_path)
+                            return 1, self.lastTestSeen
+                        addons.install(create_zip(addon_path))
 
                 self.execute_start_script()
 
                 # an open marionette session interacts badly with mochitest,
                 # delete it until we figure out why.
                 self.marionette.delete_session()
                 del self.marionette
 
@@ -3061,17 +3060,18 @@ toolbar#nav-bar {
 
 def run_test_harness(parser, options):
     parser.validate(options)
 
     logger_options = {
         key: value for key, value in vars(options).iteritems()
         if key.startswith('log') or key == 'valgrind'}
 
-    runner = MochitestDesktop(options.flavor, logger_options, quiet=options.quiet)
+    runner = MochitestDesktop(options.flavor, logger_options, options.stagedAddons,
+                              quiet=options.quiet)
 
     if hasattr(options, 'log'):
         delattr(options, 'log')
 
     options.runByManifest = False
     if options.flavor in ('plain', 'browser', 'chrome'):
         options.runByManifest = True
 
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/32_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/32_artifact.py
@@ -11,17 +11,16 @@ config = {
     # note: overridden by MOZHARNESS_ACTIONS in TaskCluster tasks
     'default_actions': [
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     # decides whether we want to use moz_sign_cmd in env
     'vcs_share_base': '/builds/hg-shared',
     # allows triggering of dependent jobs when --artifact try syntax is detected
-    'perfherder_extra_options': ['artifact'],
     #########################################################################
 
 
     #########################################################################
     ###### 32 bit specific ######
     'base_name': 'Linux_%(branch)s_Artifact_build',
     'platform': 'linux',
     'stage_platform': 'linux',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/32_debug_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/32_debug_artifact.py
@@ -13,17 +13,16 @@ config = {
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     # decides whether we want to use moz_sign_cmd in env
     'vcs_share_base': '/builds/hg-shared',
     # debug specific
     'debug_build': True,
     # allows triggering of test jobs when --artifact try syntax is detected
-    'perfherder_extra_options': ['artifact'],
     #########################################################################
 
 
     #########################################################################
     ###### 32 bit specific ######
     'base_name': 'Linux_%(branch)s_Artifact_build',
     'platform': 'linux',
     'stage_platform': 'linux-debug',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/64_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_artifact.py
@@ -12,17 +12,16 @@ config = {
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/gapi.data',
          'min_scm_level': 1},
         {'filename': '/builds/mozilla-desktop-geoloc-api.key',
          'secret_name': 'project/releng/gecko/build/level-%(scm-level)s/mozilla-desktop-geoloc-api.key',
          'min_scm_level': 2, 'default': 'try-build-has-no-secrets'},
     ],
     'vcs_share_base': '/builds/hg-shared',
     # allows triggering of dependent jobs when --artifact try syntax is detected
-    'perfherder_extra_options': ['artifact'],
     #########################################################################
 
 
     #########################################################################
     ###### 64 bit specific ######
     'base_name': 'Linux_x86-64_%(branch)s_Artifact_build',
     'platform': 'linux64',
     'stage_platform': 'linux64',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/64_debug_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_debug_artifact.py
@@ -6,17 +6,16 @@ config = {
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     'vcs_share_base': '/builds/hg-shared',
     # debug specific
     'debug_build': True,
     # decides whether we want to use moz_sign_cmd in env
     # allows triggering of test jobs when --artifact try syntax is detected
-    'perfherder_extra_options': ['artifact'],
     #########################################################################
 
 
     #########################################################################
     ###### 64 bit specific ######
     'base_name': 'Linux_x86-64_%(branch)s_Artifact_build',
     'platform': 'linux64',
     'stage_platform': 'linux64-debug',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/64_searchfox_and_debug.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_searchfox_and_debug.py
@@ -4,17 +4,16 @@ config = {
     # note: overridden by MOZHARNESS_ACTIONS in TaskCluster tasks
     'default_actions': [
         'clobber',
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     # decides whether we want to use moz_sign_cmd in env
     'vcs_share_base': '/builds/hg-shared',
-    'perfherder_extra_options': ['static-analysis'],
     #########################################################################
 
 
     #########################################################################
     ###### 64 bit specific ######
     'base_name': 'Linux_x86-64_%(branch)s_Searchfox',
     'platform': 'linux64',
     'stage_platform': 'linux64-searchfox-opt',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/64_stat_and_debug.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_stat_and_debug.py
@@ -4,17 +4,16 @@ config = {
     # note: overridden by MOZHARNESS_ACTIONS in TaskCluster tasks
     'default_actions': [
         'clobber',
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     # decides whether we want to use moz_sign_cmd in env
     'vcs_share_base': '/builds/hg-shared',
-    'perfherder_extra_options': ['static-analysis'],
     #########################################################################
 
 
     #########################################################################
     ###### 64 bit specific ######
     'base_name': 'Linux_x86-64_%(branch)s_Static_Analysis',
     'platform': 'linux64',
     'stage_platform': 'linux64-st-an-opt',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/64_stat_and_opt.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_stat_and_opt.py
@@ -4,17 +4,16 @@ config = {
     # note: overridden by MOZHARNESS_ACTIONS in TaskCluster tasks
     'default_actions': [
         'clobber',
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     # decides whether we want to use moz_sign_cmd in env
     'vcs_share_base': '/builds/hg-shared',
-    'perfherder_extra_options': ['static-analysis'],
     #########################################################################
 
 
     #########################################################################
     ###### 64 bit specific ######
     'base_name': 'Linux_x86-64_%(branch)s_Static_Analysis',
     'platform': 'linux64',
     'stage_platform': 'linux64-st-an',
--- a/testing/mozharness/configs/builds/releng_sub_linux_configs/64_valgrind.py
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_valgrind.py
@@ -4,17 +4,16 @@ config = {
     'default_actions': [
         'clobber',
         'build',
         'check-test',
         'valgrind-test',
         #'update',
     ],
     'stage_platform': 'linux64-valgrind',
-    'perfherder_extra_options': ['valgrind'],
     #### 64 bit build specific #####
     'env': {
         'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
         'DISPLAY': ':2',
         'HG_SHARE_BASE_DIR': '/builds/hg-shared',
         'MOZ_OBJDIR': '%(abs_obj_dir)s',
         'TINDERBOX_OUTPUT': '1',
         'TOOLTOOL_CACHE': '/builds/tooltool_cache',
--- a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_artifact.py
@@ -6,17 +6,16 @@ config = {
 
     'default_actions': [
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     # decides whether we want to use moz_sign_cmd in env
     'vcs_share_base': '/builds/hg-shared',
     # allows triggering of dependent jobs when --artifact try syntax is detected
-    'perfherder_extra_options': ['artifact'],
     #########################################################################
 
 
     #########################################################################
     ###### 64 bit specific ######
     'base_name': 'OS X 10.7 %(branch)s_Artifact_build',
     'platform': 'macosx64',
     'stage_platform': 'macosx64',
--- a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_cross_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_cross_artifact.py
@@ -1,4 +1,3 @@
 config = {
-    'perfherder_extra_options': ['artifact'],
     'mozconfig_variant': 'artifact',
 }
--- a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_cross_debug_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_cross_debug_artifact.py
@@ -1,4 +1,3 @@
 config = {
-    'perfherder_extra_options': ['artifact'],
     'mozconfig_variant': 'debug-artifact',
 }
--- a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_cross_debug_searchfox.py
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_cross_debug_searchfox.py
@@ -3,17 +3,16 @@ import os
 config = {
     'default_actions': [
         'clobber',
         'build',
         'update',  # decided by query_is_nightly()
     ],
     'stage_platform': 'macosx64-searchfox-debug',
     'debug_build': True,
-    'perfherder_extra_options': ['static-analysis'],
     #### 64 bit build specific #####
     'env': {
         'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
         'HG_SHARE_BASE_DIR': '/builds/hg-shared',
         'MOZ_OBJDIR': '%(abs_obj_dir)s',
         'TINDERBOX_OUTPUT': '1',
         'TOOLTOOL_CACHE': '/builds/tooltool_cache',
         'TOOLTOOL_HOME': '/builds',
--- a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_debug_artifact.py
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_debug_artifact.py
@@ -8,17 +8,16 @@ config = {
         'build',
     ],
     'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
     # decides whether we want to use moz_sign_cmd in env
     'vcs_share_base': '/builds/hg-shared',
     # debug specific
     'debug_build': True,
     # allows triggering of test jobs when --artifact try syntax is detected
-    'perfherder_extra_options': ['artifact'],
     #########################################################################
 
 
     #########################################################################
     ###### 64 bit specific ######
     'base_name': 'OS X 10.7 %(branch)s_Artifact_build',
     'platform': 'macosx64',
     'stage_platform': 'macosx64-debug',
--- a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_stat_and_debug.py
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_stat_and_debug.py
@@ -5,17 +5,16 @@ config = {
         'clobber',
         'build',
         'update',  # decided by query_is_nightly()
     ],
     'debug_build': True,
     'stage_platform': 'macosx64-st-an-debug',
     'tooltool_manifest_src': "browser/config/tooltool-manifests/macosx64/\
 clang.manifest",
-    'perfherder_extra_options': ['static-analysis'],
     #### 64 bit build specific #####
     'env': {
         'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
         'HG_SHARE_BASE_DIR': '/builds/hg-shared',
         'MOZ_OBJDIR': '%(abs_obj_dir)s',
         'TINDERBOX_OUTPUT': '1',
         'TOOLTOOL_CACHE': '/builds/tooltool_cache',
         'TOOLTOOL_HOME': '/builds',
--- a/testing/mozharness/configs/builds/releng_sub_windows_configs/32_stat_and_debug.py
+++ b/testing/mozharness/configs/builds/releng_sub_windows_configs/32_stat_and_debug.py
@@ -5,17 +5,16 @@ config = {
         'clobber',
         'build',
         'update',  # decided by query_is_nightly()
     ],
     'stage_platform': 'win32-st-an-debug',
     'debug_build': True,
     'tooltool_manifest_src': "browser/config/tooltool-manifests/win32/\
 releng.manifest",
-    'perfherder_extra_options': ['static-analysis'],
     #### 32 bit build specific #####
     'env': {
         'HG_SHARE_BASE_DIR': 'C:/builds/hg-shared',
         'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
         'MOZ_CRASHREPORTER_NO_REPORT': '1',
         'MOZ_OBJDIR': '%(abs_obj_dir)s',
         'PATH': 'C:/mozilla-build/nsis-3.01;C:/mozilla-build/python27;'
                 '%s' % (os.environ.get('path')),
--- a/testing/mozharness/configs/builds/taskcluster_sub_win32/clang.py
+++ b/testing/mozharness/configs/builds/taskcluster_sub_win32/clang.py
@@ -1,6 +1,5 @@
 config = {
-    'perfherder_extra_options': ['static-analysis'],
     'stage_platform': 'win32-st-an',
     'mozconfig_variant': 'clang',
     'artifact_flag_build_variant_in_try': None,
 }
--- a/testing/mozharness/configs/builds/taskcluster_sub_win32/clang_debug.py
+++ b/testing/mozharness/configs/builds/taskcluster_sub_win32/clang_debug.py
@@ -1,10 +1,9 @@
 config = {
-    'perfherder_extra_options': ['static-analysis'],
     'stage_platform': 'win32-st-an-debug',
     'debug_build': True,
     'env': {
         'XPCOM_DEBUG_BREAK': 'stack-and-abort',
     },
     'mozconfig_variant': 'clang-debug',
     'artifact_flag_build_variant_in_try': None,
 }
--- a/testing/mozharness/configs/builds/taskcluster_sub_win64/clang.py
+++ b/testing/mozharness/configs/builds/taskcluster_sub_win64/clang.py
@@ -1,6 +1,5 @@
 config = {
-    'perfherder_extra_options': ['static-analysis'],
     'stage_platform': 'win64-st-an',
     'mozconfig_variant': 'clang',
     'artifact_flag_build_variant_in_try': None,
 }
--- a/testing/mozharness/configs/builds/taskcluster_sub_win64/clang_debug.py
+++ b/testing/mozharness/configs/builds/taskcluster_sub_win64/clang_debug.py
@@ -1,10 +1,9 @@
 config = {
-    'perfherder_extra_options': ['static-analysis'],
     'stage_platform': 'win64-st-an-debug',
     'debug_build': True,
     'env': {
         'XPCOM_DEBUG_BREAK': 'stack-and-abort',
     },
     'mozconfig_variant': 'clang-debug',
     'artifact_flag_build_variant_in_try': None,
 }
--- a/testing/mozharness/configs/builds/taskcluster_sub_win64/searchfox_debug.py
+++ b/testing/mozharness/configs/builds/taskcluster_sub_win64/searchfox_debug.py
@@ -1,10 +1,9 @@
 config = {
-    'perfherder_extra_options': ['static-analysis'],
     'stage_platform': 'win64-st-an-debug',
     'debug_build': True,
     'env': {
         'XPCOM_DEBUG_BREAK': 'stack-and-abort',
         # Disable sccache because otherwise we won't index the files that
         # sccache optimizes away compilation for
         'SCCACHE_DISABLE': '1',
     },
--- a/testing/mozharness/configs/releases/bouncer_thunderbird.py
+++ b/testing/mozharness/configs/releases/bouncer_thunderbird.py
@@ -21,32 +21,16 @@ config = {
                 "macosx64": {
                     "path": "/thunderbird/releases/%(version)s/mac/:lang/Thunderbird%%20%(version)s.dmg",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/thunderbird/releases/%(version)s/win32/:lang/Thunderbird%%20Setup%%20%(version)s.exe",
                     "bouncer-platform": "win",
                 },
-                "opensolaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.opensolaris-i386.tar.bz2",
-                    "bouncer-platform": "opensolaris-i386",
-                },
-                "opensolaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.opensolaris-sparc.tar.bz2",
-                    "bouncer-platform": "opensolaris-sparc",
-                },
-                "solaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.solaris-i386.tar.bz2",
-                    "bouncer-platform": "solaris-i386",
-                },
-                "solaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.solaris-sparc.tar.bz2",
-                    "bouncer-platform": "solaris-sparc",
-                },
             },
         },
         "installer-ssl": {
             "product-name": "Thunderbird-%(version)s-SSL",
             "check_uptake": True,
             "ssl-only": True,
             "add-locales": True,
             "paths": {
@@ -61,32 +45,16 @@ config = {
                 "macosx64": {
                     "path": "/thunderbird/releases/%(version)s/mac/:lang/Thunderbird%%20%(version)s.dmg",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/thunderbird/releases/%(version)s/win32/:lang/Thunderbird%%20Setup%%20%(version)s.exe",
                     "bouncer-platform": "win",
                 },
-                "opensolaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.opensolaris-i386.tar.bz2",
-                    "bouncer-platform": "opensolaris-i386",
-                },
-                "opensolaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.opensolaris-sparc.tar.bz2",
-                    "bouncer-platform": "opensolaris-sparc",
-                },
-                "solaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.solaris-i386.tar.bz2",
-                    "bouncer-platform": "solaris-i386",
-                },
-                "solaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.solaris-sparc.tar.bz2",
-                    "bouncer-platform": "solaris-sparc",
-                },
             },
         },
         "complete-mar": {
             "product-name": "Thunderbird-%(version)s-Complete",
             "check_uptake": True,
             "ssl-only": False,
             "add-locales": True,
             "paths": {
@@ -101,32 +69,16 @@ config = {
                 "macosx64": {
                     "path": "/thunderbird/releases/%(version)s/update/mac/:lang/thunderbird-%(version)s.complete.mar",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/thunderbird/releases/%(version)s/update/win32/:lang/thunderbird-%(version)s.complete.mar",
                     "bouncer-platform": "win",
                 },
-                "opensolaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.opensolaris-i386.complete.mar",
-                    "bouncer-platform": "opensolaris-i386",
-                },
-                "opensolaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.opensolaris-sparc.complete.mar",
-                    "bouncer-platform": "opensolaris-sparc",
-                },
-                "solaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.solaris-i386.complete.mar",
-                    "bouncer-platform": "solaris-i386",
-                },
-                "solaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(version)s.en-US.solaris-sparc.complete.mar",
-                    "bouncer-platform": "solaris-sparc",
-                },
             },
         },
     },
     "partials": {
         "releases-dir": {
             "product-name": "Thunderbird-%(version)s-Partial-%(prev_version)s",
             "check_uptake": True,
             "ssl-only": False,
@@ -143,28 +95,12 @@ config = {
                 "macosx64": {
                     "path": "/thunderbird/releases/%(version)s/update/mac/:lang/thunderbird-%(prev_version)s-%(version)s.partial.mar",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/thunderbird/releases/%(version)s/update/win32/:lang/thunderbird-%(prev_version)s-%(version)s.partial.mar",
                     "bouncer-platform": "win",
                 },
-                "opensolaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(prev_version)s-%(version)s.en-US.opensolaris-i386.partial.mar",
-                    "bouncer-platform": "opensolaris-i386",
-                },
-                "opensolaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(prev_version)s-%(version)s.en-US.opensolaris-sparc.partial.mar",
-                    "bouncer-platform": "opensolaris-sparc",
-                },
-                "solaris-i386": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(prev_version)s-%(version)s.en-US.solaris-i386.partial.mar",
-                    "bouncer-platform": "solaris-i386",
-                },
-                "solaris-sparc": {
-                    "path": "/thunderbird/releases/%(version)s/contrib/solaris_tarball/thunderbird-%(prev_version)s-%(version)s.en-US.solaris-sparc.partial.mar",
-                    "bouncer-platform": "solaris-sparc",
-                },
             },
         },
     },
 }
--- a/testing/mozharness/mozharness/base/python.py
+++ b/testing/mozharness/mozharness/base/python.py
@@ -445,19 +445,16 @@ class PerfherderResourceOptionsMixin(Scr
                 self.info('instance_metadata.json not found; unable to '
                           'determine instance type')
             except Exception:
                 self.warning('error reading instance_metadata: %s' %
                              traceback.format_exc())
 
             opts.append('buildbot-%s' % instance)
 
-        # Allow configs to specify their own values.
-        opts.extend(self.config.get('perfherder_extra_options', []))
-
         return opts
 
 
 class ResourceMonitoringMixin(PerfherderResourceOptionsMixin):
     """Provides resource monitoring capabilities to scripts.
 
     When this class is in the inheritance chain, resource usage stats of the
     executing script will be recorded.
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -1731,21 +1731,16 @@ or run without that action (ie: --no-{ac
             })
 
         build_metrics = self._load_build_resources()
         if build_metrics:
             perfherder_data['suites'].append(build_metrics)
         perfherder_data['suites'].extend(self._load_sccache_stats())
 
         # Ensure all extra options for this configuration are present.
-        for opt in self.config.get('perfherder_extra_options', []):
-            for suite in perfherder_data['suites']:
-                if opt not in suite.get('extraOptions', []):
-                    suite.setdefault('extraOptions', []).append(opt)
-
         for opt in os.environ.get('PERFHERDER_EXTRA_OPTIONS', '').split():
             for suite in perfherder_data['suites']:
                 if opt not in suite.get('extraOptions', []):
                     suite.setdefault('extraOptions', []).append(opt)
 
         if self.query_is_nightly():
             for suite in perfherder_data['suites']:
                 suite.setdefault('extraOptions', []).insert(0, 'nightly')
--- a/testing/raptor/raptor/tests/raptor-firefox-tp6.ini
+++ b/testing/raptor/raptor/tests/raptor-firefox-tp6.ini
@@ -16,8 +16,24 @@ page_cycles = 25
 unit = ms
 lower_is_better = true
 alert_threshold = 2.0
 
 [raptor-firefox-tp6-amazon]
 test_url = https://www.amazon.com/s/url=search-alias%3Daps&field-keywords=laptop
 playback_recordings = mitmproxy-recording-amazon.mp
 measure = fnbpaint
+
+[raptor-firefox-tp6-facebook]
+test_url = https://www.facebook.com
+playback_recordings = mitmproxy-recording-facebook.mp
+measure = fnbpaint
+
+[raptor-firefox-tp6-google]
+test_url = https://www.google.com/#hl=en&q=barack+obama
+playback_recordings = mitmproxy-recording-google.mp
+measure = fnbpaint, hero
+hero = hero
+
+[raptor-firefox-tp6-youtube]
+test_url = https://www.youtube.com
+playback_recordings = mitmproxy-recording-youtube.mp
+measure = fnbpaint
--- a/testing/specialpowers/Makefile.in
+++ b/testing/specialpowers/Makefile.in
@@ -9,12 +9,10 @@ XPI_PKGNAME = specialpowers@mozilla.org
 include $(topsrcdir)/config/rules.mk
 
 libs-preqs = \
   $(call mkdir_deps,$(TEST_EXTENSIONS_DIR)) \
   $(NULL)
 
 libs:: $(libs-preqs)
 	(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
-	$(NSINSTALL) -D $(DEPTH)/_tests/testing/mochitest/extensions/specialpowers
-	cp -RL $(DEPTH)/testing/specialpowers/specialpowers $(DEPTH)/_tests/testing/mochitest/extensions
 	$(NSINSTALL) -D $(DEPTH)/_tests/reftest/specialpowers
 	cp -RL $(DEPTH)/testing/specialpowers/specialpowers $(DEPTH)/_tests/reftest
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -174980,40 +174980,16 @@
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-036-ref.html",
        "=="
       ]
      ],
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-040.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-040.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-041.html": [
     [
      "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-041.html",
      [
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-041-ref.html",
        "=="
       ]
@@ -175052,40 +175028,16 @@
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-042-ref.html",
        "=="
       ]
      ],
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-046.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-046.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-047.html": [
     [
      "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-047.html",
      [
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-047-ref.html",
        "=="
       ]
@@ -175496,40 +175448,16 @@
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-017-ref.html",
        "=="
       ]
      ],
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-020.html": [
     [
      "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-020.html",
      [
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-020-ref.html",
        "=="
       ]
@@ -175880,88 +175808,16 @@
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-025-ref.html",
        "=="
       ]
      ],
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-028.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-028.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-029.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-029.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031.html": [
-    [
-     "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031.html",
-     [
-      [
-       "/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/text-decor-3/ruby-text-decoration-01.html": [
     [
      "/css/vendor-imports/mozilla/mozilla-central-reftests/text-decor-3/ruby-text-decoration-01.html",
      [
       [
        "/css/vendor-imports/mozilla/mozilla-central-reftests/text-decor-3/ruby-text-decoration-01-ref.html",
        "=="
       ]
@@ -272970,36 +272826,26 @@
      {}
     ]
    ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-036-ref.html": [
     [
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039-ref.html": [
-    [
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-041-ref.html": [
     [
      {}
     ]
    ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-042-ref.html": [
     [
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045-ref.html": [
-    [
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-047-ref.html": [
     [
      {}
     ]
    ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-048-ref.html": [
     [
      {}
@@ -273155,26 +273001,16 @@
      {}
     ]
    ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-017-ref.html": [
     [
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019-ref.html": [
-    [
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-020-ref.html": [
     [
      {}
     ]
    ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-021-ref.html": [
     [
      {}
@@ -273315,36 +273151,16 @@
      {}
     ]
    ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-025-ref.html": [
     [
      {}
     ]
    ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030-ref.html": [
-    [
-     {}
-    ]
-   ],
-   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031-ref.html": [
-    [
-     {}
-    ]
-   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/sync-tests-filter": [
     [
      {}
     ]
    ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/sync-tests.sh": [
     [
      {}
@@ -519948,17 +519764,17 @@
    "2680b68f5c1720ce6d2b82d942ea21b0b1518587",
    "support"
   ],
   "css/css-scoping/shadow-assign-dynamic-001.html": [
    "c57e0fd5aa5be63e1cadf65a4e382798c5e05ec4",
    "reftest"
   ],
   "css/css-scoping/shadow-at-import.html": [
-   "40f2606177ad3143774d97060ac1bbfa9743801f",
+   "67295000ad3c24c2d9ab0ac556d34758f3ce654c",
    "reftest"
   ],
   "css/css-scoping/shadow-cascade-order-001.html": [
    "46913ea7e47811b11be898de5c3bd0a330ea6637",
    "testharness"
   ],
   "css/css-scoping/shadow-disabled-sheet-001.html": [
    "3de2d23c1b3339b964ec2c009832a3207a3b9dc4",
@@ -546044,17 +545860,17 @@
    "f131f271cb2f747e845584abcc445348e8c86521",
    "support"
   ],
   "css/cssom/StyleSheetList.html": [
    "0a1cd8ed56ac3a5b1a9556835d94fb80325199bf",
    "testharness"
   ],
   "css/cssom/at-namespace.html": [
-   "96da2dd244e9e19ff8ca1ca81b06c3ebdcee8267",
+   "cd3845557f5c40f51f7e3cbdfff52f440fe689b6",
    "testharness"
   ],
   "css/cssom/computed-style-001.html": [
    "0331a648e6b0d56f0e7365f1ff7d991ea77ce3e4",
    "testharness"
   ],
   "css/cssom/computed-style-002.html": [
    "d6579788bcfaf1d4c09324ba877a26ff95d6965d",
@@ -546176,29 +545992,29 @@
    "89c506ea58f2ad38eb9ecc1e5f422b81a45b07fa",
    "testharness"
   ],
   "css/cssom/inline-style-001.html": [
    "4c58b6153eabe796749dcaf181e03d7dce2c9c07",
    "testharness"
   ],
   "css/cssom/insertRule-charset-no-index.html": [
-   "cd3a96351a4c8dcd417fb03963f9d4fb0760c746",
+   "2be98274fe292089f381d216dc415ddc812a105f",
    "testharness"
   ],
   "css/cssom/insertRule-import-no-index.html": [
-   "ba89bad41a8d243f89ec91a0c02a34e97b378bc8",
+   "44ef5a2c490675d0088651dc101dbbb1fc83fdd1",
    "testharness"
   ],
   "css/cssom/insertRule-namespace-no-index.html": [
-   "109ed203fabac2da4279419deb34d5bc5a393d09",
+   "b9b63240c4a7bf52524b8e3dd36d6ca2ecb4bcdc",
    "testharness"
   ],
   "css/cssom/insertRule-no-index.html": [
-   "812f2b02d7694dd270b7a3e1ef205b99890ab216",
+   "825eb56d8e78bbdbd3bfb1861e6d40c245cd8f4b",
    "testharness"
   ],
   "css/cssom/insertRule-syntax-error-01.html": [
    "36f824b24dd56e20b7c524111512d8743745daaa",
    "testharness"
   ],
   "css/cssom/interfaces.html": [
    "42e325d3d7f6be7f557915072f61900ff611cef8",
@@ -554996,17 +554812,17 @@
    "77a40c794be3488c77edc9528d53755dfc7214b5",
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/reftest.list": [
    "76c907a127aec740e17d009a517acccd5d3e9fd4",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/reftest.list": [
-   "cf1af7daefe2db67dedf186e44744bbb03e537c3",
+   "c199bd6af0e3d2bbc8f87c3a768a1a8c4a17f7ab",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-border-box-001-ref.html": [
    "f60b429f37b066f9a16dceeb19bfa8ed4f2b0623",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-border-box-001.html": [
    "633f57d18aa6315c4073ecfefb9d6ab2220e0fc1",
@@ -555159,28 +554975,16 @@
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-037.html": [
    "87776a48f864360ed9857559aa0a4dce437c97ec",
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-038.html": [
    "61d2d2a07e2dcbd24a15b733f7c9d7dd3735ebd1",
    "reftest"
   ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039-ref.html": [
-   "80ba877ecdbb12e5f000dee707c5af2df4629a9b",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039.html": [
-   "0dad5434390d2500568fab4ca82d98147995f2a4",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-040.html": [
-   "7eaa3c8183209024684ff9a15f6b332802d91cb6",
-   "reftest"
-  ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-041-ref.html": [
    "dcdc6fdf1ff9c749b6ed0dabc9029e641336832b",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-041.html": [
    "ab31da83feb6a3176f4824c91b7bd4d9f299cb8d",
    "reftest"
   ],
@@ -555195,28 +554999,16 @@
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-043.html": [
    "617a6ec0b2651145a4693c3e1d5a374004d5d7e7",
    "reftest"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-044.html": [
    "c669e12f7d26e25364eab72272e5964bb989cad3",
    "reftest"
   ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045-ref.html": [
-   "538d22bff3687524f756303205ad18dac9e182cf",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045.html": [
-   "8458a75aec990428264f039927377d683e4bfef9",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-046.html": [
-   "bfb735e59e22b55f8e3c42827ca8bcbe7a612774",
-   "reftest"
-  ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-047-ref.html": [
    "4a138f3175f7d46a2a38643e9803ede5408db9cf",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-047.html": [
    "05f11451254f6955d495e23005e03bba056ca2e0",
    "reftest"
   ],
@@ -555479,32 +555271,16 @@
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-017-ref.html": [
    "4b324c5f55d23968d2fd6ec9d6b417e8eda1c848",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-017.html": [
    "628ef0b6c08230db7fca6639be71c63c0001156f",
    "reftest"
   ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018-ref.html": [
-   "e32f844e70a6f1b7c5c25fa691ba77321b557213",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018.html": [
-   "1d24d1acde6249154c8aecccf51c3f3d5bfc3f36",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019-ref.html": [
-   "7c0b699db61f02c5f197133c66439f699a80fd70",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019.html": [
-   "8ad1c65a27f12deb7b75865eb2b89905a4bbd4f0",
-   "reftest"
-  ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-020-ref.html": [
    "8e109e72edb9cb0d3c97677db8a98462da83054f",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-020.html": [
    "28a65a00f3010bc8b63ee81c4d293eb81e6daa08",
    "reftest"
   ],
@@ -555735,56 +555511,16 @@
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-025-ref.html": [
    "51cf20e605aa791e3d63c4902377aa3aca987ad7",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-025.html": [
    "9393c2e2250dfc1bf3ffdd68f2c352890d1e0ee8",
    "reftest"
   ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026-ref.html": [
-   "322ec7ee1fb932cb6f1908c376be2915d6e50459",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026.html": [
-   "9fea24b92061f9f679b9565d9040e4b29a18b4c3",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027-ref.html": [
-   "e91e35a1af0a2fa21e31f8ea3fa903f6d862ad13",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027.html": [
-   "b27883660453186155c9b761402ae4c038f2394c",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-028.html": [
-   "6753ffe3a09c2a71fc67efcd1041ed90de12e87c",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-029.html": [
-   "de39a15da093018531838cac20c59494cb6050b2",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030-ref.html": [
-   "05d0d53f6d038702733e4acd9562e35b2b992881",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030.html": [
-   "0f653c07806f0064d1583a7ddaeb171734188062",
-   "reftest"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031-ref.html": [
-   "7bbccdb15fdf67a67bbc243c342c668fbef23af8",
-   "support"
-  ],
-  "css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031.html": [
-   "217e140bdd429d6889102e43253e6fb64dca4705",
-   "reftest"
-  ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/sync-tests-filter": [
    "2c4512ec008c997d1254b5822ade227c057b7e24",
    "support"
   ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/sync-tests.sh": [
    "1c18dc5fdcddbbd08dbdc812f538a175e58892d7",
    "support"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-004.html.ini
@@ -0,0 +1,2 @@
+[shape-outside-004.html]
+  expected: FAIL
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/reftest.list
+++ b/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/reftest.list
@@ -38,24 +38,20 @@
 # Basic shape: circle()
 == shape-outside-circle-032.html shape-outside-circle-032-ref.html
 == shape-outside-circle-033.html shape-outside-circle-033-ref.html
 == shape-outside-circle-034.html shape-outside-circle-034-ref.html
 == shape-outside-circle-035.html shape-outside-circle-035-ref.html
 == shape-outside-circle-036.html shape-outside-circle-036-ref.html
 == shape-outside-circle-037.html shape-outside-circle-036-ref.html
 == shape-outside-circle-038.html shape-outside-circle-036-ref.html
-== shape-outside-circle-039.html shape-outside-circle-039-ref.html
-== shape-outside-circle-040.html shape-outside-circle-039-ref.html
 == shape-outside-circle-041.html shape-outside-circle-041-ref.html
 == shape-outside-circle-042.html shape-outside-circle-042-ref.html
 == shape-outside-circle-043.html shape-outside-circle-042-ref.html
 == shape-outside-circle-044.html shape-outside-circle-042-ref.html
-== shape-outside-circle-045.html shape-outside-circle-045-ref.html
-== shape-outside-circle-046.html shape-outside-circle-045-ref.html
 == shape-outside-circle-047.html shape-outside-circle-047-ref.html
 == shape-outside-circle-048.html shape-outside-circle-048-ref.html
 == shape-outside-circle-049.html shape-outside-circle-049-ref.html
 == shape-outside-circle-050.html shape-outside-circle-050-ref.html
 == shape-outside-circle-051.html shape-outside-circle-051-ref.html
 == shape-outside-circle-052.html shape-outside-circle-052-ref.html
 == shape-outside-circle-053.html shape-outside-circle-053-ref.html
 == shape-outside-circle-054.html shape-outside-circle-054-ref.html
@@ -81,18 +77,16 @@
 == shape-outside-ellipse-048.html shape-outside-ellipse-048-ref.html
 == shape-outside-ellipse-049.html shape-outside-ellipse-049-ref.html
 == shape-outside-ellipse-050.html shape-outside-ellipse-050-ref.html
 == shape-outside-ellipse-051.html shape-outside-ellipse-051-ref.html
 
 # Basic shape: inset()
 == shape-outside-inset-016.html shape-outside-inset-016-ref.html
 == shape-outside-inset-017.html shape-outside-inset-017-ref.html
-== shape-outside-inset-018.html shape-outside-inset-018-ref.html
-== shape-outside-inset-019.html shape-outside-inset-019-ref.html
 == shape-outside-inset-020.html shape-outside-inset-020-ref.html
 == shape-outside-inset-021.html shape-outside-inset-021-ref.html
 == shape-outside-inset-022.html shape-outside-inset-022-ref.html
 == shape-outside-inset-023.html shape-outside-inset-023-ref.html
 == shape-outside-inset-024.html shape-outside-inset-024-ref.html
 == shape-outside-inset-025.html shape-outside-inset-025-ref.html
 == shape-outside-inset-026.html shape-outside-inset-026-ref.html
 == shape-outside-inset-027.html shape-outside-inset-027-ref.html
@@ -101,14 +95,8 @@
 == shape-outside-polygon-018.html shape-outside-polygon-018-ref.html
 == shape-outside-polygon-019.html shape-outside-polygon-019-ref.html
 == shape-outside-polygon-020.html shape-outside-polygon-020-ref.html
 == shape-outside-polygon-021.html shape-outside-polygon-021-ref.html
 == shape-outside-polygon-022.html shape-outside-polygon-022-ref.html
 == shape-outside-polygon-023.html shape-outside-polygon-023-ref.html
 == shape-outside-polygon-024.html shape-outside-polygon-024-ref.html
 == shape-outside-polygon-025.html shape-outside-polygon-025-ref.html
-== shape-outside-polygon-026.html shape-outside-polygon-026-ref.html
-== shape-outside-polygon-027.html shape-outside-polygon-027-ref.html
-== shape-outside-polygon-028.html shape-outside-polygon-026-ref.html
-== shape-outside-polygon-029.html shape-outside-polygon-027-ref.html
-== shape-outside-polygon-030.html shape-outside-polygon-030-ref.html
-== shape-outside-polygon-031.html shape-outside-polygon-031-ref.html
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039-ref.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, circle(0%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 20px;
-    width: 20px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px; top: 0px; left: 0px;"></div>
-    <div class="box" style="height: 12px; top: 12px; left: 0px;"></div>
-    <div class="box" style="height: 36px; top: 24px; left: 0px;"></div>
-    <div class="box" style="height: 36px; top: 60px; left: 0px;"></div>
-    <div class="box" style="height: 12px; top: 96px; left: 0px;"></div>
-    <div class="box" style="height: 12px; top: 108px; left: 0px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-039.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, circle(0%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-039-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the basic shape circle(0%) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    shape-outside: circle(0%) border-box;
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-040.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, circle(closest-side at left center) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-039-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the basic shape circle(closest-side at left center) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    shape-outside: circle(closest-side at left center) border-box;
-    clip-path: circle(closest-side at left center) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045-ref.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, circle(0%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    direction: rtl;
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 20px;
-    width: 20px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px; top: 0px; right: 0px;"></div>
-    <div class="box" style="height: 12px; top: 12px; right: 0px;"></div>
-    <div class="box" style="height: 36px; top: 24px; right: 0px;"></div>
-    <div class="box" style="height: 36px; top: 60px; right: 0px;"></div>
-    <div class="box" style="height: 12px; top: 96px; right: 0px;"></div>
-    <div class="box" style="height: 12px; top: 108px; right: 0px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-045.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, circle(0%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-045-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the basic shape circle(0%) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    shape-outside: circle(0%) border-box;
-    clip-path: circle(0%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-046.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, circle(closest-side at right center) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-circle-045-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the basic shape circle(closest-side at right center) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    shape-outside: circle(closest-side at right center) border-box;
-    clip-path: circle(closest-side at right center) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 10px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 12px;"></div>
-    <div class="box" style="height: 12px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018-ref.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, inset(50%) reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px; top: 0; left: 0;"></div>
-    <div class="box" style="height: 36px; top: 24px; left: 0;"></div>
-    <div class="box" style="height: 36px; top: 60px; left: 0;"></div>
-    <div class="box" style="height: 24px; top: 96px; left: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-018.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, inset(50%)</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-inset-018-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the basic shape inset(50%) value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    shape-outside: inset(50%);
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 24px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019-ref.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, inset(50%) reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    direction: rtl;
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px; top: 0; right: 0;"></div>
-    <div class="box" style="height: 36px; top: 24px; right: 0;"></div>
-    <div class="box" style="height: 36px; top: 60px; right: 0;"></div>
-    <div class="box" style="height: 24px; top: 96px; right: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-inset-019.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, inset(50%)</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-inset-019-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the basic shape inset(50%) value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    shape-outside: inset(50%);
-    clip-path: inset(50%);
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 200px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 24px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 36px;"></div>
-    <div class="box" style="height: 24px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026-ref.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(60px 20px, 100px 60px) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px; top: 0; left: 0;"></div>
-    <div class="long box" style="height: 30px; top: 30px; left: 0;"></div>
-    <div class="long box" style="height: 20px; top: 60px; left: 0;"></div>
-    <div class="long box" style="height: 20px; top: 80px; left: 0;"></div>
-    <div class="long box" style="height: 30px; top: 100px; left: 0;"></div>
-    <div class="long box" style="height: 30px; top: 130px; left: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-026.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(60px 20px, 100px 60px) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-026-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the polygon(60px 20px, 100px 60px) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Less than three vertices, resulting an empty area. */
-    shape-outside: polygon(60px 20px, 100px 60px) border-box;
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027-ref.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(60px 20px, 100px 60px) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px; top: 0; right: 0;"></div>
-    <div class="long box" style="height: 30px; top: 30px; right: 0;"></div>
-    <div class="long box" style="height: 20px; top: 60px; right: 0;"></div>
-    <div class="long box" style="height: 20px; top: 80px; right: 0;"></div>
-    <div class="long box" style="height: 30px; top: 100px; right: 0;"></div>
-    <div class="long box" style="height: 30px; top: 130px; right: 0;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-027.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(60px 20px, 100px 60px) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-027-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the polygon(60px 20px, 100px 60px) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Less than three vertices, resulting an empty area. */
-    shape-outside: polygon(60px 20px, 100px 60px) border-box;
-    clip-path: polygon(60px 20px, 100px 60px) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-028.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-026-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the left float shape defines an empty float area by the polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* All vertices are collinear, resulting an empty area. */
-    shape-outside: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    clip-path: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-029.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-027-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the right float shape defines an empty float area by the polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* All vertices are collinear, resulting an empty area. */
-    shape-outside: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    clip-path: polygon(50% 50%, 0% 0%, 20% 20%, 100% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-
-  .long {
-    width: 200px;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 20px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-    <div class="long box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030-ref.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* Omit shape-outside */
-    clip-path: polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px; top: 0; left: 80px;"></div>
-    <div class="box" style="height: 30px; top: 30px; left: 80px;"></div>
-    <div class="box" style="height: 20px; top: 60px; left: 80px;"></div>
-    <div class="box" style="height: 20px; top: 80px; left: 80px;"></div>
-    <div class="box" style="height: 30px; top: 100px; left: 80px;"></div>
-    <div class="box" style="height: 30px; top: 130px; left: 80px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-030.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float left, polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-030-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box value.">
-  <style>
-  .container {
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: left;
-    /* polygon contains horizontal lines. */
-    shape-outside: polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box;
-    clip-path: polygon(0% 0%, 50% 0%, 100% 0%, 50% 0%, 50% 100%, 100% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031-ref.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box reference</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <style>
-  .container {
-    direction: rtl;
-    position: absolute;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* Omit shape-outside */
-    clip-path: polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    border: 20px solid lightgreen;
-    margin: 20px;
-    background-color: orange;
-  }
-
-  .box {
-    position: absolute;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px; top: 0; right: 80px;"></div>
-    <div class="box" style="height: 30px; top: 30px; right: 80px;"></div>
-    <div class="box" style="height: 20px; top: 60px; right: 80px;"></div>
-    <div class="box" style="height: 20px; top: 80px; right: 80px;"></div>
-    <div class="box" style="height: 30px; top: 100px; right: 80px;"></div>
-    <div class="box" style="height: 30px; top: 130px; right: 80px;"></div>
-  </body>
-</html>
deleted file mode 100644
--- a/testing/web-platform/tests/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-031.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-
-<html>
-  <meta charset="utf-8">
-  <title>CSS Shape Test: float right, polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box</title>
-  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
-  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
-  <link rel="help" href="https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes">
-  <link rel="match" href="shape-outside-polygon-031-ref.html">
-  <meta name="flags" content="">
-  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box value.">
-  <style>
-  .container {
-    direction: rtl;
-    width: 200px;
-    line-height: 0;
-  }
-
-  .shape {
-    float: right;
-    /* polygon contains horizontal lines. */
-    shape-outside: polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box;
-    clip-path: polygon(100% 0%, 50% 0%, 0% 0%, 50% 0%, 50% 100%, 0% 100%, 50% 100%) border-box;
-    box-sizing: content-box;
-    height: 40px;
-    width: 40px;
-    padding: 20px;
-    margin: 20px;
-    border: 20px solid lightgreen;
-    background-color: orange;
-  }
-
-  .box {
-    display: inline-block;
-    width: 80px;
-    background-color: blue;
-  }
-  </style>
-
-  <body class="container">
-    <div class="shape"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 20px;"></div>
-    <div class="box" style="height: 30px;"></div>
-    <div class="box" style="height: 30px;"></div>
-  </body>
-</html>
--- a/toolkit/components/places/PlacesSyncUtils.jsm
+++ b/toolkit/components/places/PlacesSyncUtils.jsm
@@ -77,20 +77,19 @@ XPCOMUtils.defineLazyGetter(this, "ROOTS
 
 const HistorySyncUtils = PlacesSyncUtils.history = Object.freeze({
   SYNC_ID_META_KEY: "sync/history/syncId",
   LAST_SYNC_META_KEY: "sync/history/lastSync",
 
   /**
    * Returns the current history sync ID, or `""` if one isn't set.
    */
-  async getSyncId() {
-    let syncId = await PlacesUtils.metadata.get(
-      HistorySyncUtils.SYNC_ID_META_KEY);
-    return syncId || "";
+  getSyncId() {
+    return PlacesUtils.metadata.get(
+      HistorySyncUtils.SYNC_ID_META_KEY, "");
   },
 
   /**
    * Assigns a new sync ID. This is called when we sync for the first time with
    * a new account, and when we're the first to sync after a node reassignment.
    *
    * @return {Promise} resolved once the ID has been updated.
    * @resolves to the new sync ID.
@@ -119,17 +118,17 @@ const HistorySyncUtils = PlacesSyncUtils
   async ensureCurrentSyncId(newSyncId) {
     if (!newSyncId || typeof newSyncId != "string") {
       throw new TypeError("Invalid new history sync ID");
     }
     await PlacesUtils.withConnectionWrapper(
       "HistorySyncUtils: ensureCurrentSyncId",
       async function(db) {
         let existingSyncId = await PlacesUtils.metadata.getWithConnection(
-          db, HistorySyncUtils.SYNC_ID_META_KEY);
+          db, HistorySyncUtils.SYNC_ID_META_KEY, "");
 
         if (existingSyncId == newSyncId) {
           HistorySyncLog.trace("History sync ID up-to-date",
                                { existingSyncId });
           return;
         }
 
         HistorySyncLog.info("History sync ID changed; resetting metadata",
@@ -142,18 +141,18 @@ const HistorySyncUtils = PlacesSyncUtils
   },
 
   /**
    * Returns the last sync time, in seconds, for the history collection, or 0
    * if history has never synced before.
    */
   async getLastSync() {
     let lastSync = await PlacesUtils.metadata.get(
-      HistorySyncUtils.LAST_SYNC_META_KEY);
-    return lastSync ? lastSync / 1000 : 0;
+      HistorySyncUtils.LAST_SYNC_META_KEY, 0);
+    return lastSync / 1000;
   },
 
   /**
    * Updates the history collection last sync time.
    *
    * @param lastSyncSeconds
    *        The collection last sync time, in seconds, as a number or string.
    */
@@ -402,30 +401,29 @@ const BookmarkSyncUtils = PlacesSyncUtil
 
   get ROOTS() {
     return ROOTS;
   },
 
   /**
    * Returns the current bookmarks sync ID, or `""` if one isn't set.
    */
-  async getSyncId() {
-    let syncId = await PlacesUtils.metadata.get(
-      BookmarkSyncUtils.SYNC_ID_META_KEY);
-    return syncId || "";
+  getSyncId() {
+    return PlacesUtils.metadata.get(
+      BookmarkSyncUtils.SYNC_ID_META_KEY, "");
   },
 
   /**
    * Indicates if the bookmarks engine should erase all bookmarks on the server
    * and all other clients, because the user manually restored their bookmarks
    * from a backup on this client.
    */
   async shouldWipeRemote() {
     let shouldWipeRemote = await PlacesUtils.metadata.get(
-      BookmarkSyncUtils.WIPE_REMOTE_META_KEY);
+      BookmarkSyncUtils.WIPE_REMOTE_META_KEY, false);
     return !!shouldWipeRemote;
   },
 
   /**
    * Assigns a new sync ID, bumps the change counter, and flags all items as
    * "NEW" for upload. This is called when we sync for the first time with a
    * new account, when we're the first to sync after a node reassignment, and
    * on the first sync after a manual restore.
@@ -465,17 +463,17 @@ const BookmarkSyncUtils = PlacesSyncUtil
   async ensureCurrentSyncId(newSyncId) {
     if (!newSyncId || typeof newSyncId != "string") {
       throw new TypeError("Invalid new bookmarks sync ID");
     }
     await PlacesUtils.withConnectionWrapper(
       "BookmarkSyncUtils: ensureCurrentSyncId",
       async function(db) {
         let existingSyncId = await PlacesUtils.metadata.getWithConnection(
-          db, BookmarkSyncUtils.SYNC_ID_META_KEY);
+          db, BookmarkSyncUtils.SYNC_ID_META_KEY, "");
 
         // If we don't have a sync ID, take the server's without resetting
         // sync statuses.
         if (!existingSyncId) {
           BookmarkSyncLog.info("Taking new bookmarks sync ID", { newSyncId });
           await db.executeTransaction(() => setBookmarksSyncId(db, newSyncId));
           return;
         }
@@ -502,18 +500,18 @@ const BookmarkSyncUtils = PlacesSyncUtil
   },
 
   /**
    * Returns the last sync time, in seconds, for the bookmarks collection, or 0
    * if bookmarks have never synced before.
    */
   async getLastSync() {
     let lastSync = await PlacesUtils.metadata.get(
-      BookmarkSyncUtils.LAST_SYNC_META_KEY);
-    return lastSync ? lastSync / 1000 : 0;
+      BookmarkSyncUtils.LAST_SYNC_META_KEY, 0);
+    return lastSync / 1000;
   },
 
   /**
    * Updates the bookmarks collection last sync time.
    *
    * @param lastSyncSeconds
    *        The collection last sync time, in seconds, as a number or string.
    */
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -1983,28 +1983,35 @@ XPCOMUtils.defineLazyGetter(this, "gAsyn
  * cached in memory for faster lookups.
  *
  * Since some consumers set metadata as part of an existing operation or active
  * transaction, the API also exposes a `*withConnection` variant for each
  * method that takes an open database connection.
  */
 PlacesUtils.metadata = {
   cache: new Map(),
+  jsonPrefix: "data:application/json;base64,",
 
   /**
    * Returns the value associated with a metadata key.
    *
    * @param  {String} key
    *         The metadata key to look up.
-   * @return {*}
-   *         The value associated with the key, or `null` if not set.
+   * @param  {String|Object|Array} defaultValue
+   *         Optional. The default value to return if the value is not present,
+   *         or cannot be parsed.
+   * @resolves {*}
+   *         The value associated with the key, or the defaultValue if there is one.
+   * @rejects
+   *         Rejected if the value is not found or it cannot be parsed
+   *         and there is no defaultValue.
    */
-  get(key) {
+  get(key, defaultValue) {
     return PlacesUtils.withConnectionWrapper("PlacesUtils.metadata.get",
-      db => this.getWithConnection(db, key));
+      db => this.getWithConnection(db, key, defaultValue));
   },
 
   /**
    * Sets the value for a metadata key.
    *
    * @param {String} key
    *        The metadata key to update.
    * @param {*}
@@ -2021,47 +2028,71 @@ PlacesUtils.metadata = {
    * @param {String...}
    *        One or more metadata keys to remove.
    */
   delete(...keys) {
     return PlacesUtils.withConnectionWrapper("PlacesUtils.metadata.delete",
       db => this.deleteWithConnection(db, ...keys));
   },
 
-  async getWithConnection(db, key) {
+  async getWithConnection(db, key, defaultValue) {
     key = this.canonicalizeKey(key);
     if (this.cache.has(key)) {
       return this.cache.get(key);
     }
     let rows = await db.executeCached(`
       SELECT value FROM moz_meta WHERE key = :key`,
       { key });
     let value = null;
     if (rows.length) {
       let row = rows[0];
       let rawValue = row.getResultByName("value");
       // Convert blobs back to `Uint8Array`s.
-      value = row.getTypeOfIndex(0) == row.VALUE_TYPE_BLOB ?
-              new Uint8Array(rawValue) : rawValue;
+      if (row.getTypeOfIndex(0) == row.VALUE_TYPE_BLOB) {
+        value = new Uint8Array(rawValue);
+      } else if (typeof rawValue == "string" &&
+                 rawValue.startsWith(this.jsonPrefix)) {
+        try {
+          value = JSON.parse(this._base64Decode(rawValue.substr(this.jsonPrefix.length)));
+        } catch (ex) {
+          if (defaultValue !== undefined) {
+            value = defaultValue;
+          } else {
+            throw ex;
+          }
+        }
+      } else {
+        value = rawValue;
+      }
+    } else if (defaultValue !== undefined) {
+      value = defaultValue;
+    } else {
+      throw new Error(`No data stored for key ${key}`);
     }
     this.cache.set(key, value);
     return value;
   },
 
   async setWithConnection(db, key, value) {
     if (value === null) {
       await this.deleteWithConnection(db, key);
       return;
     }
+
+    let cacheValue = value;
+    if (typeof value == "object" && ChromeUtils.getClassName(value) != "Uint8Array") {
+      value = this.jsonPrefix + this._base64Encode(JSON.stringify(value));
+    }
+
     key = this.canonicalizeKey(key);
     await db.executeCached(`
       REPLACE INTO moz_meta (key, value)
       VALUES (:key, :value)`,
       { key, value });
-    this.cache.set(key, value);
+    this.cache.set(key, cacheValue);
   },
 
   async deleteWithConnection(db, ...keys) {
     keys = keys.map(this.canonicalizeKey);
     if (!keys.length) {
       return;
     }
     await db.execute(`
@@ -2074,16 +2105,27 @@ PlacesUtils.metadata = {
   },
 
   canonicalizeKey(key) {
     if (typeof key != "string" || !/^[a-zA-Z0-9\/]+$/.test(key)) {
       throw new TypeError("Invalid metadata key: " + key);
     }
     return key.toLowerCase();
   },
+
+  _base64Encode(str) {
+    return ChromeUtils.base64URLEncode(
+      new TextEncoder("utf-8").encode(str),
+      {pad: true});
+  },
+
+  _base64Decode(str) {
+    return new TextDecoder("utf-8").decode(
+      ChromeUtils.base64URLDecode(str, {padding: "require"}));
+  },
 };
 
 /**
  * Keywords management API.
  * Sooner or later these keywords will merge with search aliases, this is an
  * interim API that should then be replaced by a unified one.
  * Keywords are associated with URLs and can have POST data.
  * The relations between URLs and keywords are the following:
--- a/toolkit/components/places/tests/unit/test_metadata.js
+++ b/toolkit/components/places/tests/unit/test_metadata.js
@@ -22,37 +22,48 @@ add_task(async function test_metadata() 
 
   await PlacesUtils.metadata.set("test/string", "hi");
   Assert.equal(await PlacesUtils.metadata.get("test/string"), "hi",
     "Should store new string value");
   await PlacesUtils.metadata.cache.clear();
   Assert.equal(await PlacesUtils.metadata.get("test/string"), "hi",
     "Should return string value after clearing cache");
 
+  await Assert.rejects(PlacesUtils.metadata.get("test/nonexistent"),
+    /No data stored for key test\/nonexistent/,
+    "Should reject for a non-existent key and no default value.");
+  Assert.equal(await PlacesUtils.metadata.get("test/nonexistent", "defaultValue"), "defaultValue",
+    "Should return the default value for a non-existent key.");
+
   // Values are untyped; it's OK to store a value of a different type for the
   // same key.
   await PlacesUtils.metadata.set("test/string", 111);
   Assert.strictEqual(await PlacesUtils.metadata.get("test/string"), 111,
     "Should replace string with integer");
   await PlacesUtils.metadata.set("test/string", null);
-  Assert.strictEqual(await PlacesUtils.metadata.get("test/string"), null,
+  await Assert.rejects(PlacesUtils.metadata.get("test/string"),
+    /No data stored for key test\/string/,
     "Should clear value when setting to NULL");
 
   await PlacesUtils.metadata.delete("test/string", "test/boolean");
-  Assert.strictEqual(await PlacesUtils.metadata.get("test/string"), null,
+  await Assert.rejects(PlacesUtils.metadata.get("test/string"),
+    /No data stored for key test\/string/,
     "Should delete string value");
-  Assert.strictEqual(await PlacesUtils.metadata.get("test/boolean"), null,
+  await Assert.rejects(PlacesUtils.metadata.get("test/boolean"),
+    /No data stored for key test\/boolean/,
     "Should delete Boolean value");
   Assert.strictEqual(await PlacesUtils.metadata.get("test/integer"), 123,
     "Should keep undeleted integer value");
 
   await PlacesTestUtils.clearMetadata();
-  Assert.strictEqual(await PlacesUtils.metadata.get("test/integer"), null,
+  await Assert.rejects(PlacesUtils.metadata.get("test/integer"),
+    /No data stored for key test\/integer/,
     "Should clear integer value");
-  Assert.strictEqual(await PlacesUtils.metadata.get("test/double"), null,
+  await Assert.rejects(PlacesUtils.metadata.get("test/double"),
+    /No data stored for key test\/double/,
     "Should clear double value");
 });
 
 add_task(async function test_metadata_canonical_keys() {
   await PlacesUtils.metadata.set("Test/Integer", 123);
   Assert.strictEqual(await PlacesUtils.metadata.get("tEsT/integer"), 123,
     "New keys should be case-insensitive");
   await PlacesUtils.metadata.set("test/integer", 456);
@@ -75,22 +86,81 @@ add_task(async function test_metadata_ca
 
 add_task(async function test_metadata_blobs() {
   let blob = new Uint8Array([1, 2, 3]);
   await PlacesUtils.metadata.set("test/blob", blob);
 
   let sameBlob = await PlacesUtils.metadata.get("test/blob");
   Assert.equal(ChromeUtils.getClassName(sameBlob), "Uint8Array",
     "Should cache typed array for blob value");
-  deepEqual(sameBlob, blob,
+  Assert.deepEqual(sameBlob, blob,
     "Should store new blob value");
 
   info("Remove blob from cache");
   await PlacesUtils.metadata.cache.clear();
 
   let newBlob = await PlacesUtils.metadata.get("test/blob");
   Assert.equal(ChromeUtils.getClassName(newBlob), "Uint8Array",
     "Should inflate blob into typed array");
-  deepEqual(newBlob, blob,
+  Assert.deepEqual(newBlob, blob,
     "Should return same blob after clearing cache");
 
   await PlacesTestUtils.clearMetadata();
 });
+
+add_task(async function test_metadata_arrays() {
+  let array = [1, 2, 3, "\u2713 \u00E0 la mode"];
+  await PlacesUtils.metadata.set("test/array", array);
+
+  let sameArray = await PlacesUtils.metadata.get("test/array");
+  Assert.ok(Array.isArray(sameArray), "Should cache array for array value");
+  Assert.deepEqual(sameArray, array,
+    "Should store new array value");
+
+  info("Remove array from cache");
+  await PlacesUtils.metadata.cache.clear();
+
+  let newArray = await PlacesUtils.metadata.get("test/array");
+  Assert.ok(Array.isArray(newArray), "Should inflate into array");
+  Assert.deepEqual(newArray, array,
+    "Should return same array after clearing cache");
+
+  await PlacesTestUtils.clearMetadata();
+});
+
+add_task(async function test_metadata_objects() {
+  let object = {foo: 123, bar: "test", meow: "\u2713 \u00E0 la mode"};
+  await PlacesUtils.metadata.set("test/object", object);
+
+  let sameObject = await PlacesUtils.metadata.get("test/object");
+  Assert.equal(typeof sameObject, "object", "Should cache object for object value");
+  Assert.deepEqual(sameObject, object,
+    "Should store new object value");
+
+  info("Remove object from cache");
+  await PlacesUtils.metadata.cache.clear();
+
+  let newObject = await PlacesUtils.metadata.get("test/object");
+  Assert.equal(typeof newObject, "object", "Should inflate into object");
+  Assert.deepEqual(newObject, object,
+    "Should return same object after clearing cache");
+
+  await PlacesTestUtils.clearMetadata();
+});
+
+add_task(async function test_metadata_unparsable() {
+  await PlacesUtils.withConnectionWrapper("test_medata", db => {
+    let data = PlacesUtils.metadata._base64Encode("{hjjkhj}");
+
+    return db.execute(`
+      INSERT INTO moz_meta (key, value)
+      VALUES ("test/unparsable", "data:application/json;base64,${data}")
+    `);
+  });
+
+  await Assert.rejects(PlacesUtils.metadata.get("test/unparsable"),
+    /SyntaxError: JSON.parse/,
+    "Should reject for an unparsable value with no default");
+  Assert.deepEqual(await PlacesUtils.metadata.get("test/unparsable", {foo: 1}),
+    {foo: 1}, "Should return the default when encountering an unparsable value.");
+
+  await PlacesTestUtils.clearMetadata();
+});
--- a/toolkit/crashreporter/tools/symbolstore.py
+++ b/toolkit/crashreporter/tools/symbolstore.py
@@ -628,16 +628,21 @@ class Dumper:
                     "subtests": [{
                         "name": "num_static_constructors",
                         "value": ctors,
                         "alertChangeType": "absolute",
                         "alertThreshold": 3
                     }]}
                 ]
             }
+            for opt in os.environ.get('PERFHERDER_EXTRA_OPTIONS', '').split():
+                for suite in perfherder_data['suites']:
+                    if opt not in suite.get('extraOptions', []):
+                        suite.setdefault('extraOptions', []).append(opt)
+
             print('PERFHERDER_DATA: %s' % json.dumps(perfherder_data),
                   file=sys.stderr)
 
         elapsed = time.time() - t_start
         print('Finished processing %s in %.2fs' % (file, elapsed),
               file=sys.stderr)
 
 # Platform-specific subclasses.  For the most part, these just have
new file mode 100644
--- /dev/null
+++ b/tools/docs/Pipfile
@@ -0,0 +1,17 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+backports-abc = "==0.5"
+boto3 = "==1.4.4"
+livereload = "==2.5.1"
+recommonmark = "==0.4.0"
+singledispatch = "==3.4.0.3"
+sphinx = "==1.6.7"
+sphinx-js = "==2.1"
+sphinx-rtd-theme = "==0.2.4"
+
+[requires]
+python_version = "2.7"
new file mode 100644
--- /dev/null
+++ b/tools/docs/Pipfile.lock
@@ -0,0 +1,263 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "bd4144a913361336dadcb4eb82b9fc24c6f36b49f7248ab222a1fb688932a1bb"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "2.7"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "alabaster": {
+            "hashes": [
+                "sha256:2eef172f44e8d301d25aff8068fddd65f767a3f04b5f15b0f4922f113aa1c732",
+                "sha256:37cdcb9e9954ed60912ebc1ca12a9d12178c26637abdf124e3cde2341c257fe0"
+            ],
+            "version": "==0.7.10"
+        },
+        "babel": {
+            "hashes": [
+                "sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669",
+                "sha256:8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23"
+            ],
+            "version": "==2.6.0"
+        },
+        "backports-abc": {
+            "hashes": [
+                "sha256:033be54514a03e255df75c5aee8f9e672f663f93abb723444caec8fe43437bde",
+                "sha256:52089f97fe7a9aa0d3277b220c1d730a85aefd64e1b2664696fe35317c5470a7"
+            ],
+            "index": "pypi",
+            "version": "==0.5"
+        },
+        "boto3": {
+            "hashes": [
+                "sha256:5050c29353fec97301116386f469fa5858ccf47201623b53cf9f74e603bda52f",
+                "sha256:518f724c4758e5a5bed114fbcbd1cf470a15306d416ff421a025b76f1d390939"
+            ],
+            "index": "pypi",
+            "version": "==1.4.4"
+        },
+        "botocore": {
+            "hashes": [
+                "sha256:26917b37fe993dc23dd1eb69c3208500fd057dc8b2e18b7c97bb26ae08ce7aa8",
+                "sha256:471d95025408cfafa30133991f35d2839fcc12a86198465a507499790ee49b4b"
+            ],
+            "version": "==1.5.95"
+        },
+        "certifi": {
+            "hashes": [
+                "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
+                "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
+            ],
+            "version": "==2018.4.16"
+        },
+        "chardet": {
+            "hashes": [
+                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+            ],
+            "version": "==3.0.4"
+        },
+        "commonmark": {
+            "hashes": [
+                "sha256:34d73ec8085923c023930dfc0bcd1c4286e28a2a82de094bb72fabcc0281cbe5"
+            ],
+            "version": "==0.5.4"
+        },
+        "docutils": {
+            "hashes": [
+                "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
+                "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
+                "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
+            ],
+            "version": "==0.14"
+        },
+        "futures": {
+            "hashes": [
+                "sha256:9ec02aa7d674acb8618afb127e27fde7fc68994c0437ad759fa094a574adb265",
+                "sha256:ec0a6cb848cc212002b9828c3e34c675e0c9ff6741dc445cab6fdd4e1085d1f1"
+            ],
+            "markers": "python_version == '2.6' or python_version == '2.7'",
+            "version": "==3.2.0"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f",
+                "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4"
+            ],
+            "version": "==2.6"
+        },
+        "imagesize": {
+            "hashes": [
+                "sha256:3620cc0cadba3f7475f9940d22431fc4d407269f1be59ec9b8edcca26440cf18",
+                "sha256:5b326e4678b6925158ccc66a9fa3122b6106d7c876ee32d7de6ce59385b96315"
+            ],
+            "version": "==1.0.0"
+        },
+        "jinja2": {
+            "hashes": [
+                "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
+                "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
+            ],
+            "version": "==2.10"
+        },
+        "jmespath": {
+            "hashes": [
+                "sha256:6a81d4c9aa62caf061cb517b4d9ad1dd300374cd4706997aff9cd6aedd61fc64",
+                "sha256:f11b4461f425740a1d908e9a3f7365c3d2e569f6ca68a2ff8bc5bcd9676edd63"
+            ],
+            "version": "==0.9.3"
+        },
+        "livereload": {
+            "hashes": [
+                "sha256:422de10d7ea9467a1ba27cbaffa84c74b809d96fb1598d9de4b9b676adf35e2c",
+                "sha256:5ed6506f5d526ee712da9f3739c27714e6f3376f3e481728d298efceae0ec83a"
+            ],
+            "index": "pypi",
+            "version": "==2.5.1"
+        },
+        "markupsafe": {
+            "hashes": [
+                "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
+            ],
+            "version": "==1.0"
+        },
+        "parsimonious": {
+            "hashes": [
+                "sha256:396d424f64f834f9463e81ba79a331661507a21f1ed7b644f7f6a744006fd938"
+            ],
+            "version": "==0.7.0"
+        },
+        "pygments": {
+            "hashes": [
+                "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
+                "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
+            ],
+            "version": "==2.2.0"
+        },
+        "python-dateutil": {
+            "hashes": [
+                "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0",
+                "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8"
+            ],
+            "version": "==2.7.3"
+        },
+        "pytz": {
+            "hashes": [
+                "sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555",
+                "sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749"
+            ],
+            "version": "==2018.4"
+        },
+        "recommonmark": {
+            "hashes": [
+                "sha256:6e29c723abcf5533842376d87c4589e62923ecb6002a8e059eb608345ddaff9d",
+                "sha256:cd8bf902e469dae94d00367a8197fb7b81fcabc9cfb79d520e0d22d0fbeaa8b7"
+            ],
+            "index": "pypi",
+            "version": "==0.4.0"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
+                "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
+            ],
+            "version": "==2.18.4"
+        },
+        "s3transfer": {
+            "hashes": [
+                "sha256:90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1",
+                "sha256:c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f"
+            ],
+            "version": "==0.1.13"
+        },
+        "singledispatch": {
+            "hashes": [
+                "sha256:5b06af87df13818d14f08a028e42f566640aef80805c3b50c5056b086e3c2b9c",
+                "sha256:833b46966687b3de7f438c761ac475213e53b306740f1abfaa86e1d1aae56aa8"
+            ],
+            "index": "pypi",
+            "version": "==3.4.0.3"
+        },
+        "six": {
+            "hashes": [
+                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
+                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
+            ],
+            "version": "==1.11.0"
+        },
+        "snowballstemmer": {
+            "hashes": [
+                "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128",
+                "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"
+            ],
+            "version": "==1.2.1"
+        },
+        "sphinx": {
+            "hashes": [
+                "sha256:832bed0dc6099c2abca957d90ff55bc1a6ec4425c13fc144adbae68a970e3775",
+                "sha256:d5b91b4dad56ffc9f19425ebaa7bc23290a0a2e9035781d5bc54822384663277"
+            ],
+            "index": "pypi",
+            "version": "==1.6.7"
+        },
+        "sphinx-js": {
+            "hashes": [
+                "sha256:8c12b2b7ccc6941cbc7c70e4fada903e2947376b48ce07cbb72c72d88f0eef1e"
+            ],
+            "index": "pypi",
+            "version": "==2.1"
+        },
+        "sphinx-rtd-theme": {
+            "hashes": [
+                "sha256:2df74b8ff6fae6965c527e97cca6c6c944886aae474b490e17f92adfbe843417",
+                "sha256:62ee4752716e698bad7de8a18906f42d33664128eea06c46b718fc7fbd1a9f5c"
+            ],
+            "index": "pypi",
+            "version": "==0.2.4"
+        },
+        "sphinxcontrib-websupport": {
+            "hashes": [
+                "sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9",
+                "sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2"
+            ],
+            "version": "==1.0.1"
+        },
+        "tornado": {
+            "hashes": [
+                "sha256:1b83d5c10550f2653380b4c77331d6f8850f287c4f67d7ce1e1c639d9222fbc7",
+                "sha256:408d129e9d13d3c55aa73f8084aa97d5f90ed84132e38d6932e63a67d5bec563",
+                "sha256:88ce0282cce70df9045e515f578c78f1ebc35dcabe1d70f800c3583ebda7f5f5",
+                "sha256:ba9fbb249ac5390bff8a1d6aa4b844fd400701069bda7d2e380dfe2217895101",
+                "sha256:c050089173c2e9272244bccfb6a8615fb9e53b79420a5551acfa76094ecc3111"
+            ],
+            "version": "==5.0.2"
+        },
+        "typing": {
+            "hashes": [
+                "sha256:3a887b021a77b292e151afb75323dea88a7bc1b3dfa92176cff8e44c8b68bddf",
+                "sha256:b2c689d54e1144bbcfd191b0832980a21c2dbcf7b5ff7a66248a60c90e951eb8",
+                "sha256:d400a9344254803a2368533e4533a4200d21eb7b6b729c173bc38201a74db3f2"
+            ],
+            "markers": "python_version < '3.5'",
+            "version": "==3.6.4"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b",
+                "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
+            ],
+            "version": "==1.22"
+        }
+    },
+    "develop": {}
+}
--- a/tools/docs/mach_commands.py
+++ b/tools/docs/mach_commands.py
@@ -53,19 +53,17 @@ class Documentation(MachCommandBase):
                      help='Upload generated files to S3.')
     def build_docs(self, path=None, fmt='html', outdir=None, auto_open=True,
                    serve=True, http=None, archive=False, upload=False):
         try:
             which.which('jsdoc')
         except which.WhichError:
             return die('jsdoc not found - please install from npm.')
 
-        self._activate_virtualenv()
-        self.virtualenv_manager.install_pip_requirements(
-            os.path.join(here, 'requirements.txt'), quiet=True)
+        self.activate_pipenv(os.path.join(here, 'Pipfile'))
 
         import webbrowser
         from livereload import Server
         from moztreedocs.package import create_tarball
 
         outdir = outdir or os.path.join(self.topobjdir, 'docs')
         savedir = os.path.join(outdir, fmt)
 
@@ -171,18 +169,16 @@ class Documentation(MachCommandBase):
             return path
 
         for d in valid_doc_dirs:
             p = os.path.join(path, d)
             if os.path.isdir(p):
                 return p
 
     def _s3_upload(self, root, project, version=None):
-        self.virtualenv_manager.install_pip_package('boto3==1.4.4')
-
         from moztreedocs.package import distribution_files
         from moztreedocs.upload import s3_upload
 
         # Files are uploaded to multiple locations:
         #
         # <project>/latest
         # <project>/<version>
         #
deleted file mode 100644
--- a/tools/docs/requirements.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-docutils==0.14 \
-    --hash=sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6 \
-    --hash=sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6 \
-    --hash=sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274
-Jinja2==2.10 \
-    --hash=sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd \
-    --hash=sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4
-packaging==16.8 \
-    --hash=sha256:99276dc6e3a7851f32027a68f1095cd3f77c148091b092ea867a351811cfe388 \
-    --hash=sha256:5d50835fdf0a7edf0b55e311b7c887786504efea1177abd7e69329a8e5ea619e
-imagesize==0.7.1 \
-    --hash=sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb \
-    --hash=sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062
-six==1.11.0 \
-    --hash=sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb \
-    --hash=sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9
-alabaster==0.7.10 \
-    --hash=sha256:2eef172f44e8d301d25aff8068fddd65f767a3f04b5f15b0f4922f113aa1c732 \
-    --hash=sha256:37cdcb9e9954ed60912ebc1ca12a9d12178c26637abdf124e3cde2341c257fe0
-snowballstemmer==1.2.1 \
-    --hash=sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89 \
-    --hash=sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128
-Pygments==2.2.0 \
-    --hash=sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d \
-    --hash=sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc
-sphinxcontrib-websupport==1.0.1 \
-    --hash=sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9 \
-    --hash=sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2
-typing==3.6.4 \
-    --hash=sha256:b2c689d54e1144bbcfd191b0832980a21c2dbcf7b5ff7a66248a60c90e951eb8 \
-    --hash=sha256:3a887b021a77b292e151afb75323dea88a7bc1b3dfa92176cff8e44c8b68bddf \
-    --hash=sha256:d400a9344254803a2368533e4533a4200d21eb7b6b729c173bc38201a74db3f2
-Babel==2.5.3 \
-    --hash=sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80 \
-    --hash=sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14
-MarkupSafe==1.0 \
-    --hash=sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665
-pyparsing==2.2.0 \
-    --hash=sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010 \
-    --hash=sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04 \
-    --hash=sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e \
-    --hash=sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07 \
-    --hash=sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5 \
-    --hash=sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18 \
-    --hash=sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58
-pytz==2018.3 \
-    --hash=sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe \
-    --hash=sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda \
-    --hash=sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9 \
-    --hash=sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f \
-    --hash=sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd \
-    --hash=sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5 \
-    --hash=sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d \
-    --hash=sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef \
-    --hash=sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0
-Sphinx==1.6.7 \
-    --hash=sha256:d5b91b4dad56ffc9f19425ebaa7bc23290a0a2e9035781d5bc54822384663277 \
-    --hash=sha256:832bed0dc6099c2abca957d90ff55bc1a6ec4425c13fc144adbae68a970e3775
-sphinx_rtd_theme==0.2.4 \
-    --hash=sha256:62ee4752716e698bad7de8a18906f42d33664128eea06c46b718fc7fbd1a9f5c \
-    --hash=sha256:2df74b8ff6fae6965c527e97cca6c6c944886aae474b490e17f92adfbe843417
-parsimonious==0.7.0 \
-    --hash=sha256:396d424f64f834f9463e81ba79a331661507a21f1ed7b644f7f6a744006fd938
-sphinx-js==2.1 \
-    --hash=sha256:8c12b2b7ccc6941cbc7c70e4fada903e2947376b48ce07cbb72c72d88f0eef1e
-CommonMark==0.5.4 \
-    --hash=sha256:34d73ec8085923c023930dfc0bcd1c4286e28a2a82de094bb72fabcc0281cbe5
-recommonmark==0.4.0 \
-    --hash=sha256:cd8bf902e469dae94d00367a8197fb7b81fcabc9cfb79d520e0d22d0fbeaa8b7 \
-    --hash=sha256:6e29c723abcf5533842376d87c4589e62923ecb6002a8e059eb608345ddaff9d
-futures==3.2.0 \
-    --hash=sha256:ec0a6cb848cc212002b9828c3e34c675e0c9ff6741dc445cab6fdd4e1085d1f1 \
-    --hash=sha256:9ec02aa7d674acb8618afb127e27fde7fc68994c0437ad759fa094a574adb265
-singledispatch==3.4.0.3 \
-    --hash=sha256:833b46966687b3de7f438c761ac475213e53b306740f1abfaa86e1d1aae56aa8 \
-    --hash=sha256:5b06af87df13818d14f08a028e42f566640aef80805c3b50c5056b086e3c2b9c
-backports_abc==0.5 \
-    --hash=sha256:52089f97fe7a9aa0d3277b220c1d730a85aefd64e1b2664696fe35317c5470a7 \
-    --hash=sha256:033be54514a03e255df75c5aee8f9e672f663f93abb723444caec8fe43437bde
-tornado==5.0.1 \
-    --hash=sha256:69194436190b777abf0b631a692b0b29ba4157d18eeee07327b486e033b944dc \
-    --hash=sha256:186ba4f280429a24120f329c7c08ea91818ff6bf47ed2ccb66f8f460698fc4ed \
-    --hash=sha256:b5bf7407f88327b80e666dabf91a1e7beb11236855a5c65ba5cf0e9e25ae296b \
-    --hash=sha256:4d192236a9ffee54cb0032f22a8a0cfa64258872f1d83d71f3356681f69a37be \
-    --hash=sha256:3e9a2333362d3dad7876d902595b64aea1a2f91d0df13191ea1f8bca5a447771
-livereload==2.5.1 \
-    --hash=sha256:5ed6506f5d526ee712da9f3739c27714e6f3376f3e481728d298efceae0ec83a \
-    --hash=sha256:422de10d7ea9467a1ba27cbaffa84c74b809d96fb1598d9de4b9b676adf35e2c