Merge autoland to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Wed, 15 Jan 2020 11:37:35 +0200
changeset 510297 5ba39736e74b8a072a63ee215545f89d5c2ec8c8
parent 510282 96793d65cdd538f5f58b66ae2cbf9357f6232a98 (current diff)
parent 510296 2c23ef1249141c1cf9ca6e3043982f0e34b890eb (diff)
child 510319 af8308da601a34e3b50eeb4b1eab306cba176220
push id37017
push usercsabou@mozilla.com
push dateWed, 15 Jan 2020 09:38:07 +0000
treeherdermozilla-central@5ba39736e74b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone74.0a1
first release with
nightly linux32
5ba39736e74b / 74.0a1 / 20200115093807 / files
nightly linux64
5ba39736e74b / 74.0a1 / 20200115093807 / files
nightly mac
5ba39736e74b / 74.0a1 / 20200115093807 / files
nightly win32
5ba39736e74b / 74.0a1 / 20200115093807 / files
nightly win64
5ba39736e74b / 74.0a1 / 20200115093807 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
--- a/dom/canvas/ClientWebGLContext.cpp
+++ b/dom/canvas/ClientWebGLContext.cpp
@@ -249,17 +249,18 @@ void ClientWebGLContext::RestoreContext(
   NS_DispatchToCurrentThread(std::move(runnable));
 }
 
 void ClientWebGLContext::Event_webglcontextrestored() {
   mAwaitingRestore = false;
   mLossStatus = webgl::LossStatus::Ready;
   mNextError = 0;
 
-  if (!CreateHostContext()) {
+  const uvec2 requestSize = {mCanvasElement->Width(), mCanvasElement->Height()};
+  if (!CreateHostContext(requestSize)) {
     mLossStatus = webgl::LossStatus::LostForever;
     return;
   }
 
   WEBGL_BRIDGE_LOGD("[%p] Posting webglcontextrestored event", this);
   (void)DispatchEvent(NS_LITERAL_STRING("webglcontextrestored"));
 }
 
@@ -632,68 +633,72 @@ void ClientWebGLContext::GetContextAttri
   result.mPreserveDrawingBuffer = options.preserveDrawingBuffer;
   result.mFailIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat;
   result.mPowerPreference = options.powerPreference;
 }
 
 // -----------------------
 
 NS_IMETHODIMP
-ClientWebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight) {
+ClientWebGLContext::SetDimensions(const int32_t signedWidth,
+                                  const int32_t signedHeight) {
   const FuncScope funcScope(*this, "<SetDimensions>");
   WEBGL_BRIDGE_LOGI("[%p] SetDimensions: (%d, %d)", this, signedWidth,
                     signedHeight);
 
   MOZ_ASSERT(mInitialOptions);
 
-  const auto size = uvec2::From(signedWidth, signedHeight);
-  if (!size) {
-    EnqueueWarning(
-        "Canvas size is too large (seems like a negative value wrapped)");
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  if (*size == mRequestedSize) return NS_OK;
-  mRequestedSize = *size;
-  mDrawingBufferSize = {};
+  uvec2 size = {static_cast<uint32_t>(signedWidth),
+                static_cast<uint32_t>(signedHeight)};
+  if (!size.x) {
+    size.x = 1;
+  }
+  if (!size.y) {
+    size.y = 1;
+  }
 
   if (mNotLost) {
-    Run<RPROC(Resize)>(*size);
+    auto& state = State();
+    state.mDrawingBufferSize = {};
+
+    Run<RPROC(Resize)>(size);
+
     MarkCanvasDirty();
     return NS_OK;
   }
 
   if (mLossStatus != webgl::LossStatus::Ready) {
     MOZ_RELEASE_ASSERT(false);
     return NS_ERROR_FAILURE;
   }
 
   // -
   // Context (re-)creation
 
-  if (!CreateHostContext()) {
+  if (!CreateHostContext(size)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
-bool ClientWebGLContext::CreateHostContext() {
+bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) {
   const auto pNotLost = std::make_shared<webgl::NotLostData>(*this);
   auto& notLost = *pNotLost;
 
   auto res = [&]() -> Result<Ok, std::string> {
     auto options = *mInitialOptions;
     if (StaticPrefs::webgl_disable_fail_if_major_performance_caveat()) {
       options.failIfMajorPerformanceCaveat = false;
     }
     const bool resistFingerprinting = ShouldResistFingerprinting();
 
     const auto& principal = GetCanvas()->NodePrincipal();
     const auto principalKey = principal->GetHashValue();
     const auto initDesc = webgl::InitContextDesc{
-        mIsWebGL2, resistFingerprinting, mRequestedSize, options, principalKey};
+        mIsWebGL2, resistFingerprinting, requestedSize, options, principalKey};
 
     // -
 
     if (!StaticPrefs::webgl_out_of_process()) {
       auto ownerData = HostWebGLContext::OwnerData{
           Some(this),
       };
       notLost.inProcess = HostWebGLContext::Create(std::move(ownerData),
@@ -791,22 +796,25 @@ bool ClientWebGLContext::CreateHostConte
         .mCurrentQueryByTarget[LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN];
   }
 
   return true;
 }
 
 // -------
 
-const uvec2& ClientWebGLContext::DrawingBufferSize() {
-  if (!mDrawingBufferSize) {
-    mDrawingBufferSize = Some(Run<RPROC(DrawingBufferSize)>());
-  }
-
-  return *mDrawingBufferSize;
+uvec2 ClientWebGLContext::DrawingBufferSize() {
+  if (IsContextLost()) return {};
+  auto& state = State();
+  auto& size = state.mDrawingBufferSize;
+  if (!size) {
+    size = Some(Run<RPROC(DrawingBufferSize)>());
+  }
+
+  return *size;
 }
 
 void ClientWebGLContext::OnMemoryPressure() {
   WEBGL_BRIDGE_LOGI("[%p] OnMemoryPressure", this);
   return Run<RPROC(OnMemoryPressure)>();
 }
 
 NS_IMETHODIMP
--- a/dom/canvas/ClientWebGLContext.h
+++ b/dom/canvas/ClientWebGLContext.h
@@ -175,19 +175,17 @@ class ContextGenerationInfo final {
   std::array<int32_t, 4> mScissor = {};
   std::array<int32_t, 4> mViewport = {};
   std::array<float, 4> mClearColor = {{0, 0, 0, 0}};
   std::array<float, 4> mBlendColor = {{0, 0, 0, 0}};
   std::array<float, 2> mDepthRange = {{0, 1}};
 
   std::vector<GLenum> mCompressedTextureFormats;
 
- public:
-  // explicit ContextGenerationInfo(ClientWebGLContext& context);
-  //~ContextGenerationInfo();
+  Maybe<uvec2> mDrawingBufferSize;
 
   ObjectId NextId() { return mLastId += 1; }
 };
 
 // -
 
 struct RemotingData final {
   // In the cross process case, the WebGL actor's ownership relationship looks
@@ -722,18 +720,16 @@ class ClientWebGLContext final : public 
  public:
   const bool mIsWebGL2;
 
   explicit ClientWebGLContext(bool webgl2);
 
  private:
   virtual ~ClientWebGLContext();
 
-  uvec2 mRequestedSize;
-  Maybe<uvec2> mDrawingBufferSize;
   const RefPtr<ClientWebGLExtensionLoseContext> mExtLoseContext;
 
   webgl::LossStatus mLossStatus = webgl::LossStatus::Ready;
   bool mAwaitingRestore = false;
 
   // -
  private:
   std::shared_ptr<webgl::NotLostData> mNotLost;
@@ -754,17 +750,17 @@ class ClientWebGLContext final : public 
   void OnContextLoss(webgl::ContextLossReason);
   void RestoreContext(webgl::LossStatus requiredStatus);
 
  private:
   bool DispatchEvent(const nsAString&) const;
   void Event_webglcontextlost();
   void Event_webglcontextrestored();
 
-  bool CreateHostContext();
+  bool CreateHostContext(const uvec2& requestedSize);
   void ThrowEvent_WebGLContextCreationError(const std::string&) const;
 
  public:
   void MarkCanvasDirty() { Invalidate(); }
 
   mozilla::dom::WebGLChild* GetChild() const {
     if (!mNotLost) return nullptr;
     if (!mNotLost->outOfProcess) return nullptr;
@@ -982,17 +978,17 @@ class ClientWebGLContext final : public 
   void GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
   void Present();
 
   RefPtr<layers::SharedSurfaceTextureClient> GetVRFrame() const;
   void ClearVRFrame() const;
 
  private:
-  const uvec2& DrawingBufferSize();
+  uvec2 DrawingBufferSize();
   bool HasAlphaSupport() { return mSurfaceInfo.supportsAlpha; }
 
   ICRData mSurfaceInfo;
 
   void AfterDrawCall() {
     if (!mNotLost) return;
     const auto& state = State();
     const bool isBackbuffer = !state.mBoundDrawFb;
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1386,16 +1386,42 @@ static mozilla::gfx::IntRect ScaleToOuts
           aOffset.x),
       NSToIntCeil(
           NSAppUnitsToFloatPixels(aRect.YMost(), float(aAppUnitsPerPixel)) *
               aYScale +
           aOffset.y));
   return rect;
 }
 
+/* This function is the same as the above except that it rounds to the
+ * nearest instead of rounding out. We use it for attempting to compute the
+ * actual pixel bounds of opaque items */
+static mozilla::gfx::IntRect ScaleToNearestPixelsOffset(
+    nsRect aRect, float aXScale, float aYScale, nscoord aAppUnitsPerPixel,
+    LayerPoint aOffset) {
+  mozilla::gfx::IntRect rect;
+  rect.SetNonEmptyBox(
+      NSToIntFloor(NSAppUnitsToFloatPixels(aRect.x, float(aAppUnitsPerPixel)) *
+                       aXScale +
+                   aOffset.x + 0.5),
+      NSToIntFloor(NSAppUnitsToFloatPixels(aRect.y, float(aAppUnitsPerPixel)) *
+                       aYScale +
+                   aOffset.y + 0.5),
+      NSToIntFloor(
+          NSAppUnitsToFloatPixels(aRect.XMost(), float(aAppUnitsPerPixel)) *
+              aXScale +
+          aOffset.x + 0.5),
+      NSToIntFloor(
+          NSAppUnitsToFloatPixels(aRect.YMost(), float(aAppUnitsPerPixel)) *
+              aYScale +
+          aOffset.y + 0.5));
+  return rect;
+}
+
+
 RenderRootStateManager* WebRenderCommandBuilder::GetRenderRootStateManager(
     wr::RenderRoot aRenderRoot) {
   return mManager->GetRenderRootStateManager(aRenderRoot);
 }
 
 void WebRenderCommandBuilder::DoGroupingForDisplayList(
     nsDisplayList* aList, nsDisplayItem* aWrappingItem,
     nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc,
@@ -2140,25 +2166,51 @@ WebRenderCommandBuilder::GenerateFallbac
 
   LayoutDeviceToLayerScale2D layerScale(scale.width, scale.height);
 
   auto trans =
       ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation());
   auto snappedTrans = LayerIntPoint::Floor(trans);
   LayerPoint residualOffset = trans - snappedTrans;
 
-  auto dtRect = LayerIntRect::FromUnknownRect(
+  nsRegion opaqueRegion =
+    aItem->GetOpaqueRegion(aDisplayListBuilder, &snap);
+  wr::OpacityType opacity = opaqueRegion.Contains(paintBounds)
+    ? wr::OpacityType::Opaque
+    : wr::OpacityType::HasAlphaChannel;
+
+  LayerIntRect dtRect, visibleRect;
+  // If we think the item is opaque we round the bounds
+  // to the nearest pixel instead of rounding them out. If we rounded
+  // out we'd potentially introduce transparent pixels.
+  //
+  // Ideally we'd be able to ask an item its bounds in pixels and whether
+  // they're all opaque. Unfortunately no such API exists so we currently
+  // just hope that we get it right.
+  if (opacity == wr::OpacityType::Opaque && snap) {
+    dtRect = LayerIntRect::FromUnknownRect(
+      ScaleToNearestPixelsOffset(paintBounds, scale.width, scale.height,
+                                 appUnitsPerDevPixel, residualOffset));
+
+    visibleRect = LayerIntRect::FromUnknownRect(
+                         ScaleToNearestPixelsOffset(
+                             aItem->GetBuildingRect(), scale.width,
+                             scale.height, appUnitsPerDevPixel, residualOffset))
+                         .Intersect(dtRect);
+  } else {
+    dtRect = LayerIntRect::FromUnknownRect(
       ScaleToOutsidePixelsOffset(paintBounds, scale.width, scale.height,
                                  appUnitsPerDevPixel, residualOffset));
 
-  auto visibleRect = LayerIntRect::FromUnknownRect(
+    visibleRect = LayerIntRect::FromUnknownRect(
                          ScaleToOutsidePixelsOffset(
                              aItem->GetBuildingRect(), scale.width,
                              scale.height, appUnitsPerDevPixel, residualOffset))
                          .Intersect(dtRect);
+  }
 
   auto visibleSize = visibleRect.Size();
   if (visibleSize.IsEmpty()) {
     return nullptr;
   }
 
   if (useBlobImage) {
     // Display item bounds should be unscaled
@@ -2206,25 +2258,22 @@ WebRenderCommandBuilder::GenerateFallbac
 
   if (needPaint || !fallbackData->GetImageKey()) {
     nsAutoPtr<nsDisplayItemGeometry> newGeometry;
     newGeometry = aItem->AllocateGeometry(aDisplayListBuilder);
     fallbackData->mGeometry = std::move(newGeometry);
 
     gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK
                                     ? gfx::SurfaceFormat::A8
-                                    : gfx::SurfaceFormat::B8G8R8A8;
+                                    : (opacity == wr::OpacityType::Opaque ?
+                                       gfx::SurfaceFormat::B8G8R8X8 :
+                                       gfx::SurfaceFormat::B8G8R8A8);
     if (useBlobImage) {
-      bool snapped;
-      nsRegion opaqueRegion =
-          aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped);
       MOZ_ASSERT(!opaqueRegion.IsComplex());
-      wr::OpacityType opacity = opaqueRegion.Contains(paintBounds)
-                                    ? wr::OpacityType::Opaque
-                                    : wr::OpacityType::HasAlphaChannel;
+
       std::vector<RefPtr<ScaledFont>> fonts;
       bool validFonts = true;
       RefPtr<WebRenderDrawEventRecorder> recorder =
           MakeAndAddRef<WebRenderDrawEventRecorder>(
               [&](MemStream& aStream,
                   std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
                 size_t count = aScaledFonts.size();
                 aStream.write((const char*)&count, sizeof(count));
--- a/layout/forms/test/mochitest.ini
+++ b/layout/forms/test/mochitest.ini
@@ -18,17 +18,17 @@ skip-if = toolkit == 'android' || e10s |
 [test_bug446663.html]
 [test_bug476308.html]
 [test_bug477531.html]
 [test_bug477700.html]
 [test_bug478219.xhtml]
 [test_bug534785.html]
 [test_bug542914.html]
 [test_bug549170.html]
-fail-if = (os == 'linux' && os_version == '18.04') # Bug 1599984
+fail-if = (os == 'linux' && os_version == '18.04' && !headless) # Bug 1599984
 [test_bug562447.html]
 [test_bug563642.html]
 [test_bug564115.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug571352.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug572406.html]
 [test_bug572649.html]
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/revert-optimization.patch
@@ -0,0 +1,53 @@
+diff --git a/src/cubeb_resampler.cpp b/src/cubeb_resampler.cpp
+index 3fc09d2..c1712cf 100644
+--- a/src/cubeb_resampler.cpp
++++ b/src/cubeb_resampler.cpp
+@@ -66,41 +66,19 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
+          (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
+          (input_buffer && !output_buffer && output_frames == 0));
+ 
+-  // When we have no pending input data and exactly as much input
+-  // as output data, we don't need to copy it into the internal buffer
+-  // and can directly forward it to the callback.
+-  void * in_buf = input_buffer;
+-  unsigned long pop_input_count = 0u;
+-  if (input_buffer && !output_buffer) {
++  if (input_buffer) {
++    if (!output_buffer) {
+       output_frames = *input_frames_count;
+-  } else if(input_buffer) {
+-    if (internal_input_buffer.length() != 0) {
+-      // In this case we have pending input data left and have
+-      // to first append the input so we can pass it as one pointer
+-      // to the callback
+-      internal_input_buffer.push(static_cast<T*>(input_buffer),
+-                                 frames_to_samples(*input_frames_count));
+-      in_buf = internal_input_buffer.data();
+-      pop_input_count = frames_to_samples(output_frames);
+-    } else if(*input_frames_count > output_frames) {
+-      // In this case we have more input that we need output and
+-      // fill the overflowing input into internal_input_buffer
+-      // Since we have no other pending data, we can nonetheless
+-      // pass the current input data directly to the callback
+-      assert(pop_input_count == 0);
+-      unsigned long samples_off = frames_to_samples(output_frames);
+-      internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
+-                                 frames_to_samples(*input_frames_count - output_frames));
+     }
++    internal_input_buffer.push(static_cast<T*>(input_buffer),
++                               frames_to_samples(*input_frames_count));
+   }
+ 
+-  long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
++  long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
++                          output_buffer, output_frames);
+ 
+   if (input_buffer) {
+-    if (pop_input_count) {
+-      internal_input_buffer.pop(nullptr, pop_input_count);
+-    }
+-
++    internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
+     *input_frames_count = output_frames;
+     drop_audio_if_needed();
+   }
--- a/media/libcubeb/src/cubeb_resampler.cpp
+++ b/media/libcubeb/src/cubeb_resampler.cpp
@@ -61,51 +61,29 @@ long passthrough_resampler<T>::fill(void
   if (input_buffer) {
     assert(input_frames_count);
   }
   assert((input_buffer && output_buffer &&
          *input_frames_count + static_cast<int>(samples_to_frames(internal_input_buffer.length())) >= output_frames) ||
          (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
          (input_buffer && !output_buffer && output_frames == 0));
 
-  // When we have no pending input data and exactly as much input
-  // as output data, we don't need to copy it into the internal buffer
-  // and can directly forward it to the callback.
-  void * in_buf = input_buffer;
-  unsigned long pop_input_count = 0u;
-  if (input_buffer && !output_buffer) {
+  if (input_buffer) {
+    if (!output_buffer) {
       output_frames = *input_frames_count;
-  } else if(input_buffer) {
-    if (internal_input_buffer.length() != 0) {
-      // In this case we have pending input data left and have
-      // to first append the input so we can pass it as one pointer
-      // to the callback
-      internal_input_buffer.push(static_cast<T*>(input_buffer),
-                                 frames_to_samples(*input_frames_count));
-      in_buf = internal_input_buffer.data();
-      pop_input_count = frames_to_samples(output_frames);
-    } else if(*input_frames_count > output_frames) {
-      // In this case we have more input that we need output and
-      // fill the overflowing input into internal_input_buffer
-      // Since we have no other pending data, we can nonetheless
-      // pass the current input data directly to the callback
-      assert(pop_input_count == 0);
-      unsigned long samples_off = frames_to_samples(output_frames);
-      internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
-                                 frames_to_samples(*input_frames_count - output_frames));
     }
+    internal_input_buffer.push(static_cast<T*>(input_buffer),
+                               frames_to_samples(*input_frames_count));
   }
 
-  long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
+  long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
+                          output_buffer, output_frames);
 
   if (input_buffer) {
-    if (pop_input_count) {
-      internal_input_buffer.pop(nullptr, pop_input_count);
-    }
-
+    internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
     *input_frames_count = output_frames;
     drop_audio_if_needed();
   }
 
   return rv;
 }
 
 template<typename T, typename InputProcessor, typename OutputProcessor>
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -86,8 +86,10 @@ if [ -n "$rev" ]; then
 else
   echo "Remember to update moz.yaml with the version details."
 fi
 
 echo "Applying disable-assert.patch on top of $rev"
 patch -p3 < disable-assert.patch
 echo "Applying disable-iaudioclient3.patch on top of $rev"
 patch -p3 < disable-iaudioclient3.patch
+echo "Applying revert-optimization.patch on top of $rev"
+patch -p1 < revert-optimization.patch
--- a/servo/components/style/values/specified/angle.rs
+++ b/servo/components/style/values/specified/angle.rs
@@ -203,38 +203,37 @@ impl Angle {
         Self::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
     }
 
     fn parse_internal<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         allow_unitless_zero: AllowUnitlessZeroAngle,
     ) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         let t = input.next()?;
+        let allow_unitless_zero = matches!(allow_unitless_zero, AllowUnitlessZeroAngle::Yes);
         match *t {
             Token::Dimension {
                 value, ref unit, ..
             } => {
                 match Angle::parse_dimension(value, unit, /* from_calc = */ false) {
                     Ok(angle) => Ok(angle),
                     Err(()) => {
                         let t = t.clone();
                         Err(input.new_unexpected_token_error(t))
                     },
                 }
             },
-            Token::Number { value, .. } if value == 0. => match allow_unitless_zero {
-                AllowUnitlessZeroAngle::Yes => Ok(Angle::zero()),
-                AllowUnitlessZeroAngle::No => {
-                    let t = t.clone();
-                    Err(input.new_unexpected_token_error(t))
-                },
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                CalcNode::parse_angle(context, input, function)
             },
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                return input.parse_nested_block(|i| CalcNode::parse_angle(context, i));
+            Token::Number { value, .. } if value == 0. && allow_unitless_zero => {
+                Ok(Angle::zero())
             },
             ref t => {
                 let t = t.clone();
                 Err(input.new_unexpected_token_error(t))
             },
         }
     }
 }
--- a/servo/components/style/values/specified/calc.rs
+++ b/servo/components/style/values/specified/calc.rs
@@ -7,21 +7,29 @@
 //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
 
 use crate::parser::ParserContext;
 use crate::values::computed;
 use crate::values::specified::length::ViewportPercentageLength;
 use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
 use crate::values::specified::{Angle, Time};
 use crate::values::{CSSFloat, CSSInteger};
-use cssparser::{AngleOrNumber, NumberOrPercentage, Parser, Token};
+use cssparser::{AngleOrNumber, CowRcStr, NumberOrPercentage, Parser, Token};
 use std::fmt::{self, Write};
 use style_traits::values::specified::AllowedNumericType;
 use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 
+/// The name of the mathematical function that we're parsing.
+#[derive(Debug, Copy, Clone)]
+pub enum MathFunction {
+    /// `calc()`
+    Calc,
+    // FIXME: min() / max() / clamp
+}
+
 /// A node inside a `Calc` expression's AST.
 #[derive(Clone, Debug)]
 pub enum CalcNode {
     /// `<length>`
     Length(NoCalcLength),
     /// `<angle>`
     Angle(Angle),
     /// `<time>`
@@ -199,31 +207,48 @@ impl CalcNode {
                     .map(CalcNode::Time)
                     .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             },
             (&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) |
             (&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
                 Ok(CalcNode::Percentage(unit_value))
             },
             (&Token::ParenthesisBlock, _) => {
-                input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
+                input.parse_nested_block(|input| {
+                    CalcNode::parse_argument(context, input, expected_unit)
+                })
             },
-            (&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {
-                input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
+            (&Token::Function(ref name), _) => {
+                let function = CalcNode::math_function(name, location)?;
+                CalcNode::parse(context, input, function, expected_unit)
             },
             (t, _) => Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 
     /// Parse a top-level `calc` expression, with all nested sub-expressions.
     ///
     /// This is in charge of parsing, for example, `2 + 3 * 100%`.
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        _function: MathFunction,
+        expected_unit: CalcUnit,
+    ) -> Result<Self, ParseError<'i>> {
+        // TODO: Do something different based on the function name. In
+        // particular, for non-calc function we need to take a list of
+        // comma-separated arguments and such.
+        input.parse_nested_block(|input| {
+            Self::parse_argument(context, input, expected_unit)
+        })
+    }
+
+    fn parse_argument<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
         expected_unit: CalcUnit,
     ) -> Result<Self, ParseError<'i>> {
         let mut root = Self::parse_product(context, input, expected_unit)?;
 
         loop {
             let start = input.state();
             match input.next_including_whitespace() {
                 Ok(&Token::WhiteSpace(_)) => {
@@ -521,109 +546,131 @@ impl CalcNode {
             },
             CalcNode::Length(..) |
             CalcNode::Percentage(..) |
             CalcNode::Angle(..) |
             CalcNode::Time(..) => return Err(()),
         })
     }
 
+    /// Given a function name, and the location from where the token came from,
+    /// return a mathematical function corresponding to that name or an error.
+    #[inline]
+    pub fn math_function<'i>(
+        name: &CowRcStr<'i>,
+        location: cssparser::SourceLocation,
+    ) -> Result<MathFunction, ParseError<'i>> {
+        if !name.eq_ignore_ascii_case("calc") {
+            return Err(location.new_unexpected_token_error(Token::Function(name.clone())));
+        }
+        Ok(MathFunction::Calc)
+    }
+
     /// Convenience parsing function for integers.
     pub fn parse_integer<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        function: MathFunction,
     ) -> Result<CSSInteger, ParseError<'i>> {
-        Self::parse_number(context, input).map(|n| n.round() as CSSInteger)
+        Self::parse_number(context, input, function).map(|n| n.round() as CSSInteger)
     }
 
     /// Convenience parsing function for `<length> | <percentage>`.
     pub fn parse_length_or_percentage<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         clamping_mode: AllowedNumericType,
+        function: MathFunction,
     ) -> Result<CalcLengthPercentage, ParseError<'i>> {
-        Self::parse(context, input, CalcUnit::LengthPercentage)?
+        Self::parse(context, input, function, CalcUnit::LengthPercentage)?
             .to_length_or_percentage(clamping_mode)
             .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for percentages.
     pub fn parse_percentage<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        function: MathFunction,
     ) -> Result<CSSFloat, ParseError<'i>> {
-        Self::parse(context, input, CalcUnit::Percentage)?
+        Self::parse(context, input, function, CalcUnit::Percentage)?
             .to_percentage()
             .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<length>`.
     pub fn parse_length<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         clamping_mode: AllowedNumericType,
+        function: MathFunction,
     ) -> Result<CalcLengthPercentage, ParseError<'i>> {
-        Self::parse(context, input, CalcUnit::Length)?
+        Self::parse(context, input, function, CalcUnit::Length)?
             .to_length_or_percentage(clamping_mode)
             .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<number>`.
     pub fn parse_number<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        function: MathFunction,
     ) -> Result<CSSFloat, ParseError<'i>> {
-        Self::parse(context, input, CalcUnit::Number)?
+        Self::parse(context, input, function, CalcUnit::Number)?
             .to_number()
             .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<angle>`.
     pub fn parse_angle<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        function: MathFunction,
     ) -> Result<Angle, ParseError<'i>> {
-        Self::parse(context, input, CalcUnit::Angle)?
+        Self::parse(context, input, function, CalcUnit::Angle)?
             .to_angle()
             .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<time>`.
     pub fn parse_time<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        function: MathFunction,
     ) -> Result<Time, ParseError<'i>> {
-        Self::parse(context, input, CalcUnit::Time)?
+        Self::parse(context, input, function, CalcUnit::Time)?
             .to_time()
             .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<number>` or `<percentage>`.
     pub fn parse_number_or_percentage<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        function: MathFunction,
     ) -> Result<NumberOrPercentage, ParseError<'i>> {
-        let node = Self::parse(context, input, CalcUnit::Percentage)?;
+        let node = Self::parse(context, input, function, CalcUnit::Percentage)?;
 
         if let Ok(value) = node.to_number() {
             return Ok(NumberOrPercentage::Number { value });
         }
 
         match node.to_percentage() {
             Ok(unit_value) => Ok(NumberOrPercentage::Percentage { unit_value }),
             Err(()) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
         }
     }
 
     /// Convenience parsing function for `<number>` or `<angle>`.
     pub fn parse_angle_or_number<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
+        function: MathFunction,
     ) -> Result<AngleOrNumber, ParseError<'i>> {
-        let node = Self::parse(context, input, CalcUnit::Angle)?;
+        let node = Self::parse(context, input, function, CalcUnit::Angle)?;
 
         if let Ok(angle) = node.to_angle() {
             let degrees = angle.degrees();
             return Ok(AngleOrNumber::Angle { degrees });
         }
 
         match node.to_number() {
             Ok(value) => Ok(AngleOrNumber::Number { value }),
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -293,18 +293,19 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::Co
                 let degrees = match angle {
                     Ok(angle) => angle.degrees(),
                     Err(()) => return Err(location.new_unexpected_token_error(token.clone())),
                 };
 
                 Ok(AngleOrNumber::Angle { degrees })
             },
             Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                input.parse_nested_block(|i| CalcNode::parse_angle_or_number(self.0, i))
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                CalcNode::parse_angle_or_number(self.0, input, function)
             },
             t => return Err(location.new_unexpected_token_error(t)),
         }
     }
 
     fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
         use crate::values::specified::Percentage;
 
@@ -318,25 +319,26 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::Co
     }
 
     fn parse_number_or_percentage<'t>(
         &self,
         input: &mut Parser<'i, 't>,
     ) -> Result<NumberOrPercentage, ParseError<'i>> {
         let location = input.current_source_location();
 
-        match input.next()?.clone() {
+        match *input.next()? {
             Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
             Token::Percentage { unit_value, .. } => {
                 Ok(NumberOrPercentage::Percentage { unit_value })
             },
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                input.parse_nested_block(|i| CalcNode::parse_number_or_percentage(self.0, i))
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                CalcNode::parse_number_or_percentage(self.0, input, function)
             },
-            t => return Err(location.new_unexpected_token_error(t)),
+            ref t => return Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 }
 
 impl Parse for Color {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -594,21 +594,21 @@ impl Length {
                     !allow_quirks.allowed(context.quirks_mode)
                 {
                     return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
                 Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
                     value,
                 ))))
             },
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => input
-                .parse_nested_block(|input| {
-                    CalcNode::parse_length(context, input, num_context)
-                        .map(|calc| Length::Calc(Box::new(calc)))
-                }),
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                let calc = CalcNode::parse_length(context, input, num_context, function)?;
+                Ok(Length::Calc(Box::new(calc)))
+            },
             ref token => return Err(location.new_unexpected_token_error(token.clone())),
         }
     }
 
     /// Parse a non-negative length
     #[inline]
     pub fn parse_non_negative<'i, 't>(
         context: &ParserContext,
@@ -817,20 +817,19 @@ impl LengthPercentage {
                     !context.parsing_mode.allows_unitless_lengths() &&
                     !allow_quirks.allowed(context.quirks_mode)
                 {
                     return Err(location.new_unexpected_token_error(token.clone()));
                 } else {
                     return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
                 }
             },
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                let calc = input.parse_nested_block(|i| {
-                    CalcNode::parse_length_or_percentage(context, i, num_context)
-                })?;
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                let calc = CalcNode::parse_length_or_percentage(context, input, num_context, function)?;
                 Ok(LengthPercentage::Calc(Box::new(calc)))
             },
             _ => return Err(location.new_unexpected_token_error(token.clone())),
         }
     }
 
     /// Parses allowing the unitless length quirk.
     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -139,18 +139,19 @@ fn parse_number_with_clamping_mode<'i, '
     let location = input.current_source_location();
     match *input.next()? {
         Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
             Ok(Number {
                 value: value.min(f32::MAX).max(f32::MIN),
                 calc_clamping_mode: None,
             })
         },
-        Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-            let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?;
+        Token::Function(ref name) => {
+            let function = CalcNode::math_function(name, location)?;
+            let result = CalcNode::parse_number(context, input, function)?;
             Ok(Number {
                 value: result.min(f32::MAX).max(f32::MIN),
                 calc_clamping_mode: Some(clamping_mode),
             })
         },
         ref t => Err(location.new_unexpected_token_error(t.clone())),
     }
 }
@@ -538,42 +539,43 @@ impl Parse for Integer {
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let location = input.current_source_location();
         match *input.next()? {
             Token::Number {
                 int_value: Some(v), ..
             } => Ok(Integer::new(v)),
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?;
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                let result = CalcNode::parse_integer(context, input, function)?;
                 Ok(Integer::from_calc(result))
             },
             ref t => Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 }
 
 impl Integer {
     /// Parse an integer value which is at least `min`.
     pub fn parse_with_minimum<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         min: i32,
     ) -> Result<Integer, ParseError<'i>> {
-        match Integer::parse(context, input) {
-            // FIXME(emilio): The spec asks us to avoid rejecting it at parse
-            // time except until computed value time.
-            //
-            // It's not totally clear it's worth it though, and no other browser
-            // does this.
-            Ok(value) if value.value() >= min => Ok(value),
-            Ok(_value) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
-            Err(e) => Err(e),
+        let value = Integer::parse(context, input)?;
+        // FIXME(emilio): The spec asks us to avoid rejecting it at parse
+        // time except until computed value time.
+        //
+        // It's not totally clear it's worth it though, and no other browser
+        // does this.
+        if value.value() < min {
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
+        Ok(value)
     }
 
     /// Parse a non-negative integer.
     pub fn parse_non_negative<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Integer, ParseError<'i>> {
         Integer::parse_with_minimum(context, input, 0)
--- a/servo/components/style/values/specified/percentage.rs
+++ b/servo/components/style/values/specified/percentage.rs
@@ -112,24 +112,24 @@ impl Percentage {
     ) -> Result<Self, ParseError<'i>> {
         let location = input.current_source_location();
         match *input.next()? {
             Token::Percentage { unit_value, .. }
                 if num_context.is_ok(context.parsing_mode, unit_value) =>
             {
                 Ok(Percentage::new(unit_value))
             },
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                let result =
-                    input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?;
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                let value = CalcNode::parse_percentage(context, input, function)?;
 
                 // TODO(emilio): -moz-image-rect is the only thing that uses
                 // the clamping mode... I guess we could disallow it...
                 Ok(Percentage {
-                    value: result,
+                    value,
                     calc_clamping_mode: Some(num_context),
                 })
             },
             ref t => Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 
     /// Parses a percentage token, but rejects it if it's negative.
--- a/servo/components/style/values/specified/time.rs
+++ b/servo/components/style/values/specified/time.rs
@@ -64,17 +64,17 @@ impl Time {
             unit,
             was_calc,
         })
     }
 
     /// Returns a `Time` value from a CSS `calc()` expression.
     pub fn from_calc(seconds: CSSFloat) -> Self {
         Time {
-            seconds: seconds,
+            seconds,
             unit: TimeUnit::Second,
             was_calc: true,
         }
     }
 
     fn parse_with_clamping_mode<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
@@ -90,21 +90,30 @@ impl Time {
             // value case, the value does not animate for SMIL at all, so we use
             // ParsingMode::DEFAULT directly.
             Token::Dimension {
                 value, ref unit, ..
             } if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => {
                 Time::parse_dimension(value, unit, /* from_calc = */ false)
                     .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             },
-            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) {
-                    Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time),
-                    _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
+            Token::Function(ref name) => {
+                let function = CalcNode::math_function(name, location)?;
+                let time = CalcNode::parse_time(context, input, function)?;
+
+                // FIXME(emilio): Rejecting calc() at parse time is wrong,
+                // was_calc should probably be replaced by calc_clamping_mode or
+                // something like we do for numbers, or we should do the
+                // clamping here instead (simpler, but technically incorrect,
+                // though still more correct than this!).
+                if !clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) {
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
+
+                Ok(time)
             },
             ref t => return Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 
     /// Parses a non-negative time value.
     pub fn parse_non_negative<'i, 't>(
         context: &ParserContext,
--- a/taskcluster/ci/config.yml
+++ b/taskcluster/ci/config.yml
@@ -79,17 +79,16 @@ treeherder:
         'Tss': 'Talos performance tests, Stylo sequential'
         'Tss-fis': 'Talos performance tests, Stylo sequential with fission enabled'
         'Tss-1proc': 'Talos performance tests with Stylo sequential without e10s'
         'tt': 'Telemetry tests'
         'tt-1proc': 'Telemetry tests without e10s'
         'SY': 'Are we slim yet tests by TaskCluster'
         'SY-fis': 'Are we slim yet tests by TaskCluster, fission enabled'
         'SYsd': 'Are we slim yet tests by TaskCluster, Stylo disabled'
-        'SYss': 'Are we slim yet tests by TaskCluster, Stylo sequential'
         'VP': 'VideoPuppeteer tests'
         'W': 'Web platform tests'
         'W-1proc': 'Web platform tests without e10s'
         'W-fis': 'Web platform tests with fission enabled'
         'W-sw': 'Web platform tests with serviceworker redesign'
         'W-sw-1proc': 'Web platform tests with serviceworker redesign, without e10s'
         'X': 'Xpcshell tests'
         'X-1proc': 'Xpcshell tests, without e10s'
--- a/taskcluster/ci/test/awsy.yml
+++ b/taskcluster/ci/test/awsy.yml
@@ -79,15 +79,8 @@ awsy-base:
 awsy-base-dmd:
     description: "Are we slim yet - about:blank base case - dmd enabled"
     treeherder-symbol: SY(ab-d)
     run-on-projects: ['try']
     mozharness:
         extra-options:
             - --base
             - --dmd
-
-awsy-stylo-sequential:
-    description: "Are we slim yet for Stylo sequential"
-    treeherder-symbol: SYss(sy)
-    mozharness:
-        extra-options:
-            - --single-stylo-traversal
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -58,23 +58,16 @@ linux1804-64-devedition/opt:
         - web-platform-tests-1804
 
 linux1804-64-asan/opt:
     build-platform: linux64-asan/opt
     test-sets:
         - linux1804-tests
         - web-platform-tests-1804
 
-# Stylo sequential runs check memory and
-# performance when using a single thread.
-linux1804-64-stylo-sequential/opt:
-    build-platform: linux64/opt
-    test-sets:
-        - awsy-stylo-sequential
-
 linux1804-64-qr/opt:
     build-platform: linux64/opt
     test-sets:
         - awsy
         - linux1804-qr-tests
         - web-platform-tests-1804
 
 linux1804-64-shippable-qr/opt:
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -322,19 +322,16 @@ raptor-firefox-power:
 
 awsy:
     - awsy
     - awsy-base
     - awsy-dmd
     - awsy-base-dmd
     - awsy-tp6
 
-awsy-stylo-sequential:
-    - awsy-stylo-sequential
-
 ##
 # Limited test sets for specific platforms
 
 linux-common-tests:
     - marionette
     - mochitest
     - mochitest-browser-chrome
     - mochitest-chrome
--- a/taskcluster/taskgraph/test/test_try_option_syntax.py
+++ b/taskcluster/taskgraph/test/test_try_option_syntax.py
@@ -236,23 +236,22 @@ class TestTryOptionSyntax(unittest.TestC
         "-u gtest[linux,win32] selects the linux and win32 platforms for gtest"
         parameters = {'try_options': parse_message('try: -u gtest[linux,win32]')}
         tos = TryOptionSyntax(parameters, graph_with_jobs, GRAPH_CONFIG)
         self.assertEqual(sorted(tos.unittests), sorted([
             {'test': 'gtest', 'platforms': ['linux', 'win32']},
         ]))
 
     def test_u_platforms_pretty(self):
-        """-u gtest[Ubuntu] selects the linux, linux64, linux64-asan
-        and linux64-stylo-sequential platforms for gtest"""
+        """-u gtest[Ubuntu] selects the linux, linux64 and linux64-asan
+        platforms for gtest"""
         parameters = {'try_options': parse_message('try: -u gtest[Ubuntu]')}
         tos = TryOptionSyntax(parameters, graph_with_jobs, GRAPH_CONFIG)
         self.assertEqual(sorted(tos.unittests), sorted([
-            {'test': 'gtest', 'platforms': ['linux32', 'linux64', 'linux64-asan',
-                                            'linux64-stylo-sequential']},
+            {'test': 'gtest', 'platforms': ['linux32', 'linux64', 'linux64-asan']},
         ]))
 
     def test_u_platforms_negated(self):
         "-u gtest[-linux] selects all platforms but linux for gtest"
         parameters = {'try_options': parse_message('try: -u gtest[-linux]')}
         tos = TryOptionSyntax(parameters, graph_with_jobs, GRAPH_CONFIG)
         all_platforms = set([x.attributes['test_platform'] for x in unittest_tasks.values()])
         self.assertEqual(sorted(tos.unittests[0]['platforms']), sorted(
--- a/taskcluster/taskgraph/transforms/job/mozharness_test.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py
@@ -27,17 +27,16 @@ from taskgraph.transforms.job.common imp
 VARIANTS = [
     'nightly',
     'shippable',
     'devedition',
     'pgo',
     'asan',
     'stylo',
     'stylo-disabled',
-    'stylo-sequential',
     'qr',
     'ccov',
 ]
 
 
 def get_variant(test_platform):
     for v in VARIANTS:
         if '-{}/'.format(v) in test_platform:
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -1461,31 +1461,16 @@ def set_test_type(config, tests):
     for test in tests:
         for test_type in ['mochitest', 'reftest', 'talos', 'raptor']:
             if test_type in test['suite'] and 'web-platform' not in test['suite']:
                 test.setdefault('tags', {})['test-type'] = test_type
         yield test
 
 
 @transforms.add
-def single_stylo_traversal_tests(config, tests):
-    """Enable single traversal for all tests on the sequential Stylo platform."""
-
-    for test in tests:
-        if not test['test-platform'].startswith('linux64-stylo-sequential/'):
-            yield test
-            continue
-
-        # Bug 1356122 - Run Stylo tests in sequential mode
-        test['mozharness'].setdefault('extra-options', [])\
-                          .append('--single-stylo-traversal')
-        yield test
-
-
-@transforms.add
 def set_worker_type(config, tests):
     """Set the worker type based on the test platform."""
     for test in tests:
         # during the taskcluster migration, this is a bit tortured, but it
         # will get simpler eventually!
         test_platform = test['test-platform']
         if test.get('worker-type'):
             # This test already has its worker type defined, so just use that (yields below)
--- a/taskcluster/taskgraph/try_option_syntax.py
+++ b/taskcluster/taskgraph/try_option_syntax.py
@@ -114,23 +114,21 @@ UNITTEST_ALIASES = {
 # new platform, and they shouldn't have to explicitly spell out the new platform
 # every time for such cases.
 #
 # Note that the test platforms here are only the prefix up to the `/`.
 UNITTEST_PLATFORM_PRETTY_NAMES = {
     'Ubuntu': [
         'linux32',
         'linux64',
-        'linux64-asan',
-        'linux64-stylo-sequential'
+        'linux64-asan'
     ],
     'x64': [
         'linux64',
-        'linux64-asan',
-        'linux64-stylo-sequential'
+        'linux64-asan'
     ],
     'Android 7.0 Moto G5 32bit': ['android-hw-g5-7.0-arm7-api-16'],
     'Android 8.0 Google Pixel 2 32bit': ['android-hw-p2-8.0-arm7-api-16'],
     'Android 8.0 Google Pixel 2 64bit': ['android-hw-p2-8.0-android-aarch64'],
     '10.14': ['macosx1014-64'],
     # other commonly-used substrings for platforms not yet supported with
     # in-tree taskgraphs:
     # '10.10.5': [..TODO..],
--- a/testing/awsy/mach_commands.py
+++ b/testing/awsy/mach_commands.py
@@ -224,19 +224,16 @@ class MachCommands(MachCommandBase):
     @CommandArgument('--per-tab-pause', group='AWSY', action='store', type=int,
                      dest='perTabPause',
                      help='Seconds to wait in between opening tabs. '
                      'Defaults to %s.' % PER_TAB_PAUSE)
     @CommandArgument('--settle-wait-time', group='AWSY', action='store', type=int,
                      dest='settleWaitTime',
                      help='Seconds to wait for things to settled down. '
                      'Defaults to %s.' % SETTLE_WAIT_TIME)
-    @CommandArgument('--single-stylo-traversal', group='AWSY', action='store_true',
-                     dest='single_stylo_traversal', default=False,
-                     help='Set STYLO_THREADS=1.')
     @CommandArgument('--dmd', group='AWSY', action='store_true',
                      dest='dmd', default=False,
                      help='Enable DMD during testing. Requires a DMD-enabled build.')
     @CommandArgument('--tp6', group='AWSY', action='store_true',
                      dest='tp6', default=False,
                      help='Use the tp6 pageset during testing.')
     def run_awsy_test(self, tests, **kwargs):
         """mach awsy-test runs the in-tree version of the Are We Slim Yet
--- a/testing/mozharness/scripts/awsy_script.py
+++ b/testing/mozharness/scripts/awsy_script.py
@@ -45,22 +45,16 @@ class AWSY(TestingMixin, MercurialScript
           "help": "Run tests without multiple processes (e10s). (Desktop builds only)",
           }],
         [["--setpref"],
          {"action": "append",
           "dest": "extra_prefs",
           "default": [],
           "help": "Extra user prefs.",
           }],
-        [["--single-stylo-traversal"],
-         {"action": "store_true",
-          "dest": "single_stylo_traversal",
-          "default": False,
-          "help": "Set STYLO_THREADS=1.",
-          }],
         [["--enable-webrender"],
          {"action": "store_true",
           "dest": "enable_webrender",
           "default": False,
           "help": "Enable the WebRender compositor in Gecko.",
           }],
         [["--base"],
          {"action": "store_true",
@@ -251,23 +245,17 @@ class AWSY(TestingMixin, MercurialScript
         cmd.append("--preferences=%s" % os.path.join(self.awsy_path, "conf", prefs_file))
         if dmd_enabled:
             cmd.append("--setpref=security.sandbox.content.level=0")
         cmd.append(test_file)
 
         if self.config['enable_webrender']:
             cmd.append('--enable-webrender')
 
-        if self.config['single_stylo_traversal']:
-            env['STYLO_THREADS'] = '1'
-        else:
-            env['STYLO_THREADS'] = '4'
-
-        # TODO: consider getting rid of this as stylo is enabled by default
-        env['STYLO_FORCE_ENABLED'] = '1'
+        env['STYLO_THREADS'] = '4'
 
         env['MOZ_UPLOAD_DIR'] = dirs['abs_blob_upload_dir']
         if not os.path.isdir(env['MOZ_UPLOAD_DIR']):
             self.mkdir_p(env['MOZ_UPLOAD_DIR'])
         if self.query_minidump_stackwalk():
             env['MINIDUMP_STACKWALK'] = self.minidump_stackwalk_path
         env['MINIDUMP_SAVE_PATH'] = dirs['abs_blob_upload_dir']
         env['RUST_BACKTRACE'] = '1'
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -137,22 +137,16 @@ class DesktopUnittest(TestingMixin, Merc
          ],
         [["--allow-software-gl-layers"], {
             "action": "store_true",
             "dest": "allow_software_gl_layers",
             "default": False,
             "help": "Permits a software GL implementation (such as LLVMPipe) to use "
                     "the GL compositor."}
          ],
-        [["--single-stylo-traversal"], {
-            "action": "store_true",
-            "dest": "single_stylo_traversal",
-            "default": False,
-            "help": "Forcibly enable single thread traversal in Stylo with STYLO_THREADS=1"}
-         ],
         [["--enable-webrender"], {
             "action": "store_true",
             "dest": "enable_webrender",
             "default": False,
             "help": "Enable the WebRender compositor in Gecko."}
          ],
         [["--gpu-required"], {
             "action": "store_true",
@@ -884,20 +878,17 @@ class DesktopUnittest(TestingMixin, Merc
                 env['MINIDUMP_SAVE_PATH'] = self.query_abs_dirs()['abs_blob_upload_dir']
                 env['RUST_BACKTRACE'] = 'full'
                 if not os.path.isdir(env['MOZ_UPLOAD_DIR']):
                     self.mkdir_p(env['MOZ_UPLOAD_DIR'])
 
                 if self.config['allow_software_gl_layers']:
                     env['MOZ_LAYERS_ALLOW_SOFTWARE_GL'] = '1'
 
-                if self.config['single_stylo_traversal']:
-                    env['STYLO_THREADS'] = '1'
-                else:
-                    env['STYLO_THREADS'] = '4'
+                env['STYLO_THREADS'] = '4'
 
                 env = self.query_env(partial_env=env, log_level=INFO)
                 cmd_timeout = self.get_timeout_for_category(suite_category)
 
                 summary = {}
                 for per_test_args in self.query_args(suite):
                     # Make sure baseline code coverage tests are never
                     # skipped and that having them run has no influence
--- a/testing/mozharness/scripts/web_platform_tests.py
+++ b/testing/mozharness/scripts/web_platform_tests.py
@@ -81,22 +81,16 @@ class WebPlatformTest(TestingMixin, Merc
             "help": "Specify headless virtual screen width (default: 1600)."}
          ],
         [["--headless-height"], {
             "action": "store",
             "dest": "headless_height",
             "default": "1200",
             "help": "Specify headless virtual screen height (default: 1200)."}
          ],
-        [["--single-stylo-traversal"], {
-            "action": "store_true",
-            "dest": "single_stylo_traversal",
-            "default": False,
-            "help": "Forcibly enable single thread traversal in Stylo with STYLO_THREADS=1"}
-         ],
         [["--setpref"], {
             "action": "append",
             "metavar": "PREF=VALUE",
             "dest": "extra_prefs",
             "default": [],
             "help": "Defines an extra user preference."}
          ],
         [["--include"], {
@@ -245,21 +239,16 @@ class WebPlatformTest(TestingMixin, Merc
         if c['extra_prefs']:
             cmd.extend(['--setpref={}'.format(p) for p in c['extra_prefs']])
 
         if not c["e10s"]:
             cmd.append("--disable-e10s")
         if c["enable_webrender"]:
             cmd.append("--enable-webrender")
 
-        if c["single_stylo_traversal"]:
-            cmd.append("--stylo-threads=1")
-        else:
-            cmd.append("--stylo-threads=4")
-
         if not (self.verify_enabled or self.per_test_coverage):
             test_paths = json.loads(os.environ.get('MOZHARNESS_TEST_PATHS', '""'))
             if test_paths:
                 keys = (['web-platform-tests-%s' % test_type for test_type in test_types] +
                         ['web-platform-tests'])
                 for key in keys:
                     if key in test_paths:
                         relpaths = [os.path.relpath(p, 'testing/web-platform')
@@ -360,20 +349,17 @@ class WebPlatformTest(TestingMixin, Merc
 
         if self.config['allow_software_gl_layers']:
             env['MOZ_LAYERS_ALLOW_SOFTWARE_GL'] = '1'
         if self.config['headless']:
             env['MOZ_HEADLESS'] = '1'
             env['MOZ_HEADLESS_WIDTH'] = self.config['headless_width']
             env['MOZ_HEADLESS_HEIGHT'] = self.config['headless_height']
 
-        if self.config['single_stylo_traversal']:
-            env['STYLO_THREADS'] = '1'
-        else:
-            env['STYLO_THREADS'] = '4'
+        env['STYLO_THREADS'] = '4'
 
         if self.is_android:
             env['ADB_PATH'] = self.adb_path
 
         env = self.query_env(partial_env=env, log_level=INFO)
 
         start_time = datetime.now()
         max_per_test_time = timedelta(minutes=60)