Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 14 Dec 2016 16:53:27 +0100
changeset 325889 9a2a507e5b17b8bbb2360196a69df59fedc4e535
parent 325888 40d0c5bed75dee425255eca86b3c1d600154f863 (current diff)
parent 325877 18b5a7a5d833f09b1f1e5cc45f4c0ff6235a1d5e (diff)
child 325890 8fcd8f50264be9c64aa85ac39097808f5bd59b30
push id31078
push userkwierso@gmail.com
push dateThu, 15 Dec 2016 00:06:52 +0000
treeherdermozilla-central@b1ab720c6d3e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone53.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 mozilla-inbound
dom/media/AccurateSeekTask.cpp
dom/media/AccurateSeekTask.h
ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl
ipc/ipdl/test/ipdl/ok/multiStartState.ipdl
ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl
ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl
ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl
ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5225,29 +5225,33 @@
           var tab = getTabFromAudioEvent(event)
           if (!tab) {
             return;
           }
 
           if (!tab.hasAttribute("blocked")) {
             tab.setAttribute("blocked", true);
             this._tabAttrModified(tab, ["blocked"]);
+            tab.startMediaBlockTimer();
           }
         ]]>
       </handler>
       <handler event="DOMAudioPlaybackBlockStopped">
         <![CDATA[
           var tab = getTabFromAudioEvent(event)
           if (!tab) {
             return;
           }
 
           if (tab.hasAttribute("blocked")) {
             tab.removeAttribute("blocked");
             this._tabAttrModified(tab, ["blocked"]);
+            let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
+            hist.add(2 /* unblockByVisitingTab */);
+            tab.finishMediaBlockTimer();
           }
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tabbox"
            extends="chrome://global/content/bindings/tabbox.xml#tabbox">
@@ -6945,45 +6949,63 @@
             tabContainer._afterHoveredTab.removeAttribute("afterhovered");
             tabContainer._afterHoveredTab = null;
           }
 
           tabContainer._hoveredTab = null;
         ]]></body>
       </method>
 
+      <method name="startMediaBlockTimer">
+        <body><![CDATA[
+          TelemetryStopwatch.start("TAB_MEDIA_BLOCKING_TIME_MS", this);
+        ]]></body>
+      </method>
+
+       <method name="finishMediaBlockTimer">
+        <body><![CDATA[
+          TelemetryStopwatch.finish("TAB_MEDIA_BLOCKING_TIME_MS", this);
+        ]]></body>
+      </method>
+
       <method name="toggleMuteAudio">
         <parameter name="aMuteReason"/>
         <body>
         <![CDATA[
           let tabContainer = this.parentNode;
           let browser = this.linkedBrowser;
           let modifiedAttrs = [];
+          let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
+
           if (browser.audioBlocked) {
             this.removeAttribute("blocked");
             modifiedAttrs.push("blocked");
 
             // We don't want sound icon flickering between "blocked", "none" and
             // "sound-playing", here adding the "soundplaying" is to keep the
             // transition smoothly.
             if (!this.hasAttribute("soundplaying")) {
               this.setAttribute("soundplaying", true);
               modifiedAttrs.push("soundplaying");
             }
 
             browser.resumeMedia();
+            hist.add(3 /* unblockByClickingIcon */);
+            this.finishMediaBlockTimer();
           } else {
             if (browser.audioMuted) {
               browser.unmute();
               this.removeAttribute("muted");
               BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
+              hist.add(1 /* unmute */);
             } else {
               browser.mute();
               this.setAttribute("muted", "true");
               BrowserUITelemetry.countTabMutingEvent("mute", aMuteReason);
+              hist.add(0 /* mute */);
             }
             this.muteReason = aMuteReason || null;
             modifiedAttrs.push("muted");
           }
           tabContainer.tabbrowser._tabAttrModified(this, modifiedAttrs);
         ]]>
         </body>
       </method>
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -84,17 +84,17 @@ const kDownloadsStringsRequiringFormatti
   shortTimeLeftHours: true,
   shortTimeLeftDays: true,
   statusSeparator: true,
   statusSeparatorBeforeNumber: true,
   fileExecutableSecurityWarning: true
 };
 
 const kDownloadsStringsRequiringPluralForm = {
-  otherDownloads2: true
+  otherDownloads3: true
 };
 
 const kPartialDownloadSuffix = ".part";
 
 const kPrefBranch = Services.prefs.getBranch("browser.download.");
 
 var PrefObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
@@ -1515,22 +1515,21 @@ DownloadsSummaryData.prototype = {
    * Computes aggregate values based on the current state of downloads.
    */
   _refreshProperties() {
     // Pre-load summary with default values.
     let summary =
       DownloadsCommon.summarizeDownloads(this._downloadsForSummary());
 
     this._description = DownloadsCommon.strings
-                                       .otherDownloads2(summary.numActive);
+                                       .otherDownloads3(summary.numDownloading);
     this._percentComplete = summary.percentComplete;
 
-    // If all downloads are paused, show the progress indicator as paused.
-    this._showingProgress = summary.numDownloading > 0 ||
-                            summary.numPaused > 0;
+    // Only show the downloading items.
+    this._showingProgress = summary.numDownloading > 0;
 
     // Display the estimated time left, if present.
     if (summary.rawTimeLeft == -1) {
       // There are no downloads with a known time left.
       this._lastRawTimeLeft = -1;
       this._lastTimeLeft = -1;
       this._details = "";
     } else {
--- a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
+++ b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
@@ -291,24 +291,27 @@ add_task(function* test_favicon_firstPar
 add_task(function* test_favicon_cache_firstParty() {
   // Clear all image caches and network caches before running the test.
   clearAllImageCaches();
 
   let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
                         .getService(Ci.nsICacheStorageService);
   networkCache.clear();
 
+  // Start to observer the event of that favicon has been fully loaded and cached.
+  let promiseForFaviconLoaded = waitOnFaviconLoaded(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
+  // Start to observer for the favicon response of the first tab.
+  let responsePromise = waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
   // Open the tab for the first site.
   let tabInfoA = yield openTab(TEST_SITE_ONE + TEST_CACHE_PAGE);
 
-  // Start to observer the event of that favicon has been fully loaded and cached.
-  let promiseForFaviconLoaded = waitOnFaviconLoaded(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
-
-  // Wait for the favicon response of the first tab.
-  let response = yield waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+  // Waiting for the favicon response.
+  let response = yield responsePromise;
 
   // Make sure the favicon is loaded through the network and its first party domain is correct.
   is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network.");
   is(response.firstPartyDomain, FIRST_PARTY_ONE, "We should only observe the network response for the first first party.");
 
   // Waiting until the favicon has been loaded and cached.
   yield promiseForFaviconLoaded;
 
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
@@ -25,21 +25,21 @@
      -->
 <!ENTITY downloadDetails.width            "50ch">
 
 <!-- LOCALIZATION NOTE (downloadsSummary.minWidth2):
      Minimum width for the main description of the downloads summary,
      which is displayed at the bottom of the Downloads Panel if the
      number of downloads exceeds the limit that the panel can display.
 
-     A good rule of thumb here is to look at the otherDownloads2 string
+     A good rule of thumb here is to look at the otherDownloads3 string
      in downloads.properties, and make a reasonable estimate of its
      maximum length. For English, this seems like a reasonable limit:
 
-     + 999 other downloads
+     999 files downloading
 
      that's 21 characters, so we set the minimum width to 21ch.
      -->
 <!ENTITY downloadsSummary.minWidth2       "21ch">
 
 <!ENTITY cmd.pause.label                  "Pause">
 <!ENTITY cmd.pause.accesskey              "P">
 <!ENTITY cmd.resume.label                 "Resume">
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.properties
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.properties
@@ -95,14 +95,14 @@ shortTimeLeftDays=%1$Sd
 # even for right-to-left languages, unless the defaults are not suitable.
 statusSeparator=%1$S \u2014 %2$S
 statusSeparatorBeforeNumber=%1$S \u2014  %2$S
 
 fileExecutableSecurityWarning=“%S” is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch “%S”?
 fileExecutableSecurityWarningTitle=Open Executable File?
 fileExecutableSecurityWarningDontAsk=Don’t ask me this again
 
-# LOCALIZATION NOTE (otherDownloads2):
+# LOCALIZATION NOTE (otherDownloads3):
 # This is displayed in an item at the bottom of the Downloads Panel when
 # there are more downloads than can fit in the list in the panel. Use a
 # semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/Localization_and_Plurals
-otherDownloads2=+ %1$S other download; + %1$S other downloads
+otherDownloads3=%1$S file downloading;%1$S files downloading
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -186,30 +186,31 @@ AudioChannelAgent::InitInternal(nsPIDOMW
           "owner = %p, hasCallback = %d\n", this, mAudioChannelType,
           mWindow.get(), (!!mCallback || !!mWeakCallback)));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig,
-                                        bool aAudible)
+                                        uint8_t aAudible)
 {
   if (NS_WARN_IF(!aConfig)) {
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
-  MOZ_ASSERT(AudioChannelService::AudibleState::eAudible == true &&
-             AudioChannelService::AudibleState::eNotAudible == false);
+  MOZ_ASSERT(AudioChannelService::AudibleState::eNotAudible == 0 &&
+             AudioChannelService::AudibleState::eMaybeAudible == 1 &&
+             AudioChannelService::AudibleState::eAudible == 2);
   service->RegisterAudioChannelAgent(this,
     static_cast<AudioChannelService::AudibleState>(aAudible));
 
   AudioPlaybackConfig config = service->GetMediaConfig(mWindow,
                                                        mAudioChannelType);
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
@@ -237,17 +238,17 @@ AudioChannelAgent::NotifyStoppedPlaying(
     service->UnregisterAudioChannelAgent(this);
   }
 
   mIsRegToService = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-AudioChannelAgent::NotifyStartedAudible(bool aAudible, uint32_t aReason)
+AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible, uint32_t aReason)
 {
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, NotifyStartedAudible, this = %p, "
           "audible = %d, reason = %d\n", this, aAudible, aReason));
 
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_FAILURE;
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -1222,24 +1222,24 @@ void
 AudioChannelService::AudioChannelWindow::AppendAgent(AudioChannelAgent* aAgent,
                                                      AudibleState aAudible)
 {
   MOZ_ASSERT(aAgent);
 
   RequestAudioFocus(aAgent);
   AppendAgentAndIncreaseAgentsNum(aAgent);
   AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing);
-  if (aAudible) {
+  if (aAudible == AudibleState::eAudible) {
     AudioAudibleChanged(aAgent,
                         AudibleState::eAudible,
                         AudibleChangedReasons::eDataAudibleChanged);
-  } else if (IsEnableAudioCompetingForAllAgents() && !aAudible) {
+  } else if (IsEnableAudioCompetingForAllAgents() &&
+             aAudible != AudibleState::eAudible) {
     NotifyAudioCompetingChanged(aAgent, true);
   }
-  MaybeNotifyMediaBlocked(aAgent);
 }
 
 void
 AudioChannelService::AudioChannelWindow::RemoveAgent(AudioChannelAgent* aAgent)
 {
   MOZ_ASSERT(aAgent);
 
   RemoveAgentAndReduceAgentsNum(aAgent);
@@ -1300,23 +1300,26 @@ AudioChannelService::AudioChannelWindow:
 
 void
 AudioChannelService::AudioChannelWindow::AudioAudibleChanged(AudioChannelAgent* aAgent,
                                                              AudibleState aAudible,
                                                              AudibleChangedReasons aReason)
 {
   MOZ_ASSERT(aAgent);
 
-  if (aAudible) {
+  if (aAudible == AudibleState::eAudible) {
     AppendAudibleAgentIfNotContained(aAgent, aReason);
   } else {
     RemoveAudibleAgentIfContained(aAgent, aReason);
   }
 
-  NotifyAudioCompetingChanged(aAgent, aAudible);
+  NotifyAudioCompetingChanged(aAgent, aAudible == AudibleState::eAudible);
+  if (aAudible != AudibleState::eNotAudible) {
+    MaybeNotifyMediaBlocked(aAgent);
+  }
 }
 
 void
 AudioChannelService::AudioChannelWindow::AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
                                                                           AudibleChangedReasons aReason)
 {
   MOZ_ASSERT(aAgent);
   MOZ_ASSERT(mAgents.Contains(aAgent));
@@ -1363,17 +1366,19 @@ AudioChannelService::AudioChannelWindow:
 }
 
 void
 AudioChannelService::AudioChannelWindow::NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow,
                                                                    AudibleState aAudible,
                                                                    AudibleChangedReasons aReason)
 {
   RefPtr<AudioPlaybackRunnable> runnable =
-    new AudioPlaybackRunnable(aWindow, aAudible, aReason);
+    new AudioPlaybackRunnable(aWindow,
+                              aAudible == AudibleState::eAudible,
+                              aReason);
   DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
 }
 
 void
 AudioChannelService::AudioChannelWindow::NotifyChannelActive(uint64_t aWindowID,
                                                              AudioChannel aChannel,
                                                              bool aActive)
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -60,19 +60,25 @@ public:
 class AudioChannelService final : public nsIAudioChannelService
                                 , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIAUDIOCHANNELSERVICE
 
-  enum AudibleState : bool {
-    eAudible = true,
-    eNotAudible = false
+  /**
+   * eNotAudible : agent is not audible
+   * eMaybeAudible : agent is not audible now, but it might be audible later
+   * eAudible : agent is audible now
+   */
+  enum AudibleState : uint8_t {
+    eNotAudible = 0,
+    eMaybeAudible = 1,
+    eAudible = 2
   };
 
   enum AudioCaptureState : bool {
     eCapturing = true,
     eNotCapturing = false
   };
 
   enum AudibleChangedReasons : uint32_t {
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -158,17 +158,17 @@ interface nsIAudioChannelAgent : nsISupp
   /**
    * Notify the agent that we want to start playing.
    * Note: Gecko component SHOULD call this function first then start to
    *          play audio stream only when return value is true.
    *
    * @param config
    *    It contains the playback related states (volume/mute/suspend)
    */
-  void notifyStartedPlaying(in AudioPlaybackConfig config, in bool audible);
+  void notifyStartedPlaying(in AudioPlaybackConfig config, in uint8_t audible);
 
   /**
    * Notify the agent we no longer want to play.
    *
    * Note : even if notifyStartedPlaying() returned false, the agent would
    * still be registered with the audio channel service and receive callbacks
    * for status changes. So notifyStoppedPlaying must still eventually be
    * called to unregister the agent with the channel service.
@@ -178,10 +178,10 @@ interface nsIAudioChannelAgent : nsISupp
 
   /**
    * Notify agent that we already start producing audible data.
    *
    * Note : sometime audio might become silent during playing, this method is used to
    * notify the actually audible state to other services which want to know
    * about that, ex. tab sound indicator.
    */
-  void notifyStartedAudible(in bool audible, in uint32_t reason);
+  void notifyStartedAudible(in uint8_t audible, in uint32_t reason);
 };
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -748,17 +748,17 @@ public:
   void
   NotifyAudioPlaybackChanged(AudibleChangedReasons aReason)
   {
     MOZ_ASSERT(!mIsShutDown);
     if (!IsPlayingStarted()) {
       return;
     }
 
-    bool newAudibleState = IsOwnerAudible();
+    AudibleState newAudibleState = IsOwnerAudible();
     if (mIsOwnerAudible == newAudibleState) {
       return;
     }
 
     mIsOwnerAudible = newAudibleState;
     mAudioChannelAgent->NotifyStartedAudible(mIsOwnerAudible, aReason);
   }
 
@@ -971,35 +971,35 @@ private:
   bool
   IsSuspended() const
   {
     return (mSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
             mSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
             mSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
   }
 
-  bool
+  AudibleState
   IsOwnerAudible() const
   {
     // Muted or the volume should not be ~0
     if (mOwner->Muted() || (std::fabs(mOwner->Volume()) <= 1e-7)) {
-      return false;
-    }
-
-    // No sound can be heard during suspending.
-    if (IsSuspended()) {
-      return false;
-    }
-
-    // Silent audio track.
-    if (!mOwner->mIsAudioTrackAudible) {
-      return false;
-    }
-
-    return true;
+      return AudioChannelService::AudibleState::eNotAudible;
+    }
+
+    // No audio track.
+    if (!mOwner->HasAudio()) {
+      return AudioChannelService::AudibleState::eNotAudible;
+    }
+
+    // Might be audible but not yet.
+    if (mOwner->HasAudio() && !mOwner->mIsAudioTrackAudible) {
+      return AudioChannelService::AudibleState::eMaybeAudible;
+    }
+
+    return AudioChannelService::AudibleState::eAudible;
   }
 
   bool
   IsPlayingThroughTheAudioChannel() const
   {
     // If we have an error, we are not playing.
     if (mOwner->GetError()) {
       return false;
@@ -1059,18 +1059,18 @@ private:
   // It's used to reduce the power consumption, we won't play the auto-play
   // audio/video in the page we have never visited before. MediaElement would
   // be resumed when the page is active. See bug647429 for more details.
   // - SUSPENDED_STOP_DISPOSABLE
   // When we permanently lost platform audio focus, we should stop playing
   // and stop the audio channel agent. MediaElement can only be restarted by
   // play().
   SuspendTypes mSuspended;
-  // True if media element is audible for users.
-  bool mIsOwnerAudible;
+  // Indicate whether media element is audible for users.
+  AudibleState mIsOwnerAudible;
   bool mIsShutDown;
 };
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement::AudioChannelAgentCallback)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLMediaElement::AudioChannelAgentCallback)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelAgent)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -6971,17 +6971,22 @@ HTMLMediaElement::ShouldElementBePaused(
   }
 
   return false;
 }
 
 void
 HTMLMediaElement::SetMediaInfo(const MediaInfo& aInfo)
 {
+  const bool oldHasAudio = mMediaInfo.HasAudio();
   mMediaInfo = aInfo;
+  if (aInfo.HasAudio() != oldHasAudio) {
+    NotifyAudioPlaybackChanged(
+      AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
+  }
   if (mAudioChannelWrapper) {
     mAudioChannelWrapper->AudioCaptureStreamChangeIfNeeded();
   }
 }
 
 void
 HTMLMediaElement::AudioCaptureStreamChange(bool aCapture)
 {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -33,16 +33,17 @@
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 typedef uint32_t SuspendTypes;
 typedef uint32_t AudibleChangedReasons;
+typedef uint8_t AudibleState;
 
 namespace mozilla {
 class DecoderDoctorDiagnostics;
 class DOMMediaStream;
 class ErrorResult;
 class MediaResource;
 class MediaDecoder;
 class VideoFrameContainer;
deleted file mode 100644
--- a/dom/media/AccurateSeekTask.cpp
+++ /dev/null
@@ -1,463 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "AccurateSeekTask.h"
-#include "MediaDecoderReaderWrapper.h"
-#include "mozilla/AbstractThread.h"
-#include "mozilla/Assertions.h"
-#include "nsPrintfCString.h"
-
-namespace mozilla {
-
-extern LazyLogModule gMediaDecoderLog;
-extern LazyLogModule gMediaSampleLog;
-
-// avoid redefined macro in unified build
-#undef FMT
-#undef DECODER_LOG
-#undef SAMPLE_LOG
-#undef DECODER_WARN
-
-#define FMT(x, ...) "[AccurateSeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__
-#define DECODER_LOG(...) MOZ_LOG(gMediaDecoderLog, LogLevel::Debug,   (FMT(__VA_ARGS__)))
-#define SAMPLE_LOG(...)  MOZ_LOG(gMediaSampleLog,  LogLevel::Debug,   (FMT(__VA_ARGS__)))
-#define DECODER_WARN(...) NS_WARNING(nsPrintfCString(FMT(__VA_ARGS__)).get())
-
-AccurateSeekTask::AccurateSeekTask(const void* aDecoderID,
-                                   AbstractThread* aThread,
-                                   MediaDecoderReaderWrapper* aReader,
-                                   const SeekTarget& aTarget,
-                                   const MediaInfo& aInfo,
-                                   const media::TimeUnit& aEnd,
-                                   int64_t aCurrentMediaTime)
-  : SeekTask(aDecoderID, aThread, aReader, aTarget)
-  , mCurrentTimeBeforeSeek(media::TimeUnit::FromMicroseconds(aCurrentMediaTime))
-  , mAudioRate(aInfo.mAudio.mRate)
-  , mDoneAudioSeeking(!aInfo.HasAudio() || aTarget.IsVideoOnly())
-  , mDoneVideoSeeking(!aInfo.HasVideo())
-{
-  AssertOwnerThread();
-}
-
-AccurateSeekTask::~AccurateSeekTask()
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(mIsDiscarded);
-}
-
-void
-AccurateSeekTask::Discard()
-{
-  AssertOwnerThread();
-
-  // Disconnect MDSM.
-  RejectIfExist(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
-
-  // Disconnect MediaDecoderReaderWrapper.
-  mSeekRequest.DisconnectIfExists();
-
-  mIsDiscarded = true;
-}
-
-bool
-AccurateSeekTask::NeedToResetMDSM() const
-{
-  AssertOwnerThread();
-  return true;
-}
-
-int64_t
-AccurateSeekTask::CalculateNewCurrentTime() const
-{
-  AssertOwnerThread();
-
-  const int64_t seekTime = mTarget.GetTime().ToMicroseconds();
-
-  // For the accurate seek, we always set the newCurrentTime = seekTime so that
-  // the updated HTMLMediaElement.currentTime will always be the seek target;
-  // we rely on the MediaSink to handles the gap between the newCurrentTime and
-  // the real decoded samples' start time.
-  if (mTarget.IsAccurate()) {
-    return seekTime;
-  }
-
-  // For the fast seek, we update the newCurrentTime with the decoded audio and
-  // video samples, set it to be the one which is closet to the seekTime.
-  if (mTarget.IsFast()) {
-
-    // A situation that both audio and video approaches the end.
-    if (!mSeekedAudioData && !mSeekedVideoData) {
-      return seekTime;
-    }
-
-    const int64_t audioStart = mSeekedAudioData ? mSeekedAudioData->mTime : INT64_MAX;
-    const int64_t videoStart = mSeekedVideoData ? mSeekedVideoData->mTime : INT64_MAX;
-    const int64_t audioGap = std::abs(audioStart - seekTime);
-    const int64_t videoGap = std::abs(videoStart - seekTime);
-    return audioGap <= videoGap ? audioStart : videoStart;
-  }
-
-  MOZ_ASSERT(false, "AccurateSeekTask doesn't handle other seek types.");
-  return 0;
-}
-
-void
-AccurateSeekTask::HandleAudioDecoded(MediaData* aAudio)
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
-
-  RefPtr<MediaData> audio(aAudio);
-  MOZ_ASSERT(audio);
-
-  // The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
-  // resolved.
-
-  SAMPLE_LOG("OnAudioDecoded [%lld,%lld]", audio->mTime, audio->GetEndTime());
-
-  // Video-only seek doesn't reset audio decoder. There might be pending audio
-  // requests when AccurateSeekTask::Seek() begins. We will just store the data
-  // without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget().
-  if (mTarget.IsVideoOnly()) {
-    mSeekedAudioData = audio.forget();
-    return;
-  }
-
-  AdjustFastSeekIfNeeded(audio);
-
-  if (mTarget.IsFast()) {
-    // Non-precise seek; we can stop the seek at the first sample.
-    mSeekedAudioData = audio;
-    mDoneAudioSeeking = true;
-  } else {
-    nsresult rv = DropAudioUpToSeekTarget(audio);
-    if (NS_FAILED(rv)) {
-      RejectIfExist(rv, __func__);
-      return;
-    }
-  }
-
-  if (!mDoneAudioSeeking) {
-    RequestAudioData();
-    return;
-  }
-  MaybeFinishSeek();
-}
-
-void
-AccurateSeekTask::HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart)
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
-
-  RefPtr<MediaData> video(aVideo);
-  MOZ_ASSERT(video);
-
-  // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
-  // resolved.
-
-  SAMPLE_LOG("OnVideoDecoded [%lld,%lld]", video->mTime, video->GetEndTime());
-
-  AdjustFastSeekIfNeeded(video);
-
-  if (mTarget.IsFast()) {
-    // Non-precise seek. We can stop the seek at the first sample.
-    mSeekedVideoData = video;
-    mDoneVideoSeeking = true;
-  } else {
-    nsresult rv = DropVideoUpToSeekTarget(video.get());
-    if (NS_FAILED(rv)) {
-      RejectIfExist(rv, __func__);
-      return;
-    }
-  }
-
-  if (!mDoneVideoSeeking) {
-    RequestVideoData();
-    return;
-  }
-  MaybeFinishSeek();
-}
-
-void
-AccurateSeekTask::HandleNotDecoded(MediaData::Type aType, const MediaResult& aError)
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
-
-  SAMPLE_LOG("OnNotDecoded type=%d reason=%u", aType, aError.Code());
-
-  // Ignore pending requests from video-only seek.
-  if (aType == MediaData::AUDIO_DATA && mTarget.IsVideoOnly()) {
-    return;
-  }
-
-  // If the decoder is waiting for data, we tell it to call us back when the
-  // data arrives.
-  if (aError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
-    mReader->WaitForData(aType);
-    return;
-  }
-
-  if (aError == NS_ERROR_DOM_MEDIA_CANCELED) {
-    if (aType == MediaData::AUDIO_DATA) {
-      RequestAudioData();
-    } else {
-      RequestVideoData();
-    }
-    return;
-  }
-
-  if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
-    if (aType == MediaData::AUDIO_DATA) {
-      mIsAudioQueueFinished = true;
-      mDoneAudioSeeking = true;
-    } else {
-      mIsVideoQueueFinished = true;
-      mDoneVideoSeeking = true;
-      if (mFirstVideoFrameAfterSeek) {
-        // Hit the end of stream. Move mFirstVideoFrameAfterSeek into
-        // mSeekedVideoData so we have something to display after seeking.
-        mSeekedVideoData = mFirstVideoFrameAfterSeek.forget();
-      }
-    }
-    MaybeFinishSeek();
-    return;
-  }
-
-  // This is a decode error, delegate to the generic error path.
-  RejectIfExist(aError, __func__);
-}
-
-void
-AccurateSeekTask::HandleAudioWaited(MediaData::Type aType)
-{
-  AssertOwnerThread();
-
-  // Ignore pending requests from video-only seek.
-  if (mTarget.IsVideoOnly()) {
-    return;
-  }
-  RequestAudioData();
-}
-
-void
-AccurateSeekTask::HandleVideoWaited(MediaData::Type aType)
-{
-  AssertOwnerThread();
-  RequestVideoData();
-}
-
-void
-AccurateSeekTask::HandleNotWaited(const WaitForDataRejectValue& aRejection)
-{
-  AssertOwnerThread();
-}
-
-RefPtr<AccurateSeekTask::SeekTaskPromise>
-AccurateSeekTask::Seek(const media::TimeUnit& aDuration)
-{
-  AssertOwnerThread();
-
-  // Do the seek.
-  mSeekRequest.Begin(mReader->Seek(mTarget, aDuration)
-    ->Then(OwnerThread(), __func__, this,
-           &AccurateSeekTask::OnSeekResolved, &AccurateSeekTask::OnSeekRejected));
-
-  return mSeekTaskPromise.Ensure(__func__);
-}
-
-void
-AccurateSeekTask::RequestAudioData()
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(!mDoneAudioSeeking);
-  MOZ_ASSERT(!mReader->IsRequestingAudioData());
-  MOZ_ASSERT(!mReader->IsWaitingAudioData());
-  mReader->RequestAudioData();
-}
-
-void
-AccurateSeekTask::RequestVideoData()
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(!mDoneVideoSeeking);
-  MOZ_ASSERT(!mReader->IsRequestingVideoData());
-  MOZ_ASSERT(!mReader->IsWaitingVideoData());
-  mReader->RequestVideoData(false, media::TimeUnit());
-}
-
-nsresult
-AccurateSeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
-{
-  AssertOwnerThread();
-
-  RefPtr<AudioData> audio(aSample->As<AudioData>());
-  MOZ_ASSERT(audio && mTarget.IsAccurate());
-
-  CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, mAudioRate);
-  if (!sampleDuration.isValid()) {
-    return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
-  }
-
-  if (audio->mTime + sampleDuration.value() <= mTarget.GetTime().ToMicroseconds()) {
-    // Our seek target lies after the frames in this AudioData. Don't
-    // push it onto the audio queue, and keep decoding forwards.
-    return NS_OK;
-  }
-
-  if (audio->mTime > mTarget.GetTime().ToMicroseconds()) {
-    // The seek target doesn't lie in the audio block just after the last
-    // audio frames we've seen which were before the seek target. This
-    // could have been the first audio data we've seen after seek, i.e. the
-    // seek terminated after the seek target in the audio stream. Just
-    // abort the audio decode-to-target, the state machine will play
-    // silence to cover the gap. Typically this happens in poorly muxed
-    // files.
-    DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
-    mSeekedAudioData = audio;
-    mDoneAudioSeeking = true;
-    return NS_OK;
-  }
-
-  // The seek target lies somewhere in this AudioData's frames, strip off
-  // any frames which lie before the seek target, so we'll begin playback
-  // exactly at the seek target.
-  NS_ASSERTION(mTarget.GetTime().ToMicroseconds() >= audio->mTime,
-               "Target must at or be after data start.");
-  NS_ASSERTION(mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
-               "Data must end after target.");
-
-  CheckedInt64 framesToPrune =
-    UsecsToFrames(mTarget.GetTime().ToMicroseconds() - audio->mTime, mAudioRate);
-  if (!framesToPrune.isValid()) {
-    return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
-  }
-  if (framesToPrune.value() > audio->mFrames) {
-    // We've messed up somehow. Don't try to trim frames, the |frames|
-    // variable below will overflow.
-    DECODER_WARN("Can't prune more frames that we have!");
-    return NS_ERROR_FAILURE;
-  }
-  uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
-  uint32_t channels = audio->mChannels;
-  AlignedAudioBuffer audioData(frames * channels);
-  if (!audioData) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  memcpy(audioData.get(),
-         audio->mAudioData.get() + (framesToPrune.value() * channels),
-         frames * channels * sizeof(AudioDataValue));
-  CheckedInt64 duration = FramesToUsecs(frames, mAudioRate);
-  if (!duration.isValid()) {
-    return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
-  }
-  RefPtr<AudioData> data(new AudioData(audio->mOffset,
-                                       mTarget.GetTime().ToMicroseconds(),
-                                       duration.value(),
-                                       frames,
-                                       Move(audioData),
-                                       channels,
-                                       audio->mRate));
-  MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
-  mSeekedAudioData = data;
-  mDoneAudioSeeking = true;
-
-  return NS_OK;
-}
-
-nsresult
-AccurateSeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
-{
-  AssertOwnerThread();
-
-  RefPtr<VideoData> video(aSample->As<VideoData>());
-  MOZ_ASSERT(video);
-  DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
-              video->mTime, video->GetEndTime());
-  const int64_t target = mTarget.GetTime().ToMicroseconds();
-
-  // If the frame end time is less than the seek target, we won't want
-  // to display this frame after the seek, so discard it.
-  if (target >= video->GetEndTime()) {
-    DECODER_LOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
-                video->mTime, video->GetEndTime(), target);
-    mFirstVideoFrameAfterSeek = video;
-  } else {
-    if (target >= video->mTime && video->GetEndTime() >= target) {
-      // The seek target lies inside this frame's time slice. Adjust the frame's
-      // start time to match the seek target. We do this by replacing the
-      // first frame with a shallow copy which has the new timestamp.
-      RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video.get(), target);
-      video = temp;
-    }
-    mFirstVideoFrameAfterSeek = nullptr;
-
-    DECODER_LOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
-                video->mTime, video->GetEndTime(), target);
-
-    MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
-    mSeekedVideoData = video;
-    mDoneVideoSeeking = true;
-  }
-
-  return NS_OK;
-}
-
-void
-AccurateSeekTask::MaybeFinishSeek()
-{
-  AssertOwnerThread();
-  if (mDoneAudioSeeking && mDoneVideoSeeking) {
-    Resolve(__func__); // Call to MDSM::SeekCompleted();
-  }
-}
-
-void
-AccurateSeekTask::OnSeekResolved(media::TimeUnit)
-{
-  AssertOwnerThread();
-
-  mSeekRequest.Complete();
-  // We must decode the first samples of active streams, so we can determine
-  // the new stream time. So dispatch tasks to do that.
-  if (!mDoneVideoSeeking) {
-    RequestVideoData();
-  }
-  if (!mDoneAudioSeeking) {
-    RequestAudioData();
-  }
-}
-
-void
-AccurateSeekTask::OnSeekRejected(nsresult aResult)
-{
-  AssertOwnerThread();
-
-  mSeekRequest.Complete();
-  MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
-  RejectIfExist(aResult, __func__);
-}
-
-void
-AccurateSeekTask::AdjustFastSeekIfNeeded(MediaData* aSample)
-{
-  AssertOwnerThread();
-  if (mTarget.IsFast() &&
-      mTarget.GetTime() > mCurrentTimeBeforeSeek &&
-      aSample->mTime < mCurrentTimeBeforeSeek.ToMicroseconds()) {
-    // We are doing a fastSeek, but we ended up *before* the previous
-    // playback position. This is surprising UX, so switch to an accurate
-    // seek and decode to the seek target. This is not conformant to the
-    // spec, fastSeek should always be fast, but until we get the time to
-    // change all Readers to seek to the keyframe after the currentTime
-    // in this case, we'll just decode forward. Bug 1026330.
-    mTarget.SetType(SeekTarget::Accurate);
-  }
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/AccurateSeekTask.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef ACCURATE_SEEK_TASK_H
-#define ACCURATE_SEEK_TASK_H
-
-#include "SeekTask.h"
-#include "MediaDecoderReader.h"
-#include "SeekJob.h"
-
-namespace mozilla {
-
-class AccurateSeekTask final : public SeekTask {
-public:
-  AccurateSeekTask(const void* aDecoderID,
-                   AbstractThread* aThread,
-                   MediaDecoderReaderWrapper* aReader,
-                   const SeekTarget& aTarget,
-                   const MediaInfo& aInfo,
-                   const media::TimeUnit& aEnd,
-                   int64_t aCurrentMediaTime);
-
-  void Discard() override;
-
-  RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
-
-  bool NeedToResetMDSM() const override;
-
-  int64_t CalculateNewCurrentTime() const override;
-
-  void HandleAudioDecoded(MediaData* aAudio) override;
-
-  void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override;
-
-  void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) override;
-
-  void HandleAudioWaited(MediaData::Type aType) override;
-
-  void HandleVideoWaited(MediaData::Type aType) override;
-
-  void HandleNotWaited(const WaitForDataRejectValue& aRejection) override;
-
-private:
-  ~AccurateSeekTask();
-
-  void RequestVideoData();
-
-  void RequestAudioData();
-
-  nsresult DropAudioUpToSeekTarget(MediaData* aSample);
-
-  nsresult DropVideoUpToSeekTarget(MediaData* aSample);
-
-  void MaybeFinishSeek();
-
-  void OnSeekResolved(media::TimeUnit);
-
-  void OnSeekRejected(nsresult aResult);
-
-  void AdjustFastSeekIfNeeded(MediaData* aSample);
-
-  /*
-   * Internal state.
-   */
-  const media::TimeUnit mCurrentTimeBeforeSeek;
-  const uint32_t mAudioRate;  // Audio sample rate.
-  bool mDoneAudioSeeking;
-  bool mDoneVideoSeeking;
-
-  // This temporarily stores the first frame we decode after we seek.
-  // This is so that if we hit end of stream while we're decoding to reach
-  // the seek target, we will still have a frame that we can display as the
-  // last frame in the media.
-  RefPtr<MediaData> mFirstVideoFrameAfterSeek;
-
-  /*
-   * Track the current seek promise made by the reader.
-   */
-  MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
-};
-
-} // namespace mozilla
-
-#endif /* ACCURATE_SEEK_TASK_H */
--- a/dom/media/MediaDecoderReaderWrapper.cpp
+++ b/dom/media/MediaDecoderReaderWrapper.cpp
@@ -28,19 +28,19 @@ MediaDecoderReaderWrapper::StartTime() c
 
 RefPtr<MediaDecoderReaderWrapper::MetadataPromise>
 MediaDecoderReaderWrapper::ReadMetadata()
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   MOZ_ASSERT(!mShutdown);
   return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
                      &MediaDecoderReader::AsyncReadMetadata)
-         ->ThenPromise(mOwnerThread, __func__, this,
-                       &MediaDecoderReaderWrapper::OnMetadataRead,
-                       &MediaDecoderReaderWrapper::OnMetadataNotRead);
+         ->Then(mOwnerThread, __func__, this,
+                &MediaDecoderReaderWrapper::OnMetadataRead,
+                &MediaDecoderReaderWrapper::OnMetadataNotRead);
 }
 
 void
 MediaDecoderReaderWrapper::RequestAudioData()
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   MOZ_ASSERT(!mShutdown);
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -32,17 +32,16 @@
 #include "nsContentUtils.h"
 #include "nsIEventTarget.h"
 #include "nsITimer.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include "nsDeque.h"
 #include "prenv.h"
 
-#include "AccurateSeekTask.h"
 #include "AudioSegment.h"
 #include "DOMMediaStream.h"
 #include "ImageContainer.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderReaderWrapper.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaShutdownManager.h"
@@ -81,16 +80,17 @@ using namespace mozilla::media;
 #define DECODER_WARN(x, ...) NS_WARNING(nsPrintfCString(FMT(x, ##__VA_ARGS__)).get())
 #define DUMP_LOG(x, ...) NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString(FMT(x, ##__VA_ARGS__)).get(), nullptr, nullptr, -1)
 
 // Used by StateObject and its sub-classes
 #define SFMT(x, ...) "Decoder=%p state=%s " x, mMaster->mDecoderID, ToStateStr(GetState()), ##__VA_ARGS__
 #define SLOG(x, ...) MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, (SFMT(x, ##__VA_ARGS__)))
 #define SWARN(x, ...) NS_WARNING(nsPrintfCString(SFMT(x, ##__VA_ARGS__)).get())
 #define SDUMP(x, ...) NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString(SFMT(x, ##__VA_ARGS__)).get(), nullptr, nullptr, -1)
+#define SSAMPLELOG(x, ...) MOZ_LOG(gMediaSampleLog, LogLevel::Debug, (SFMT(x, ##__VA_ARGS__)))
 
 // Certain constants get stored as member variables and then adjusted by various
 // scale factors on a per-decoder basis. We want to make sure to avoid using these
 // constants directly, so we put them in a namespace.
 namespace detail {
 
 // If audio queue has less than this many usecs of decoded audio, we won't risk
 // trying to decode the video, we'll skip decoding video up to the next
@@ -263,16 +263,19 @@ protected:
 
     Exit();
 
     master->mState = s->GetState();
     master->mStateObj.reset(s);
     return s->Enter(Move(aArgs)...);
   }
 
+  RefPtr<MediaDecoder::SeekPromise>
+  SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility);
+
   // Take a raw pointer in order not to change the life cycle of MDSM.
   // It is guaranteed to be valid by MDSM.
   Master* mMaster;
 };
 
 /**
  * Purpose: decode metadata like duration and dimensions of the media resource.
  *
@@ -765,68 +768,40 @@ public:
     // in the middle of seeking and won't have a valid video frame to show
     // when seek is done.
     if (mMaster->mVideoDecodeSuspended) {
       mMaster->mVideoDecodeSuspended = false;
       mMaster->mOnPlaybackEvent.Notify(MediaEventType::ExitVideoSuspend);
       Reader()->SetVideoBlankDecode(false);
     }
 
-    // Create a new SeekTask instance for the incoming seek task.
-    if (mSeekJob.mTarget.IsAccurate() ||
-        mSeekJob.mTarget.IsFast()) {
-      mSeekTask = new AccurateSeekTask(
-        mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
-        Info(), mMaster->Duration(), mMaster->GetMediaTime());
-    } else if (mSeekJob.mTarget.IsNextFrame()) {
-      mSeekTask = new NextFrameSeekTask(
-        mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
-        Info(), mMaster->Duration(),mMaster->GetMediaTime(),
-        AudioQueue(), VideoQueue());
-    } else {
-      MOZ_DIAGNOSTIC_ASSERT(false, "Cannot handle this seek task.");
-    }
+    CreateSeekTask();
 
     // Don't stop playback for a video-only seek since audio is playing.
     if (!mSeekJob.mTarget.IsVideoOnly()) {
       mMaster->StopPlayback();
     }
 
     mMaster->UpdatePlaybackPositionInternal(mSeekJob.mTarget.GetTime().ToMicroseconds());
 
     if (aVisibility == EventVisibility::Observable) {
       mMaster->mOnPlaybackEvent.Notify(MediaEventType::SeekStarted);
       // We want dormant actions to be transparent to the user.
       // So we only notify the change when the seek request is from the user.
       mMaster->UpdateNextFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING);
     }
 
-    // Reset our state machine and decoding pipeline before seeking.
-    if (mSeekTask->NeedToResetMDSM()) {
-      if (mSeekJob.mTarget.IsVideoOnly()) {
-        mMaster->Reset(TrackInfo::kVideoTrack);
-      } else {
-        mMaster->Reset();
-      }
-    }
-
-    // Do the seek.
-    mSeekTaskRequest.Begin(mSeekTask->Seek(mMaster->Duration())
-      ->Then(OwnerThread(), __func__,
-             [this] (const SeekTaskResolveValue& aValue) {
-               OnSeekTaskResolved(aValue);
-             },
-             [this] (const SeekTaskRejectValue& aValue) {
-               OnSeekTaskRejected(aValue);
-             }));
+    ResetMDSM();
+
+    DoSeek();
 
     return mSeekJob.mPromise.Ensure(__func__);
   }
 
-  void Exit() override
+  virtual void Exit() override
   {
     mSeekTaskRequest.DisconnectIfExists();
     mSeekJob.RejectIfExists(__func__);
     mSeekTask->Discard();
   }
 
   State GetState() const override
   {
@@ -869,17 +844,545 @@ public:
   }
 
   void HandleResumeVideoDecoding() override
   {
     // We set mVideoDecodeSuspended to false in Enter().
     MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
   }
 
+protected:
+  SeekJob mSeekJob;
+  RefPtr<SeekTask> mSeekTask;
+  MozPromiseRequestHolder<SeekTask::SeekTaskPromise> mSeekTaskRequest;
+
+  void SeekCompleted();
+
 private:
+  virtual void CreateSeekTask() = 0;
+
+  virtual void ResetMDSM() = 0;
+
+  virtual void DoSeek() = 0;
+
+  virtual int64_t CalculateNewCurrentTime() const = 0;
+};
+
+class MediaDecoderStateMachine::AccurateSeekingState
+  : public MediaDecoderStateMachine::SeekingState
+{
+public:
+  explicit AccurateSeekingState(Master* aPtr) : SeekingState(aPtr)
+  {
+  }
+
+  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
+                                          EventVisibility aVisibility)
+  {
+    MOZ_ASSERT(aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast());
+    return SeekingState::Enter(Move(aSeekJob), aVisibility);
+  }
+
+  void Exit() override
+  {
+    // Disconnect MediaDecoder.
+    mSeekJob.RejectIfExists(__func__);
+
+    // Disconnect MediaDecoderReaderWrapper.
+    mSeekRequest.DisconnectIfExists();
+  }
+
+  void HandleAudioDecoded(MediaData* aAudio) override
+  {
+    MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
+
+    RefPtr<MediaData> audio(aAudio);
+    MOZ_ASSERT(audio);
+
+    // The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
+    // resolved.
+
+    SSAMPLELOG("HandleAudioDecoded [%lld,%lld]", audio->mTime, audio->GetEndTime());
+
+    // Video-only seek doesn't reset audio decoder. There might be pending audio
+    // requests when AccurateSeekTask::Seek() begins. We will just store the data
+    // without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget().
+    if (mSeekJob.mTarget.IsVideoOnly()) {
+      mSeekedAudioData = audio.forget();
+      return;
+    }
+
+    AdjustFastSeekIfNeeded(audio);
+
+    if (mSeekJob.mTarget.IsFast()) {
+      // Non-precise seek; we can stop the seek at the first sample.
+      mSeekedAudioData = audio;
+      mDoneAudioSeeking = true;
+    } else {
+      nsresult rv = DropAudioUpToSeekTarget(audio);
+      if (NS_FAILED(rv)) {
+        OnSeekTaskRejected(rv);
+        return;
+      }
+    }
+
+    if (!mDoneAudioSeeking) {
+      RequestAudioData();
+      return;
+    }
+    MaybeFinishSeek();
+  }
+
+  void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
+  {
+    MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
+
+    RefPtr<MediaData> video(aVideo);
+    MOZ_ASSERT(video);
+
+    // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
+    // resolved.
+
+    SSAMPLELOG("HandleVideoDecoded [%lld,%lld]", video->mTime, video->GetEndTime());
+
+    AdjustFastSeekIfNeeded(video);
+
+    if (mSeekJob.mTarget.IsFast()) {
+      // Non-precise seek. We can stop the seek at the first sample.
+      mSeekedVideoData = video;
+      mDoneVideoSeeking = true;
+    } else {
+      nsresult rv = DropVideoUpToSeekTarget(video.get());
+      if (NS_FAILED(rv)) {
+        OnSeekTaskRejected(rv);
+        return;
+      }
+    }
+
+    if (!mDoneVideoSeeking) {
+      RequestVideoData();
+      return;
+    }
+    MaybeFinishSeek();
+  }
+
+  void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) override
+  {
+    MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
+
+    SSAMPLELOG("OnNotDecoded type=%d reason=%u", aType, aError.Code());
+
+    // Ignore pending requests from video-only seek.
+    if (aType == MediaData::AUDIO_DATA && mSeekJob.mTarget.IsVideoOnly()) {
+      return;
+    }
+
+    // If the decoder is waiting for data, we tell it to call us back when the
+    // data arrives.
+    if (aError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
+      Reader()->WaitForData(aType);
+      return;
+    }
+
+    if (aError == NS_ERROR_DOM_MEDIA_CANCELED) {
+      if (aType == MediaData::AUDIO_DATA) {
+        RequestAudioData();
+      } else {
+        RequestVideoData();
+      }
+      return;
+    }
+
+    if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
+      if (aType == MediaData::AUDIO_DATA) {
+        mIsAudioQueueFinished = true;
+        mDoneAudioSeeking = true;
+      } else {
+        mIsVideoQueueFinished = true;
+        mDoneVideoSeeking = true;
+        if (mFirstVideoFrameAfterSeek) {
+          // Hit the end of stream. Move mFirstVideoFrameAfterSeek into
+          // mSeekedVideoData so we have something to display after seeking.
+          mSeekedVideoData = mFirstVideoFrameAfterSeek.forget();
+        }
+      }
+      MaybeFinishSeek();
+      return;
+    }
+
+    // This is a decode error, delegate to the generic error path.
+    OnSeekTaskRejected(aError);
+  }
+
+  void HandleAudioWaited(MediaData::Type aType) override
+  {
+    MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
+
+    // Ignore pending requests from video-only seek.
+    if (mSeekJob.mTarget.IsVideoOnly()) {
+      return;
+    }
+    RequestAudioData();
+  }
+
+  void HandleVideoWaited(MediaData::Type aType) override
+  {
+    MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
+
+    RequestVideoData();
+  }
+
+  void HandleNotWaited(const WaitForDataRejectValue& aRejection) override
+  {
+    MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
+  }
+
+private:
+  void CreateSeekTask() override
+  {
+    mCurrentTimeBeforeSeek = TimeUnit::FromMicroseconds(mMaster->GetMediaTime());
+    mDoneAudioSeeking = !Info().HasAudio() || mSeekJob.mTarget.IsVideoOnly();
+    mDoneVideoSeeking = !Info().HasVideo();
+  }
+
+  void ResetMDSM() override
+  {
+    if (mSeekJob.mTarget.IsVideoOnly()) {
+      mMaster->Reset(TrackInfo::kVideoTrack);
+    } else {
+      mMaster->Reset();
+    }
+  }
+
+  void DoSeek() override
+  {
+    // Request the demuxer to perform seek.
+    mSeekRequest.Begin(Reader()->Seek(mSeekJob.mTarget, mMaster->Duration())
+      ->Then(OwnerThread(), __func__,
+             [this] (media::TimeUnit aUnit) {
+               OnSeekResolved(aUnit);
+             },
+             [this] (nsresult aResult) {
+               OnSeekRejected(aResult);
+             }));
+  }
+
+  int64_t CalculateNewCurrentTime() const override
+  {
+    const int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
+
+    // For the accurate seek, we always set the newCurrentTime = seekTime so that
+    // the updated HTMLMediaElement.currentTime will always be the seek target;
+    // we rely on the MediaSink to handles the gap between the newCurrentTime and
+    // the real decoded samples' start time.
+    if (mSeekJob.mTarget.IsAccurate()) {
+      return seekTime;
+    }
+
+    // For the fast seek, we update the newCurrentTime with the decoded audio and
+    // video samples, set it to be the one which is closet to the seekTime.
+    if (mSeekJob.mTarget.IsFast()) {
+
+      // A situation that both audio and video approaches the end.
+      if (!mSeekedAudioData && !mSeekedVideoData) {
+        return seekTime;
+      }
+
+      const int64_t audioStart = mSeekedAudioData ? mSeekedAudioData->mTime : INT64_MAX;
+      const int64_t videoStart = mSeekedVideoData ? mSeekedVideoData->mTime : INT64_MAX;
+      const int64_t audioGap = std::abs(audioStart - seekTime);
+      const int64_t videoGap = std::abs(videoStart - seekTime);
+      return audioGap <= videoGap ? audioStart : videoStart;
+    }
+
+    MOZ_ASSERT(false, "AccurateSeekTask doesn't handle other seek types.");
+    return 0;
+  }
+
+  void OnSeekResolved(media::TimeUnit) {
+    mSeekRequest.Complete();
+
+    // We must decode the first samples of active streams, so we can determine
+    // the new stream time. So dispatch tasks to do that.
+    if (!mDoneVideoSeeking) {
+      RequestVideoData();
+    }
+    if (!mDoneAudioSeeking) {
+      RequestAudioData();
+    }
+  }
+
+  void OnSeekRejected(nsresult aResult) {
+    mSeekRequest.Complete();
+
+    MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
+    OnSeekTaskRejected(aResult);
+  }
+
+  void RequestAudioData()
+  {
+    MOZ_ASSERT(!mDoneAudioSeeking);
+    MOZ_ASSERT(!Reader()->IsRequestingAudioData());
+    MOZ_ASSERT(!Reader()->IsWaitingAudioData());
+    Reader()->RequestAudioData();
+  }
+
+  void RequestVideoData()
+  {
+    MOZ_ASSERT(!mDoneVideoSeeking);
+    MOZ_ASSERT(!Reader()->IsRequestingVideoData());
+    MOZ_ASSERT(!Reader()->IsWaitingVideoData());
+    Reader()->RequestVideoData(false, media::TimeUnit());
+  }
+
+  void AdjustFastSeekIfNeeded(MediaData* aSample)
+  {
+    if (mSeekJob.mTarget.IsFast() &&
+        mSeekJob.mTarget.GetTime() > mCurrentTimeBeforeSeek &&
+        aSample->mTime < mCurrentTimeBeforeSeek.ToMicroseconds()) {
+      // We are doing a fastSeek, but we ended up *before* the previous
+      // playback position. This is surprising UX, so switch to an accurate
+      // seek and decode to the seek target. This is not conformant to the
+      // spec, fastSeek should always be fast, but until we get the time to
+      // change all Readers to seek to the keyframe after the currentTime
+      // in this case, we'll just decode forward. Bug 1026330.
+      mSeekJob.mTarget.SetType(SeekTarget::Accurate);
+    }
+  }
+
+  nsresult DropAudioUpToSeekTarget(MediaData* aSample)
+  {
+    RefPtr<AudioData> audio(aSample->As<AudioData>());
+    MOZ_ASSERT(audio && mSeekJob.mTarget.IsAccurate());
+
+    CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, Info().mAudio.mRate);
+    if (!sampleDuration.isValid()) {
+      return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
+    }
+
+    if (audio->mTime + sampleDuration.value() <= mSeekJob.mTarget.GetTime().ToMicroseconds()) {
+      // Our seek target lies after the frames in this AudioData. Don't
+      // push it onto the audio queue, and keep decoding forwards.
+      return NS_OK;
+    }
+
+    if (audio->mTime > mSeekJob.mTarget.GetTime().ToMicroseconds()) {
+      // The seek target doesn't lie in the audio block just after the last
+      // audio frames we've seen which were before the seek target. This
+      // could have been the first audio data we've seen after seek, i.e. the
+      // seek terminated after the seek target in the audio stream. Just
+      // abort the audio decode-to-target, the state machine will play
+      // silence to cover the gap. Typically this happens in poorly muxed
+      // files.
+      SWARN("Audio not synced after seek, maybe a poorly muxed file?");
+      mSeekedAudioData = audio;
+      mDoneAudioSeeking = true;
+      return NS_OK;
+    }
+
+    // The seek target lies somewhere in this AudioData's frames, strip off
+    // any frames which lie before the seek target, so we'll begin playback
+    // exactly at the seek target.
+    NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() >= audio->mTime,
+                 "Target must at or be after data start.");
+    NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
+                 "Data must end after target.");
+
+    CheckedInt64 framesToPrune =
+      UsecsToFrames(mSeekJob.mTarget.GetTime().ToMicroseconds() - audio->mTime, Info().mAudio.mRate);
+    if (!framesToPrune.isValid()) {
+      return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
+    }
+    if (framesToPrune.value() > audio->mFrames) {
+      // We've messed up somehow. Don't try to trim frames, the |frames|
+      // variable below will overflow.
+      SWARN("Can't prune more frames that we have!");
+      return NS_ERROR_FAILURE;
+    }
+    uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
+    uint32_t channels = audio->mChannels;
+    AlignedAudioBuffer audioData(frames * channels);
+    if (!audioData) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    memcpy(audioData.get(),
+           audio->mAudioData.get() + (framesToPrune.value() * channels),
+           frames * channels * sizeof(AudioDataValue));
+    CheckedInt64 duration = FramesToUsecs(frames, Info().mAudio.mRate);
+    if (!duration.isValid()) {
+      return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
+    }
+    RefPtr<AudioData> data(new AudioData(audio->mOffset,
+                                         mSeekJob.mTarget.GetTime().ToMicroseconds(),
+                                         duration.value(),
+                                         frames,
+                                         Move(audioData),
+                                         channels,
+                                         audio->mRate));
+    MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
+    mSeekedAudioData = data;
+    mDoneAudioSeeking = true;
+
+    return NS_OK;
+  }
+
+  nsresult DropVideoUpToSeekTarget(MediaData* aSample)
+  {
+    RefPtr<VideoData> video(aSample->As<VideoData>());
+    MOZ_ASSERT(video);
+    SLOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
+         video->mTime, video->GetEndTime());
+    const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
+
+    // If the frame end time is less than the seek target, we won't want
+    // to display this frame after the seek, so discard it.
+    if (target >= video->GetEndTime()) {
+      SLOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
+           video->mTime, video->GetEndTime(), target);
+      mFirstVideoFrameAfterSeek = video;
+    } else {
+      if (target >= video->mTime && video->GetEndTime() >= target) {
+        // The seek target lies inside this frame's time slice. Adjust the frame's
+        // start time to match the seek target. We do this by replacing the
+        // first frame with a shallow copy which has the new timestamp.
+        RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video.get(), target);
+        video = temp;
+      }
+      mFirstVideoFrameAfterSeek = nullptr;
+
+      SLOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
+                  video->mTime, video->GetEndTime(), target);
+
+      MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
+      mSeekedVideoData = video;
+      mDoneVideoSeeking = true;
+    }
+
+    return NS_OK;
+  }
+
+  void MaybeFinishSeek()
+  {
+    if (mDoneAudioSeeking && mDoneVideoSeeking) {
+      OnSeekTaskResolved();
+    }
+  }
+
+  void OnSeekTaskResolved()
+  {
+    if (mSeekedAudioData) {
+      mMaster->Push(mSeekedAudioData);
+      mMaster->mDecodedAudioEndTime = std::max(
+        mSeekedAudioData->GetEndTime(), mMaster->mDecodedAudioEndTime);
+    }
+
+    if (mSeekedVideoData) {
+      mMaster->Push(mSeekedVideoData);
+      mMaster->mDecodedVideoEndTime = std::max(
+        mSeekedVideoData->GetEndTime(), mMaster->mDecodedVideoEndTime);
+    }
+
+    if (mIsAudioQueueFinished) {
+      AudioQueue().Finish();
+    }
+
+    if (mIsVideoQueueFinished) {
+      VideoQueue().Finish();
+    }
+
+    SeekCompleted();
+  }
+
+  void OnSeekTaskRejected(MediaResult aError)
+  {
+    if (mIsAudioQueueFinished) {
+      AudioQueue().Finish();
+    }
+
+    if (mIsVideoQueueFinished) {
+      VideoQueue().Finish();
+    }
+
+    mMaster->DecodeError(aError);
+  }
+
+  /*
+   * Track the current seek promise made by the reader.
+   */
+  MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
+
+  /*
+   * Internal state.
+   */
+  media::TimeUnit mCurrentTimeBeforeSeek;
+  bool mDoneAudioSeeking = false;
+  bool mDoneVideoSeeking = false;
+
+  // This temporarily stores the first frame we decode after we seek.
+  // This is so that if we hit end of stream while we're decoding to reach
+  // the seek target, we will still have a frame that we can display as the
+  // last frame in the media.
+  RefPtr<MediaData> mFirstVideoFrameAfterSeek;
+
+  /*
+   * Information which are going to be returned to MDSM.
+   */
+  RefPtr<MediaData> mSeekedAudioData;
+  RefPtr<MediaData> mSeekedVideoData;
+  bool mIsAudioQueueFinished = false;
+  bool mIsVideoQueueFinished = false;
+};
+
+class MediaDecoderStateMachine::NextFrameSeekingState
+  : public MediaDecoderStateMachine::SeekingState
+{
+public:
+  explicit NextFrameSeekingState(Master* aPtr) : SeekingState(aPtr)
+  {
+  }
+
+  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
+                                          EventVisibility aVisibility)
+  {
+    MOZ_ASSERT(aSeekJob.mTarget.IsNextFrame());
+    return SeekingState::Enter(Move(aSeekJob), aVisibility);
+  }
+
+private:
+  void CreateSeekTask() override
+  {
+    mSeekTask = new NextFrameSeekTask(
+      mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
+      Info(), mMaster->Duration(),mMaster->GetMediaTime(),
+      AudioQueue(), VideoQueue());
+  }
+
+  void ResetMDSM() override
+  {
+    // Do nothing.
+  }
+
+  void DoSeek() override
+  {
+    mSeekTaskRequest.Begin(mSeekTask->Seek(mMaster->Duration())
+      ->Then(OwnerThread(), __func__,
+             [this] (const SeekTaskResolveValue& aValue) {
+               OnSeekTaskResolved(aValue);
+             },
+             [this] (const SeekTaskRejectValue& aValue) {
+               OnSeekTaskRejected(aValue);
+             }));
+  }
+
+  int64_t CalculateNewCurrentTime() const override
+  {
+    return mSeekTask->CalculateNewCurrentTime();
+  }
+
   void OnSeekTaskResolved(const SeekTaskResolveValue& aValue)
   {
     mSeekTaskRequest.Complete();
 
     if (aValue.mSeekedAudioData) {
       mMaster->Push(aValue.mSeekedAudioData);
       mMaster->mDecodedAudioEndTime = std::max(
         aValue.mSeekedAudioData->GetEndTime(), mMaster->mDecodedAudioEndTime);
@@ -912,21 +1415,16 @@ private:
 
     if (aValue.mIsVideoQueueFinished) {
       VideoQueue().Finish();
     }
 
     mMaster->DecodeError(aValue.mError);
   }
 
-  void SeekCompleted();
-
-  SeekJob mSeekJob;
-  MozPromiseRequestHolder<SeekTask::SeekTaskPromise> mSeekTaskRequest;
-  RefPtr<SeekTask> mSeekTask;
 };
 
 /**
  * Purpose: stop playback until enough data is decoded to continue playback.
  *
  * Transition to:
  *   SEEKING if any seek request.
  *   SHUTDOWN if any decode error.
@@ -1222,17 +1720,17 @@ StateObject::HandleNotWaited(const WaitF
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::
 StateObject::HandleSeek(SeekTarget aTarget)
 {
   SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
   SeekJob seekJob;
   seekJob.mTarget = aTarget;
-  return SetState<SeekingState>(Move(seekJob), EventVisibility::Observable);
+  return SetSeekingState(Move(seekJob), EventVisibility::Observable);
 }
 
 RefPtr<ShutdownPromise>
 MediaDecoderStateMachine::
 StateObject::HandleShutdown()
 {
   return SetState<ShutdownState>();
 }
@@ -1297,22 +1795,38 @@ StateObject::HandleResumeVideoDecoding()
   const SeekTarget::Type type = mMaster->HasAudio()
                                 ? SeekTarget::Type::Accurate
                                 : SeekTarget::Type::PrevSyncPoint;
 
   seekJob.mTarget = SeekTarget(mMaster->GetMediaTime(),
                                type,
                                true /* aVideoOnly */);
 
-  SetState<SeekingState>(Move(seekJob), EventVisibility::Suppressed)->Then(
+  SetSeekingState(Move(seekJob), EventVisibility::Suppressed)->Then(
     AbstractThread::MainThread(), __func__,
     [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
     [](){});
 }
 
+RefPtr<MediaDecoder::SeekPromise>
+MediaDecoderStateMachine::
+StateObject::SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility)
+{
+  if (aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast()) {
+    return SetState<AccurateSeekingState>(Move(aSeekJob), aVisibility);
+  }
+
+  if (aSeekJob.mTarget.IsNextFrame()) {
+    return SetState<NextFrameSeekingState>(Move(aSeekJob), aVisibility);
+  }
+
+  MOZ_ASSERT_UNREACHABLE("Unknown SeekTarget::Type.");
+  return nullptr;
+}
+
 void
 MediaDecoderStateMachine::
 DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
 {
   mMetadataRequest.Complete();
 
   // Set mode to PLAYBACK after reading metadata.
   Resource()->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
@@ -1359,26 +1873,26 @@ DecodeMetadataState::OnMetadataRead(Meta
 void
 MediaDecoderStateMachine::
 DormantState::HandlePlayStateChanged(MediaDecoder::PlayState aPlayState)
 {
   if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
     // Exit dormant when the user wants to play.
     MOZ_ASSERT(!Info().IsEncrypted() || mMaster->mCDMProxy);
     MOZ_ASSERT(mMaster->mSentFirstFrameLoadedEvent);
-    SetState<SeekingState>(Move(mPendingSeek), EventVisibility::Suppressed);
+    SetSeekingState(Move(mPendingSeek), EventVisibility::Suppressed);
   }
 }
 
 void
 MediaDecoderStateMachine::
 WaitForCDMState::HandleCDMProxyReady()
 {
   if (mPendingSeek.Exists()) {
-    SetState<SeekingState>(Move(mPendingSeek), EventVisibility::Observable);
+    SetSeekingState(Move(mPendingSeek), EventVisibility::Observable);
   } else {
     SetState<DecodingFirstFrameState>();
   }
 }
 
 void
 MediaDecoderStateMachine::
 DecodingFirstFrameState::Enter()
@@ -1490,17 +2004,17 @@ DecodingState::MaybeStartBuffering()
     SetState<BufferingState>();
   }
 }
 
 void
 MediaDecoderStateMachine::
 SeekingState::SeekCompleted()
 {
-  const int64_t newCurrentTime = mSeekTask->CalculateNewCurrentTime();
+  const int64_t newCurrentTime = CalculateNewCurrentTime();
 
   bool isLiveStream = Resource()->IsLiveStream();
   if (newCurrentTime == mMaster->Duration().ToMicroseconds() && !isLiveStream) {
     // Seeked to end of media. Explicitly finish the queues so DECODING
     // will transition to COMPLETED immediately. Note we don't do
     // this when playing a live stream, since the end of media will advance
     // once we download more data!
     AudioQueue().Finish();
@@ -1651,17 +2165,17 @@ ShutdownState::Enter()
   master->mNextFrameStatus.DisconnectAll();
   master->mCurrentPosition.DisconnectAll();
   master->mPlaybackOffset.DisconnectAll();
   master->mIsAudioDataAudible.DisconnectAll();
 
   // Shut down the watch manager to stop further notifications.
   master->mWatchManager.Shutdown();
 
-  return Reader()->Shutdown()->ThenPromise(
+  return Reader()->Shutdown()->Then(
     OwnerThread(), __func__, master,
     &MediaDecoderStateMachine::FinishShutdown,
     &MediaDecoderStateMachine::FinishShutdown);
 }
 
 #define INIT_WATCHABLE(name, val) \
   name(val, "MediaDecoderStateMachine::" #name)
 #define INIT_MIRROR(name, val) \
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -260,16 +260,18 @@ public:
 private:
   class StateObject;
   class DecodeMetadataState;
   class WaitForCDMState;
   class DormantState;
   class DecodingFirstFrameState;
   class DecodingState;
   class SeekingState;
+  class AccurateSeekingState;
+  class NextFrameSeekingState;
   class BufferingState;
   class CompletedState;
   class ShutdownState;
 
   static const char* ToStateStr(State aState);
   static const char* ToStr(NextFrameStatus aStatus);
   const char* ToStateStr();
 
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -926,17 +926,17 @@ MediaFormatReader::OnDemuxFailed(TrackTy
 
 void
 MediaFormatReader::DoDemuxVideo()
 {
   auto p = mVideo.mTrackDemuxer->GetSamples(1);
 
   if (mVideo.mFirstDemuxedSampleTime.isNothing()) {
     RefPtr<MediaFormatReader> self = this;
-    p = p->ThenPromise(OwnerThread(), __func__,
+    p = p->Then(OwnerThread(), __func__,
                 [self] (RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
                   self->OnFirstDemuxCompleted(TrackInfo::kVideoTrack, aSamples);
                 },
                 [self] (const MediaResult& aError) {
                   self->OnFirstDemuxFailed(TrackInfo::kVideoTrack, aError);
                 });
   }
 
@@ -992,17 +992,17 @@ MediaFormatReader::RequestAudioData()
 
 void
 MediaFormatReader::DoDemuxAudio()
 {
   auto p = mAudio.mTrackDemuxer->GetSamples(1);
 
   if (mAudio.mFirstDemuxedSampleTime.isNothing()) {
     RefPtr<MediaFormatReader> self = this;
-    p = p->ThenPromise(OwnerThread(), __func__,
+    p = p->Then(OwnerThread(), __func__,
                 [self] (RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
                   self->OnFirstDemuxCompleted(TrackInfo::kAudioTrack, aSamples);
                 },
                 [self] (const MediaResult& aError) {
                   self->OnFirstDemuxFailed(TrackInfo::kAudioTrack, aError);
                 });
   }
 
--- a/dom/media/NextFrameSeekTask.cpp
+++ b/dom/media/NextFrameSeekTask.cpp
@@ -50,23 +50,16 @@ NextFrameSeekTask::Discard()
   AssertOwnerThread();
 
   // Disconnect MDSM.
   RejectIfExist(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
 
   mIsDiscarded = true;
 }
 
-bool
-NextFrameSeekTask::NeedToResetMDSM() const
-{
-  AssertOwnerThread();
-  return false;
-}
-
 int64_t
 NextFrameSeekTask::CalculateNewCurrentTime() const
 {
   AssertOwnerThread();
 
   // The HTMLMediaElement.currentTime should be updated to the seek target
   // which has been updated to the next frame's time.
   return mTarget.GetTime().ToMicroseconds();
--- a/dom/media/NextFrameSeekTask.h
+++ b/dom/media/NextFrameSeekTask.h
@@ -33,33 +33,30 @@ public:
                    int64_t aCurrentTime,
                    MediaQueue<MediaData>& aAudioQueue,
                    MediaQueue<MediaData>& aVideoQueue);
 
   void Discard() override;
 
   RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
 
-  bool NeedToResetMDSM() const override;
-
   int64_t CalculateNewCurrentTime() const override;
 
   void HandleAudioDecoded(MediaData* aAudio) override;
 
   void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override;
 
   void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) override;
 
   void HandleAudioWaited(MediaData::Type aType) override;
 
   void HandleVideoWaited(MediaData::Type aType) override;
 
   void HandleNotWaited(const WaitForDataRejectValue& aRejection) override;
 
-private:
   ~NextFrameSeekTask();
 
   void RequestVideoData();
 
   bool NeedMoreVideo() const;
 
   bool IsVideoRequestPending() const;
 
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -3,16 +3,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/. */
 
 #ifndef SEEK_TASK_H
 #define SEEK_TASK_H
 
 #include "mozilla/MozPromise.h"
+#include "MediaData.h"          // For MediaData::Type.
+#include "MediaDecoderReader.h" // For WaitForDataRejectValue.
 #include "MediaResult.h"
 #include "SeekTarget.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaData;
 class MediaDecoderReaderWrapper;
@@ -51,18 +53,16 @@ public:
 
   using SeekTaskPromise =
     MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
 
   virtual void Discard() = 0;
 
   virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) = 0;
 
-  virtual bool NeedToResetMDSM() const = 0;
-
   virtual int64_t CalculateNewCurrentTime() const = 0;
 
   virtual void HandleAudioDecoded(MediaData* aAudio) = 0;
 
   virtual void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) = 0;
 
   virtual void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) = 0;
 
@@ -75,16 +75,17 @@ public:
 protected:
   SeekTask(const void* aDecoderID,
            AbstractThread* aThread,
            MediaDecoderReaderWrapper* aReader,
            const SeekTarget& aTarget);
 
   virtual ~SeekTask();
 
+public:
   void Resolve(const char* aCallSite);
 
   void RejectIfExist(const MediaResult& aError, const char* aCallSite);
 
   void AssertOwnerThread() const;
 
   AbstractThread* OwnerThread() const;
 
--- a/dom/media/flac/FlacDemuxer.cpp
+++ b/dom/media/flac/FlacDemuxer.cpp
@@ -55,17 +55,17 @@ public:
   // Parse the current packet and check that it made a valid flac frame header.
   // From https://xiph.org/flac/format.html#frame_header
   // A valid header is one that can be decoded without error and that has a
   // valid CRC.
   // aPacket must points to a buffer that is at least FLAC_MAX_FRAME_HEADER_SIZE
   // bytes.
   bool Parse(const uint8_t* aPacket)
   {
-    mp4_demuxer::BitReader br(aPacket, FLAC_MAX_FRAME_HEADER_SIZE);
+    mp4_demuxer::BitReader br(aPacket, FLAC_MAX_FRAME_HEADER_SIZE * 8);
 
     // Frame sync code.
     if ((br.ReadBits(15) & 0x7fff) != 0x7ffc) {
       return false;
     }
 
     // Variable block size stream code.
     mVariableBlockSize = br.ReadBit();
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -904,17 +904,17 @@ GeckoMediaPluginServiceParent::AsyncAddP
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   nsString dir(aDirectory);
   RefPtr<GeckoMediaPluginServiceParent> self = this;
   return InvokeAsync<nsString&&>(
            thread, this, __func__,
            &GeckoMediaPluginServiceParent::AddOnGMPThread, dir)
-    ->ThenPromise(AbstractThread::MainThread(), __func__,
+    ->Then(AbstractThread::MainThread(), __func__,
       [dir, self]() -> void {
         LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s succeeded",
               NS_ConvertUTF16toUTF8(dir).get()));
         MOZ_ASSERT(NS_IsMainThread());
         self->UpdateContentProcessGMPCapabilities();
       },
       [dir]() -> void {
         LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s failed",
@@ -1129,17 +1129,17 @@ GeckoMediaPluginServiceParent::AddOnGMPT
 
   RefPtr<GMPParent> gmp = CreateGMPParent();
   if (!gmp) {
     NS_WARNING("Can't Create GMPParent");
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   RefPtr<GeckoMediaPluginServiceParent> self(this);
-  return gmp->Init(this, directory)->ThenPromise(thread, __func__,
+  return gmp->Init(this, directory)->Then(thread, __func__,
     [gmp, self, dir]() -> void {
       LOGD(("%s::%s: %s Succeeded", __CLASS__, __FUNCTION__, dir.get()));
       {
         MutexAutoLock lock(self->mMutex);
         self->mPlugins.AppendElement(gmp);
       }
     },
     [dir]() -> void {
--- a/dom/media/gtest/TestMozPromise.cpp
+++ b/dom/media/gtest/TestMozPromise.cpp
@@ -143,29 +143,29 @@ TEST(MozPromise, AsyncResolve)
 
 TEST(MozPromise, CompletionPromises)
 {
   bool invokedPass = false;
   AutoTaskQueue atq;
   RefPtr<TaskQueue> queue = atq.Queue();
   RunOnTaskQueue(queue, [queue, &invokedPass] () -> void {
     TestPromise::CreateAndResolve(40, __func__)
-    ->ThenPromise(queue, __func__,
+    ->Then(queue, __func__,
       [] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndResolve(aVal + 10, __func__); },
       DO_FAIL)
-    ->ThenPromise(queue, __func__, [&invokedPass] () -> void { invokedPass = true; }, DO_FAIL)
-    ->ThenPromise(queue, __func__,
+    ->Then(queue, __func__, [&invokedPass] () -> void { invokedPass = true; }, DO_FAIL)
+    ->Then(queue, __func__,
       [queue] (int aVal) -> RefPtr<TestPromise> {
         RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
         nsCOMPtr<nsIRunnable> resolver = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(aVal - 8), 10);
         queue->Dispatch(resolver.forget());
         return RefPtr<TestPromise>(p);
       },
       DO_FAIL)
-    ->ThenPromise(queue, __func__,
+    ->Then(queue, __func__,
       [queue] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndReject(double(aVal - 42) + 42.0, __func__); },
       DO_FAIL)
     ->Then(queue, __func__,
       DO_FAIL,
       [queue, &invokedPass] (double aVal) -> void { EXPECT_EQ(aVal, 42.0); EXPECT_TRUE(invokedPass); queue->BeginShutdown(); });
   });
 }
 
@@ -223,17 +223,17 @@ TEST(MozPromise, Chaining)
   AutoTaskQueue atq;
   RefPtr<TaskQueue> queue = atq.Queue();
   MozPromiseRequestHolder<TestPromise> holder;
 
   RunOnTaskQueue(queue, [queue, &holder] () {
     auto p = TestPromise::CreateAndResolve(42, __func__);
     const size_t kIterations = 100;
     for (size_t i = 0; i < kIterations; ++i) {
-      p = p->ThenPromise(queue, __func__,
+      p = p->Then(queue, __func__,
         [] (int aVal) {
           EXPECT_EQ(aVal, 42);
         },
         [] () {}
       );
 
       if (i == kIterations / 2) {
         p->Then(queue, __func__,
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -72,17 +72,16 @@ XPIDL_SOURCES += [
     'nsIDOMNavigatorUserMedia.idl',
     'nsIMediaManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'AbstractMediaDecoder.h',
-    'AccurateSeekTask.h',
     'ADTSDecoder.h',
     'ADTSDemuxer.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
     'AudioConverter.h',
     'AudioMixer.h',
     'AudioPacketizer.h',
@@ -183,17 +182,16 @@ EXPORTS.mozilla.dom += [
     'TextTrackRegion.h',
     'VideoPlaybackQuality.h',
     'VideoStreamTrack.h',
     'VideoTrack.h',
     'VideoTrackList.h',
 ]
 
 UNIFIED_SOURCES += [
-    'AccurateSeekTask.cpp',
     'ADTSDecoder.cpp',
     'ADTSDemuxer.cpp',
     'AudioCaptureStream.cpp',
     'AudioChannelFormat.cpp',
     'AudioCompactor.cpp',
     'AudioConverter.cpp',
     'AudioSegment.cpp',
     'AudioStream.cpp',
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -94,28 +94,16 @@ public:
   {
     mDecoderCallback = nullptr;
   }
 
 protected:
   MediaDataDecoderCallback* mDecoderCallback;
 };
 
-struct SampleTime final
-{
-  SampleTime(int64_t aStart, int64_t aDuration)
-    : mStart(aStart)
-    , mDuration(aDuration)
-  {}
-
-  int64_t mStart;
-  int64_t mDuration;
-};
-
-
 class RemoteVideoDecoder final : public RemoteDataDecoder
 {
 public:
   // Hold an output buffer and render it to the surface when the frame is sent to compositor, or
   // release it if not presented.
   class RenderOrReleaseOutput : public VideoData::Listener
   {
   public:
@@ -283,39 +271,45 @@ public:
   }
 
   bool SupportDecoderRecycling() const override { return mIsCodecSupportAdaptivePlayback; }
 
 private:
   class DurationQueue {
   public:
 
+    DurationQueue() : mMutex("Video duration queue") {}
+
     void Clear()
     {
+      MutexAutoLock lock(mMutex);
       mValues.clear();
     }
 
     void Put(int64_t aDurationUs)
     {
+      MutexAutoLock lock(mMutex);
       mValues.emplace_back(aDurationUs);
     }
 
     Maybe<int64_t> Get()
     {
+      MutexAutoLock lock(mMutex);
       if (mValues.empty()) {
         return Nothing();
       }
 
       auto value = Some(mValues.front());
       mValues.pop_front();
 
       return value;
     }
 
   private:
+    Mutex mMutex; // To protect mValues.
     std::deque<int64_t> mValues;
   };
 
   layers::ImageContainer* mImageContainer;
   const VideoInfo& mConfig;
   RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
   DurationQueue mInputDurations;
   bool mIsCodecSupportAdaptivePlayback = false;
--- a/dom/media/platforms/omx/OmxDataDecoder.cpp
+++ b/dom/media/platforms/omx/OmxDataDecoder.cpp
@@ -254,25 +254,25 @@ OmxDataDecoder::DoAsyncShutdown()
   MOZ_ASSERT(!mFlushing);
 
   mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
   mWatchManager.Unwatch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
 
   // Flush to all ports, so all buffers can be returned from component.
   RefPtr<OmxDataDecoder> self = this;
   mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
-    ->ThenPromise(mOmxTaskQueue, __func__,
+    ->Then(mOmxTaskQueue, __func__,
            [self] () -> RefPtr<OmxCommandPromise> {
              LOGL("DoAsyncShutdown: flush complete");
              return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
            },
            [self] () {
              self->mOmxLayer->Shutdown();
            })
-    ->ThenPromise(mOmxTaskQueue, __func__,
+    ->Then(mOmxTaskQueue, __func__,
            [self] () -> RefPtr<OmxCommandPromise> {
              RefPtr<OmxCommandPromise> p =
                self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
 
              // According to spec 3.1.1.2.2.1:
              // OMX_StateLoaded needs to be sent before releasing buffers.
              // And state transition from OMX_StateIdle to OMX_StateLoaded
              // is completed when all of the buffers have been removed
@@ -791,17 +791,17 @@ OmxDataDecoder::PortSettingsChanged()
                                               sizeof(def));
   CHECK_OMX_ERR(err);
 
   RefPtr<OmxDataDecoder> self = this;
   if (def.bEnabled) {
     // 1. disable port.
     LOG("PortSettingsChanged: disable port %d", def.nPortIndex);
     mOmxLayer->SendCommand(OMX_CommandPortDisable, mPortSettingsChanged, nullptr)
-      ->ThenPromise(mOmxTaskQueue, __func__,
+      ->Then(mOmxTaskQueue, __func__,
              [self, def] () -> RefPtr<OmxCommandPromise> {
                // 3. enable port.
                // Send enable port command.
                RefPtr<OmxCommandPromise> p =
                  self->mOmxLayer->SendCommand(OMX_CommandPortEnable,
                                               self->mPortSettingsChanged,
                                               nullptr);
 
--- a/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl
+++ b/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl
@@ -23,15 +23,12 @@ namespace plugins {
 protocol PPluginBackgroundDestroyer {
     manager PPluginInstance;
 
     // The ctor message for this protocol serves double-duty as
     // notification that that the background is stale.
 
 parent:
     async __delete__();
-
-state DESTROYING:
-    recv __delete__;
 };
 
 }  // namespace plugins
 }  // namespace mozilla
--- a/embedding/components/webbrowserpersist/PWebBrowserPersistDocument.ipdl
+++ b/embedding/components/webbrowserpersist/PWebBrowserPersistDocument.ipdl
@@ -68,23 +68,25 @@ child:
   async SetPersistFlags(uint32_t aNewFlags);
   async PWebBrowserPersistResources();
   async PWebBrowserPersistSerialize(WebBrowserPersistURIMap aMap,
                                     nsCString aRequestedContentType,
                                     uint32_t aEncoderFlags,
                                     uint32_t aWrapColumn);
   async __delete__();
 
+/*
 state START:
   recv Attributes goto MAIN;
   recv InitFailure goto FAILED;
 
 state MAIN:
   send SetPersistFlags goto MAIN;
   send PWebBrowserPersistResources goto MAIN;
   send PWebBrowserPersistSerialize goto MAIN;
   send __delete__;
 
 state FAILED:
   send __delete__;
+*/
 };
 
 } // namespace mozilla
--- a/gfx/2d/Point.h
+++ b/gfx/2d/Point.h
@@ -143,17 +143,17 @@ struct PointTyped :
 };
 typedef PointTyped<UnknownUnits> Point;
 typedef PointTyped<UnknownUnits, double> PointDouble;
 
 template<class units>
 IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) {
   return IntPointTyped<units>::Round(aPoint.x, aPoint.y);
 }
-  
+
 template<class units>
 IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) {
   return IntPointTyped<units>::Truncate(aPoint.x, aPoint.y);
 }
 
 template<class units, class F = Float>
 struct Point3DTyped :
   public BasePoint3D< F, Point3DTyped<units, F> > {
@@ -213,29 +213,39 @@ struct Point4DTyped :
   static_assert(IsPixel<units>::value,
                 "'units' must be a coordinate system tag");
 
   typedef BasePoint4D< F, Point4DTyped<units, F> > Super;
 
   Point4DTyped() : Super() {}
   Point4DTyped(F aX, F aY, F aZ, F aW) : Super(aX, aY, aZ, aW) {}
 
+  explicit Point4DTyped(const Point3DTyped<units, F>& aPoint)
+    : Super(aPoint.x, aPoint.y, aPoint.z, 1) {}
+
   // XXX When all of the code is ported, the following functions to convert to and from
   // unknown types should be removed.
 
   static Point4DTyped<units, F> FromUnknownPoint(const Point4DTyped<UnknownUnits, F>& aPoint) {
     return Point4DTyped<units, F>(aPoint.x, aPoint.y, aPoint.z, aPoint.w);
   }
 
   Point4DTyped<UnknownUnits, F> ToUnknownPoint() const {
     return Point4DTyped<UnknownUnits, F>(this->x, this->y, this->z, this->w);
   }
 
-  PointTyped<units, F> As2DPoint() {
-    return PointTyped<units, F>(this->x / this->w, this->y / this->w);
+  PointTyped<units, F> As2DPoint() const {
+    return PointTyped<units, F>(this->x / this->w,
+                                this->y / this->w);
+  }
+
+  Point3DTyped<units, F> As3DPoint() const {
+    return Point3DTyped<units, F>(this->x / this->w,
+                                  this->y / this->w,
+                                  this->z / this->w);
   }
 };
 typedef Point4DTyped<UnknownUnits> Point4D;
 typedef Point4DTyped<UnknownUnits, double> PointDouble4D;
 
 template<class units>
 struct IntSizeTyped :
   public BaseSize< int32_t, IntSizeTyped<units> >,
--- a/gfx/2d/Polygon.h
+++ b/gfx/2d/Polygon.h
@@ -12,84 +12,136 @@
 #include "Point.h"
 #include "Triangle.h"
 
 #include <initializer_list>
 
 namespace mozilla {
 namespace gfx {
 
-// Polygon3DTyped stores the points of a convex planar polygon.
+template<class Units>
+Point4DTyped<Units>
+CalculateEdgeIntersect(const Point4DTyped<Units>& aFirst,
+                       const Point4DTyped<Units>& aSecond)
+{
+  static const float w = 0.00001f;
+  const float t = (w - aFirst.w) / (aSecond.w - aFirst.w);
+  return aFirst + (aSecond - aFirst) * t;
+}
+
 template<class Units>
-class Polygon3DTyped {
+nsTArray<Point4DTyped<Units>>
+ClipHomogeneous(const nsTArray<Point4DTyped<Units>>& aPoints)
+{
+  nsTArray<Point4DTyped<Units>> outPoints;
+
+  const size_t pointCount = aPoints.Length();
+  for (size_t i = 0; i < pointCount; ++i) {
+    const Point4DTyped<Units>& first = aPoints[i];
+    const Point4DTyped<Units>& second = aPoints[(i + 1) % pointCount];
+
+    MOZ_ASSERT(first.w != 0.0f || second.w != 0.0f);
+
+    if (first.w > 0.0f) {
+      outPoints.AppendElement(first);
+    }
+
+    if ((first.w <= 0.0f) ^ (second.w <= 0.0f)) {
+      outPoints.AppendElement(CalculateEdgeIntersect(first, second));
+    }
+  }
+
+  return outPoints;
+}
+
+template<class Units>
+nsTArray<Point4DTyped<Units>>
+ToPoints4D(const nsTArray<Point3DTyped<Units>>& aPoints)
+{
+  nsTArray<Point4DTyped<Units>> points;
+
+  for (const Point3DTyped<Units>& point : aPoints) {
+    points.AppendElement(Point4DTyped<Units>(point));
+  }
+
+  return points;
+}
+
+// PolygonTyped stores the points of a convex planar polygon.
+template<class Units>
+class PolygonTyped {
+  typedef Point3DTyped<Units> Point3DType;
+  typedef Point4DTyped<Units> Point4DType;
+
 public:
-  Polygon3DTyped() {}
+  PolygonTyped() {}
 
-  explicit Polygon3DTyped(const std::initializer_list<Point3DTyped<Units>>& aPoints,
-                          Point3DTyped<Units> aNormal =
-                            Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
-    : mNormal(aNormal), mPoints(aPoints)
+  explicit PolygonTyped(const std::initializer_list<Point3DType>& aPoints)
+    : mNormal(DefaultNormal()),
+      mPoints(ToPoints4D(nsTArray<Point3DType>(aPoints)))
   {
 #ifdef DEBUG
     EnsurePlanarPolygon();
 #endif
   }
 
-  explicit Polygon3DTyped(nsTArray<Point3DTyped<Units>>&& aPoints,
-                          Point3DTyped<Units> aNormal =
-                            Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
-    : mNormal(aNormal), mPoints(Move(aPoints))
+  explicit PolygonTyped(const nsTArray<Point3DType>& aPoints)
+    : mNormal(DefaultNormal()), mPoints(ToPoints4D(aPoints))
   {
 #ifdef DEBUG
     EnsurePlanarPolygon();
 #endif
   }
 
-  explicit Polygon3DTyped(const nsTArray<Point3DTyped<Units>>& aPoints,
-                          Point3DTyped<Units> aNormal =
-                            Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
+  explicit PolygonTyped(const nsTArray<Point4DType>& aPoints,
+                          const Point4DType& aNormal = DefaultNormal())
     : mNormal(aNormal), mPoints(aPoints)
-  {
-#ifdef DEBUG
-    EnsurePlanarPolygon();
-#endif
-  }
+  {}
+
+  explicit PolygonTyped(nsTArray<Point4DType>&& aPoints,
+                          const Point4DType& aNormal = DefaultNormal())
+    : mNormal(aNormal), mPoints(Move(aPoints))
+  {}
 
   RectTyped<Units> BoundingBox() const
   {
+    if (mPoints.IsEmpty()) {
+      return RectTyped<Units>();
+    }
+
     float minX, maxX, minY, maxY;
     minX = maxX = mPoints[0].x;
     minY = maxY = mPoints[0].y;
 
-    for (const Point3DTyped<Units>& point : mPoints) {
+    for (const Point4DType& point : mPoints) {
       minX = std::min(point.x, minX);
       maxX = std::max(point.x, maxX);
 
       minY = std::min(point.y, minY);
       maxY = std::max(point.y, maxY);
     }
 
     return RectTyped<Units>(minX, minY, maxX - minX, maxY - minY);
   }
 
-  nsTArray<float>
-  CalculateDotProducts(const Polygon3DTyped<Units>& aPlane,
-                       size_t& aPos, size_t& aNeg) const
+  nsTArray<float> CalculateDotProducts(const PolygonTyped<Units>& aPlane,
+                                       size_t& aPos, size_t& aNeg) const
   {
     // Point classification might produce incorrect results due to numerical
     // inaccuracies. Using an epsilon value makes the splitting plane "thicker".
     const float epsilon = 0.05f;
 
     MOZ_ASSERT(!aPlane.GetPoints().IsEmpty());
-    const Point3DTyped<Units>& planeNormal = aPlane.GetNormal();
-    const Point3DTyped<Units>& planePoint = aPlane[0];
+    const Point4DType& planeNormal = aPlane.GetNormal();
+    const Point4DType& planePoint = aPlane[0];
 
     aPos = aNeg = 0;
     nsTArray<float> dotProducts;
-    for (const Point3DTyped<Units>& point : mPoints) {
+
+    for (const Point4DType& point : mPoints) {
       float dot = (point - planePoint).DotProduct(planeNormal);
 
       if (dot > epsilon) {
         aPos++;
       } else if (dot < -epsilon) {
         aNeg++;
       } else {
         // The point is within the thick plane.
@@ -98,70 +150,97 @@ public:
 
       dotProducts.AppendElement(dot);
     }
 
     return dotProducts;
   }
 
   // Clips the polygon against the given 2D rectangle.
-  Polygon3DTyped<Units> ClipPolygon(const RectTyped<Units>& aRect) const
+  PolygonTyped<Units> ClipPolygon(const RectTyped<Units>& aRect) const
   {
-    Polygon3DTyped<Units> polygon(mPoints, mNormal);
+    if (aRect.IsEmpty()) {
+      return PolygonTyped<Units>();
+    }
 
-    // Left edge
-    ClipPolygonWithEdge(polygon, aRect.BottomLeft(), aRect.TopLeft());
+    return ClipPolygon(FromRect(aRect));
+  }
+
+  // Clips the polygon against the given polygon in 2D.
+  PolygonTyped<Units> ClipPolygon(const PolygonTyped<Units>& aPolygon) const
+  {
+    const nsTArray<Point4DType>& points = aPolygon.GetPoints();
 
-    // Bottom edge
-    ClipPolygonWithEdge(polygon, aRect.BottomRight(), aRect.BottomLeft());
+    if (mPoints.IsEmpty() || points.IsEmpty()) {
+      return PolygonTyped<Units>();
+    }
+
+    PolygonTyped<Units> polygon(mPoints, mNormal);
 
-    // Right edge
-    ClipPolygonWithEdge(polygon, aRect.TopRight(), aRect.BottomRight());
+    const size_t pointCount = points.Length();
+    for (size_t i = 0; i < pointCount; ++i) {
+      const Point4DType p1 = points[(i + 1) % pointCount];
+      const Point4DType p2 = points[i];
 
-    // Top edge
-    ClipPolygonWithEdge(polygon, aRect.TopLeft(), aRect.TopRight());
+      const Point4DType normal(p2.y - p1.y, p1.x - p2.x, 0.0f, 0.0f);
+      const PolygonTyped<Units> plane({p1, p2}, normal);
+
+      ClipPolygonWithPlane(polygon, plane);
+    }
+
+    if (polygon.GetPoints().Length() < 3) {
+      return PolygonTyped<Units>();
+    }
 
     return polygon;
   }
 
-  const Point3DTyped<Units>& GetNormal() const
+  static PolygonTyped<Units> FromRect(const RectTyped<Units>& aRect)
+  {
+    return PolygonTyped<Units> {
+      Point3DType(aRect.x, aRect.y, 0.0f),
+      Point3DType(aRect.x, aRect.y + aRect.height, 0.0f),
+      Point3DType(aRect.x + aRect.width, aRect.y + aRect.height, 0.0f),
+      Point3DType(aRect.x + aRect.width, aRect.y, 0.0f)
+    };
+  }
+
+  const Point4DType& GetNormal() const
   {
     return mNormal;
   }
 
-  const nsTArray<Point3DTyped<Units>>& GetPoints() const
+  const nsTArray<Point4DType>& GetPoints() const
   {
     return mPoints;
   }
 
-  const Point3DTyped<Units>& operator[](size_t aIndex) const
+  const Point4DType& operator[](size_t aIndex) const
   {
     MOZ_ASSERT(mPoints.Length() > aIndex);
     return mPoints[aIndex];
   }
 
-  void SplitPolygon(const Polygon3DTyped<Units>& aSplittingPlane,
+  void SplitPolygon(const Point4DType& aNormal,
                     const nsTArray<float>& aDots,
-                    nsTArray<Point3DTyped<Units>>& aBackPoints,
-                    nsTArray<Point3DTyped<Units>>& aFrontPoints) const
+                    nsTArray<Point4DType>& aBackPoints,
+                    nsTArray<Point4DType>& aFrontPoints) const
   {
     static const auto Sign = [](const float& f) {
       if (f > 0.0f) return 1;
       if (f < 0.0f) return -1;
       return 0;
     };
 
-    const Point3DTyped<Units>& normal = aSplittingPlane.GetNormal();
     const size_t pointCount = mPoints.Length();
-
     for (size_t i = 0; i < pointCount; ++i) {
       size_t j = (i + 1) % pointCount;
 
-      const Point3DTyped<Units>& a = mPoints[i];
-      const Point3DTyped<Units>& b = mPoints[j];
+      const Point4DType& a = mPoints[i];
+      const Point4DType& b = mPoints[j];
       const float dotA = aDots[i];
       const float dotB = aDots[j];
 
       // The point is in front of or on the plane.
       if (dotA >= 0) {
         aFrontPoints.AppendElement(a);
       }
 
@@ -170,20 +249,20 @@ public:
         aBackPoints.AppendElement(a);
       }
 
       // If the sign of the dot products changes between two consecutive
       // vertices, then the plane intersects with the polygon edge.
       // The case where the polygon edge is within the plane is handled above.
       if (Sign(dotA) && Sign(dotB) && Sign(dotA) != Sign(dotB)) {
         // Calculate the line segment and plane intersection point.
-        const Point3DTyped<Units> ab = b - a;
-        const float dotAB = ab.DotProduct(normal);
+        const Point4DType ab = b - a;
+        const float dotAB = ab.DotProduct(aNormal);
         const float t = -dotA / dotAB;
-        const Point3DTyped<Units> p = a + (ab * t);
+        const Point4DType p = a + (ab * t);
 
         // Add the intersection point to both polygons.
         aBackPoints.AppendElement(p);
         aFrontPoints.AppendElement(p);
       }
     }
   }
 
@@ -202,94 +281,107 @@ public:
       triangles.AppendElement(Move(triangle));
     }
 
     return triangles;
   }
 
   void TransformToLayerSpace(const Matrix4x4Typed<Units, Units>& aTransform)
   {
-    TransformPoints(aTransform);
-    mNormal = Point3DTyped<Units>(0.0f, 0.0f, 1.0f);
+    TransformPoints(aTransform, true);
+    mNormal = DefaultNormal();
   }
 
   void TransformToScreenSpace(const Matrix4x4Typed<Units, Units>& aTransform)
   {
-    TransformPoints(aTransform);
+    TransformPoints(aTransform, false);
+    mPoints = ClipHomogeneous(mPoints);
 
     // Normal vectors should be transformed using inverse transpose.
     mNormal = aTransform.Inverse().Transpose().TransformPoint(mNormal);
   }
 
 private:
-  void ClipPolygonWithEdge(Polygon3DTyped<Units>& aPolygon,
-                           const PointTyped<Units>& aFirst,
-                           const PointTyped<Units>& aSecond) const
+  void ClipPolygonWithPlane(PolygonTyped<Units>& aPolygon,
+                            const PolygonTyped<Units>& aPlane) const
   {
-    const Point3DTyped<Units> a(aFirst.x, aFirst.y, 0.0f);
-    const Point3DTyped<Units> b(aSecond.x, aSecond.y, 0.0f);
-    const Point3DTyped<Units> normal(b.y - a.y, a.x - b.x, 0.0f);
-    Polygon3DTyped<Units> plane({a, b}, normal);
+    size_t pos, neg;
+    const nsTArray<float> dots =
+      aPolygon.CalculateDotProducts(aPlane, pos, neg);
 
-    size_t pos, neg;
-    nsTArray<float> dots = aPolygon.CalculateDotProducts(plane, pos, neg);
-
-    nsTArray<Point3DTyped<Units>> backPoints, frontPoints;
-    aPolygon.SplitPolygon(plane, dots, backPoints, frontPoints);
+    nsTArray<Point4DType> backPoints, frontPoints;
+    aPolygon.SplitPolygon(aPlane.GetNormal(), dots, backPoints, frontPoints);
 
     // Only use the points that are behind the clipping plane.
-    aPolygon = Polygon3DTyped<Units>(Move(backPoints), aPolygon.GetNormal());
+    aPolygon = PolygonTyped<Units>(Move(backPoints), aPolygon.GetNormal());
+  }
+
+  static Point4DType DefaultNormal()
+  {
+    return Point4DType(0.0f, 0.0f, 1.0f, 0.0f);
   }
 
 #ifdef DEBUG
   void EnsurePlanarPolygon() const
   {
     if (mPoints.Length() <= 3) {
       // Polygons with three or less points are guaranteed to be planar.
       return;
     }
 
     // This normal calculation method works only for planar polygons.
     // The resulting normal vector will point towards the viewer when the
     // polygon has a counter-clockwise winding order from the perspective
     // of the viewer.
-    Point3DTyped<Units> normal;
+    Point3DType normal;
+    const Point3DType p0 = mPoints[0].As3DPoint();
 
     for (size_t i = 1; i < mPoints.Length() - 1; ++i) {
-      normal +=
-        (mPoints[i] - mPoints[0]).CrossProduct(mPoints[i + 1] - mPoints[0]);
+      const Point3DType p1 = mPoints[i].As3DPoint();
+      const Point3DType p2 = mPoints[i + 1].As3DPoint();
+
+      normal += (p1 - p0).CrossProduct(p2 - p0);
     }
 
     // Ensure that at least one component is greater than zero.
     // This avoids division by zero when normalizing the vector.
     bool hasNonZeroComponent = std::abs(normal.x) > 0.0f ||
                                std::abs(normal.y) > 0.0f ||
                                std::abs(normal.z) > 0.0f;
+
     MOZ_ASSERT(hasNonZeroComponent);
 
     normal.Normalize();
 
     // Ensure that the polygon is planar.
     // http://mathworld.wolfram.com/Point-PlaneDistance.html
     const float epsilon = 0.01f;
-    for (const Point3DTyped<Units>& point : mPoints) {
-      float d = normal.DotProduct(point - mPoints[0]);
+    for (const Point4DType& point : mPoints) {
+      const Point3DType p1 = point.As3DPoint();
+      const float d = normal.DotProduct(p1 - p0);
+
       MOZ_ASSERT(std::abs(d) < epsilon);
     }
   }
 #endif
-  void TransformPoints(const Matrix4x4Typed<Units, Units>& aTransform)
+
+  void TransformPoints(const Matrix4x4Typed<Units, Units>& aTransform,
+                       const bool aDivideByW)
   {
-    for (Point3DTyped<Units>& point : mPoints) {
+    for (Point4DType& point : mPoints) {
       point = aTransform.TransformPoint(point);
+
+      if (aDivideByW && point.w > 0.0f) {
+          point = point / point.w;
+      }
     }
   }
 
-  Point3DTyped<Units> mNormal;
-  nsTArray<Point3DTyped<Units>> mPoints;
+  Point4DType mNormal;
+  nsTArray<Point4DType> mPoints;
 };
 
-typedef Polygon3DTyped<UnknownUnits> Polygon3D;
+typedef PolygonTyped<UnknownUnits> Polygon;
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_POLYGON_H */
--- a/gfx/layers/BSPTree.cpp
+++ b/gfx/layers/BSPTree.cpp
@@ -15,17 +15,17 @@ LayerPolygon PopFront(std::deque<LayerPo
   aLayers.pop_front();
   return layer;
 }
 
 void
 BSPTree::BuildDrawOrder(const UniquePtr<BSPTreeNode>& aNode,
                         nsTArray<LayerPolygon>& aLayers) const
 {
-  const gfx::Point3D& normal = aNode->First().GetNormal();
+  const gfx::Point4D& normal = aNode->First().GetNormal();
 
   UniquePtr<BSPTreeNode> *front = &aNode->front;
   UniquePtr<BSPTreeNode> *back = &aNode->back;
 
   // Since the goal is to return the draw order from back to front, we reverse
   // the traversal order if the current polygon is facing towards the camera.
   const bool reverseOrder = normal.z > 0.0f;
 
@@ -53,21 +53,21 @@ BSPTree::BuildDrawOrder(const UniquePtr<
 void
 BSPTree::BuildTree(UniquePtr<BSPTreeNode>& aRoot,
                    std::deque<LayerPolygon>& aLayers)
 {
   if (aLayers.empty()) {
     return;
   }
 
-  const gfx::Polygon3D& plane = aRoot->First();
+  const gfx::Polygon& plane = aRoot->First();
   std::deque<LayerPolygon> backLayers, frontLayers;
 
   for (LayerPolygon& layerPolygon : aLayers) {
-    const Maybe<gfx::Polygon3D>& geometry = layerPolygon.geometry;
+    const Maybe<gfx::Polygon>& geometry = layerPolygon.geometry;
 
     size_t pos = 0, neg = 0;
     nsTArray<float> dots = geometry->CalculateDotProducts(plane, pos, neg);
 
     // Back polygon
     if (pos == 0 && neg > 0) {
       backLayers.push_back(Move(layerPolygon));
     }
@@ -76,24 +76,29 @@ BSPTree::BuildTree(UniquePtr<BSPTreeNode
       frontLayers.push_back(Move(layerPolygon));
     }
     // Coplanar polygon
     else if (pos == 0 && neg == 0) {
       aRoot->layers.push_back(Move(layerPolygon));
     }
     // Polygon intersects with the splitting plane.
     else if (pos > 0 && neg > 0) {
-      nsTArray<gfx::Point3D> backPoints, frontPoints;
-      geometry->SplitPolygon(plane, dots, backPoints, frontPoints);
+      nsTArray<gfx::Point4D> backPoints, frontPoints;
+      geometry->SplitPolygon(plane.GetNormal(), dots, backPoints, frontPoints);
 
-      const gfx::Point3D& normal = geometry->GetNormal();
+      const gfx::Point4D& normal = geometry->GetNormal();
       Layer *layer = layerPolygon.layer;
 
-      backLayers.push_back(LayerPolygon(layer, Move(backPoints), normal));
-      frontLayers.push_back(LayerPolygon(layer, Move(frontPoints), normal));
+      if (backPoints.Length() >= 3) {
+        backLayers.push_back(LayerPolygon(layer, Move(backPoints), normal));
+      }
+
+      if (frontPoints.Length() >= 3) {
+        frontLayers.push_back(LayerPolygon(layer, Move(frontPoints), normal));
+      }
     }
   }
 
   if (!backLayers.empty()) {
     aRoot->back.reset(new BSPTreeNode(PopFront(backLayers)));
     BuildTree(aRoot->back, backLayers);
   }
 
--- a/gfx/layers/BSPTree.h
+++ b/gfx/layers/BSPTree.h
@@ -19,39 +19,40 @@ namespace layers {
 class Layer;
 
 // Represents a layer that might have a non-rectangular geometry.
 struct LayerPolygon {
   explicit LayerPolygon(Layer *aLayer)
     : layer(aLayer) {}
 
   LayerPolygon(Layer *aLayer,
-               gfx::Polygon3D&& aGeometry)
+               gfx::Polygon&& aGeometry)
     : layer(aLayer), geometry(Some(aGeometry)) {}
 
   LayerPolygon(Layer *aLayer,
-               nsTArray<gfx::Point3D>&& aPoints, const gfx::Point3D& aNormal)
-    : layer(aLayer), geometry(Some(gfx::Polygon3D(Move(aPoints), aNormal))) {}
+               nsTArray<gfx::Point4D>&& aPoints,
+               const gfx::Point4D& aNormal)
+    : layer(aLayer), geometry(Some(gfx::Polygon(Move(aPoints), aNormal))) {}
 
   Layer *layer;
-  Maybe<gfx::Polygon3D> geometry;
+  Maybe<gfx::Polygon> geometry;
 };
 
 LayerPolygon PopFront(std::deque<LayerPolygon>& aLayers);
 
 // Represents a node in a BSP tree. The node contains at least one layer with
 // associated geometry that is used as a splitting plane, and at most two child
 // nodes that represent the splitting planes that further subdivide the space.
 struct BSPTreeNode {
   explicit BSPTreeNode(LayerPolygon&& layer)
   {
     layers.push_back(Move(layer));
   }
 
-  const gfx::Polygon3D& First() const
+  const gfx::Polygon& First() const
   {
     MOZ_ASSERT(layers[0].geometry);
     return *layers[0].geometry;
   }
 
   UniquePtr<BSPTreeNode> front;
   UniquePtr<BSPTreeNode> back;
   std::deque<LayerPolygon> layers;
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -222,101 +222,101 @@ Compositor::DrawDiagnosticsInternal(Diag
 
   SlowDrawRect(aVisibleRect, color, aClipRect, aTransform, lWidth);
 }
 
 static void
 UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
                          const gfx::Rect& aRect,
                          const gfx::Rect& aIntersection,
-                         gfx::Rect aTextureCoords)
+                         const gfx::Rect& aTextureCoords)
 {
   // Calculate the relative offset of the intersection within the layer.
   float dx = (aIntersection.x - aRect.x) / aRect.width;
   float dy = (aIntersection.y - aRect.y) / aRect.height;
 
   // Update the texture offset.
   float x = aTextureCoords.x + dx * aTextureCoords.width;
   float y = aTextureCoords.y + dy * aTextureCoords.height;
 
   // Scale the texture width and height.
   float w = aTextureCoords.width * aIntersection.width / aRect.width;
   float h = aTextureCoords.height * aIntersection.height / aRect.height;
 
-  static const auto ValidateAndClamp = [](float& f) {
-    // Allow some numerical inaccuracy.
-    MOZ_ASSERT(f >= -0.0001f && f <= 1.0001f);
-
+  static const auto Clamp = [](float& f)
+  {
     if (f >= 1.0f) f = 1.0f;
     if (f <= 0.0f) f = 0.0f;
   };
 
   auto UpdatePoint = [&](const gfx::Point& p, gfx::Point& t)
   {
     t.x = x + (p.x - aIntersection.x) / aIntersection.width * w;
     t.y = y + (p.y - aIntersection.y) / aIntersection.height * h;
 
-    ValidateAndClamp(t.x);
-    ValidateAndClamp(t.y);
+    Clamp(t.x);
+    Clamp(t.y);
   };
 
   UpdatePoint(aTriangle.p1, aTriangle.textureCoords.p1);
   UpdatePoint(aTriangle.p2, aTriangle.textureCoords.p2);
   UpdatePoint(aTriangle.p3, aTriangle.textureCoords.p3);
 }
 
 void
 Compositor::DrawGeometry(const gfx::Rect& aRect,
                          const gfx::IntRect& aClipRect,
                          const EffectChain& aEffectChain,
                          gfx::Float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Rect& aVisibleRect,
-                         const Maybe<gfx::Polygon3D>& aGeometry)
+                         const Maybe<gfx::Polygon>& aGeometry)
 {
-  if (!aGeometry) {
+  if (!aGeometry || !SupportsLayerGeometry()) {
     DrawQuad(aRect, aClipRect, aEffectChain,
              aOpacity, aTransform, aVisibleRect);
     return;
   }
 
   // Cull invisible polygons.
   if (aRect.Intersect(aGeometry->BoundingBox()).IsEmpty()) {
     return;
   }
 
-  gfx::Polygon3D clipped = aGeometry->ClipPolygon(aRect);
-  nsTArray<gfx::Triangle> triangles = clipped.ToTriangles();
+  const gfx::Polygon clipped = aGeometry->ClipPolygon(aRect);
 
-  for (gfx::Triangle& geometry : triangles) {
-    const gfx::Rect intersection = aRect.Intersect(geometry.BoundingBox());
+  for (gfx::Triangle& triangle : clipped.ToTriangles()) {
+    const gfx::Rect intersection = aRect.Intersect(triangle.BoundingBox());
 
     // Cull invisible triangles.
     if (intersection.IsEmpty()) {
       continue;
     }
 
     MOZ_ASSERT(aRect.width > 0.0f && aRect.height > 0.0f);
     MOZ_ASSERT(intersection.width > 0.0f && intersection.height > 0.0f);
 
-    gfx::TexturedTriangle triangle(Move(geometry));
-    triangle.width = aRect.width;
-    triangle.height = aRect.height;
+    gfx::TexturedTriangle texturedTriangle(Move(triangle));
+    texturedTriangle.width = aRect.width;
+    texturedTriangle.height = aRect.height;
 
     // Since the texture was created for non-split geometry, we need to
     // update the texture coordinates to account for the split.
-    if (aEffectChain.mPrimaryEffect->mType == EffectTypes::RGB) {
+    const EffectTypes type = aEffectChain.mPrimaryEffect->mType;
+
+    if (type == EffectTypes::RGB || type == EffectTypes::YCBCR ||
+        type == EffectTypes::NV12 || type == EffectTypes::RENDER_TARGET) {
       TexturedEffect* texturedEffect =
         static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
 
-      UpdateTextureCoordinates(triangle, aRect, intersection,
+      UpdateTextureCoordinates(texturedTriangle, aRect, intersection,
                                texturedEffect->mTextureCoords);
     }
 
-    DrawTriangle(triangle, aClipRect, aEffectChain,
+    DrawTriangle(texturedTriangle, aClipRect, aEffectChain,
                  aOpacity, aTransform, aVisibleRect);
   }
 }
 
 void
 Compositor::SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& aColor,
                      const gfx::IntRect& aClipRect,
                      const gfx::Matrix4x4& aTransform, int aStrokeWidth)
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -7,17 +7,17 @@
 #define MOZILLA_GFX_COMPOSITOR_H
 
 #include "Units.h"                      // for ScreenPoint
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for already_AddRefed, RefCounted
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
-#include "mozilla/gfx/Polygon.h"        // for Polygon3D
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
 #include "mozilla/gfx/Types.h"          // for Float
 #include "mozilla/gfx/Triangle.h"       // for Triangle, TexturedTriangle
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticTypes, etc
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"
@@ -307,24 +307,24 @@ public:
   virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) = 0;
 
   void DrawGeometry(const gfx::Rect& aRect,
                     const gfx::IntRect& aClipRect,
                     const EffectChain &aEffectChain,
                     gfx::Float aOpacity,
                     const gfx::Matrix4x4& aTransform,
                     const gfx::Rect& aVisibleRect,
-                    const Maybe<gfx::Polygon3D>& aGeometry);
+                    const Maybe<gfx::Polygon>& aGeometry);
 
   void DrawGeometry(const gfx::Rect& aRect,
                     const gfx::IntRect& aClipRect,
                     const EffectChain &aEffectChain,
                     gfx::Float aOpacity,
                     const gfx::Matrix4x4& aTransform,
-                    const Maybe<gfx::Polygon3D>& aGeometry)
+                    const Maybe<gfx::Polygon>& aGeometry)
   {
     DrawGeometry(aRect, aClipRect, aEffectChain, aOpacity,
                  aTransform, aRect, aGeometry);
   }
 
   /**
    * Tell the compositor to draw a quad. What to do draw and how it is
    * drawn is specified by aEffectChain. aRect is the quad to draw, in user space.
@@ -354,16 +354,21 @@ public:
                             const EffectChain& aEffectChain,
                             gfx::Float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Rect& aVisibleRect)
   {
     MOZ_CRASH("Compositor::DrawTriangle is not implemented for the current platform!");
   }
 
+  virtual bool SupportsLayerGeometry() const
+  {
+    return false;
+  }
+
   /**
    * Draw an unfilled solid color rect. Typically used for debugging overlays.
    */
   void SlowDrawRect(const gfx::Rect& aRect, const gfx::Color& color,
                 const gfx::IntRect& aClipRect = gfx::IntRect(),
                 const gfx::Matrix4x4& aTransform = gfx::Matrix4x4(),
                 int aStrokeWidth = 1);
 
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -23,17 +23,19 @@
 #include "gfx2DGlue.h"
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/Telemetry.h"          // for Accumulate
 #include "mozilla/ToString.h"
 #include "mozilla/dom/Animation.h"      // for ComputedTimingFunction
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/BSPTree.h"     // for BSPTree
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayerAnimationUtils.h"  // for TimingFunctionToComputedTimingFunction
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
 #include "mozilla/layers/LayersTypes.h"  // for TextureDumpMode
@@ -42,16 +44,19 @@
 #include "nsAString.h"
 #include "nsCSSValue.h"                 // for nsCSSValue::Array, etc
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsStyleStruct.h"              // for nsTimingFunction, etc
 #include "protobuf/LayerScopePacket.pb.h"
 #include "mozilla/Compression.h"
 #include "TreeTraversal.h"              // for ForEachNode
 
+#include <deque>
+#include <set>
+
 uint8_t gLayerManagerLayerBuilder;
 
 namespace mozilla {
 namespace layers {
 
 FILE*
 FILEOrDefault(FILE* aFile)
 {
@@ -1332,44 +1337,132 @@ ContainerLayer::Collect3DContextLeaves(n
         else {
           aToSort.AppendElement(layer);
           return TraversalFlag::Skip;
         }
       }
   );
 }
 
-void
-ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
+static nsTArray<LayerPolygon>
+SortLayersWithBSPTree(nsTArray<Layer*>& aArray)
+{
+  std::deque<LayerPolygon> inputLayers;
+  nsTArray<LayerPolygon> orderedLayers;
+
+  // Build a list of polygons to be sorted.
+  for (Layer* layer : aArray) {
+    // Ignore invisible layers.
+    if (!layer->IsVisible()) {
+      continue;
+    }
+
+    const gfx::IntRect& bounds =
+      layer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+    const gfx::Matrix4x4& transform = layer->GetEffectiveTransform();
+
+    gfx::Polygon polygon = gfx::Polygon::FromRect(gfx::Rect(bounds));
+
+    // Transform the polygon to screen space.
+    polygon.TransformToScreenSpace(transform);
+
+    if (polygon.GetPoints().Length() >= 3) {
+      inputLayers.push_back(LayerPolygon(layer, Move(polygon)));
+    }
+  }
+
+  if (inputLayers.empty()) {
+    return orderedLayers;
+  }
+
+  // Build a BSP tree from the list of polygons.
+  BSPTree tree(inputLayers);
+  orderedLayers = Move(tree.GetDrawOrder());
+
+  // Transform the polygons back to layer space.
+  for (LayerPolygon& layerPolygon : orderedLayers) {
+    gfx::Matrix4x4 inverse =
+      layerPolygon.layer->GetEffectiveTransform().Inverse();
+
+    MOZ_ASSERT(layerPolygon.geometry);
+    layerPolygon.geometry->TransformToLayerSpace(inverse);
+  }
+
+  return orderedLayers;
+}
+
+static nsTArray<LayerPolygon>
+StripLayerGeometry(const nsTArray<LayerPolygon>& aLayers)
+{
+  nsTArray<LayerPolygon> layers;
+  std::set<Layer*> uniqueLayers;
+
+  for (const LayerPolygon& layerPolygon : aLayers) {
+    auto result = uniqueLayers.insert(layerPolygon.layer);
+
+    if (result.second) {
+      // Layer was added to the set.
+      layers.AppendElement(LayerPolygon(layerPolygon.layer));
+    }
+  }
+
+  return layers;
+}
+
+nsTArray<LayerPolygon>
+ContainerLayer::SortChildrenBy3DZOrder(SortMode aSortMode)
 {
   AutoTArray<Layer*, 10> toSort;
+  nsTArray<LayerPolygon> drawOrder;
 
-  for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
-    ContainerLayer* container = l->AsContainerLayer();
+  for (Layer* layer = GetFirstChild(); layer; layer = layer->GetNextSibling()) {
+    ContainerLayer* container = layer->AsContainerLayer();
+
     if (container && container->Extend3DContext() &&
         !container->UseIntermediateSurface()) {
+
+      // Collect 3D layers in toSort array.
       container->Collect3DContextLeaves(toSort);
-    } else {
+
+      // Sort the 3D layers.
       if (toSort.Length() > 0) {
-        SortLayersBy3DZOrder(toSort);
-        aArray.AppendElements(Move(toSort));
-        // XXX The move analysis gets confused here, because toSort gets moved
-        // here, and then gets used again outside of the loop. To clarify that
-        // we realize that the array is going to be empty to the move checker,
-        // we clear it again here. (This method renews toSort for the move
-        // analysis)
+        nsTArray<LayerPolygon> sorted = SortLayersWithBSPTree(toSort);
+        drawOrder.AppendElements(Move(sorted));
+
         toSort.ClearAndRetainStorage();
       }
-      aArray.AppendElement(l);
+
+      continue;
     }
+
+    drawOrder.AppendElement(LayerPolygon(layer));
+  }
+
+  if (aSortMode == SortMode::WITHOUT_GEOMETRY) {
+    // Compositor does not support arbitrary layers, strip the layer geometry
+    // and duplicate layers.
+    return StripLayerGeometry(drawOrder);
   }
-  if (toSort.Length() > 0) {
-    SortLayersBy3DZOrder(toSort);
-    aArray.AppendElements(Move(toSort));
+
+  return drawOrder;
+}
+
+bool
+ContainerLayer::AnyAncestorOrThisIs3DContextLeaf()
+{
+  Layer* parent = this;
+  while (parent != nullptr) {
+    if (parent->Is3DContextLeaf()) {
+      return true;
+    }
+
+    parent = parent->GetParent();
   }
+
+  return false;
 }
 
 void
 ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
 {
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
 
@@ -1392,17 +1485,18 @@ ContainerLayer::DefaultComputeEffectiveT
      * WebKit/blink behaviour, but is changing in the latest spec.
      */
     float opacity = GetEffectiveOpacity();
     CompositionOp blendMode = GetEffectiveMixBlendMode();
     if ((HasMultipleChildren() || Creates3DContextWithExtendingChildren()) &&
         ((opacity != 1.0f && !Extend3DContext()) ||
          (blendMode != CompositionOp::OP_OVER))) {
       useIntermediateSurface = true;
-    } else if (!idealTransform.Is2D() && Creates3DContextWithExtendingChildren()) {
+    } else if ((!idealTransform.Is2D() || AnyAncestorOrThisIs3DContextLeaf()) &&
+               Creates3DContextWithExtendingChildren()) {
       useIntermediateSurface = true;
     } else {
       useIntermediateSurface = false;
       gfx::Matrix contTransform;
       bool checkClipRect = false;
       bool checkMaskLayers = false;
 
       if (!idealTransform.Is2D(&contTransform)) {
@@ -1704,17 +1798,17 @@ void WriteSnapshotToDumpFile(Compositor*
   RefPtr<SourceSurface> surf = aTarget->Snapshot();
   RefPtr<DataSourceSurface> dSurf = surf->GetDataSurface();
   WriteSnapshotToDumpFile_internal(aCompositor, dSurf);
 }
 #endif
 
 void
 Layer::Dump(std::stringstream& aStream, const char* aPrefix,
-            bool aDumpHtml, bool aSorted)
+            bool aDumpHtml, bool aSorted, const Maybe<gfx::Polygon>& aGeometry)
 {
 #ifdef MOZ_DUMP_PAINTING
   bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsHostLayer() &&
                                AsHostLayer()->GetCompositableHost();
   bool dumpClientTexture = gfxEnv::DumpPaint() && AsShadowableLayer() &&
                            AsShadowableLayer()->GetCompositableClient();
   nsCString layerId(Name());
   layerId.Append('-');
@@ -1724,17 +1818,17 @@ Layer::Dump(std::stringstream& aStream, 
     aStream << nsPrintfCString(R"(<li><a id="%p" )", this).get();
 #ifdef MOZ_DUMP_PAINTING
     if (dumpCompositorTexture || dumpClientTexture) {
       aStream << nsPrintfCString(R"lit(href="javascript:ViewImage('%s')")lit", layerId.BeginReading()).get();
     }
 #endif
     aStream << ">";
   }
-  DumpSelf(aStream, aPrefix);
+  DumpSelf(aStream, aPrefix, aGeometry);
 
 #ifdef MOZ_DUMP_PAINTING
   if (dumpCompositorTexture) {
     AsHostLayer()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
   } else if (dumpClientTexture) {
     if (aDumpHtml) {
       aStream << nsPrintfCString(R"(<script>array["%s"]=")", layerId.BeginReading()).get();
     }
@@ -1772,48 +1866,70 @@ Layer::Dump(std::stringstream& aStream, 
 #ifdef MOZ_DUMP_PAINTING
   for (size_t i = 0; i < mExtraDumpInfo.Length(); i++) {
     const nsCString& str = mExtraDumpInfo[i];
     aStream << aPrefix << "  Info:\n" << str.get();
   }
 #endif
 
   if (ContainerLayer* container = AsContainerLayer()) {
-    AutoTArray<Layer*, 12> children;
+    nsTArray<LayerPolygon> children;
     if (aSorted) {
-      container->SortChildrenBy3DZOrder(children);
+      children =
+        container->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITH_GEOMETRY);
     } else {
       for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
-        children.AppendElement(l);
+        children.AppendElement(LayerPolygon(l));
       }
     }
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
     if (aDumpHtml) {
       aStream << "<ul>";
     }
 
-    for (Layer* child : children) {
-      child->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
+    for (LayerPolygon& child : children) {
+      child.layer->Dump(aStream, pfx.get(), aDumpHtml, aSorted, child.geometry);
     }
 
     if (aDumpHtml) {
       aStream << "</ul>";
     }
   }
 
   if (aDumpHtml) {
     aStream << "</li>";
   }
 }
 
+static void
+DumpGeometry(std::stringstream& aStream, const Maybe<gfx::Polygon>& aGeometry)
+{
+  aStream << " [geometry=[";
+
+  const nsTArray<gfx::Point4D>& points = aGeometry->GetPoints();
+  for (size_t i = 0; i < points.Length(); ++i) {
+    const gfx::IntPoint point = TruncatedToInt(points[i].As2DPoint());
+    const char* sfx = (i != points.Length() - 1) ? "," : "";
+    AppendToString(aStream, point, "", sfx);
+  }
+
+  aStream << "]]";
+}
+
 void
-Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix)
+Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix,
+                const Maybe<gfx::Polygon>& aGeometry)
 {
   PrintInfo(aStream, aPrefix);
+
+  if (aGeometry) {
+    DumpGeometry(aStream, aGeometry);
+  }
+
   aStream << "\n";
 }
 
 void
 Layer::Dump(layerscope::LayersPacket* aPacket, const void* aParent)
 {
   DumpPacket(aPacket, aParent);
 
@@ -2384,17 +2500,17 @@ LayerManager::Dump(std::stringstream& aS
       aStream << "</li></ul>";
     }
     return;
   }
 
   if (aDumpHtml) {
     aStream << "<ul>";
   }
-  GetRoot()->Dump(aStream, pfx.get(), aDumpHtml);
+  GetRoot()->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
   if (aDumpHtml) {
     aStream << "</ul></li></ul>";
   }
   aStream << "\n";
 }
 
 void
 LayerManager::DumpSelf(std::stringstream& aStream, const char* aPrefix, bool aSorted)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -28,16 +28,17 @@
 #include "mozilla/TimeStamp.h"          // for TimeStamp, TimeDuration
 #include "mozilla/UniquePtr.h"          // for UniquePtr
 #include "mozilla/gfx/BaseMargin.h"     // for BaseMargin
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/TiledRegion.h"    // for TiledIntRegion
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/gfx/UserData.h"       // for UserData, etc
+#include "mozilla/layers/BSPTree.h"     // for LayerPolygon
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsAutoPtr, nsRefPtr, etc
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsCSSPropertyID.h"              // for nsCSSPropertyID
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
@@ -1686,21 +1687,23 @@ public:
   void SetNextSibling(Layer* aSibling) { mNextSibling = aSibling; }
   void SetPrevSibling(Layer* aSibling) { mPrevSibling = aSibling; }
 
   /**
    * Dump information about this layer manager and its managed tree to
    * aStream.
    */
   void Dump(std::stringstream& aStream, const char* aPrefix="",
-            bool aDumpHtml=false, bool aSorted=false);
+            bool aDumpHtml=false, bool aSorted=false,
+            const Maybe<gfx::Polygon>& aGeometry=Nothing());
   /**
    * Dump information about just this layer manager itself to aStream.
    */
-  void DumpSelf(std::stringstream& aStream, const char* aPrefix="");
+  void DumpSelf(std::stringstream& aStream, const char* aPrefix="",
+                const Maybe<gfx::Polygon>& aGeometry=Nothing());
 
   /**
    * Dump information about this layer and its child & sibling layers to
    * layerscope packet.
    */
   void Dump(layerscope::LayersPacket* aPacket, const void* aParent);
 
   /**
@@ -2148,23 +2151,27 @@ public:
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScaleToResolution", this));
     mScaleToResolution = aScaleToResolution;
     mPresShellResolution = aResolution;
     Mutated();
   }
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
 
-  void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
+  enum class SortMode {
+    WITH_GEOMETRY,
+    WITHOUT_GEOMETRY,
+  };
 
-  // These getters can be used anytime.
+  nsTArray<LayerPolygon> SortChildrenBy3DZOrder(SortMode aSortMode);
 
   virtual ContainerLayer* AsContainerLayer() override { return this; }
   virtual const ContainerLayer* AsContainerLayer() const override { return this; }
 
+  // These getters can be used anytime.
   virtual Layer* GetFirstChild() const override { return mFirstChild; }
   virtual Layer* GetLastChild() const override { return mLastChild; }
   float GetPreXScale() const { return mPreXScale; }
   float GetPreYScale() const { return mPreYScale; }
   float GetInheritedXScale() const { return mInheritedXScale; }
   float GetInheritedYScale() const { return mInheritedYScale; }
   float GetPresShellResolution() const { return mPresShellResolution; }
   bool ScaleToResolution() const { return mScaleToResolution; }
@@ -2236,18 +2243,39 @@ public:
   }
 
 protected:
   friend class ReadbackProcessor;
 
   void DidInsertChild(Layer* aLayer);
   void DidRemoveChild(Layer* aLayer);
 
+  bool AnyAncestorOrThisIs3DContextLeaf();
+
   void Collect3DContextLeaves(nsTArray<Layer*>& aToSort);
 
+  // Collects child layers that do not extend 3D context. For ContainerLayers
+  // that do extend 3D context, the 3D context leaves are collected.
+  nsTArray<Layer*> CollectChildren() {
+    nsTArray<Layer*> children;
+
+    for (Layer* layer = GetFirstChild(); layer; layer = layer->GetNextSibling()) {
+      ContainerLayer* container = layer->AsContainerLayer();
+
+      if (container && container->Extend3DContext() &&
+          !container->UseIntermediateSurface()) {
+        container->Collect3DContextLeaves(children);
+      } else {
+        children.AppendElement(layer);
+      }
+    }
+
+    return children;
+  }
+
   ContainerLayer(LayerManager* aManager, void* aImplData);
 
   /**
    * A default implementation of ComputeEffectiveTransforms for use by OpenGL
    * and D3D.
    */
   void DefaultComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface);
 
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -715,20 +715,22 @@ BasicLayerManager::PaintSelfOrChildren(P
     } else {
       data->Paint(aGroupTarget->GetDrawTarget(),
                   aGroupTarget->GetDeviceOffset(),
                   aPaintContext.mLayer->GetMaskLayer());
     }
   } else {
     ContainerLayer* container =
         static_cast<ContainerLayer*>(aPaintContext.mLayer);
-    AutoTArray<Layer*, 12> children;
-    container->SortChildrenBy3DZOrder(children);
+
+    nsTArray<LayerPolygon> children =
+      container->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITHOUT_GEOMETRY);
+
     for (uint32_t i = 0; i < children.Length(); i++) {
-      Layer* layer = children.ElementAt(i);
+      Layer* layer = children.ElementAt(i).layer;
       if (layer->IsBackfaceHidden()) {
         continue;
       }
       if (!layer->AsContainerLayer() && !layer->IsVisible()) {
         continue;
       }
 
       PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
--- a/gfx/layers/client/ClientContainerLayer.h
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -42,25 +42,23 @@ protected:
 
     MOZ_COUNT_DTOR(ClientContainerLayer);
   }
 
 public:
   virtual void RenderLayer() override
   {
     RenderMaskLayers(this);
-    
+
     DefaultComputeSupportsComponentAlphaChildren();
 
-    AutoTArray<Layer*, 12> children;
-    SortChildrenBy3DZOrder(children);
-
     ReadbackProcessor readback;
     readback.BuildUpdates(this);
 
+    nsTArray<Layer*> children = CollectChildren();
     for (uint32_t i = 0; i < children.Length(); i++) {
       Layer* child = children.ElementAt(i);
 
       ToClientLayer(child)->RenderLayerWithReadback(&readback);
 
       if (!ClientManager()->GetRepeatTransaction() &&
           !child->GetInvalidRegion().IsEmpty()) {
         child->Mutated();
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -73,17 +73,18 @@ CanvasLayerComposite::GetRenderState()
 {
   if (mDestroyed || !mCompositableHost || !mCompositableHost->IsAttached()) {
     return LayerRenderState();
   }
   return mCompositableHost->GetRenderState();
 }
 
 void
-CanvasLayerComposite::RenderLayer(const IntRect& aClipRect)
+CanvasLayerComposite::RenderLayer(const IntRect& aClipRect,
+                                  const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!mCompositableHost || !mCompositableHost->IsAttached()) {
     return;
   }
 
   mCompositor->MakeCurrent();
 
 #ifdef MOZ_DUMP_PAINTING
--- a/gfx/layers/composite/CanvasLayerComposite.h
+++ b/gfx/layers/composite/CanvasLayerComposite.h
@@ -46,17 +46,18 @@ public:
   virtual void Disconnect() override
   {
     Destroy();
   }
 
   virtual void SetLayerManager(HostLayerManager* aManager) override;
 
   virtual Layer* GetLayer() override;
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
 
   virtual void CleanupResources() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   CompositableHost* GetCompositableHost() override;
 
   virtual HostLayer* AsHostLayer() override { return this; }
--- a/gfx/layers/composite/ColorLayerComposite.cpp
+++ b/gfx/layers/composite/ColorLayerComposite.cpp
@@ -15,26 +15,29 @@
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
-ColorLayerComposite::RenderLayer(const IntRect& aClipRect)
+ColorLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                                 const Maybe<gfx::Polygon>& aGeometry)
 {
   Rect rect(GetBounds());
+
   const Matrix4x4& transform = GetEffectiveTransform();
 
   RenderWithAllMasks(this, mCompositor, aClipRect,
                      [&](EffectChain& effectChain, const IntRect& clipRect) {
     GenEffectChain(effectChain);
-    mCompositor->DrawQuad(rect, clipRect, effectChain, GetEffectiveOpacity(),
-                          transform);
+
+    mCompositor->DrawGeometry(rect, clipRect, effectChain,
+                              GetEffectiveOpacity(), transform, aGeometry);
   });
 
   mCompositor->DrawDiagnostics(DiagnosticFlags::COLOR, rect, aClipRect,
                                transform);
 }
 
 void
 ColorLayerComposite::GenEffectChain(EffectChain& aEffect)
--- a/gfx/layers/composite/ColorLayerComposite.h
+++ b/gfx/layers/composite/ColorLayerComposite.h
@@ -42,17 +42,19 @@ public:
   virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
   }
 
   virtual void Destroy() override { mDestroyed = true; }
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
+
   virtual void CleanupResources() override {};
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual HostLayer* AsHostLayer() override { return this; }
 
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -9,16 +9,17 @@
 #include <stdint.h>                     // for uint64_t
 #include <stdio.h>                      // for FILE
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr, RefCounted, etc
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/Effects.h"     // for Texture Effect
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/layers/LayersMessages.h"
@@ -78,17 +79,18 @@ public:
 
   // composite the contents of this buffer host to the compositor's surface
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) = 0;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) = 0;
 
   /**
    * Update the content host.
    * aUpdated is the region which should be updated
    * aUpdatedRegionBack is the region in aNewBackResult which has been updated
    */
   virtual bool UpdateThebes(const ThebesBufferData& aData,
                             const nsIntRegion& aUpdated,
@@ -286,17 +288,17 @@ private:
  * that is accessed only on the compositor thread. During a layer transaction we
  * send the message OpAttachAsyncCompositable(ID, PLayer), and on the compositor
  * side we lookup the ID in the map and attach the corresponding compositable to
  * the layer.
  *
  * CompositableMap must be global because the image bridge doesn't have any
  * reference to whatever we have created with PLayerTransaction. So, the only way to
  * actually connect these two worlds is to have something global that they can
- * both query (in the same  thread). The map is not allocated the map on the 
+ * both query (in the same  thread). The map is not allocated the map on the
  * stack to avoid the badness of static initialization.
  *
  * Also, we have a compositor/PLayerTransaction protocol/etc. per layer manager, and the
  * ImageBridge is used by all the existing compositors that have a video, so
  * there isn't an instance or "something" that lives outside the boudaries of a
  * given layer manager on the compositor thread except the image bridge and the
  * thread itself.
  */
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -47,21 +47,21 @@
 #define XY(k)    (k).x, (k).y
 #define WH(k)    (k).width, (k).height
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 
-static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
-                          LayerManagerComposite* aManager,
-                          Layer* aLayer)
+static void
+DrawLayerInfo(const RenderTargetIntRect& aClipRect,
+              LayerManagerComposite* aManager,
+              Layer* aLayer)
 {
-
   if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
     // XXX - should figure out a way to render this, but for now this
     // is hard to do, since it will often get superimposed over the first
     // child of the layer, which is bad.
     return;
   }
 
   std::stringstream ss;
@@ -72,24 +72,18 @@ static void DrawLayerInfo(const RenderTa
   uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().width, 500);
 
   IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft();
   aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft,
                                           aLayer->GetEffectiveTransform(), 16,
                                           maxWidth);
 }
 
-template<class ContainerT>
-static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
-{
-  gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
-  return surfaceRect;
-}
-
-static void PrintUniformityInfo(Layer* aLayer)
+static void
+PrintUniformityInfo(Layer* aLayer)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
   if (!profiler_is_active()) {
     return;
   }
 
   // Don't want to print a log for smaller layers
   if (aLayer->GetLocalVisibleRegion().GetBounds().width < 300 ||
@@ -103,23 +97,79 @@ static void PrintUniformityInfo(Layer* a
   }
 
   Point translation = transform.As2D().GetTranslation();
   LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
   PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
 #endif
 }
 
+static Maybe<gfx::Polygon>
+SelectLayerGeometry(const Maybe<gfx::Polygon>& aParentGeometry,
+                    const Maybe<gfx::Polygon>& aChildGeometry)
+{
+  // Both the parent and the child layer were split.
+  if (aParentGeometry && aChildGeometry) {
+    // As we use intermediate surface in these cases, this branch should never
+    // get executed.
+    MOZ_ASSERT(false,
+               "Both parent and child geometry present in nested 3D context!");
+    return Some(aParentGeometry->ClipPolygon(*aChildGeometry));
+  }
+
+  // The parent layer was split.
+  if (aParentGeometry) {
+    return aParentGeometry;
+  }
+
+  // The child layer was split.
+  if(aChildGeometry) {
+    return aChildGeometry;
+  }
+
+  // No split.
+  return Nothing();
+}
+
+static void
+TransformLayerGeometry(Layer* aLayer, Maybe<gfx::Polygon>& aGeometry)
+{
+  Layer* parent = aLayer->GetParent();
+  gfx::Matrix4x4 transform;
+
+  // Collect all parent transforms.
+  while (parent != nullptr && !parent->Is3DContextLeaf()) {
+    transform = transform * parent->GetLocalTransform();
+    parent = parent->GetParent();
+  }
+
+  // Transform the geometry to the parent 3D context leaf coordinate space.
+  aGeometry->TransformToScreenSpace(transform.ProjectTo2D().Inverse());
+}
+
+
+template<class ContainerT>
+static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
+{
+  gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
+  return surfaceRect;
+}
+
+
 /* all of the per-layer prepared data we need to maintain */
 struct PreparedLayer
 {
-  PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
-    mLayer(aLayer), mClipRect(aClipRect) {}
+  PreparedLayer(LayerComposite *aLayer,
+                RenderTargetIntRect aClipRect,
+                Maybe<gfx::Polygon> aGeometry)
+  : mLayer(aLayer), mClipRect(aClipRect), mGeometry(aGeometry) {}
+
   LayerComposite* mLayer;
   RenderTargetIntRect mClipRect;
+  Maybe<Polygon> mGeometry;
 };
 
 /* all of the prepared data that we need in RenderLayer() */
 struct PreparedData
 {
   RefPtr<CompositingRenderTarget> mTmpTarget;
   AutoTArray<PreparedLayer, 12> mLayers;
   bool mNeedsSurfaceCopy;
@@ -129,27 +179,30 @@ struct PreparedData
 template<class ContainerT> void
 ContainerPrepare(ContainerT* aContainer,
                  LayerManagerComposite* aManager,
                  const RenderTargetIntRect& aClipRect)
 {
   aContainer->mPrepared = MakeUnique<PreparedData>();
   aContainer->mPrepared->mNeedsSurfaceCopy = false;
 
-  /**
-   * Determine which layers to draw.
-   */
-  AutoTArray<Layer*, 12> children;
-  aContainer->SortChildrenBy3DZOrder(children);
+  const ContainerLayerComposite::SortMode sortMode =
+    aManager->GetCompositor()->SupportsLayerGeometry()
+    ? ContainerLayerComposite::SortMode::WITH_GEOMETRY
+    : ContainerLayerComposite::SortMode::WITHOUT_GEOMETRY;
 
-  for (uint32_t i = 0; i < children.Length(); i++) {
-    LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->AsHostLayer());
+  const nsTArray<LayerPolygon> polygons =
+    aContainer->SortChildrenBy3DZOrder(sortMode);
 
-    RenderTargetIntRect clipRect = layerToRender->GetLayer()->
-        CalculateScissorRect(aClipRect);
+  for (const LayerPolygon& layer : polygons) {
+    LayerComposite* layerToRender =
+      static_cast<LayerComposite*>(layer.layer->ImplData());
+
+    RenderTargetIntRect clipRect =
+      layerToRender->GetLayer()->CalculateScissorRect(aClipRect);
 
     if (layerToRender->GetLayer()->IsBackfaceHidden()) {
       continue;
     }
 
     // We don't want to skip container layers because otherwise their mPrepared
     // may be null which is not allowed.
     if (!layerToRender->GetLayer()->AsContainerLayer()) {
@@ -163,17 +216,17 @@ ContainerPrepare(ContainerT* aContainer,
         CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer());
         continue;
       }
     }
 
     CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
 
     layerToRender->Prepare(clipRect);
-    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
+    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, layer.geometry));
   }
 
   CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
 
   /**
    * Setup our temporary surface for rendering the contents of this container.
    */
 
@@ -329,28 +382,30 @@ RenderMinimap(ContainerT* aContainer, La
     compositor->SlowDrawRect(r, criticalDisplayPortColor, clipRect, aContainer->GetEffectiveTransform());
   }
 
   // Render the viewport.
   r = transform.TransformBounds(viewRect.ToUnknownRect());
   compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
 }
 
-
 template<class ContainerT> void
-RenderLayers(ContainerT* aContainer,
-	     LayerManagerComposite* aManager,
-	     const RenderTargetIntRect& aClipRect)
+RenderLayers(ContainerT* aContainer, LayerManagerComposite* aManager,
+             const RenderTargetIntRect& aClipRect,
+             const Maybe<gfx::Polygon>& aGeometry)
 {
   Compositor* compositor = aManager->GetCompositor();
 
   for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
     PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
+
+    const gfx::IntRect clipRect = preparedData.mClipRect.ToUnknownRect();
     LayerComposite* layerToRender = preparedData.mLayer;
-    const RenderTargetIntRect& clipRect = preparedData.mClipRect;
+    const Maybe<gfx::Polygon>& childGeometry = preparedData.mGeometry;
+
     Layer* layer = layerToRender->GetLayer();
 
     if (layerToRender->HasStaleCompositor()) {
       continue;
     }
 
     if (gfxPrefs::LayersDrawFPS()) {
       for (const auto& metadata : layer->GetAllScrollMetadata()) {
@@ -367,46 +422,53 @@ RenderLayers(ContainerT* aContainer,
         color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy"
       }
       // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
       // and only fill in that area. However the layer bounds takes into account the base translation
       // for the painted layer whereas the checkerboard region does not. One does not simply
       // intersect areas in different coordinate spaces. So we do this a little more permissively
       // and only fill in the background when we know there is checkerboard, which in theory
       // should only occur transiently.
-      gfx::IntRect layerBounds = layer->GetLayerBounds();
       EffectChain effectChain(layer);
       effectChain.mPrimaryEffect = new EffectSolidColor(color);
-      aManager->GetCompositor()->DrawQuad(gfx::Rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height),
-                                          clipRect.ToUnknownRect(),
+      aManager->GetCompositor()->DrawQuad(gfx::Rect(layer->GetLayerBounds()), clipRect,
                                           effectChain, layer->GetEffectiveOpacity(),
                                           layer->GetEffectiveTransform());
     }
 
     if (layerToRender->HasLayerBeenComposited()) {
       // Composer2D will compose this layer so skip GPU composition
       // this time. The flag will be reset for the next composition phase
       // at the beginning of LayerManagerComposite::Rener().
       gfx::IntRect clearRect = layerToRender->GetClearRect();
       if (!clearRect.IsEmpty()) {
         // Clear layer's visible rect on FrameBuffer with transparent pixels
         gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height);
         compositor->ClearRect(fbRect);
         layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0));
       }
     } else {
-      layerToRender->RenderLayer(clipRect.ToUnknownRect());
+      Maybe<gfx::Polygon> geometry =
+        SelectLayerGeometry(aGeometry, childGeometry);
+
+      // If we are dealing with a nested 3D context, we might need to transform
+      // the geometry to the coordinate space of the parent 3D context leaf.
+      if (geometry && !layer->Is3DContextLeaf()) {
+        TransformLayerGeometry(layer, geometry);
+      }
+
+      layerToRender->RenderLayer(clipRect, geometry);
     }
 
     if (gfxPrefs::UniformityInfo()) {
       PrintUniformityInfo(layer);
     }
 
     if (gfxPrefs::DrawLayerInfo()) {
-      DrawLayerInfo(clipRect, aManager, layer);
+      DrawLayerInfo(preparedData.mClipRect, aManager, layer);
     }
 
     // Draw a border around scrollable layers.
     // A layer can be scrolled by multiple scroll frames. Draw a border
     // for each.
     // Within the list of scroll frames for a layer, the layer border for a
     // scroll frame lower down is affected by the async transforms on scroll
     // frames higher up, so loop from the top down, and accumulate an async
@@ -497,25 +559,29 @@ RenderIntermediate(ContainerT* aContaine
   RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
 
   if (!surface) {
     return;
   }
 
   compositor->SetRenderTarget(surface);
   // pre-render all of the layers into our temporary
-  RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+  RenderLayers(aContainer, aManager,
+               RenderTargetIntRect::FromUnknownRect(aClipRect),
+               Nothing());
+
   // Unbind the current surface and rebind the previous one.
   compositor->SetRenderTarget(previousTarget);
 }
 
 template<class ContainerT> void
 ContainerRender(ContainerT* aContainer,
                  LayerManagerComposite* aManager,
-                 const gfx::IntRect& aClipRect)
+                 const gfx::IntRect& aClipRect,
+                 const Maybe<gfx::Polygon>& aGeometry)
 {
   MOZ_ASSERT(aContainer->mPrepared);
 
   if (aContainer->UseIntermediateSurface()) {
     RefPtr<CompositingRenderTarget> surface;
 
     if (aContainer->mPrepared->mNeedsSurfaceCopy) {
       // we needed to copy the background so we waited until now to render the intermediate
@@ -527,38 +593,42 @@ ContainerRender(ContainerT* aContainer,
     }
 
     if (!surface) {
       aContainer->mPrepared = nullptr;
       return;
     }
 
     gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+
     RefPtr<Compositor> compositor = aManager->GetCompositor();
 #ifdef MOZ_DUMP_PAINTING
     if (gfxEnv::DumpCompositorTextures()) {
       RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
       if (surf) {
         WriteSnapshotToDumpFile(aContainer, surf);
       }
     }
 #endif
 
     RefPtr<ContainerT> container = aContainer;
     RenderWithAllMasks(aContainer, compositor, aClipRect,
                        [&, surface, compositor, container](EffectChain& effectChain, const IntRect& clipRect) {
       effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
-      compositor->DrawQuad(visibleRect, clipRect, effectChain,
-                           container->GetEffectiveOpacity(),
-                           container->GetEffectiveTransform());
+
+      compositor->DrawGeometry(visibleRect, clipRect, effectChain,
+                               container->GetEffectiveOpacity(),
+                               container->GetEffectiveTransform(), aGeometry);
     });
+
   } else {
-    RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
+    RenderLayers(aContainer, aManager,
+                 RenderTargetIntRect::FromUnknownRect(aClipRect),
+                 aGeometry);
   }
-  aContainer->mPrepared = nullptr;
 
   // If it is a scrollable container layer with no child layers, and one of the APZCs
   // attached to it has a nonempty async transform, then that transform is not applied
   // to any visible content. Display a warning box (conditioned on the FPS display being
   // enabled).
   if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) {
     // Since aContainer doesn't have any children we can just iterate from the top metrics
     // on it down to the bottom using GetFirstChild and not worry about walking onto another
@@ -618,19 +688,26 @@ ContainerLayerComposite::GetFirstChildCo
 {
   if (!mFirstChild) {
     return nullptr;
    }
   return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
 }
 
 void
-ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+ContainerLayerComposite::Cleanup()
 {
-  ContainerRender(this, mCompositeManager, aClipRect);
+  mPrepared = nullptr;
+}
+
+void
+ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                                     const Maybe<gfx::Polygon>& aGeometry)
+{
+  ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
 }
 
 void
 ContainerLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
 {
   ContainerPrepare(this, mCompositeManager, aClipRect);
 }
 
@@ -669,19 +746,20 @@ RefLayerComposite::GetFirstChildComposit
 {
   if (!mFirstChild) {
     return nullptr;
    }
   return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
 }
 
 void
-RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                               const Maybe<gfx::Polygon>& aGeometry)
 {
-  ContainerRender(this, mCompositeManager, aClipRect);
+  ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
 }
 
 void
 RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
 {
   ContainerPrepare(this, mCompositeManager, aClipRect);
 }
 
--- a/gfx/layers/composite/ContainerLayerComposite.h
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -24,21 +24,23 @@ class ContainerLayerComposite : public C
 {
   template<class ContainerT>
   friend void ContainerPrepare(ContainerT* aContainer,
                                LayerManagerComposite* aManager,
                                const RenderTargetIntRect& aClipRect);
   template<class ContainerT>
   friend void ContainerRender(ContainerT* aContainer,
                               LayerManagerComposite* aManager,
-                              const RenderTargetIntRect& aClipRect);
+                              const RenderTargetIntRect& aClipRect,
+                              const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderLayers(ContainerT* aContainer,
                            LayerManagerComposite* aManager,
-                           const RenderTargetIntRect& aClipRect);
+                           const RenderTargetIntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderIntermediate(ContainerT* aContainer,
                    LayerManagerComposite* aManager,
                    const gfx::IntRect& aClipRect,
                    RefPtr<CompositingRenderTarget> surface);
   template<class ContainerT>
   friend RefPtr<CompositingRenderTarget>
   CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
@@ -74,17 +76,21 @@ public:
       child->SetLayerManager(aManager);
     }
   }
 
   virtual void Destroy() override;
 
   LayerComposite* GetFirstChildComposite() override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void Cleanup() override;
+
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
+
   virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void CleanupResources() override;
@@ -123,21 +129,23 @@ class RefLayerComposite : public RefLaye
 {
   template<class ContainerT>
   friend void ContainerPrepare(ContainerT* aContainer,
                                LayerManagerComposite* aManager,
                                const RenderTargetIntRect& aClipRect);
   template<class ContainerT>
   friend void ContainerRender(ContainerT* aContainer,
                               LayerManagerComposite* aManager,
-                              const gfx::IntRect& aClipRect);
+                              const gfx::IntRect& aClipRect,
+                              const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderLayers(ContainerT* aContainer,
                            LayerManagerComposite* aManager,
-                           const gfx::IntRect& aClipRect);
+                           const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry);
   template<class ContainerT>
   friend void RenderIntermediate(ContainerT* aContainer,
                    LayerManagerComposite* aManager,
                    const gfx::IntRect& aClipRect,
                    RefPtr<CompositingRenderTarget> surface);
   template<class ContainerT>
   friend RefPtr<CompositingRenderTarget>
   CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
@@ -158,17 +166,19 @@ protected:
 public:
   /** LayerOGL implementation */
   Layer* GetLayer() override { return this; }
 
   void Destroy() override;
 
   LayerComposite* GetFirstChildComposite() override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
+
   virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void CleanupResources() override;
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -33,17 +33,18 @@ ContentHostBase::~ContentHostBase()
 
 void
 ContentHostTexture::Composite(LayerComposite* aLayer,
                               EffectChain& aEffectChain,
                               float aOpacity,
                               const gfx::Matrix4x4& aTransform,
                               const SamplingFilter aSamplingFilter,
                               const IntRect& aClipRect,
-                              const nsIntRegion* aVisibleRegion)
+                              const nsIntRegion* aVisibleRegion,
+                              const Maybe<gfx::Polygon>& aGeometry)
 {
   NS_ASSERTION(aVisibleRegion, "Requires a visible region");
 
   AutoLockCompositableHost lock(this);
   if (lock.Failed()) {
     return;
   }
 
@@ -175,17 +176,20 @@ ContentHostTexture::Composite(LayerCompo
           }
           gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
                          tileScreenRect.width, tileScreenRect.height);
 
           effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
                                         Float(tileRegionRect.y) / texRect.height,
                                         Float(tileRegionRect.width) / texRect.width,
                                         Float(tileRegionRect.height) / texRect.height);
-          GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+
+          GetCompositor()->DrawGeometry(rect, aClipRect, aEffectChain,
+                                        aOpacity, aTransform, aGeometry);
+
           if (usingTiles) {
             DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
             if (iterOnWhite) {
               diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
             }
             GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect,
                                              aTransform, mFlashCounter);
           }
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -11,16 +11,17 @@
 #include "mozilla-config.h"             // for MOZ_DUMP_PAINTING
 #include "CompositableHost.h"           // for CompositableHost, etc
 #include "RotatedBuffer.h"              // for RotatedContentBuffer, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for etc
 #include "mozilla/layers/TextureHost.h"  // for TextureHost
 #include "mozilla/mozalloc.h"           // for operator delete
@@ -118,17 +119,18 @@ public:
   { }
 
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) override;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false) override;
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -282,17 +282,18 @@ void ImageHost::Attach(Layer* aLayer,
 
 void
 ImageHost::Composite(LayerComposite* aLayer,
                      EffectChain& aEffectChain,
                      float aOpacity,
                      const gfx::Matrix4x4& aTransform,
                      const gfx::SamplingFilter aSamplingFilter,
                      const gfx::IntRect& aClipRect,
-                     const nsIntRegion* aVisibleRegion)
+                     const nsIntRegion* aVisibleRegion,
+                     const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!GetCompositor()) {
     // should only happen when a tab is dragged to another window and
     // async-video is still sending frames but we haven't attached the
     // set the new compositor yet.
     return;
   }
 
@@ -387,18 +388,18 @@ ImageHost::Composite(LayerComposite* aLa
         effect->mTextureCoords = Rect(Float(rect.x - tileRect.x) / tileRect.width,
                                       Float(rect.y - tileRect.y) / tileRect.height,
                                       Float(rect.width) / tileRect.width,
                                       Float(rect.height) / tileRect.height);
         if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
           effect->mTextureCoords.y = effect->mTextureCoords.YMost();
           effect->mTextureCoords.height = -effect->mTextureCoords.height;
         }
-        GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
-                                  aOpacity, aTransform);
+        GetCompositor()->DrawGeometry(rect, aClipRect, aEffectChain,
+                                      aOpacity, aTransform, aGeometry);
         GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
                                          rect, aClipRect, aTransform, mFlashCounter);
       } while (it->NextTile());
       it->EndBigImageIteration();
       // layer border
       GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect,
                                        aClipRect, aTransform, mFlashCounter);
     } else {
@@ -408,18 +409,18 @@ ImageHost::Composite(LayerComposite* aLa
                                     Float(img->mPictureRect.width) / textureSize.width,
                                     Float(img->mPictureRect.height) / textureSize.height);
 
       if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
         effect->mTextureCoords.y = effect->mTextureCoords.YMost();
         effect->mTextureCoords.height = -effect->mTextureCoords.height;
       }
 
-      GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
-                                aOpacity, aTransform);
+      GetCompositor()->DrawGeometry(pictureRect, aClipRect, aEffectChain,
+                                    aOpacity, aTransform, aGeometry);
       GetCompositor()->DrawDiagnostics(diagnosticFlags,
                                        pictureRect, aClipRect,
                                        aTransform, mFlashCounter);
     }
   }
 
   // Update mBias last. This can change which frame ChooseImage(Index) would
   // return, and we don't want to do that until we've finished compositing
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -7,16 +7,17 @@
 #define MOZILLA_GFX_IMAGEHOST_H
 
 #include <stdio.h>                      // for FILE
 #include "CompositableHost.h"           // for CompositableHost
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsCOMPtr.h"                   // for already_AddRefed
@@ -43,17 +44,18 @@ public:
   virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
 
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) override;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
 
   virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
 
   virtual void RemoveTextureHost(TextureHost* aTexture) override;
 
   virtual void UseOverlaySource(OverlaySource aOverlay,
                                 const gfx::IntRect& aPictureRect) override;
 
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -85,17 +85,18 @@ ImageLayerComposite::SetLayerManager(Hos
   LayerComposite::SetLayerManager(aManager);
   mManager = aManager;
   if (mImageHost) {
     mImageHost->SetCompositor(mCompositor);
   }
 }
 
 void
-ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
+ImageLayerComposite::RenderLayer(const IntRect& aClipRect,
+                                 const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!mImageHost || !mImageHost->IsAttached()) {
     return;
   }
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxEnv::DumpCompositorTextures()) {
     RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface();
--- a/gfx/layers/composite/ImageLayerComposite.h
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -40,17 +40,18 @@ public:
   virtual void Disconnect() override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
   virtual Layer* GetLayer() override;
 
   virtual void SetLayerManager(HostLayerManager* aManager) override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
 
   virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
 
   virtual void CleanupResources() override;
 
   CompositableHost* GetCompositableHost() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -568,17 +568,17 @@ LayerManagerComposite::DrawPaintTimes(Co
 
 static uint16_t sFrameCount = 0;
 void
 LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
 {
   bool drawFps = gfxPrefs::LayersDrawFPS();
   bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
   bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
-  
+
   TimeStamp now = TimeStamp::Now();
 
   if (drawFps) {
     if (!mFPS) {
       mFPS = MakeUnique<FPSState>();
     }
 
     float alpha = 1;
@@ -921,17 +921,18 @@ LayerManagerComposite::Render(const nsIn
   if (haveLayerEffects) {
     previousTarget = PushGroupForLayerEffects();
   } else {
     mTwoPassTmpTarget = nullptr;
   }
 
   // Render our layers.
   RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot));
-  RootLayer()->RenderLayer(clipRect.ToUnknownRect());
+  RootLayer()->RenderLayer(clipRect.ToUnknownRect(), Nothing());
+  RootLayer()->Cleanup();
 
   if (!mRegionToClear.IsEmpty()) {
     for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
       const IntRect& r = iter.Get();
       mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height));
     }
   }
 
@@ -1128,17 +1129,17 @@ LayerManagerComposite::RenderToPresentat
   // is cleared.
   ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
   egl->fClearColor(0.0, 0.0, 0.0, 0.0);
   egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
 
   const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);
 
   RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
-  RootLayer()->RenderLayer(clipRect);
+  RootLayer()->RenderLayer(clipRect, Nothing());
 
   mCompositor->EndFrame();
 }
 #endif
 
 class TextLayerComposite : public TextLayer,
                            public LayerComposite
 {
@@ -1165,17 +1166,18 @@ public:
   virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
   }
 
   virtual void Destroy() override { mDestroyed = true; }
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override {}
   virtual void CleanupResources() override {};
 
   virtual void GenEffectChain(EffectChain& aEffect) override {}
 
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual HostLayer* AsHostLayer() override { return this; }
 
@@ -1208,17 +1210,18 @@ public:
   virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
   }
 
   virtual void Destroy() override { mDestroyed = true; }
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override {}
   virtual void CleanupResources() override {};
 
   virtual void GenEffectChain(EffectChain& aEffect) override {}
 
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual HostLayer* AsHostLayer() override { return this; }
 
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -518,17 +518,17 @@ public:
   // These getters can be used anytime.
   float GetShadowOpacity() { return mShadowOpacity; }
   const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
   const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
   const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
   gfx::Matrix4x4 GetShadowTransform();
   bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
   bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
-  
+
   /**
    * Return true if a checkerboarding background color needs to be drawn
    * for this layer.
    */
   virtual bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr) { return false; }
 
 protected:
   HostLayerManager* mCompositorManager;
@@ -572,27 +572,29 @@ public:
   {
     return nullptr;
   }
 
   /* Do NOT call this from the generic LayerComposite destructor.  Only from the
    * concrete class destructor
    */
   virtual void Destroy();
+  virtual void Cleanup() {}
 
   /**
    * Perform a first pass over the layer tree to render all of the intermediate
    * surfaces that we can. This allows us to avoid framebuffer switches in the
    * middle of our render which is inefficient especially on mobile GPUs. This
    * must be called before RenderLayer.
    */
   virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
 
   // TODO: This should also take RenderTargetIntRect like Prepare.
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) = 0;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) = 0;
 
   virtual bool SetCompositableHost(CompositableHost*)
   {
     // We must handle this gracefully, see bug 967824
     NS_WARNING("called SetCompositableHost for a layer type not accepting a compositable");
     return false;
   }
 
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -6,16 +6,17 @@
 #include "PaintedLayerComposite.h"
 #include "CompositableHost.h"           // for TiledLayerProperties, etc
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for CSSRect, LayerPixel, etc
 #include "gfxEnv.h"                     // for gfxEnv
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter::LINEAR
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ContentHost.h"  // for ContentHost
 #include "mozilla/layers/Effects.h"     // for EffectChain
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsAString.h"
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
@@ -93,17 +94,18 @@ PaintedLayerComposite::GetRenderState()
 {
   if (!mBuffer || !mBuffer->IsAttached() || mDestroyed) {
     return LayerRenderState();
   }
   return mBuffer->GetRenderState();
 }
 
 void
-PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
+PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
+                                   const Maybe<gfx::Polygon>& aGeometry)
 {
   if (!mBuffer || !mBuffer->IsAttached()) {
     return;
   }
   PROFILER_LABEL("PaintedLayerComposite", "RenderLayer",
     js::ProfileEntry::Category::GRAPHICS);
 
   Compositor* compositor = mCompositeManager->GetCompositor();
@@ -118,27 +120,24 @@ PaintedLayerComposite::RenderLayer(const
   if (gfxEnv::DumpCompositorTextures()) {
     RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
     if (surf) {
       WriteSnapshotToDumpFile(this, surf);
     }
   }
 #endif
 
-
   RenderWithAllMasks(this, compositor, aClipRect,
-                     [&](EffectChain& effectChain, const gfx::IntRect& clipRect) {
+                     [&](EffectChain& effectChain,
+                     const gfx::IntRect& clipRect) {
     mBuffer->SetPaintWillResample(MayResample());
 
-    mBuffer->Composite(this, effectChain,
-                       GetEffectiveOpacity(),
-                       GetEffectiveTransform(),
-                       GetSamplingFilter(),
-                       clipRect,
-                       &visibleRegion);
+    mBuffer->Composite(this, effectChain, GetEffectiveOpacity(),
+                       GetEffectiveTransform(), GetSamplingFilter(),
+                       clipRect, &visibleRegion, aGeometry);
   });
 
   mBuffer->BumpFlashCounter();
 
   compositor->MakeCurrent();
 }
 
 CompositableHost*
--- a/gfx/layers/composite/PaintedLayerComposite.h
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -46,17 +46,18 @@ public:
   CompositableHost* GetCompositableHost() override;
 
   virtual void Destroy() override;
 
   virtual Layer* GetLayer() override;
 
   virtual void SetLayerManager(HostLayerManager* aManager) override;
 
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
+  virtual void RenderLayer(const gfx::IntRect& aClipRect,
+                           const Maybe<gfx::Polygon>& aGeometry) override;
 
   virtual void CleanupResources() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
   virtual HostLayer* AsHostLayer() override { return this; }
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -389,17 +389,18 @@ TiledLayerBufferComposite::Clear()
 
 void
 TiledContentHost::Composite(LayerComposite* aLayer,
                             EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::SamplingFilter aSamplingFilter,
                             const gfx::IntRect& aClipRect,
-                            const nsIntRegion* aVisibleRegion /* = nullptr */)
+                            const nsIntRegion* aVisibleRegion /* = nullptr */,
+                            const Maybe<gfx::Polygon>& aGeometry)
 {
   MOZ_ASSERT(mCompositor);
   // Reduce the opacity of the low-precision buffer to make it a
   // little more subtle and less jarring. In particular, text
   // rendered at low-resolution and scaled tends to look pretty
   // heavy and this helps mitigate that. When we reduce the opacity
   // we also make sure to draw the background color behind the
   // reduced-opacity tile so that content underneath doesn't show
@@ -434,33 +435,35 @@ TiledContentHost::Composite(LayerComposi
     renderRegion = &tmpRegion;
   }
 #endif
 
   // Render the low and high precision buffers.
   RenderLayerBuffer(mLowPrecisionTiledBuffer,
                     lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
                     aEffectChain, lowPrecisionOpacityReduction * aOpacity,
-                    aSamplingFilter, aClipRect, *renderRegion, aTransform);
+                    aSamplingFilter, aClipRect, *renderRegion, aTransform, aGeometry);
+
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aSamplingFilter,
-                    aClipRect, *renderRegion, aTransform);
+                    aClipRect, *renderRegion, aTransform, aGeometry);
 }
 
 
 void
 TiledContentHost::RenderTile(TileHost& aTile,
                              EffectChain& aEffectChain,
                              float aOpacity,
                              const gfx::Matrix4x4& aTransform,
                              const gfx::SamplingFilter aSamplingFilter,
                              const gfx::IntRect& aClipRect,
                              const nsIntRegion& aScreenRegion,
                              const IntPoint& aTextureOffset,
                              const IntSize& aTextureBounds,
-                             const gfx::Rect& aVisibleRect)
+                             const gfx::Rect& aVisibleRect,
+                             const Maybe<gfx::Polygon>& aGeometry)
 {
   MOZ_ASSERT(!aTile.IsPlaceholderTile());
 
   AutoLockTextureHost autoLock(aTile.mTextureHost);
   AutoLockTextureHost autoLockOnWhite(aTile.mTextureHostOnWhite);
   if (autoLock.Failed() ||
       autoLockOnWhite.Failed()) {
     NS_WARNING("Failed to lock tile");
@@ -493,35 +496,39 @@ TiledContentHost::RenderTile(TileHost& a
     Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
     Rect textureRect(rect.x - aTextureOffset.x, rect.y - aTextureOffset.y,
                      rect.width, rect.height);
 
     effect->mTextureCoords = Rect(textureRect.x / aTextureBounds.width,
                                   textureRect.y / aTextureBounds.height,
                                   textureRect.width / aTextureBounds.width,
                                   textureRect.height / aTextureBounds.height);
-    mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, aTransform, aVisibleRect);
+
+    mCompositor->DrawGeometry(graphicsRect, aClipRect, aEffectChain, opacity,
+                              aTransform, aVisibleRect, aGeometry);
   }
+
   DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
   if (aTile.mTextureHostOnWhite) {
     flags |= DiagnosticFlags::COMPONENT_ALPHA;
   }
   mCompositor->DrawDiagnostics(flags,
                                aScreenRegion, aClipRect, aTransform, mFlashCounter);
 }
 
 void
 TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                                     const Color* aBackgroundColor,
                                     EffectChain& aEffectChain,
                                     float aOpacity,
                                     const gfx::SamplingFilter aSamplingFilter,
                                     const gfx::IntRect& aClipRect,
                                     nsIntRegion aVisibleRegion,
-                                    gfx::Matrix4x4 aTransform)
+                                    gfx::Matrix4x4 aTransform,
+                                    const Maybe<Polygon>& aGeometry)
 {
   if (!mCompositor) {
     NS_WARNING("Can't render tiled content host - no compositor");
     return;
   }
   float resolution = aLayerBuffer.GetResolution();
   gfx::Size layerScale(1, 1);
 
@@ -567,17 +574,18 @@ TiledContentHost::RenderLayerBuffer(Tile
   if (aBackgroundColor) {
     nsIntRegion backgroundRegion = compositeRegion;
     backgroundRegion.ScaleRoundOut(resolution, resolution);
     EffectChain effect;
     effect.mPrimaryEffect = new EffectSolidColor(*aBackgroundColor);
     for (auto iter = backgroundRegion.RectIter(); !iter.Done(); iter.Next()) {
       const IntRect& rect = iter.Get();
       Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
-      mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, aTransform);
+      mCompositor->DrawGeometry(graphicsRect, aClipRect, effect,
+                                1.0, aTransform, aGeometry);
     }
   }
 
   for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
     TileHost& tile = aLayerBuffer.GetTile(i);
     if (tile.IsPlaceholderTile()) {
       continue;
     }
@@ -594,17 +602,19 @@ TiledContentHost::RenderLayerBuffer(Tile
       continue;
     }
 
     tileDrawRegion.ScaleRoundOut(resolution, resolution);
     RenderTile(tile, aEffectChain, aOpacity,
                aTransform, aSamplingFilter, aClipRect, tileDrawRegion,
                tileOffset * resolution, aLayerBuffer.GetTileSize(),
                gfx::Rect(visibleRect.x, visibleRect.y,
-                         visibleRect.width, visibleRect.height));
+                         visibleRect.width, visibleRect.height),
+               aGeometry);
+
     if (tile.mTextureHostOnWhite) {
       componentAlphaDiagnostic = DiagnosticFlags::COMPONENT_ALPHA;
     }
   }
 
   gfx::Rect rect(visibleRect.x, visibleRect.y,
                  visibleRect.width, visibleRect.height);
   GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTENT | componentAlphaDiagnostic,
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -231,17 +231,18 @@ public:
                            const SurfaceDescriptorTiles& aTiledDescriptor);
 
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) override;
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
 
   virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
 
   virtual TiledContentHost* AsTiledContentHost() override { return this; }
 
   virtual void Attach(Layer* aLayer,
                       Compositor* aCompositor,
                       AttachFlags aFlags = NO_FLAGS) override;
@@ -261,29 +262,31 @@ private:
 
   void RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                          const gfx::Color* aBackgroundColor,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::SamplingFilter aSamplingFilter,
                          const gfx::IntRect& aClipRect,
                          nsIntRegion aMaskRegion,
-                         gfx::Matrix4x4 aTransform);
+                         gfx::Matrix4x4 aTransform,
+                         const Maybe<gfx::Polygon>& aGeometry);
 
   // Renders a single given tile.
   void RenderTile(TileHost& aTile,
                   EffectChain& aEffectChain,
                   float aOpacity,
                   const gfx::Matrix4x4& aTransform,
                   const gfx::SamplingFilter aSamplingFilter,
                   const gfx::IntRect& aClipRect,
                   const nsIntRegion& aScreenRegion,
                   const gfx::IntPoint& aTextureOffset,
                   const gfx::IntSize& aTextureBounds,
-                  const gfx::Rect& aVisibleRect);
+                  const gfx::Rect& aVisibleRect,
+                  const Maybe<gfx::Polygon>& aGeometry);
 
   void EnsureTileStore() {}
 
   TiledLayerBufferComposite    mTiledBuffer;
   TiledLayerBufferComposite    mLowPrecisionTiledBuffer;
 };
 
 } // namespace layers
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1814,10 +1814,16 @@ PerUnitTexturePoolOGL::DestroyTextures()
   if (mGL && mGL->MakeCurrent()) {
     if (mTextures.Length() > 0) {
       mGL->fDeleteTextures(mTextures.Length(), &mTextures[0]);
     }
   }
   mTextures.SetLength(0);
 }
 
+bool
+CompositorOGL::SupportsLayerGeometry() const
+{
+  return gfxPrefs::OGLLayerGeometry();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -166,16 +166,18 @@ public:
 
   virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
                             const gfx::IntRect& aClipRect,
                             const EffectChain& aEffectChain,
                             gfx::Float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Rect& aVisibleRect) override;
 
+  virtual bool SupportsLayerGeometry() const override;
+
   virtual void EndFrame() override;
 
   virtual bool SupportsPartialTextureUpdate() override;
 
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
   {
     if (!mGLContext)
       return false;
--- a/gfx/tests/gtest/PolygonTestUtils.cpp
+++ b/gfx/tests/gtest/PolygonTestUtils.cpp
@@ -8,46 +8,56 @@
 #include <cmath>
 
 namespace mozilla {
 namespace gfx {
 
 const float kEpsilon = 0.001f;
 
 // Compares two points while allowing some numerical inaccuracy.
+bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs)
+{
+  const auto d = lhs - rhs;
+
+  return std::abs(d.x) < kEpsilon &&
+         std::abs(d.y) < kEpsilon &&
+         std::abs(d.z) < kEpsilon &&
+         std::abs(d.w) < kEpsilon;
+}
+
 bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs)
 {
-  const Point3D d = lhs - rhs;
+  const auto d = lhs - rhs;
 
   return std::abs(d.x) < kEpsilon &&
          std::abs(d.y) < kEpsilon &&
          std::abs(d.z) < kEpsilon;
 }
 
 bool FuzzyEquals(const Point& lhs, const Point& rhs)
 {
-  const Point d = lhs - rhs;
+  const auto d = lhs - rhs;
 
   return std::abs(d.x) < kEpsilon &&
          std::abs(d.y) < kEpsilon;
 }
 
 bool operator==(const Triangle& lhs, const Triangle& rhs)
 {
   return FuzzyEquals(lhs.p1, rhs.p1) &&
          FuzzyEquals(lhs.p2, rhs.p2) &&
          FuzzyEquals(lhs.p3, rhs.p3);
 }
 
 // Compares the points of two polygons and ensures
 // that the points are in the same winding order.
-bool operator==(const Polygon3D& lhs, const Polygon3D& rhs)
+bool operator==(const Polygon& lhs, const Polygon& rhs)
 {
-  const nsTArray<Point3D>& left = lhs.GetPoints();
-  const nsTArray<Point3D>& right = rhs.GetPoints();
+  const auto& left = lhs.GetPoints();
+  const auto& right = rhs.GetPoints();
 
   // Polygons do not have the same amount of points.
   if (left.Length() != right.Length()) {
     return false;
   }
 
   const size_t pointCount = left.Length();
 
@@ -94,39 +104,39 @@ TEST(PolygonTestUtils, TestSanity)
                           Point3D(0.0f, 0.0f, 0.0f)));
 
   EXPECT_FALSE(FuzzyEquals(Point3D(0.0f, 0.0f, 0.0f),
                            Point3D(0.01f, 0.01f, 0.01f)));
 
   EXPECT_FALSE(FuzzyEquals(Point3D(0.01f, 0.01f, 0.01f),
                            Point3D(0.0f, 0.0f, 0.0f)));
 
-  Polygon3D p1 {
+  Polygon p1 {
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f)
   };
 
   // Same points as above shifted forward by one position.
-  Polygon3D shifted {
+  Polygon shifted {
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f)
   };
 
-  Polygon3D p2 {
+  Polygon p2 {
     Point3D(0.00001f, 0.00001f, 1.00001f),
     Point3D(1.00001f, 0.00001f, 1.00001f),
     Point3D(1.00001f, 1.00001f, 1.00001f),
     Point3D(0.00001f, 1.00001f, 1.00001f)
   };
 
-  Polygon3D p3 {
+  Polygon p3 {
     Point3D(0.01f, 0.01f, 1.01f),
     Point3D(1.01f, 0.01f, 1.01f),
     Point3D(1.01f, 1.01f, 1.01f),
     Point3D(0.01f, 1.01f, 1.01f)
   };
 
   // Trivial equals
   EXPECT_TRUE(p1 == p1);
--- a/gfx/tests/gtest/PolygonTestUtils.h
+++ b/gfx/tests/gtest/PolygonTestUtils.h
@@ -11,21 +11,22 @@
 #include "nsTArray.h"
 #include "Point.h"
 #include "Polygon.h"
 #include "Triangle.h"
 
 namespace mozilla {
 namespace gfx {
 
+bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs);
 bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs);
 bool FuzzyEquals(const Point& lhs, const Point& rhs);
 
 bool operator==(const Triangle& lhs, const Triangle& rhs);
-bool operator==(const Polygon3D& lhs, const Polygon3D& rhs);
+bool operator==(const Polygon& lhs, const Polygon& rhs);
 
 // Compares two arrays with the equality operator.
 template<typename T>
 void AssertArrayEQ(const nsTArray<T>& rhs, const nsTArray<T>& lhs)
 {
   ASSERT_EQ(lhs.Length(), rhs.Length());
 
   for (size_t i = 0; i < lhs.Length(); ++i) {
--- a/gfx/tests/gtest/TestBSPTree.cpp
+++ b/gfx/tests/gtest/TestBSPTree.cpp
@@ -11,21 +11,21 @@
 
 #include <deque>
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 namespace {
 
-static void RunTest(std::deque<Polygon3D> aPolygons,
-                    std::deque<Polygon3D> aExpected)
+static void RunTest(std::deque<Polygon> aPolygons,
+                    std::deque<Polygon> aExpected)
 {
   std::deque<LayerPolygon> layers;
-  for (Polygon3D& polygon : aPolygons) {
+  for (Polygon& polygon : aPolygons) {
     layers.push_back(LayerPolygon(nullptr, Move(polygon)));
   }
 
   const BSPTree tree(layers);
   const nsTArray<LayerPolygon> order = tree.GetDrawOrder();
 
   EXPECT_EQ(aExpected.size(), order.Length());
 
@@ -34,926 +34,926 @@ static void RunTest(std::deque<Polygon3D
   }
 }
 
 } // namespace
 
 
 TEST(BSPTree, SameNode)
 {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(0.0f, 0.0f, 0.0f),
       Point3D(1.0f, 0.0f, 0.0f),
       Point3D(1.0f, 1.0f, 0.0f),
       Point3D(0.0f, 1.0f, 0.0f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.0f, 0.0f, 0.0f),
       Point3D(1.0f, 0.0f, 0.0f),
       Point3D(1.0f, 1.0f, 0.0f),
       Point3D(0.0f, 1.0f, 0.0f)
     }
   };
 
   ::RunTest(polygons, polygons);
 }
 
 TEST(BSPTree, OneChild)
 {
-  const Polygon3D p1 {
+  const Polygon p1 {
     Point3D(0.0f, 0.0f, 0.0f),
     Point3D(1.0f, 0.0f, 0.0f),
     Point3D(1.0f, 1.0f, 0.0f),
     Point3D(0.0f, 1.0f, 0.0f)
   };
 
-  const Polygon3D p2 {
+  const Polygon p2 {
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f)
   };
 
   ::RunTest({p1, p2}, {p1, p2});
   ::RunTest({p2, p1}, {p1, p2});
 }
 
 TEST(BSPTree, SharedEdge1)
 {
-  Polygon3D p1 {
+  Polygon p1 {
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f)
   };
 
-  Polygon3D p2 {
+  Polygon p2 {
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(2.0f, 2.0f, 1.0f),
     Point3D(2.0f, 0.0f, 1.0f)
   };
 
   ::RunTest({p1, p2}, {p1, p2});
 }
 
 TEST(BSPTree, SharedEdge2)
 {
-  Polygon3D p1 {
+  Polygon p1 {
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f)
   };
 
-  Polygon3D p2 {
+  Polygon p2 {
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(2.0f, 2.0f, 0.0f),
     Point3D(2.0f, 0.0f, 0.0f)
   };
 
   ::RunTest({p1, p2}, {p2, p1});
 }
 
 TEST(BSPTree, SplitSharedEdge)
 {
-  Polygon3D p1 {
+  Polygon p1 {
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f)
   };
 
-  Polygon3D p2 {
+  Polygon p2 {
     Point3D(1.0f, 0.0f, 2.0f),
     Point3D(1.0f, 1.0f, 2.0f),
     Point3D(1.0f, 1.0f, 0.0f),
     Point3D(1.0f, 0.0f, 0.0f)
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(1.0f, 1.0f, 1.0f),
       Point3D(1.0f, 1.0f, 0.0f),
       Point3D(1.0f, 0.0f, 0.0f),
       Point3D(1.0f, 0.0f, 1.0f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(1.0f, 0.0f, 1.0f),
       Point3D(0.0f, 0.0f, 1.0f),
       Point3D(0.0f, 1.0f, 1.0f),
       Point3D(1.0f, 1.0f, 1.0f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(1.0f, 0.0f, 2.0f),
       Point3D(1.0f, 1.0f, 2.0f),
       Point3D(1.0f, 1.0f, 1.0f),
       Point3D(1.0f, 0.0f, 1.0f)
     }
   };
 
   ::RunTest({p1, p2}, expected);
 }
 
 TEST(BSPTree, SplitSimple1)
 {
-  Polygon3D p1 {
+  Polygon p1 {
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f)
   };
 
-  Polygon3D p2 {
+  Polygon p2 {
     Point3D(0.0f, 0.0f, 2.0f),
     Point3D(1.0f, 0.0f, 2.0f),
     Point3D(1.0f, 1.0f, 0.0f),
     Point3D(0.0f, 1.0f, 0.0f)
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(0.0f, 1.0f, 0.0f),
       Point3D(0.0f, 0.5f, 1.0f),
       Point3D(1.0f, 0.5f, 1.0f),
       Point3D(1.0f, 1.0f, 0.0f)
     },
     p1,
-    Polygon3D {
+    Polygon {
       Point3D(0.0f, 0.0f, 2.0f),
       Point3D(1.0f, 0.0f, 2.0f),
       Point3D(1.0f, 0.5f, 1.0f),
       Point3D(0.0f, 0.5f, 1.0f)
     }
   };
 
   ::RunTest({p1, p2}, expected);
 }
 
 TEST(BSPTree, SplitSimple2) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-5.00000f, -5.00000f, 0.00000f),
       Point3D(-5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, -5.00000f, 0.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.00000f, -5.00000f, -5.00000f),
       Point3D(0.00000f, 5.00000f, -5.00000f),
       Point3D(0.00000f, 5.00000f, 5.00000f),
       Point3D(0.00000f, -5.00000f, 5.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(0.00000f, -5.00000f, 0.00000f),
       Point3D(0.00000f, -5.00000f, -5.00000f),
       Point3D(0.00000f, 5.00000f, -5.00000f),
       Point3D(0.00000f, 5.00000f, 0.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-5.00000f, -5.00000f, 0.00000f),
       Point3D(-5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, -5.00000f, 0.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.00000f, 5.00000f, 0.00000f),
       Point3D(0.00000f, 5.00000f, 5.00000f),
       Point3D(0.00000f, -5.00000f, 5.00000f),
       Point3D(0.00000f, -5.00000f, 0.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, NoSplit1) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(0.00000f, 10.00000f, 0.00000f),
       Point3D(0.00000f, 0.00000f, 0.00000f),
       Point3D(10.00000f, 0.00000f, 0.00000f),
       Point3D(10.00000f, 10.00000f, 0.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.00000f, 10.00000f, -5.00000f),
       Point3D(0.00000f, 0.00000f, -5.00000f),
       Point3D(10.00000f, 0.00000f, -5.00000f),
       Point3D(10.00000f, 10.00000f, -5.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.00000f, 10.00000f, 5.00000f),
       Point3D(0.00000f, 0.00000f, 5.00000f),
       Point3D(10.00000f, 0.00000f, 5.00000f),
       Point3D(10.00000f, 10.00000f, 5.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(0.00000f, 10.00000f, -5.00000f),
       Point3D(0.00000f, 0.00000f, -5.00000f),
       Point3D(10.00000f, 0.00000f, -5.00000f),
       Point3D(10.00000f, 10.00000f, -5.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.00000f, 10.00000f, 0.00000f),
       Point3D(0.00000f, 0.00000f, 0.00000f),
       Point3D(10.00000f, 0.00000f, 0.00000f),
       Point3D(10.00000f, 10.00000f, 0.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.00000f, 10.00000f, 5.00000f),
       Point3D(0.00000f, 0.00000f, 5.00000f),
       Point3D(10.00000f, 0.00000f, 5.00000f),
       Point3D(10.00000f, 10.00000f, 5.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, NoSplit2) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-5.00000f, -5.00000f, 0.00000f),
       Point3D(-5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, -5.00000f, 0.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(0.00000f, 5.00000f, -15.00000f),
       Point3D(0.00000f, -5.00000f, -15.00000f),
       Point3D(0.00000f, -5.00000f, -10.00000f),
       Point3D(0.00000f, 5.00000f, -10.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(0.00000f, 5.00000f, -15.00000f),
       Point3D(0.00000f, -5.00000f, -15.00000f),
       Point3D(0.00000f, -5.00000f, -10.00000f),
       Point3D(0.00000f, 5.00000f, -10.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-5.00000f, -5.00000f, 0.00000f),
       Point3D(-5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, 5.00000f, 0.00000f),
       Point3D(5.00000f, -5.00000f, 0.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate0degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 2.00000f, 2.00000f),
       Point3D(-0.00000f, -2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, -2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.00000f, 2.00000f),
       Point3D(2.00000f, -0.00000f, -2.00000f),
       Point3D(-2.00000f, 0.00000f, -2.00000f),
       Point3D(-2.00000f, 0.00010f, 2.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, 0.00000f, 2.00000f),
       Point3D(2.00000f, -0.00000f, -2.00000f),
       Point3D(-2.00000f, 0.00000f, -2.00000f),
       Point3D(-2.00000f, 0.00010f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, 2.00000f, 2.00000f),
       Point3D(-0.00000f, -2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, -2.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate20degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 1.19540f, 2.56350f),
       Point3D(-0.00000f, -2.56340f, 1.19540f),
       Point3D(0.00010f, -1.19530f, -2.56340f),
       Point3D(0.00010f, 2.56350f, -1.19530f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, -0.68400f, 1.87940f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, -0.68400f, 1.87940f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, 1.19540f, 2.56350f),
       Point3D(-0.00000f, -2.56340f, 1.19540f),
       Point3D(0.00010f, -1.19530f, -2.56340f),
       Point3D(0.00010f, 2.56350f, -1.19530f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate40degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -0.73200f, 2.73210f),
       Point3D(-0.00000f, -2.73200f, -0.73200f),
       Point3D(0.00010f, 0.73210f, -2.73200f),
       Point3D(0.00010f, 2.73210f, 0.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, -1.73200f, 1.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, -1.73200f, 1.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, -0.73200f, 2.73210f),
       Point3D(-0.00000f, -2.73200f, -0.73200f),
       Point3D(0.00010f, 0.73210f, -2.73200f),
       Point3D(0.00010f, 2.73210f, 0.73210f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate60degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -2.73200f, 0.73210f),
       Point3D(-0.00000f, -0.73200f, -2.73200f),
       Point3D(0.00010f, 2.73210f, -0.73200f),
       Point3D(0.00010f, 0.73210f, 2.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, -1.73200f, -1.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-2.00000f, 1.26793f, 0.73210f),
       Point3D(-2.00000f, -1.73200f, -1.00000f),
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(2.00000f, 1.26793f, 0.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, -2.73200f, 0.73210f),
       Point3D(-0.00000f, -0.73200f, -2.73200f),
       Point3D(0.00010f, 2.73210f, -0.73200f),
       Point3D(0.00010f, 0.73210f, 2.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.26793f, 0.73210f),
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, 1.26793f, 0.73210f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate80degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -1.19530f, -2.56340f),
       Point3D(-0.00000f, 2.56350f, -1.19530f),
       Point3D(0.00010f, 1.19540f, 2.56350f),
       Point3D(0.00010f, -2.56340f, 1.19540f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, 0.68410f, -1.87930f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, -1.19530f, -2.56340f),
       Point3D(-0.00000f, 2.56350f, -1.19530f),
       Point3D(0.00010f, 1.19540f, 2.56350f),
       Point3D(0.00010f, -2.56340f, 1.19540f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, 0.68410f, -1.87930f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate100degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 2.73210f, -0.73200f),
       Point3D(-0.00000f, 0.73210f, 2.73210f),
       Point3D(0.00010f, -2.73200f, 0.73210f),
       Point3D(0.00010f, -0.73200f, -2.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, 1.73210f, 1.00010f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -1.26783f, -0.73200f),
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, -1.26783f, -0.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, 2.73210f, -0.73200f),
       Point3D(-0.00000f, 0.73210f, 2.73210f),
       Point3D(0.00010f, -2.73200f, 0.73210f),
       Point3D(0.00010f, -0.73200f, -2.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-2.00000f, -1.26783f, -0.73200f),
       Point3D(-2.00000f, 1.73210f, 1.00010f),
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(2.00000f, -1.26783f, -0.73200f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate120degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -0.73200f, 2.73210f),
       Point3D(-0.00000f, -2.73200f, -0.73200f),
       Point3D(0.00010f, 0.73210f, -2.73200f),
       Point3D(0.00010f, 2.73210f, 0.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, -1.73200f, 1.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, 1.73210f, -0.99990f),
       Point3D(-2.00000f, -1.73200f, 1.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, -0.73200f, 2.73210f),
       Point3D(-0.00000f, -2.73200f, -0.73200f),
       Point3D(0.00010f, 0.73210f, -2.73200f),
       Point3D(0.00010f, 2.73210f, 0.73210f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate140degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -1.19530f, -2.56340f),
       Point3D(-0.00000f, 2.56350f, -1.19530f),
       Point3D(0.00010f, 1.19540f, 2.56350f),
       Point3D(0.00010f, -2.56340f, 1.19540f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, 0.68410f, -1.87930f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, -1.19530f, -2.56340f),
       Point3D(-0.00000f, 2.56350f, -1.19530f),
       Point3D(0.00010f, 1.19540f, 2.56350f),
       Point3D(0.00010f, -2.56340f, 1.19540f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, 0.68410f, -1.87930f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate160degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 2.00000f, 2.00000f),
       Point3D(-0.00000f, -2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, -2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, 2.00000f, 2.00000f),
       Point3D(-0.00000f, -2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, -2.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate180degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -2.00000f, -2.00000f),
       Point3D(-0.00000f, 2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, -2.00000f, -2.00000f),
       Point3D(-0.00000f, 2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate200degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 1.19540f, 2.56350f),
       Point3D(-0.00000f, -2.56340f, 1.19540f),
       Point3D(0.00010f, -1.19530f, -2.56340f),
       Point3D(0.00010f, 2.56350f, -1.19530f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, -0.68400f, 1.87940f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, -0.68400f, 1.87940f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, 1.19540f, 2.56350f),
       Point3D(-0.00000f, -2.56340f, 1.19540f),
       Point3D(0.00010f, -1.19530f, -2.56340f),
       Point3D(0.00010f, 2.56350f, -1.19530f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate220degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 0.73210f, -2.73200f),
       Point3D(-0.00000f, 2.73210f, 0.73210f),
       Point3D(0.00010f, -0.73200f, 2.73210f),
       Point3D(0.00010f, -2.73200f, -0.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, 1.73210f, -0.99990f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, 0.73210f, -2.73200f),
       Point3D(-0.00000f, 2.73210f, 0.73210f),
       Point3D(0.00010f, -0.73200f, 2.73210f),
       Point3D(0.00010f, -2.73200f, -0.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, 1.73210f, -0.99990f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate240degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -2.73200f, 0.73210f),
       Point3D(-0.00000f, -0.73200f, -2.73200f),
       Point3D(0.00010f, 2.73210f, -0.73200f),
       Point3D(0.00010f, 0.73210f, 2.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, -1.73200f, -1.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-2.00000f, 1.26793f, 0.73210f),
       Point3D(-2.00000f, -1.73200f, -1.00000f),
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(2.00000f, 1.26793f, 0.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, -2.73200f, 0.73210f),
       Point3D(-0.00000f, -0.73200f, -2.73200f),
       Point3D(0.00010f, 2.73210f, -0.73200f),
       Point3D(0.00010f, 0.73210f, 2.73210f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.26793f, 0.73210f),
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, 1.73210f, 1.00010f),
       Point3D(-2.00000f, 1.26793f, 0.73210f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate260degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 1.19540f, 2.56350f),
       Point3D(-0.00000f, -2.56340f, 1.19540f),
       Point3D(0.00010f, -1.19530f, -2.56340f),
       Point3D(0.00010f, 2.56350f, -1.19530f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, -0.68400f, 1.87940f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, 0.68410f, -1.87930f),
       Point3D(-2.00000f, -0.68400f, 1.87940f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, 1.19540f, 2.56350f),
       Point3D(-0.00000f, -2.56340f, 1.19540f),
       Point3D(0.00010f, -1.19530f, -2.56340f),
       Point3D(0.00010f, 2.56350f, -1.19530f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate280degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 2.73210f, -0.73200f),
       Point3D(-0.00000f, 0.73210f, 2.73210f),
       Point3D(0.00010f, -2.73200f, 0.73210f),
       Point3D(0.00010f, -0.73200f, -2.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, 1.73210f, 1.00010f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(2.00000f, -1.26783f, -0.73200f),
       Point3D(2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, -1.73200f, -1.00000f),
       Point3D(-2.00000f, -1.26783f, -0.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-0.00000f, 2.73210f, -0.73200f),
       Point3D(-0.00000f, 0.73210f, 2.73210f),
       Point3D(0.00010f, -2.73200f, 0.73210f),
       Point3D(0.00010f, -0.73200f, -2.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(-2.00000f, -1.26783f, -0.73200f),
       Point3D(-2.00000f, 1.73210f, 1.00010f),
       Point3D(2.00000f, 1.73210f, 1.00010f),
       Point3D(2.00000f, -1.26783f, -0.73200f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate300degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, 0.73210f, -2.73200f),
       Point3D(-0.00000f, 2.73210f, 0.73210f),
       Point3D(0.00010f, -0.73200f, 2.73210f),
       Point3D(0.00010f, -2.73200f, -0.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, 1.73210f, -0.99990f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, 0.73210f, -2.73200f),
       Point3D(-0.00000f, 2.73210f, 0.73210f),
       Point3D(0.00010f, -0.73200f, 2.73210f),
       Point3D(0.00010f, -2.73200f, -0.73200f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 1.73210f, -0.99990f),
       Point3D(2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, -1.73200f, 1.00000f),
       Point3D(-2.00000f, 1.73210f, -0.99990f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate320degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -1.19530f, -2.56340f),
       Point3D(-0.00000f, 2.56350f, -1.19530f),
       Point3D(0.00010f, 1.19540f, 2.56350f),
       Point3D(0.00010f, -2.56340f, 1.19540f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, 0.68410f, -1.87930f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, -1.19530f, -2.56340f),
       Point3D(-0.00000f, 2.56350f, -1.19530f),
       Point3D(0.00010f, 1.19540f, 2.56350f),
       Point3D(0.00010f, -2.56340f, 1.19540f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.68410f, -1.87930f),
       Point3D(2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, -0.68400f, 1.87940f),
       Point3D(-2.00000f, 0.68410f, -1.87930f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate340degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -2.00000f, -2.00000f),
       Point3D(-0.00000f, 2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, -2.00000f, -2.00000f),
       Point3D(-0.00000f, 2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
 
 TEST(BSPTree, TwoPlaneIntersectRotate360degrees) {
-  const std::deque<Polygon3D> polygons {
-    Polygon3D {
+  const std::deque<Polygon> polygons {
+    Polygon {
       Point3D(-0.00000f, -2.00000f, -2.00000f),
       Point3D(-0.00000f, 2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f)
     }
   };
 
-  const std::deque<Polygon3D> expected {
-    Polygon3D {
+  const std::deque<Polygon> expected {
+    Polygon {
       Point3D(-0.00000f, -2.00000f, -2.00000f),
       Point3D(-0.00000f, 2.00000f, -2.00000f),
       Point3D(0.00010f, 2.00000f, 2.00000f),
       Point3D(0.00010f, -2.00000f, 2.00000f)
     },
-    Polygon3D {
+    Polygon {
       Point3D(2.00000f, 0.00010f, -2.00000f),
       Point3D(2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f)
     }
   };
   ::RunTest(polygons, expected);
 }
--- a/gfx/tests/gtest/TestPolygon.cpp
+++ b/gfx/tests/gtest/TestPolygon.cpp
@@ -9,37 +9,37 @@
 
 #include "nsTArray.h"
 #include "Point.h"
 #include "Polygon.h"
 #include "Triangle.h"
 
 using namespace mozilla::gfx;
 
-TEST(Polygon3D, TriangulateRectangle)
+TEST(Polygon, TriangulateRectangle)
 {
-  const Polygon3D p {
+  const Polygon p {
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f)
   };
 
   const nsTArray<Triangle> triangles = p.ToTriangles();
   const nsTArray<Triangle> expected = {
     Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f)),
     Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))
   };
 
   AssertArrayEQ(triangles, expected);
 }
 
-TEST(Polygon3D, TriangulatePentagon)
+TEST(Polygon, TriangulatePentagon)
 {
-  const Polygon3D p {
+  const Polygon p {
     Point3D(0.0f, 0.0f, 1.0f),
     Point3D(0.0f, 1.0f, 1.0f),
     Point3D(0.5f, 1.5f, 1.0f),
     Point3D(1.0f, 1.0f, 1.0f),
     Point3D(1.0f, 0.0f, 1.0f)
   };
 
   const nsTArray<Triangle> triangles = p.ToTriangles();
@@ -47,97 +47,89 @@ TEST(Polygon3D, TriangulatePentagon)
     Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(0.5f, 1.5f)),
     Triangle(Point(0.0f, 0.0f), Point(0.5f, 1.5f), Point(1.0f, 1.0f)),
     Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))
   };
 
   AssertArrayEQ(triangles, expected);
 }
 
-TEST(Polygon3D, ClipRectangle)
+void
+TestClipRect(const Polygon& aPolygon,
+             const Polygon& aExpected,
+             const Rect& aRect)
 {
-  Polygon3D clipped, expected;
+  const Polygon res = aPolygon.ClipPolygon(Polygon::FromRect(aRect));
+  EXPECT_TRUE(res == aExpected);
+}
 
-  Polygon3D polygon {
+TEST(Polygon, ClipRectangle)
+{
+  Polygon polygon {
     Point3D(0.0f, 0.0f, 0.0f),
     Point3D(0.0f, 1.0f, 0.0f),
     Point3D(1.0f, 1.0f, 0.0f),
     Point3D(1.0f, 0.0f, 0.0f)
   };
-
-  clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 1.0f, 1.0f));
-  EXPECT_TRUE(clipped == polygon);
+  TestClipRect(polygon, polygon, Rect(0.0f, 0.0f, 1.0f, 1.0f));
 
-
-  clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 0.8f, 0.8f));
-  expected = Polygon3D {
+  Polygon expected = Polygon {
     Point3D(0.0f, 0.0f, 0.0f),
     Point3D(0.0f, 0.8f, 0.0f),
     Point3D(0.8f, 0.8f, 0.0f),
     Point3D(0.8f, 0.0f, 0.0f)
   };
-  EXPECT_TRUE(clipped == expected);
-
+  TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
 
-  clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.8f, 0.8f));
-  expected = Polygon3D {
+  expected = Polygon {
     Point3D(0.2f, 0.2f, 0.0f),
     Point3D(0.2f, 1.0f, 0.0f),
     Point3D(1.0f, 1.0f, 0.0f),
     Point3D(1.0f, 0.2f, 0.0f)
   };
-  EXPECT_TRUE(clipped == expected);
-
+  TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
 
-  clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.6f, 0.6f));
-  expected = Polygon3D {
+  expected = Polygon {
     Point3D(0.2f, 0.2f, 0.0f),
     Point3D(0.2f, 0.8f, 0.0f),
     Point3D(0.8f, 0.8f, 0.0f),
     Point3D(0.8f, 0.2f, 0.0f)
   };
-  EXPECT_TRUE(clipped == expected);
+  TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
 }
 
-TEST(Polygon3D, ClipTriangle)
+TEST(Polygon, ClipTriangle)
 {
-  Polygon3D clipped, expected;
-  const Polygon3D polygon {
+  Polygon clipped, expected;
+  const Polygon polygon {
     Point3D(0.0f, 0.0f, 0.0f),
     Point3D(0.0f, 1.0f, 0.0f),
     Point3D(1.0f, 1.0f, 0.0f)
   };
 
-  clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 1.0f, 1.0f));
-  expected = Polygon3D {
+  expected = Polygon {
     Point3D(0.0f, 0.0f, 0.0f),
     Point3D(0.0f, 1.0f, 0.0f),
     Point3D(1.0f, 1.0f, 0.0f)
   };
-  EXPECT_TRUE(clipped == expected);
-
+  TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 1.0f, 1.0f));
 
-  clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 0.8f, 0.8f));
-  expected = Polygon3D {
+  expected = Polygon {
     Point3D(0.0f, 0.0f, 0.0f),
     Point3D(0.0f, 0.8f, 0.0f),
     Point3D(0.8f, 0.8f, 0.0f)
   };
-  EXPECT_TRUE(clipped == expected);
-
+  TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
 
-  clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.8f, 0.8f));
-  expected = Polygon3D {
+  expected = Polygon {
     Point3D(0.2f, 0.2f, 0.0f),
     Point3D(0.2f, 1.0f, 0.0f),
     Point3D(1.0f, 1.0f, 0.0f)
   };
-  EXPECT_TRUE(clipped == expected);
-
+  TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
 
-  clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.6f, 0.6f));
-  expected = Polygon3D {
+  expected = Polygon {
     Point3D(0.2f, 0.2f, 0.0f),
     Point3D(0.2f, 0.8f, 0.0f),
     Point3D(0.8f, 0.8f, 0.0f)
   };
-  EXPECT_TRUE(clipped == expected);
-}
+  TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
+}
\ No newline at end of file
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -522,16 +522,17 @@ private:
   DECL_GFX_PREF(Once, "layers.tiles.adjust",                   LayersTilesAdjust, bool, true);
   DECL_GFX_PREF(Once, "layers.tiles.edge-padding",             TileEdgePaddingEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.enabled",          LayerTileFadeInEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.duration-ms",      LayerTileFadeInDuration, uint32_t, 250);
   DECL_GFX_PREF(Live, "layers.transaction.warning-ms",         LayerTransactionWarning, uint32_t, 200);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, true);
   DECL_GFX_PREF(Live, "layers.draw-mask-debug",                DrawMaskLayer, bool, false);
+  DECL_GFX_PREF(Live, "layers.geometry.opengl.enabled",        OGLLayerGeometry, bool, false);
 
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled",    ScrollBehaviorEnabled, bool, true);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-max-velocity", ScrollSnapPredictionMaxVelocity, int32_t, 2000);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-sensitivity", ScrollSnapPredictionSensitivity, float, 0.750f);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.proximity-threshold", ScrollSnapProximityThreshold, int32_t, 200);
   DECL_GFX_PREF(Live, "layout.css.touch_action.enabled",       TouchActionEnabled, bool, false);
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -76,21 +76,20 @@ VRManager::VRManager()
   }
 #endif
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
   // OpenVR is cross platform compatible
   mgr = VRDisplayManagerOpenVR::Create();
   if (mgr) {
     mManagers.AppendElement(mgr);
-  }
-
-  controllerMgr = VRControllerManagerOpenVR::Create();
-  if (controllerMgr) {
-    mControllerManagers.AppendElement(controllerMgr);
+    controllerMgr = VRControllerManagerOpenVR::Create();
+    if (controllerMgr) {
+      mControllerManagers.AppendElement(controllerMgr);
+    }
   }
 
   // OSVR is cross platform compatible
   mgr = VRDisplayManagerOSVR::Create();
   if (mgr) {
       mManagers.AppendElement(mgr);
   }
 #endif
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -63,18 +63,16 @@ class Visitor:
         for opens in p.opensStmts:
             opens.accept(self)
         for mgr in p.managers:
             mgr.accept(self)
         for managed in p.managesStmts:
             managed.accept(self)
         for msgDecl in p.messageDecls:
             msgDecl.accept(self)
-        for transitionStmt in p.transitionStmts:
-            transitionStmt.accept(self)
 
     def visitNamespace(self, ns):
         pass
 
     def visitSpawnsStmt(self, spawns):
         pass
 
     def visitBridgesStmt(self, bridges):
@@ -90,28 +88,16 @@ class Visitor:
         pass
 
     def visitMessageDecl(self, md):
         for inParam in md.inParams:
             inParam.accept(self)
         for outParam in md.outParams:
             outParam.accept(self)
 
-    def visitTransitionStmt(self, ts):
-        ts.state.accept(self)
-        for trans in ts.transitions:
-            trans.accept(self)
-
-    def visitTransition(self, t):
-        for toState in t.toStates:
-            toState.accept(self)
-
-    def visitState(self, s):
-        pass
-
     def visitParam(self, decl):
         pass
 
     def visitTypeSpec(self, ts):
         pass
 
     def visitDecl(self, d):
         pass
@@ -241,18 +227,16 @@ class Protocol(NamespacedNode):
         self.sendSemantics = ASYNC
         self.nested = NOT_NESTED
         self.spawnsStmts = [ ]
         self.bridgesStmts = [ ]
         self.opensStmts = [ ]
         self.managers = [ ]
         self.managesStmts = [ ]
         self.messageDecls = [ ]
-        self.transitionStmts = [ ]
-        self.startStates = [ ]
 
 class StructField(Node):
     def __init__(self, loc, type, name):
         Node.__init__(self, loc)
         self.typespec = type
         self.name = name
 
 class StructDecl(NamespacedNode):
@@ -317,93 +301,16 @@ class MessageDecl(Node):
         for modifier in modifiers:
             if modifier.startswith('compress'):
                 self.compress = modifier
             elif modifier == 'verify':
                 self.verify = modifier
             elif modifier != '':
                 raise Exception, "Unexpected message modifier `%s'"% modifier
 
-class Transition(Node):
-    def __init__(self, loc, trigger, msg, toStates):
-        Node.__init__(self, loc)
-        self.trigger = trigger
-        self.msg = msg
-        self.toStates = toStates
-
-    def __cmp__(self, o):
-        c = cmp(self.msg, o.msg)
-        if c: return c
-        c = cmp(self.trigger, o.trigger)
-        if c: return c
-
-    def __hash__(self): return hash(str(self))
-    def __str__(self): return '%s %s'% (self.trigger, self.msg)
-
-    @staticmethod
-    def nameToTrigger(name):
-        return { 'send': SEND, 'recv': RECV, 'call': CALL, 'answer': ANSWER }[name]
-
-Transition.NULL = Transition(Loc.NONE, None, None, [ ])
-
-class TransitionStmt(Node):
-    def __init__(self, loc, state, transitions):
-        Node.__init__(self, loc)
-        self.state = state
-        self.transitions = transitions
-
-    @staticmethod
-    def makeNullStmt(state):
-        return TransitionStmt(Loc.NONE, state, [ Transition.NULL ])
-
-class SEND:
-    pretty = 'send'
-    @classmethod
-    def __hash__(cls): return hash(cls.pretty)
-    @classmethod
-    def direction(cls): return OUT
-class RECV:
-    pretty = 'recv'
-    @classmethod
-    def __hash__(cls): return hash(cls.pretty)
-    @classmethod
-    def direction(cls): return IN
-class CALL:
-    pretty = 'call'
-    @classmethod
-    def __hash__(cls): return hash(cls.pretty)
-    @classmethod
-    def direction(cls): return OUT
-class ANSWER:
-    pretty = 'answer'
-    @classmethod
-    def __hash__(cls): return hash(cls.pretty)
-    @classmethod
-    def direction(cls): return IN
-
-class State(Node):
-    def __init__(self, loc, name, start=False):
-        Node.__init__(self, loc)
-        self.name = name
-        self.start = start
-    def __eq__(self, o):
-         return (isinstance(o, State)
-                 and o.name == self.name
-                 and o.start == self.start)
-    def __hash__(self):
-        return hash(repr(self))
-    def __ne__(self, o):
-        return not (self == o)
-    def __repr__(self): return '<State %r start=%r>'% (self.name, self.start)
-    def __str__(self): return '<State %s start=%s>'% (self.name, self.start)
-
-State.ANY = State(Loc.NONE, '[any]', start=True)
-State.DEAD = State(Loc.NONE, '[dead]', start=False)
-State.DYING = State(Loc.NONE, '[dying]', start=False)
-
 class Param(Node):
     def __init__(self, loc, typespec, name):
         Node.__init__(self, loc)
         self.name = name
         self.typespec = typespec
 
 class TypeSpec(Node):
     def __init__(self, loc, spec):
--- a/ipc/ipdl/ipdl/cgen.py
+++ b/ipc/ipdl/ipdl/cgen.py
@@ -46,35 +46,33 @@ Also known as pretty-printing.'''
         self.println('include "'+ inc.file +'";')
 
     def visitProtocolInclude(self, inc):
         self.println('include protocol "'+ inc.file +'";')
         if inc.tu.filename not in self.printed:
             self.println('/* Included file:')
             IPDLCodeGen(outf=self.outf, indentCols=self.indentCols,
                         printed=self.printed).visitTranslationUnit(inc.tu)
-        
+
             self.println('*/')
 
     def visitProtocol(self, p):
         self.println()
         for namespace in p.namespaces:  namespace.accept(self)
 
         self.println('%s protocol %s\n{'% (p.sendSemantics[0], p.name))
         self.indent()
 
         for mgs in p.managesStmts:
             mgs.accept(self)
         if len(p.managesStmts):  self.println()
 
         for msgDecl in p.messageDecls:  msgDecl.accept(self)
         self.println()
 
-        for transStmt in p.transitionStmts:  transStmt.accept(self)
-
         self.dedent()
         self.println('}')
         self.write('}\n'* len(p.namespaces))
 
     def visitManagerStmt(self, mgr):
         self.printdentln('manager '+ mgr.name +';')
 
     def visitManagesStmt(self, mgs):
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -1335,20 +1335,16 @@ with some new IPDL/C++ nodes that are tu
         return _HybridDecl(decl.type, decl.progname)
 
     def visitMessageDecl(self, md):
         md.namespace = self.protocolName
         md.params = [ param.accept(self) for param in md.inParams ]
         md.returns = [ ret.accept(self) for ret in md.outParams ]
         MessageDecl.upgrade(md)
 
-    def visitTransitionStmt(self, ts):
-        name = ts.state.decl.progname
-        ts.state.decl.cxxname = name
-        ts.state.decl.cxxenum = ExprVar(self.protocolName +'::'+ name)
 
 ##-----------------------------------------------------------------------------
 
 def msgenums(protocol, pretty=False):
     msgenum = TypeEnum('MessageType')
     msgstart = _messageStartName(protocol.decl.type) +' << 16'
     msgenum.addId(protocol.name + 'Start', msgstart)
 
@@ -1545,23 +1541,17 @@ class _GenerateProtocolCode(ipdl.ast.Vis
         stateenum = TypeEnum('State')
         # NB: __Dead is the first state on purpose, so that it has
         # value '0'
         stateenum.addId(_deadState().name)
         stateenum.addId(_nullState().name)
         stateenum.addId(_errorState().name)
         if self.protocol.decl.type.hasReentrantDelete:
             stateenum.addId(_dyingState().name)
-        for ts in p.transitionStmts:
-            stateenum.addId(ts.state.decl.cxxname)
-        if len(p.transitionStmts):
-            startstate = p.transitionStmts[0].state.decl.cxxname
-        else:
-            startstate = _nullState().name
-        stateenum.addId(_startState().name, startstate)
+        stateenum.addId(_startState().name, _nullState().name)
 
         ns.addstmts([ StmtDecl(Decl(stateenum,'')), Whitespace.NL ])
 
         # spit out message type enum and classes
         msgenum = msgenums(self.protocol)
         ns.addstmts([ StmtDecl(Decl(msgenum, '')), Whitespace.NL ])
 
         tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc())
@@ -1676,80 +1666,31 @@ class _GenerateProtocolCode(ipdl.ast.Vis
         return openfunc
 
 
     def genTransitionFunc(self):
         ptype = self.protocol.decl.type
         usesend, sendvar = set(), ExprVar('Send__')
         userecv, recvvar = set(), ExprVar('Recv__')
 
-        def sameTrigger(trigger, actionexpr):
-            if trigger is ipdl.ast.SEND or trigger is ipdl.ast.CALL:
-                usesend.add('yes')
-                return ExprBinary(sendvar, '==', actionexpr)
-            else:
-                userecv.add('yes')
-                return ExprBinary(recvvar, '==',
-                                  actionexpr)
-
-        def stateEnum(s):
-            if s is ipdl.ast.State.DEAD:
-                return _deadState()
-            else:
-                return ExprVar(s.decl.cxxname)
-
         # bool Transition(Trigger trigger, State* next)
         # The state we are transitioning from is stored in *next.
         fromvar = ExprVar('from')
         triggervar = ExprVar('trigger')
         nextvar = ExprVar('next')
         msgexpr = ExprSelect(triggervar, '.', 'mMessage')
-        actionexpr = ExprSelect(triggervar, '.', 'mAction')
 
         transitionfunc = FunctionDefn(FunctionDecl(
             'Transition',
             params=[ Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
                      Decl(Type('State', ptr=1), nextvar.name) ],
             ret=Type.BOOL))
 
         fromswitch = StmtSwitch(fromvar)
 
-        for ts in self.protocol.transitionStmts:
-            msgswitch = StmtSwitch(msgexpr)
-
-            msgToTransitions = { }
-
-            for t in ts.transitions:
-                msgid = t.msg._md.msgId()
-
-                ifsametrigger = StmtIf(sameTrigger(t.trigger, actionexpr))
-                # FIXME multi-out states
-                for nextstate in t.toStates: break
-                ifsametrigger.addifstmts([
-                    StmtExpr(ExprAssn(ExprDeref(nextvar),
-                                      stateEnum(nextstate))),
-                    StmtReturn(ExprLiteral.TRUE)
-                ])
-
-                transitions = msgToTransitions.get(msgid, [ ])
-                transitions.append(ifsametrigger)
-                msgToTransitions[msgid] = transitions
-
-            for msgid, transitions in msgToTransitions.iteritems():
-                block = Block()
-                block.addstmts(transitions +[ StmtBreak() ])
-                msgswitch.addcase(CaseLabel(msgid), block)
-
-            msgblock = Block()
-            msgblock.addstmts([
-                msgswitch,
-                StmtBreak()
-            ])
-            fromswitch.addcase(CaseLabel(ts.state.decl.cxxname), msgblock)
-
         # special cases for Null and Error
         nullerrorblock = Block()
         if ptype.hasDelete:
             ifdelete = StmtIf(ExprBinary(_deleteId(), '==', msgexpr))
             if ptype.hasReentrantDelete:
                 nextState = _dyingState()
             else:
                 nextState = _deadState()
@@ -1795,23 +1736,16 @@ class _GenerateProtocolCode(ipdl.ast.Vis
                 StmtDecl(Decl(Type('int32_t', const=1), recvvar.name),
                          init=ExprVar('mozilla::ipc::Trigger::Recv')))
         if usesend or userecv:
             transitionfunc.addstmt(Whitespace.NL)
 
         transitionfunc.addstmt(StmtDecl(Decl(Type('State'), fromvar.name),
                                         init=ExprDeref(nextvar)))
         transitionfunc.addstmt(fromswitch)
-        # all --> Error transitions break to here.  But only insert this
-        # block if there is any possibility of such transitions.
-        if self.protocol.transitionStmts:
-            transitionfunc.addstmts([
-                StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
-                StmtReturn(ExprLiteral.FALSE),
-            ])
 
         return transitionfunc
 
 ##--------------------------------------------------
 
 def _generateMessageConstructor(clsname, msgid, nested, prio, prettyName, compress):
     routingId = ExprVar('routingId')
 
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -100,48 +100,41 @@ class Parser:
 
 def locFromTok(p, num):
     return Loc(Parser.current.filename, p.lineno(num))
 
 
 ##-----------------------------------------------------------------------------
 
 reserved = set((
-        'answer',
         'as',
         'async',
         'both',
         'bridges',
-        'call',
         'child',
         'class',
         'compress',
         'compressall',
         '__delete__',
         'delete',                       # reserve 'delete' to prevent its use
         'from',
-        'goto',
         'include',
         'intr',
         'manager',
         'manages',
         'namespace',
         'nested',
         'nullable',
         'opens',
         'or',
         'parent',
         'prio',
         'protocol',
-        'recv',
         'returns',
-        'send',
         'spawns',
-        'start',
-        'state',
         'struct',
         'sync',
         'union',
         'upto',
         'using',
         'verify'))
 tokens = [
     'COLONCOLON', 'ID', 'STRING',
@@ -455,19 +448,20 @@ def p_ManagesStmt(p):
     p[0] = ManagesStmt(locFromTok(p, 1), p[2])
 
 
 ##--------------------
 ## Message decls
 
 def p_MessageDeclsOpt(p):
     """MessageDeclsOpt : MessageDeclThing MessageDeclsOpt
-                       | TransitionStmtsOpt"""
-    if 2 == len(p):
-        p[0] = p[1]
+                       | """
+    if 1 == len(p):
+        # we fill in |loc| in the Protocol rule
+        p[0] = Protocol(None)
     else:
         p[2].messageDecls.insert(0, p[1])
         p[0] = p[2]
 
 def p_MessageDeclThing(p):
     """MessageDeclThing : MessageDirectionLabel ':' MessageDecl ';'
                         | MessageDecl ';'"""
     if 3 == len(p):
@@ -555,81 +549,16 @@ def p_MessageVerify(p):
     """MessageVerify : VERIFY"""
     p[0] = p[1]
 
 def p_MessageCompress(p):
     """MessageCompress : COMPRESS
                        | COMPRESSALL"""
     p[0] = p[1]
 
-##--------------------
-## State machine
-
-def p_TransitionStmtsOpt(p):
-    """TransitionStmtsOpt : TransitionStmt TransitionStmtsOpt
-                          |"""
-    if 1 == len(p):
-        # we fill in |loc| in the Protocol rule
-        p[0] = Protocol(None)
-    else:
-        p[2].transitionStmts.insert(0, p[1])
-        p[0] = p[2]
-
-def p_TransitionStmt(p):
-    """TransitionStmt : OptionalStart STATE State ':' Transitions"""
-    p[3].start = p[1]
-    p[0] = TransitionStmt(locFromTok(p, 2), p[3], p[5])
-
-def p_OptionalStart(p):
-    """OptionalStart : START
-                     | """
-    p[0] = (len(p) == 2)                # True iff 'start' specified
-
-def p_Transitions(p):
-    """Transitions : Transitions Transition
-                   | Transition"""
-    if 3 == len(p):
-        p[1].append(p[2])
-        p[0] = p[1]
-    else:
-        p[0] = [ p[1] ]
-
-def p_Transition(p):
-    """Transition : Trigger ID GOTO StateList ';'
-                  | Trigger __DELETE__ ';'
-                  | Trigger DELETE ';'"""
-    if 'delete' == p[2]:
-        _error(locFromTok(p, 1), "`delete' is a reserved identifier")
-
-    loc, trigger = p[1]
-    if 6 == len(p):
-        nextstates = p[4]
-    else:
-        nextstates = [ State.DEAD ]
-    p[0] = Transition(loc, trigger, p[2], nextstates)
-
-def p_Trigger(p):
-    """Trigger : SEND
-               | RECV
-               | CALL
-               | ANSWER"""
-    p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ]
-
-def p_StateList(p):
-    """StateList : StateList OR State
-                 | State"""
-    if 2 == len(p):
-        p[0] = [ p[1] ]
-    else:
-        p[1].append(p[3])
-        p[0] = p[1]
-
-def p_State(p):
-    """State : ID"""
-    p[0] = State(locFromTok(p, 1), p[1])
 
 ##--------------------
 ## Minor stuff
 def p_Nested(p):
     """Nested : ID"""
     kinds = {'not': 1,
              'inside_sync': 2,
              'inside_cpow': 3}
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -1,36 +1,30 @@
 # vim: set ts=4 sw=4 tw=99 et:
 # 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 os, sys
 
-from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, StructDecl, TransitionStmt
+from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, StructDecl
 from ipdl.ast import TypeSpec, UnionDecl, UsingStmt, Visitor
 from ipdl.ast import ASYNC, SYNC, INTR
-from ipdl.ast import IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
+from ipdl.ast import IN, OUT, INOUT
 from ipdl.ast import NOT_NESTED, INSIDE_SYNC_NESTED, INSIDE_CPOW_NESTED
 import ipdl.builtin as builtin
 
 _DELETE_MSG = '__delete__'
 
 
 def _otherside(side):
     if side == 'parent':  return 'child'
     elif side == 'child': return 'parent'
     else:  assert 0 and 'unknown side "%s"'% (side)
 
-def unique_pairs(s):
-    n = len(s)
-    for i, e1 in enumerate(s):
-        for j in xrange(i+1, n):
-            yield (e1, s[j])
-
 def cartesian_product(s1, s2):
     for e1 in s1:
         for e2 in s2:
             yield (e1, e2)
 
 
 class TypeVisitor:
     def __init__(self):
@@ -44,35 +38,31 @@ class TypeVisitor:
         pass
 
     def visitBuiltinCxxType(self, t, *args):
         pass
 
     def visitImportedCxxType(self, t, *args):
         pass
 
-    def visitStateType(self, s, *args):
-        pass
-
     def visitMessageType(self, m, *args):
         for param in m.params:
             param.accept(self, *args)
         for ret in m.returns:
             ret.accept(self, *args)
         if m.cdtype is not None:
             m.cdtype.accept(self, *args)
 
     def visitProtocolType(self, p, *args):
         # NB: don't visit manager and manages. a naive default impl
         # could result in an infinite loop
         pass
 
     def visitActorType(self, a, *args):
         a.protocol.accept(self, *args)
-        a.state.accept(self, *args)
 
     def visitStructType(self, s, *args):
         if s in self.visited:
             return
 
         self.visited.add(s)
         for field in s.fields:
             field.accept(self, *args)
@@ -190,17 +180,16 @@ class ImportedCxxType(CxxType):
         return self.qname.baseid
     def fullname(self):
         return str(self.qname)
 
 ##--------------------
 class IPDLType(Type):
     def isIPDL(self):  return True
     def isVisible(self): return True
-    def isState(self): return False
     def isMessage(self): return False
     def isProtocol(self): return False
     def isActor(self): return False
     def isStruct(self): return False
     def isUnion(self): return False
     def isArray(self): return False
     def isAtom(self):  return True
     def isCompound(self): return False
@@ -233,27 +222,16 @@ class IPDLType(Type):
         elif greater.isInterrupt():
             return True
 
         return False
 
     def needsMoreJuiceThan(self, o):
         return not IPDLType.convertsTo(self, o)
 
-class StateType(IPDLType):
-    def __init__(self, protocol, name, start=False):
-        self.protocol = protocol
-        self.name = name
-        self.start = start
-    def isState(self): return True
-    def name(self):
-        return self.name
-    def fullname(self):
-        return self.name()
-
 class MessageType(IPDLType):
     def __init__(self, nested, prio, sendSemantics, direction,
                  ctor=False, dtor=False, cdtype=None, compress=False,
                  verify=False):
         assert not (ctor and dtor)
         assert not (ctor or dtor) or type is not None
 
         self.nested = nested
@@ -290,25 +268,24 @@ class Bridge:
     def __cmp__(self, o):
         return cmp(self.parent, o.parent) or cmp(self.child, o.child)
     def __eq__(self, o):
         return self.parent == o.parent and self.child == o.child
     def __hash__(self):
         return hash(self.parent) + hash(self.child)
 
 class ProtocolType(IPDLType):
-    def __init__(self, qname, nestedRange, sendSemantics, stateless=False):
+    def __init__(self, qname, nestedRange, sendSemantics):
         self.qname = qname
         self.nestedRange = nestedRange
         self.sendSemantics = sendSemantics
         self.spawns = set()             # ProtocolType
         self.opens = set()              # ProtocolType
         self.managers = []           # ProtocolType
         self.manages = [ ]
-        self.stateless = stateless
         self.hasDelete = False
         self.hasReentrantDelete = False
     def isProtocol(self): return True
 
     def name(self):
         return self.qname.baseid
     def fullname(self):
         return str(self.qname)
@@ -359,19 +336,18 @@ class ProtocolType(IPDLType):
     def isToplevel(self):
         return not self.isManaged()
 
     def manager(self):
         assert 1 == len(self.managers)
         for mgr in self.managers: return mgr
 
 class ActorType(IPDLType):
-    def __init__(self, protocol, state=None, nullable=0):
+    def __init__(self, protocol, nullable=0):
         self.protocol = protocol
-        self.state = state
         self.nullable = nullable
     def isActor(self): return True
 
     def name(self):
         return self.protocol.name()
     def fullname(self):
         return self.protocol.fullname()
 
@@ -634,20 +610,16 @@ With this information, it finally type c
         # now that the nodes have decls, type checking is much easier.
         if not runpass(CheckTypes(self.errors)):
             return False
 
         if not (runpass(BuildProcessGraph(self.errors))
                 and runpass(CheckProcessGraph(self.errors))):
             return False
 
-        if (tu.protocol
-            and len(tu.protocol.startStates)
-            and not runpass(CheckStateMachine(self.errors))):
-            return False
         return True
 
     def reportErrors(self, errout):
         for error in self.errors:
             print >>errout, error
 
 
 class TcheckVisitor(Visitor):
@@ -707,18 +679,17 @@ class GatherDecls(TcheckVisitor):
             # to put both the namespace and non-namespaced name in the
             # global scope.  try to figure out something better; maybe
             # a type-neutral |using| that works for C++ and protocol
             # types?
             qname = p.qname()
             fullname = str(qname)
             p.decl = self.declare(
                 loc=p.loc,
-                type=ProtocolType(qname, p.nestedRange, p.sendSemantics,
-                                  stateless=(0 == len(p.transitionStmts))),
+                type=ProtocolType(qname, p.nestedRange, p.sendSemantics),
                 shortname=p.name,
                 fullname=None if 0 == len(qname.quals) else fullname)
 
             p.parentEndpointDecl = self.declare(
                 loc=p.loc,
                 type=EndpointType(QualifiedId(p.loc, 'Endpoint<' + fullname + 'Parent>', ['mozilla', 'ipc'])),
                 shortname='Endpoint<' + p.name + 'Parent>')
             p.childEndpointDecl = self.declare(
@@ -921,104 +892,16 @@ class GatherDecls(TcheckVisitor):
             ctordecl = self.symtab.lookup(mgdname +'Constructor')
 
             if not (ctordecl and ctordecl.type.isCtor()):
                 self.error(
                     managed.loc,
                     "constructor declaration required for managed protocol `%s' (managed by protocol `%s')",
                     mgdname, p.name)
 
-        p.states = { }
-
-        if len(p.transitionStmts):
-            p.startStates = [ ts for ts in p.transitionStmts
-                              if ts.state.start ]
-            if 0 == len(p.startStates):
-                p.startStates = [ p.transitionStmts[0] ]
-
-        # declare implicit "any", "dead", and "dying" states
-        self.declare(loc=State.ANY.loc,
-                     type=StateType(p.decl.type, State.ANY.name, start=False),
-                     progname=State.ANY.name)
-        self.declare(loc=State.DEAD.loc,
-                     type=StateType(p.decl.type, State.DEAD.name, start=False),
-                     progname=State.DEAD.name)
-        if p.decl.type.hasReentrantDelete:
-            self.declare(loc=State.DYING.loc,
-                         type=StateType(p.decl.type, State.DYING.name, start=False),
-                         progname=State.DYING.name)
-
-        # declare each state before decorating their mention
-        for trans in p.transitionStmts:
-            p.states[trans.state] = trans
-            trans.state.decl = self.declare(
-                loc=trans.state.loc,
-                type=StateType(p.decl.type, trans.state, trans.state.start),
-                progname=trans.state.name)
-
-        for trans in p.transitionStmts:
-            self.seentriggers = set()
-            trans.accept(self)
-
-        if not (p.decl.type.stateless
-                or (p.decl.type.isToplevel()
-                    and None is self.symtab.lookup(_DELETE_MSG))):
-            # add a special state |state DEAD: null goto DEAD;|
-            deadtrans = TransitionStmt.makeNullStmt(State.DEAD)
-            p.states[State.DEAD] = deadtrans
-            if p.decl.type.hasReentrantDelete:
-                dyingtrans = TransitionStmt.makeNullStmt(State.DYING)
-                p.states[State.DYING] = dyingtrans
-
-        # visit the message decls once more and resolve the state names
-        # attached to actor params and returns
-        def resolvestate(loc, actortype):
-            assert actortype.isIPDL() and actortype.isActor()
-
-            # already resolved this guy's state
-            if isinstance(actortype.state, Decl):
-                return
-
-            if actortype.state is None:
-                # we thought this was a C++ type until type checking,
-                # when we realized it was an IPDL actor type.  But
-                # that means that the actor wasn't specified to be in
-                # any particular state
-                actortype.state = State.ANY
-
-            statename = actortype.state.name
-            # FIXME/cjones: this is just wrong.  we need the symbol table
-            # of the protocol this actor refers to.  low priority bug
-            # since nobody's using this feature yet
-            statedecl = self.symtab.lookup(statename)
-            if statedecl is None:
-                self.error(
-                    loc,
-                    "protocol `%s' does not have the state `%s'",
-                    actortype.protocol.name(),
-                    statename)
-            elif not statedecl.type.isState():
-                self.error(
-                    loc,
-                    "tag `%s' is supposed to be of state type, but is instead of type `%s'",
-                    statename,
-                    statedecl.type.typename())
-            else:
-                actortype.state = statedecl.type
-
-        for msg in p.messageDecls:
-            for iparam in msg.inParams:
-                loc = iparam.loc
-                for actortype in iteractortypes(iparam.type):
-                    resolvestate(loc, actortype)
-            for oparam in msg.outParams:
-                loc = oparam.loc
-                for actortype in iteractortypes(oparam.type):
-                    resolvestate(loc, actortype)
-
         # FIXME/cjones declare all the little C++ thingies that will
         # be generated.  they're not relevant to IPDL itself, but
         # those ("invisible") symbols can clash with others in the
         # IPDL spec, and we'd like to catch those before C++ compilers
         # are allowed to obfuscate the error
 
         self.symtab.exitScope(p)
 
@@ -1158,66 +1041,16 @@ class GatherDecls(TcheckVisitor):
         md.decl = self.declare(
             loc=loc,
             type=msgtype,
             progname=msgname)
         md.protocolDecl = self.currentProtocolDecl
         md.decl._md = md
 
 
-    def visitTransitionStmt(self, ts):
-        self.seentriggers = set()
-        TcheckVisitor.visitTransitionStmt(self, ts)
-
-    def visitTransition(self, t):
-        loc = t.loc
-
-        # check the trigger message
-        mname = t.msg
-        if t in self.seentriggers:
-            self.error(loc, "trigger `%s' appears multiple times", t.msg)
-        self.seentriggers.add(t)
-
-        mdecl = self.symtab.lookup(mname)
-        if mdecl is not None and mdecl.type.isIPDL() and mdecl.type.isProtocol():
-            mdecl = self.symtab.lookup(mname +'Constructor')
-
-        if mdecl is None:
-            self.error(loc, "message `%s' has not been declared", mname)
-        elif not mdecl.type.isMessage():
-            self.error(
-                loc,
-                "`%s' should have message type, but instead has type `%s'",
-                mname, mdecl.type.typename())
-        else:
-            t.msg = mdecl
-
-        # check the to-states
-        seenstates = set()
-        for toState in t.toStates:
-            sname = toState.name
-            sdecl = self.symtab.lookup(sname)
-
-            if sname in seenstates:
-                self.error(loc, "to-state `%s' appears multiple times", sname)
-            seenstates.add(sname)
-
-            if sdecl is None:
-                self.error(loc, "state `%s' has not been declared", sname)
-            elif not sdecl.type.isState():
-                self.error(
-                    loc, "`%s' should have state type, but instead has type `%s'",
-                    sname, sdecl.type.typename())
-            else:
-                toState.decl = sdecl
-                toState.start = sdecl.type.start
-
-        t.toStates = set(t.toStates)
-
-
     def _canonicalType(self, itype, typespec):
         loc = typespec.loc
         if itype.isIPDL():
             if itype.isProtocol():
                 itype = ActorType(itype,
                                   nullable=typespec.nullable)
 
         if typespec.nullable and not (itype.isIPDL() and itype.isActor()):
@@ -1517,37 +1350,16 @@ class CheckTypes(TcheckVisitor):
 
         if mtype.isCtor() and not ptype.isManagerOf(mtype.constructedType()):
             self.error(
                 loc,
                 "ctor for protocol `%s', which is not managed by protocol `%s'",
                 mname[:-len('constructor')], pname)
 
 
-    def visitTransition(self, t):
-        _YNC = [ ASYNC, SYNC ]
-
-        loc = t.loc
-        impliedDirection, impliedSems = {
-            SEND: [ OUT, _YNC ], RECV: [ IN, _YNC ],
-            CALL: [ OUT, INTR ],  ANSWER: [ IN, INTR ],
-         } [t.trigger]
-
-        if (OUT is impliedDirection and t.msg.type.isIn()
-            or IN is impliedDirection and t.msg.type.isOut()
-            or _YNC is impliedSems and t.msg.type.isInterrupt()
-            or INTR is impliedSems and (not t.msg.type.isInterrupt())):
-            mtype = t.msg.type
-
-            self.error(
-                loc, "%s %s message `%s' is not `%s'd",
-                mtype.sendSemantics.pretty, mtype.direction.pretty,
-                t.msg.progname,
-                t.trigger.pretty)
-
 ##-----------------------------------------------------------------------------
 
 class Process:
     def __init__(self):
         self.actors = set()         # set(Actor)
         self.edges = { }            # Actor -> [ SpawnsEdge ]
         self.spawn = set()          # set(Actor)
 
@@ -1863,313 +1675,8 @@ class CheckProcessGraph(TcheckVisitor):
             print 'Bridges'
             for bridgeList in ProcessGraph.bridges.itervalues():
                 for bridge in bridgeList:
                     print '  ', bridge
             print 'Opens'
             for opensList in ProcessGraph.opens.itervalues():
                 for opens in opensList:
                     print '  ', opens
-
-##-----------------------------------------------------------------------------
-
-class CheckStateMachine(TcheckVisitor):
-    def __init__(self, errors):
-        # don't need the symbol table, we just want the error reporting
-        TcheckVisitor.__init__(self, None, errors)
-        self.p = None
-
-    def visitProtocol(self, p):
-        self.p = p
-        self.checkReachability(p)
-        for ts in p.transitionStmts:
-            ts.accept(self)
-
-    def visitTransitionStmt(self, ts):
-        # We want to disallow "race conditions" in protocols.  These
-        # can occur when a protocol state machine has a state that
-        # allows triggers of opposite direction.  That declaration
-        # allows the parent to send the child a message at the
-        # exact instance the child sends the parent a message.  One of
-        # those messages would (probably) violate the state machine
-        # and cause the child to be terminated.  It's obviously very
-        # nice if we can forbid this at the level of IPDL state
-        # machines, rather than resorting to static or dynamic
-        # checking of C++ implementation code.
-        #
-        # An easy way to avoid this problem in IPDL is to only allow
-        # "unidirectional" protocol states; that is, from each state,
-        # only send or only recv triggers are allowed.  This approach
-        # is taken by the Singularity project's "contract-based
-        # message channels."  However, this can be something of a
-        # notational burden for stateful protocols.
-        #
-        # If two messages race, the effect is that the parent's and
-        # child's states get temporarily out of sync.  Informally,
-        # IPDL allows this *only if* the state machines get out of
-        # sync for only *one* step (state machine transition), then
-        # sync back up.  This is a design decision: the states could
-        # be allowd to get out of sync for any constant k number of
-        # steps.  (If k is unbounded, there's no point in presenting
-        # the abstraction of parent and child actor states being
-        # "entangled".)  The working hypothesis is that the more steps
-        # the states are allowed to be out of sync, the harder it is
-        # to reason about the protocol.
-        #
-        # Slightly less informally, two messages are allowed to race
-        # only if processing them in either order leaves the protocol
-        # in the same state.  That is, messages A and B are allowed to
-        # race only if processing A then B leaves the protocol in
-        # state S, *and* processing B then A also leaves the protocol
-        # in state S.  Technically, if this holds, then messages A and
-        # B could be called "commutative" wrt to actor state.
-        #
-        # "Formally", state machine definitions must adhere to two
-        # rules.
-        #
-        #   *Rule 1*: from a state S, all sync triggers must be of the same
-        # "direction," i.e. only |send| or only |recv|
-        #
-        # (Pairs of sync messages can't commute, because otherwise
-        # deadlock can occur from simultaneously in-flight sync
-        # requests.)
-        #
-        #   *Rule 2*: the "Diamond Rule".
-        #   from a state S,
-        #     for any pair of triggers t1 and t2,
-        #         where t1 and t2 have opposite direction,
-        #         and t1 transitions to state T1 and t2 to T2,
-        #       then the following must be true:
-        #         (T2 allows the trigger t1, transitioning to state U)
-        #         and
-        #         (T1 allows the trigger t2, transitioning to state U)
-        #         and
-        #         (
-        #           (
-        #             (all of T1's triggers have the same direction as t2)
-        #             and
-        #             (all of T2's triggers have the same direction as t1)
-        #           )
-        #           or
-        #           (T1, T2, and U are the same "terminal state")
-        #         )
-        #
-        # A "terminal state" S is one from which all triggers
-        # transition back to S itself.
-        #
-        # The presence of triggers with multiple out states complicates
-        # this check slightly, but doesn't fundamentally change it.
-        #
-        #   from a state S,
-        #     for any pair of triggers t1 and t2,
-        #         where t1 and t2 have opposite direction,
-        #       for each pair of states (T1, T2) \in t1_out x t2_out,
-        #           where t1_out is the set of outstates from t1
-        #                 t2_out is the set of outstates from t2
-        #                 t1_out x t2_out is their Cartesian product
-        #                 and t1 transitions to state T1 and t2 to T2,
-        #         then the following must be true:
-        #           (T2 allows the trigger t1, with out-state set { U })
-        #           and
-        #           (T1 allows the trigger t2, with out-state set { U })
-        #           and
-        #           (
-        #             (
-        #               (all of T1's triggers have the same direction as t2)
-        #               and
-        #               (all of T2's triggers have the same direction as t1)
-        #             )
-        #             or
-        #             (T1, T2, and U are the same "terminal state")
-        #           )
-
-        # check Rule 1
-        syncdirection = None
-        syncok = True
-        for trans in ts.transitions:
-            if not trans.msg.type.isSync(): continue
-            if syncdirection is None:
-                syncdirection = trans.trigger.direction()
-            elif syncdirection is not trans.trigger.direction():
-                self.error(
-                    trans.loc,
-                    "sync trigger at state `%s' in protocol `%s' has different direction from earlier sync trigger at same state",
-                    ts.state.name, self.p.name)
-                syncok = False
-        # don't check the Diamond Rule if Rule 1 doesn't hold
-        if not syncok:
-            return
-
-        # helper functions
-        def triggerTargets(S, t):
-            '''Return the set of states transitioned to from state |S|
-upon trigger |t|, or { } if |t| is not a trigger in |S|.'''
-            for trans in self.p.states[S].transitions:
-                if t.trigger is trans.trigger and t.msg is trans.msg:
-                    return trans.toStates
-            return set()
-
-        def allTriggersSameDirectionAs(S, t):
-            '''Return true iff all the triggers from state |S| have the same
-direction as trigger |t|'''
-            direction = t.direction()
-            for trans in self.p.states[S].transitions:
-                if direction != trans.trigger.direction():
-                    return False
-            return True
-
-        def terminalState(S):
-            '''Return true iff |S| is a "terminal state".'''
-            for trans in self.p.states[S].transitions:
-                for S_ in trans.toStates:
-                    if S_ != S:  return False
-            return True
-
-        def sameTerminalState(S1, S2, S3):
-            '''Return true iff states |S1|, |S2|, and |S3| are all the same
-"terminal state".'''
-            if isinstance(S3, set):
-                assert len(S3) == 1
-                for S3_ in S3: pass
-                S3 = S3_
-
-            return (S1 == S2 == S3) and terminalState(S1)
-
-        S = ts.state.name
-
-        # check the Diamond Rule
-        for (t1, t2) in unique_pairs(ts.transitions):
-            # if the triggers have the same direction, they can't race,
-            # since only one endpoint can initiate either (and delivery
-            # is in-order)
-            if t1.trigger.direction() == t2.trigger.direction():
-                continue
-
-            loc = t1.loc
-            t1_out = t1.toStates
-            t2_out = t2.toStates
-
-            for (T1, T2) in cartesian_product(t1_out, t2_out):
-                # U1 <- { u | T1 --t2--> u }
-                U1 = triggerTargets(T1, t2)
-                # U2 <- { u | T2 --t1--> u }
-                U2 = triggerTargets(T2, t1)
-
-                # don't report more than one Diamond Rule violation
-                # per state. there may be O(n^4) total, way too many
-                # for a human to parse
-                #
-                # XXX/cjones: could set a limit on #printed and stop
-                # after that limit ...
-                raceError = False
-                errT1 = None
-                errT2 = None
-
-                if 0 == len(U1) or 0 == len(U2):
-                    print "******* case 1"
-                    raceError = True
-                elif 1 < len(U1) or 1 < len(U2):
-                    raceError = True
-                    # there are potentially many unpaired states; just
-                    # pick two
-                    print "******* case 2"
-                    for u1, u2 in cartesian_product(U1, U2):
-                        if u1 != u2:
-                            errT1, errT2 = u1, u2
-                            break
-                elif U1 != U2:
-                    print "******* case 3"
-                    raceError = True
-                    for errT1 in U1: pass
-                    for errT2 in U2: pass
-
-                if raceError:
-                    self.reportRaceError(loc, S,
-                                         [ T1, t1, errT1 ],
-                                         [ T2, t2, errT2 ])
-                    return
-
-                if not ((allTriggersSameDirectionAs(T1, t2.trigger)
-                           and allTriggersSameDirectionAs(T2, t1.trigger))
-                          or sameTerminalState(T1, T2, U1)):
-                    self.reportRunawayError(loc, S, [ T1, t1, None ], [ T2, t2, None ])
-                    return
-
-    def checkReachability(self, p):
-        def explore(ts, visited):
-            if ts.state in visited:
-                return
-            visited.add(ts.state)
-            for outedge in ts.transitions:
-                for toState in outedge.toStates:
-                    explore(p.states[toState], visited)
-
-        checkfordelete = (State.DEAD in p.states)
-
-        allvisited = set()         # set(State)
-        for root in p.startStates:
-            visited = set()
-
-            explore(root, visited)
-            allvisited.update(visited)
-
-            if checkfordelete and State.DEAD not in visited:
-                self.error(
-                    root.loc,
-                    "when starting from state `%s', actors of protocol `%s' cannot be deleted", root.state.name, p.name)
-
-        for ts in p.states.itervalues():
-            if ts.state is not State.DEAD and ts.state not in allvisited:
-                self.error(ts.loc,
-                           "unreachable state `%s' in protocol `%s'",
-                           ts.state.name, p.name)
-
-
-    def _normalizeTransitionSequences(self, t1Seq, t2Seq):
-        T1, M1, U1 = t1Seq
-        T2, M2, U2 = t2Seq
-        assert M1 is not None and M2 is not None
-
-        # make sure that T1/M1/U1 is the parent side of the race
-        if M1.trigger is RECV or M1.trigger is ANSWER:
-            T1, M1, U1, T2, M2, U2 = T2, M2, U2, T1, M1, U1
-
-        def stateName(S):
-            if S: return S.name
-            return '[error]'
-
-        T1 = stateName(T1)
-        T2 = stateName(T2)
-        U1 = stateName(U1)
-        U2 = stateName(U2)
-
-        return T1, M1.msg.progname, U1, T2, M2.msg.progname, U2
-
-
-    def reportRaceError(self, loc, S, t1Seq, t2Seq):
-        T1, M1, U1, T2, M2, U2 = self._normalizeTransitionSequences(t1Seq, t2Seq)
-        self.error(
-            loc,
-"""in protocol `%(P)s', the sequence of events
-     parent:    +--`send %(M1)s'-->( state `%(T1)s' )--`recv %(M2)s'-->( state %(U1)s )
-               /
- ( state `%(S)s' )
-               \\
-      child:    +--`send %(M2)s'-->( state `%(T2)s' )--`recv %(M1)s'-->( state %(U2)s )
-results in error(s) or leaves parent/child state out of sync for more than one step and is thus a race hazard; i.e., triggers `%(M1)s' and `%(M2)s' fail to commute in state `%(S)s'"""% {
-                'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2,
-                'T1': T1, 'T2': T2, 'U1': U1, 'U2': U2
-        })
-
-
-    def reportRunawayError(self, loc, S, t1Seq, t2Seq):
-        T1, M1, _, T2, M2, __ = self._normalizeTransitionSequences(t1Seq, t2Seq)
-        self.error(
-            loc,
-        """in protocol `%(P)s', the sequence of events
-     parent:    +--`send %(M1)s'-->( state `%(T1)s' )
-               /
- ( state `%(S)s' )
-               \\
-      child:    +--`send %(M2)s'-->( state `%(T2)s' )
-lead to parent/child states in which parent/child state can become more than one step out of sync (though this divergence might not lead to error conditions)"""% {
-                'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2, 'T1': T1, 'T2': T2
-        })
--- a/ipc/ipdl/test/cxx/PTestActorPunning.ipdl
+++ b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl
@@ -16,24 +16,25 @@ child:
     async Start();
 
 parent:
     async PTestActorPunningPunned();
     async PTestActorPunningSub();
     async Pun(PTestActorPunningSub a, Bad bad);
     async __delete__();
 
-
+/*
 state PING:
     send Start goto CONSTRUCTING;
 
 state CONSTRUCTING:
     recv PTestActorPunningPunned goto CONSTRUCTING;
     recv PTestActorPunningSub goto CONSTRUCTING;
     recv Pun goto DEAD;
     // We never make it past this transition, --> error.
 
 state DEAD:
     recv __delete__;
+*/
 };
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
+++ b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
@@ -10,17 +10,19 @@ protocol PTestBridgeMain {
     child opens PTestBridgeMainSub;
 
 child:
     async Start();
 
 parent:
     async __delete__();
 
+/*
 state START:
     send Start goto DEAD;
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
@@ -14,20 +14,22 @@ child:
     intr HiRpc();
 
 parent:
     async Hello();
     sync HelloSync();
     intr HelloRpc();
     async __delete__();
 
+/*
 state START:       recv Hello goto HI;
 state HI:          send Hi goto HELLO_SYNC;
 state HELLO_SYNC:  recv HelloSync goto HELLO_RPC;
 state HELLO_RPC:   answer HelloRpc goto HI_RPC;
 state HI_RPC:      call HiRpc goto DEAD;
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
@@ -7,19 +7,21 @@ namespace _ipdltest {
 protocol PTestBridgeSub {
 child:
     async Ping();
 
 parent:
     async BridgeEm();
     async __delete__();
 
+/*
 state START:
     send Ping goto BRIDGEEM;
 state BRIDGEEM:
     recv BridgeEm goto DEAD;
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl
+++ b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl
@@ -7,16 +7,18 @@ namespace mozilla {
 namespace _ipdltest {
 
 // NB: needs to be RPC so that the parent blocks on the child's crash.
 intr protocol PTestCrashCleanup {
 child:
     intr DIEDIEDIE();
     async __delete__();
 
+/*
 state ALIVE:
     call DIEDIEDIE goto CRASH;
 state CRASH:
     send __delete__;
+*/
 };
 
 }
 }
--- a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
@@ -96,16 +96,17 @@ parent:
 
     // test that the ParamTraits<nsTArray>::Read() workaround for
     // nsTArray's incorrect memmove() semantics works properly
     // (nsIntRegion isn't memmove()able)
     sync Test18(nsIntRegion[] ops);
 
     sync Dummy(ShmemUnion su) returns (ShmemUnion rsu);
 
+/*
 state CONSTRUCTING:
     send PTestDataStructuresSub goto CONSTRUCTING;
     send Start goto TEST1;
 state TEST1:   recv Test1 goto TEST2;
 state TEST2:   recv Test2 goto TEST3;
 state TEST3:   recv Test3 goto TEST4;
 state TEST4:   recv Test4 goto TEST5;
 state TEST5:   recv Test5 goto TEST6;
@@ -120,13 +121,14 @@ state TEST13:  recv Test13 goto TEST14;
 state TEST14:  recv Test14 goto TEST15;
 state TEST15:  recv Test15 goto TEST16;
 state TEST16:  recv Test16 goto TEST17;
 state TEST17:  recv Test17 goto TEST18;
 state TEST18:  recv Test18 goto DEAD;
 
 state DEAD:
     recv __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
 
--- a/ipc/ipdl/test/cxx/PTestDesc.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDesc.ipdl
@@ -11,21 +11,22 @@ child:
 
     async Test(PTestDescSubsub a);
 
     async __delete__();
 
 parent:
     async Ok(PTestDescSubsub a);
 
-
+/*
 state CONSTRUCT:
     call PTestDescSub goto TEST;
 state TEST:
     send Test goto ACK;
 state ACK:
     recv Ok goto DEAD;
 state DEAD:
     send __delete__;
+*/
 };
 
 }
 }
--- a/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl
+++ b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl
@@ -11,20 +11,22 @@ child:
     intr HiRpc();
 
 parent:
     async Hello();
     sync HelloSync();
     intr HelloRpc();
     async __delete__();
 
+/*
 state START:       recv Hello goto HI;
 state HI:          send Hi goto HELLO_SYNC;
 state HELLO_SYNC:  recv HelloSync goto HELLO_RPC;
 state HELLO_RPC:   answer HelloRpc goto HI_RPC;
 state HI_RPC:      call HiRpc goto DEAD;
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest2
--- a/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl
+++ b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl
@@ -4,16 +4,18 @@ namespace mozilla {
 namespace _ipdltest {
 
 intr protocol PTestFailedCtor {
     manages PTestFailedCtorSub; 
 child:
     intr PTestFailedCtorSub();
     async __delete__();
 
+/*
 state CONSTRUCT:
     call PTestFailedCtorSub goto DEAD;
 state DEAD:
     send __delete__;
+*/
 };
 
 }
 }
--- a/ipc/ipdl/test/cxx/PTestHangs.ipdl
+++ b/ipc/ipdl/test/cxx/PTestHangs.ipdl
@@ -9,17 +9,17 @@ both:
 parent:
     async Nonce();
 
 child:
     async Start();
     intr Hang();
     async __delete__();
 
-
+/*
 state START:
     send Start goto RACE;
 
 state RACE:
     recv Nonce goto RACE1;
     call StackFrame goto RACE2;
 state RACE1:
     call StackFrame goto FRAME2;
@@ -29,12 +29,13 @@ state RACE2:
 // So as to test unwinding the RPC stack
 state FRAME2: answer StackFrame goto FRAME3;
 state FRAME3: call StackFrame goto FRAME4;
 state FRAME4: answer StackFrame goto HANG;
 state HANG:   call Hang goto DEATH;
 
 state DEATH:
     send __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl
+++ b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl
@@ -14,16 +14,17 @@ parent:
 
 child:
     async Start();
     async Wakeup();
     async Wakeup3();
     intr Child();
     async __delete__();
 
+/*
 state START:
     send Start goto TEST1;
 
 // First test: race while no other messages are on the Interrupt stack
 state TEST1:
     recv StartRace goto RACE1;
 state RACE1:
     call Race goto DUMMY1_1;
@@ -71,12 +72,14 @@ state CHECK:
     recv GetAnsweredParent goto DYING;
     // because of deferred processing, the parent receives the child's
     // message here
     answer Parent goto DYING;
 
 
 state DYING:
     send __delete__;
+*/
+
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl
+++ b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl
@@ -6,16 +6,17 @@ parent:
     sync StartDeath();
     async Orphan();
 
 child:
     async Start();
     intr Exit();
     async __delete__();
 
+/*
 state START:
     send Start goto START_DEATH;
 
 state START_DEATH:
     recv StartDeath goto EXITING;
 
 state EXITING:
     recv Orphan goto QUITTING1;
@@ -23,12 +24,13 @@ state EXITING:
 
 state QUITTING1:
     call Exit goto DEAD;
 state QUITTING2:
     recv Orphan goto DEAD;
 
 state DEAD:
     send __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestJSON.ipdl
+++ b/ipc/ipdl/test/cxx/PTestJSON.ipdl
@@ -36,19 +36,21 @@ child:
     async Start();
 
 parent:
     async PTestHandle();
     sync Test(JSONVariant i)
         returns (JSONVariant o);
     async __delete__();
 
+/*
 state START:
     send Start goto TEST;
 
 state TEST:
     recv PTestHandle goto TEST;
     recv Test goto TEST;
     recv __delete__;
+*/
 };
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestLatency.ipdl
+++ b/ipc/ipdl/test/cxx/PTestLatency.ipdl
@@ -15,16 +15,17 @@ child:
     async CompressedSpam(uint32_t seqno) compress;
     intr Synchro2() returns (uint32_t lastSeqno,
                             uint32_t numMessagesDispatched);
 
 parent:
     async Pong();
     async Pong5();
 
+/*
 state START:
     // if the timing resolution is too low, abort the test
     send __delete__;
     // otherwise, kick off the ping/pong trials
     send Ping goto PONG;
 
     // Trial 1: single ping/pong latency
 state PING:
@@ -63,13 +64,14 @@ state SPAM:
     // Trial 5: lots of async spam, but compressed to cut down on
     // dispatch overhead
 state COMPRESSED_SPAM:          // compressed spam, mmm
     send CompressedSpam goto COMPRESSED_SPAM;
     call Synchro2 goto DONE;
 
 state DONE:
     send __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl
@@ -12,23 +12,25 @@ parent:
     async OK();
 
 child:
     async PTestMultiMgrsLeft();
     async PTestMultiMgrsRight();
     async Check();
     async __delete__();
 
+/*
 state START:
     send PTestMultiMgrsLeft goto CONSTRUCT_RIGHT;
 state CONSTRUCT_RIGHT:
     send PTestMultiMgrsRight goto CHILD_CHECK;
 state CHILD_CHECK:
     send Check goto CHILD_ACK;
 state CHILD_ACK:
     recv OK goto DONE;
 
 state DONE:
     send __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl
@@ -5,14 +5,16 @@ namespace mozilla {
 namespace _ipdltest {
 
 protocol PTestMultiMgrsBottom {
     manager PTestMultiMgrsLeft or PTestMultiMgrsRight;
 
 child:
     async __delete__();
 
+/*
 state DOA:
     send __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl
@@ -8,17 +8,19 @@ protocol PTestMultiMgrsLeft {
     manager PTestMultiMgrs;
 
     manages PTestMultiMgrsBottom;
 
 child:
     async PTestMultiMgrsBottom();
     async __delete__();
 
+/*
 state START:
     send PTestMultiMgrsBottom goto DONE;
 
 state DONE:
     send __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl
@@ -8,17 +8,19 @@ protocol PTestMultiMgrsRight {
     manager PTestMultiMgrs;
 
     manages PTestMultiMgrsBottom;
 
 child:
     async PTestMultiMgrsBottom();
     async __delete__();
 
+/*
 state START:
     send PTestMultiMgrsBottom goto DONE;
 
 state DONE:
     send __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
+++ b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
@@ -8,27 +8,28 @@ intr protocol PTestNestedLoops {
 child:
     async Start();
     intr R();
     async __delete__();
 
 parent:
     async Nonce();
 
-
+/*
 state START:
     send Start goto RACE;
 
 state RACE:
     recv Nonce goto RACE1;
     call R goto RACE2;
 state RACE1:
     call R goto DEAD;
 state RACE2:
     recv Nonce goto DEAD;
 
 state DEAD:
     send __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestOpens.ipdl
+++ b/ipc/ipdl/test/cxx/PTestOpens.ipdl
@@ -9,17 +9,19 @@ protocol PTestOpens {
     child opens PTestOpensOpened;
 
 child:
     async Start();
 
 parent:
     async __delete__();
 
+/*
 state START:
     send Start goto DEAD;
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
+++ b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
@@ -9,20 +9,22 @@ child:
     intr HiRpc();
 
 parent:
     async Hello();
     sync HelloSync();
     intr HelloRpc();
     async __delete__();
 
+/*
 state START:       recv Hello goto HI;
 state HI:          send Hi goto HELLO_SYNC;
 state HELLO_SYNC:  recv HelloSync goto HELLO_RPC;
 state HELLO_RPC:   answer HelloRpc goto HI_RPC;
 state HI_RPC:      call HiRpc goto DEAD;
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest2
--- a/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl
+++ b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl
@@ -7,16 +7,17 @@ child:
     async _A();
     async ChildTest();
     async __delete__();
 
 parent:
     intr _R() returns (int replyNum);
     async A_();
 
+/*
 state PARENT_START:
     call R_ goto PARENT_S1;
 
 state PARENT_S1:
     recv A_ goto PARENT_S2;
 
 state PARENT_S2:
     call R_ goto CHILD_TEST;
@@ -30,12 +31,13 @@ state CHILD_START:
 state CHILD_S1:
     send _A goto CHILD_S2;
 
 state CHILD_S2:
     answer _R goto DYING;
 
 state DYING:
     send __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestSanity.ipdl
+++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl
@@ -7,22 +7,23 @@ protocol PTestSanity {
 
 child:
     async Ping(int zero, float zeroPtFive, int8_t dummy);
     async __delete__();
 
 parent:
     async Pong(int one, float zeroPtTwoFive, uint8_t dummy);
 
-
+/*
 state PING:
     send Ping goto PONG;
 
 state PONG:
     recv Pong goto DEAD;
 
 state DEAD:
     send __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestSelfManage.ipdl
+++ b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl
@@ -7,16 +7,18 @@ namespace _ipdltest {
 protocol PTestSelfManage {
     manager PTestSelfManageRoot or PTestSelfManage;
     manages PTestSelfManage;
 
 child:
     async PTestSelfManage();
     async __delete__();
 
+/*
 state LIVE:
     send PTestSelfManage goto LIVE;
     send __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl
+++ b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl
@@ -6,18 +6,20 @@ namespace _ipdltest {
 
 protocol PTestSelfManageRoot {
     manages PTestSelfManage;
 
 child:
     async PTestSelfManage();
     async __delete__();
 
+/*
 state LIVE:
     send PTestSelfManage goto DEAD;
 
 state DEAD:
     send __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestShmem.ipdl
+++ b/ipc/ipdl/test/cxx/PTestShmem.ipdl
@@ -4,19 +4,20 @@ namespace _ipdltest {
 protocol PTestShmem {
 child:
     async Give(Shmem mem, Shmem unsafe, size_t expectedSize);
 
 parent:
     async Take(Shmem mem, Shmem unsafe, size_t expectedSize);
     async __delete__();
 
-
+/*
 state GIVING:
     send Give goto TAKING;
 
 state TAKING:
     recv Take goto TAKING;
     recv __delete__;
+*/
 };
 
 }
 }
--- a/ipc/ipdl/test/cxx/PTestShutdown.ipdl
+++ b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
@@ -16,22 +16,23 @@ parent:
     async PTestShutdownSub(bool expectCrash);
 
     // Used to synchronize between parent and child, to avoid races
     // around flushing socket write queues
     sync Sync();
 
     async __delete__();
 
-
+/*
 state START:
     send Start goto TESTING;
 
 state TESTING:
     recv PTestShutdownSub goto TESTING;
     recv Sync goto DYING;
 
 state DYING:
     recv __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
@@ -10,21 +10,23 @@ intr protocol PTestShutdownSub {
 
 both:
     intr StackFrame();
 
 parent:
     async PTestShutdownSubsub(bool expectParentDeleted);
     sync __delete__();
 
+/*
 state CREATING:
     recv PTestShutdownSubsub goto CREATING;
     answer StackFrame goto DUMMYFRAME;
 
 state DUMMYFRAME:
     call StackFrame goto DEAD;
 
 state DEAD:
     recv __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
+++ b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
@@ -4,14 +4,16 @@ namespace mozilla {
 namespace _ipdltest {
 
 sync protocol PTestShutdownSubsub {
     manager PTestShutdownSub;
 
 parent:
     sync __delete__();
 
+/*
 state LIVE:
     recv __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestStackHooks.ipdl
+++ b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl
@@ -15,17 +15,17 @@ parent:
     intr Rpc();
 
 both:
     intr StackFrame();
 
 parent:
     async __delete__();
 
-
+/*
 state START:
     send Start goto TEST1;
 
 state TEST1:
     recv Async goto TEST2;
 
 state TEST2:
     recv Sync goto TEST3;
@@ -44,13 +44,14 @@ state TEST5:
     answer StackFrame goto TEST5_2;
 state TEST5_2:
     call StackFrame goto TEST5_3;
 state TEST5_3:
     recv Sync goto DEAD;
 
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestSyncError.ipdl
+++ b/ipc/ipdl/test/cxx/PTestSyncError.ipdl
@@ -7,22 +7,23 @@ sync protocol PTestSyncError {
 
 child:
     async Start();
 
 parent:
     sync Error();
     async __delete__();
 
-
+/*
 state START:
     send Start goto SYNC_ERROR;
 
 state SYNC_ERROR:
     recv Error goto DEAD;
 
 state DEAD:
     recv __delete__;
+*/
 };
 
 
 } // namespace mozilla
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl
+++ b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl
@@ -10,17 +10,17 @@ child:
     async Note1();
     async Note2();
 
 parent:
     sync Sync1();
     sync Sync2();
     async __delete__();
 
-
+/*
 state START:
     send Start goto TEST1;
 
 state TEST1:
     recv Sync1 goto TEST1_P2;
 state TEST1_P2:
     send Note1 goto TEST2;
 
@@ -30,12 +30,13 @@ state TEST2_P2:
     call StackFrame goto TEST2_P3;
 state TEST2_P3:
     recv Sync2 goto TEST2_P4;
 state TEST2_P4:
     send Note2 goto DONE;
 
 state DONE:
     recv __delete__;
+*/
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/TestHangs.cpp
+++ b/ipc/ipdl/test/cxx/TestHangs.cpp
@@ -84,18 +84,17 @@ TestHangsParent::ShouldContinueFromReply
     return false;
 }
 
 mozilla::ipc::IPCResult
 TestHangsParent::AnswerStackFrame()
 {
     ++mNumAnswerStackFrame;
 
-    // XXX This assertion will get deleted as part of bug 1316757.
-    MOZ_ASSERT((PTestHangs::HANG != state()) == (mNumAnswerStackFrame == 1));
+    // MOZ_ASSERT((PTestHangs::HANG != state()) == (mNumAnswerStackFrame == 1));
 
     if (mNumAnswerStackFrame == 1) {
         if (CallStackFrame()) {
           fail("should have timed out!");
         }
     } else if (mNumAnswerStackFrame == 2) {
         // minimum possible, 2 ms.  We want to detecting a hang to race
         // with the reply coming in, as reliably as possible
--- a/ipc/ipdl/test/cxx/TestLatency.cpp
+++ b/ipc/ipdl/test/cxx/TestLatency.cpp
@@ -87,18 +87,17 @@ TestLatencyParent::RecvPong()
     return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TestLatencyParent::RecvPong5()
 {
     ++mWhichPong5;
 
-    // XXX This assertion will be deleted as part of bug 1316757.
-    MOZ_ASSERT((PTestLatency::PING5 != state()) == (mWhichPong5 < 5));
+    // MOZ_ASSERT((PTestLatency::PING5 != state()) == (mWhichPong5 < 5));
 
     if (mWhichPong5 < 5) {
         return IPC_OK();
     }
 
     mWhichPong5 = 0;
 
     TimeDuration thisTrial = (TimeStamp::Now() - mStart);
@@ -209,18 +208,17 @@ TestLatencyChild::RecvPing()
     return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TestLatencyChild::RecvPing5()
 {
     ++mWhichPing5;
 
-    // XXX This assertion will be deleted as part of bug 1316757.
-    MOZ_ASSERT((PTestLatency::PONG1 != state()) == (mWhichPing5 < 5));
+    // MOZ_ASSERT((PTestLatency::PONG1 != state()) == (mWhichPing5 < 5));
 
     if (mWhichPing5 < 5) {
         return IPC_OK();
     }
 
     mWhichPing5 = 0;
 
     if (!SendPong5() ||
--- a/ipc/ipdl/test/cxx/TestStackHooks.cpp
+++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp
@@ -95,23 +95,21 @@ TestStackHooksChild::AnswerStackFrame()
 
     if (!mOnStack)
         fail("missed stack notification");
 
     if (1 != mIncallDepth)
         fail("missed EnteredCall or ExitedCall hook");
 
     if (mNumAnswerStackFrame == 1) {
-        // XXX This assertion will be deleted as part of bug 1316757.
-        MOZ_ASSERT(PTestStackHooks::TEST4_3 == state());
+        // MOZ_ASSERT(PTestStackHooks::TEST4_3 == state());
         if (!SendAsync())
             fail("sending Async()");
     } else if (mNumAnswerStackFrame == 2) {
-        // XXX This assertion will be deleted as part of bug 1316757.
-        MOZ_ASSERT(PTestStackHooks::TEST5_3 == state());
+        // MOZ_ASSERT(PTestStackHooks::TEST5_3 == state());
         if (!SendSync())
             fail("sending Sync()");
     } else {
         fail("unexpected state");
     }
 
     if (!mOnStack)
         fail("bad stack exit notification");
--- a/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl
@@ -1,12 +1,10 @@
 include protocol Delete;
 
 sync protocol DeleteSub {
     manager Delete;
 
 parent:
     sync __delete__(int x) returns (double d);
 
-state START:
-    recv __delete__;
 };
 
--- a/ipc/ipdl/test/ipdl/ok/compositor.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/compositor.ipdl
@@ -1,11 +1,9 @@
 include protocol content;
 
 sync protocol compositor {
     bridges compositor, content;
 
 child:
     async __delete__();
 
-state DEAD:
-    send __delete__;
 };
--- a/ipc/ipdl/test/ipdl/ok/content.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/content.ipdl
@@ -7,11 +7,9 @@ sync protocol content {
     parent spawns compositor as parent;
     parent spawns jetpack;
     child spawns plugin;
     child opens media;
 
 child:
     async __delete__();
 
-state DEAD:
-    send __delete__;
 };
--- a/ipc/ipdl/test/ipdl/ok/jetpack.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/jetpack.ipdl
@@ -1,7 +1,5 @@
 sync protocol jetpack {
 child:
     async __delete__();
 
-state DEAD:
-    send __delete__;
 };
--- a/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl
@@ -2,11 +2,9 @@ include protocol content;
 include protocol jetpack;
 
 intr protocol jetpackContent {
     bridges jetpack, content;
 
 child:
     async __delete__();
 
-state DEAD:
-    send __delete__;
 };
--- a/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
@@ -3,11 +3,9 @@ include protocol managedProtocol;
 // sanity check of managed/manager protocols
 
 protocol managerProtocol {
     manages managedProtocol;
 
 parent:
     async managedProtocol(int i);
 
-state CREATING:
-    recv managedProtocol goto CREATING;
 };
--- a/ipc/ipdl/test/ipdl/ok/media.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/media.ipdl
@@ -1,7 +1,5 @@
 sync protocol media {
 child:
     async __delete__();
 
-state DEAD:
-    send __delete__;
 };
deleted file mode 100644
--- a/ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl
+++ /dev/null
@@ -1,12 +0,0 @@
-protocol multiOutStates {
-child: async Msg();
-
-    // sanity check that multi-out-states are being processed correctly
-
-state S1:
-    send Msg goto S2 or S3 or S4;
-
-state S2: send Msg goto S2;
-state S3: send Msg goto S3;
-state S4: send Msg goto S4;
-};
deleted file mode 100644
--- a/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl
+++ /dev/null
@@ -1,11 +0,0 @@
-protocol multiStartState {
-
-child: async Msg(); async __delete__();
-
-start state S1:
-    send Msg goto S1; send __delete__;
-
-start state S2:
-    send Msg goto S2; send __delete__;
-
-};
--- a/ipc/ipdl/test/ipdl/ok/plugin.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/plugin.ipdl
@@ -1,7 +1,5 @@
 intr protocol plugin {
 child:
     async __delete__();
 
-state DEAD:
-    send __delete__;
 };
deleted file mode 100644
--- a/ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl
+++ /dev/null
@@ -1,27 +0,0 @@
-protocol race_DiamondRule1 {
-
-child:
-    async Msg1();
-    async Msg1_();
-parent:
-    async Msg2();
-    async Msg2_();
-
-    // OK: this state machine is one of the simplest that follows the
-    // Diamond Rule
-
-start state S1:
-    send Msg1 goto S2;
-    recv Msg2 goto S3;
-
-state S2:
-    recv Msg2 goto S4;
-    recv Msg2_ goto S2;
-
-state S3:
-    send Msg1 goto S4;
-    send Msg1_ goto S3;
-
-state S4:
-    send Msg1 goto S4;
-};
deleted file mode 100644
--- a/ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl
+++ /dev/null
@@ -1,61 +0,0 @@
-protocol race_KitchenSink {
-child:
-    async Msg1();
-    async Msg1_();
-parent:
-    async Msg2();
-    async Msg2_();
-
-
-    // concatenation of a few other state machines, should be OK
-
-start state S1:
-    send Msg1 goto S2;
-    recv Msg2 goto S3;
-
-state S2:
-    recv Msg2 goto S4;
-    recv Msg2_ goto S2;
-
-state S3:
-    send Msg1 goto S4;
-    send Msg1_ goto S3;
-
-state S4:
-    send Msg1 goto S4;
-
-
-
-start state S5:
-    send Msg1 goto S5;
-    recv Msg2 goto S5;
-
-
-
-start state S15:
-    send Msg1 goto S16 or S17;
-    recv Msg2 goto S18 or S19;
-
-state S16:
-    recv Msg2 goto S20;
-    recv Msg2_ goto S18;
-
-state S17:
-    recv Msg2 goto S20;
-    recv Msg2_ goto S15;
-
-state S18:
-    send Msg1 goto S20;
-    send Msg1_ goto S15;
-
-state S19:
-    send Msg1 goto S20;
-    send Msg1_ goto S16;
-
-state S20:
-    send Msg1 goto S20;
-    send Msg1_ goto S20;
-    recv Msg2 goto S20;
-    recv Msg2_ goto S20;
-
-};
deleted file mode 100644
--- a/ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl
+++ /dev/null
@@ -1,35 +0,0 @@
-protocol race_MultiOut {
-child:
-    async Msg1();
-    async Msg1_();
-parent:
-    async Msg2();
-    async Msg2_();
-
-start state S15:
-    send Msg1 goto S16 or S17;
-    recv Msg2 goto S18 or S19;
-
-state S16:
-    recv Msg2 goto S20;
-    recv Msg2_ goto S18;
-
-state S17:
-    recv Msg2 goto S20;
-    recv Msg2_ goto S15;
-
-state S18:
-    send Msg1 goto S20;
-    send Msg1_ goto S15;
-
-state S19:
-    send Msg1 goto S20;
-    send Msg1_ goto S16;
-
-state S20:
-    send Msg1 goto S20;
-    send Msg1_ goto S20;
-    recv Msg2 goto S20;
-    recv Msg2_ goto S20;
-
-};
deleted file mode 100644
--- a/ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl
+++ /dev/null
@@ -1,16 +0,0 @@
-protocol race_Stateless {
-//    manages Child;
-
-child:
-    async Msg1();
-    async Msg1_();
-parent:
-    async Msg2();
-    async Msg2_();
-
-
-    // OK: this is trivial stateless protocol, so race-free "by definition"
-start state S5:
-    send Msg1 goto S5;
-    recv Msg2 goto S5;
-};
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/TaggedAnonymousMemory.h"
 
 #include "js/HeapAPI.h"
 #include "vm/Runtime.h"
 
 #if defined(XP_WIN)
 
+#include "mozilla/Sprintf.h"
 #include "jswin.h"
 #include <psapi.h>
 
 #elif defined(SOLARIS)
 
 #include <sys/mman.h>
 #include <unistd.h>
 
@@ -848,17 +849,17 @@ void
 ProtectPages(void* p, size_t size)
 {
     MOZ_ASSERT(size % pageSize == 0);
     MOZ_RELEASE_ASSERT(size > 0);
     MOZ_RELEASE_ASSERT(p);
 #if defined(XP_WIN)
     DWORD oldProtect;
     if (!VirtualProtect(p, size, PAGE_NOACCESS, &oldProtect)) {
-        snprintf(sCrashReason, sizeof(sCrashReason),
+        SprintfLiteral(sCrashReason,
             "MOZ_CRASH(VirtualProtect(PAGE_NOACCESS) failed! Error code: %u)", GetLastError());
         MOZ_CRASH_ANNOTATE(sCrashReason);
         MOZ_REALLY_CRASH();
     }
     MOZ_ASSERT(oldProtect == PAGE_READWRITE);
 #else  // assume Unix
     if (mprotect(p, size, PROT_NONE))
         MOZ_CRASH("mprotect(PROT_NONE) failed");
@@ -869,17 +870,17 @@ void
 MakePagesReadOnly(void* p, size_t size)
 {
     MOZ_ASSERT(size % pageSize == 0);
     MOZ_RELEASE_ASSERT(size > 0);
     MOZ_RELEASE_ASSERT(p);
 #if defined(XP_WIN)
     DWORD oldProtect;
     if (!VirtualProtect(p, size, PAGE_READONLY, &oldProtect)) {
-        snprintf(sCrashReason, sizeof(sCrashReason),
+        SprintfLiteral(sCrashReason,
             "MOZ_CRASH(VirtualProtect(PAGE_READONLY) failed! Error code: %u)", GetLastError());
         MOZ_CRASH_ANNOTATE(sCrashReason);
         MOZ_REALLY_CRASH();
     }
     MOZ_ASSERT(oldProtect == PAGE_READWRITE);
 #else  // assume Unix
     if (mprotect(p, size, PROT_READ))
         MOZ_CRASH("mprotect(PROT_READ) failed");
@@ -890,17 +891,17 @@ void
 UnprotectPages(void* p, size_t size)
 {
     MOZ_ASSERT(size % pageSize == 0);
     MOZ_RELEASE_ASSERT(size > 0);
     MOZ_RELEASE_ASSERT(p);
 #if defined(XP_WIN)
     DWORD oldProtect;
     if (!VirtualProtect(p, size, PAGE_READWRITE, &oldProtect)) {
-        snprintf(sCrashReason, sizeof(sCrashReason),
+        SprintfLiteral(sCrashReason,
             "MOZ_CRASH(VirtualProtect(PAGE_READWRITE) failed! Error code: %u)", GetLastError());
         MOZ_CRASH_ANNOTATE(sCrashReason);
         MOZ_REALLY_CRASH();
     }
     MOZ_ASSERT(oldProtect == PAGE_NOACCESS || oldProtect == PAGE_READONLY);
 #else  // assume Unix
     if (mprotect(p, size, PROT_READ | PROT_WRITE))
         MOZ_CRASH("mprotect(PROT_READ | PROT_WRITE) failed");
--- a/layout/tools/reftest/reftest.jsm
+++ b/layout/tools/reftest/reftest.jsm
@@ -709,16 +709,22 @@ function BuildConditionSandbox(aURL) {
 #endif
 
 #if MOZ_WEBRTC
     sandbox.webrtc = true;
 #else
     sandbox.webrtc = false;
 #endif
 
+#ifdef MOZ_STYLO
+    sandbox.stylo = true;
+#else
+    sandbox.stylo = false;
+#endif
+
     var hh = CC[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"].
                  getService(CI.nsIHttpProtocolHandler);
     var httpProps = ["userAgent", "appName", "appVersion", "vendor",
                      "vendorSub", "product", "productSub", "platform",
                      "oscpu", "language", "misc"];
     sandbox.http = new sandbox.Object();
     httpProps.forEach((x) => sandbox.http[x] = hh[x]);
 
--- a/media/libstagefright/binding/BitReader.cpp
+++ b/media/libstagefright/binding/BitReader.cpp
@@ -1,39 +1,71 @@
 /* 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/. */
 
+// Derived from Stagefright's ABitReader.
+
 #include "mp4_demuxer/BitReader.h"
-#include <media/stagefright/foundation/ABitReader.h>
 
 using namespace mozilla;
-using namespace stagefright;
 
 namespace mp4_demuxer
 {
 
 BitReader::BitReader(const mozilla::MediaByteBuffer* aBuffer)
-  : mBitReader(new ABitReader(aBuffer->Elements(), aBuffer->Length()))
-  , mSize(aBuffer->Length()) {}
+  : BitReader(aBuffer->Elements(), aBuffer->Length() * 8)
+{
+}
+
+BitReader::BitReader(const mozilla::MediaByteBuffer* aBuffer, size_t aBits)
+  : BitReader(aBuffer->Elements(), aBits)
+{
+}
 
-BitReader::BitReader(const uint8_t* aBuffer, size_t aLength)
-  : mBitReader(new ABitReader(aBuffer, aLength))
-  , mSize(aLength) {}
+BitReader::BitReader(const uint8_t* aBuffer, size_t aBits)
+  : mData(aBuffer)
+  , mOriginalBitSize(aBits)
+  , mTotalBitsLeft(aBits)
+  , mSize((aBits + 7) / 8)
+  , mReservoir(0)
+  , mNumBitsLeft(0)
+{
+}
 
-BitReader::~BitReader() {}
+BitReader::~BitReader() { }
 
 uint32_t
 BitReader::ReadBits(size_t aNum)
 {
   MOZ_ASSERT(aNum <= 32);
-  if (mBitReader->numBitsLeft() < aNum) {
+  if (mTotalBitsLeft < aNum) {
+    NS_ASSERTION(false, "Reading past end of buffer");
     return 0;
   }
-  return mBitReader->getBits(aNum);
+  uint32_t result = 0;
+  while (aNum > 0) {
+    if (mNumBitsLeft == 0) {
+      FillReservoir();
+    }
+
+    size_t m = aNum;
+    if (m > mNumBitsLeft) {
+      m = mNumBitsLeft;
+    }
+
+    result = (result << m) | (mReservoir >> (32 - m));
+    mReservoir <<= m;
+    mNumBitsLeft -= m;
+    mTotalBitsLeft -= m;
+
+    aNum -= m;
+  }
+
+  return result;
 }
 
 // Read unsigned integer Exp-Golomb-coded.
 uint32_t
 BitReader::ReadUE()
 {
   uint32_t i = 0;
 
@@ -93,18 +125,38 @@ BitReader::ReadUTF8()
   }
   val &= (top << 1) - 1;
   return val;
 }
 
 size_t
 BitReader::BitCount() const
 {
-  return mSize * 8 - mBitReader->numBitsLeft();
+  return mOriginalBitSize - mTotalBitsLeft;
 }
 
 size_t
 BitReader::BitsLeft() const
 {
-  return mBitReader->numBitsLeft();
+  return mTotalBitsLeft;
+}
+
+void
+BitReader::FillReservoir()
+{
+  if (mSize == 0) {
+    NS_ASSERTION(false, "Attempting to fill reservoir from past end of data");
+    return;
+  }
+
+  mReservoir = 0;
+  size_t i;
+  for (i = 0; mSize > 0 && i < 4; i++) {
+    mReservoir = (mReservoir << 8) | *mData;
+    mData++;
+    mSize--;
+  }
+
+  mNumBitsLeft = 8 * i;
+  mReservoir <<= 32 - mNumBitsLeft;
 }
 
 } // namespace mp4_demuxer
--- a/media/libstagefright/binding/H264.cpp
+++ b/media/libstagefright/binding/H264.cpp
@@ -8,16 +8,34 @@
 #include "mp4_demuxer/BitReader.h"
 #include "mp4_demuxer/ByteReader.h"
 #include "mp4_demuxer/ByteWriter.h"
 #include "mp4_demuxer/H264.h"
 #include <media/stagefright/foundation/ABitReader.h>
 #include <limits>
 #include <cmath>
 
+#define READSE(var, min, max)                                                  \
+  {                                                                            \
+    int32_t val = br.ReadSE();                                                 \
+    if (val < min || val > max) {                                              \
+      return false;                                                            \
+    }                                                                          \
+    aDest.var = val;                                                           \
+  }
+
+#define READUE(var, max)                                                       \
+  {                                                                            \
+    uint32_t uval = br.ReadUE();                                               \
+    if (uval > max) {                                                          \
+      return false;                                                            \
+    }                                                                          \
+    aDest.var = uval;                                                          \
+  }
+
 using namespace mozilla;
 
 namespace mp4_demuxer {
 
 // Default scaling lists (per spec).
 // ITU H264:
 // Table 7-2 – Assignment of mnemonic names to scaling list indices and
 // specification of fall-back rule
@@ -99,16 +117,62 @@ scaling_list(BitReader& aBr, uint8_t (&a
 
 template <size_t N>
 static void
 scaling_list(BitReader& aBr, uint8_t (&aScalingList)[N], const uint8_t (&aDefaultList)[N])
 {
   detail::scaling_list(aBr, aScalingList, N, aDefaultList, nullptr);
 }
 
+static uint32_t
+GetBitLength(const mozilla::MediaByteBuffer* aNAL)
+{
+  size_t size = aNAL->Length();
+
+  while (size > 0 && aNAL->ElementAt(size - 1) == 0) {
+    size--;
+  }
+
+  if (!size) {
+    return 0;
+  }
+
+  if (size > UINT32_MAX / 8) {
+    // We can't represent it, we'll use as much as we can.
+    return UINT32_MAX;
+  }
+
+  uint8_t v = aNAL->ElementAt(size - 1);
+  size *= 8;
+
+  // Remove the stop bit and following trailing zeros.
+  if (v) {
+    // Count the consecutive zero bits (trailing) on the right by binary search.
+    // Adapted from Matt Whitlock algorithm to only bother with 8 bits integers.
+    uint32_t c;
+    if (v & 1) {
+      // Special case for odd v (assumed to happen half of the time).
+      c = 0;
+    } else {
+      c = 1;
+      if ((v & 0xf) == 0) {
+        v >>= 4;
+        c += 4;
+      }
+      if ((v & 0x3) == 0) {
+        v >>= 2;
+        c += 2;
+      }
+      c -= v & 0x1;
+    }
+    size -= c + 1;
+  }
+  return size;
+}
+
 SPSData::SPSData()
 {
   PodZero(this);
   // Default values when they aren't defined as per ITU-T H.264 (2014/02).
   chroma_format_idc = 1;
   video_format = 5;
   colour_primaries = 2;
   transfer_characteristics = 2;
@@ -196,43 +260,41 @@ ConditionDimension(float aValue)
 }
 
 /* static */ bool
 H264::DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest)
 {
   if (!aSPS) {
     return false;
   }
-  BitReader br(aSPS);
+  BitReader br(aSPS, GetBitLength(aSPS));
 
   aDest.profile_idc = br.ReadBits(8);
   aDest.constraint_set0_flag = br.ReadBit();
   aDest.constraint_set1_flag = br.ReadBit();
   aDest.constraint_set2_flag = br.ReadBit();
   aDest.constraint_set3_flag = br.ReadBit();
   aDest.constraint_set4_flag = br.ReadBit();
   aDest.constraint_set5_flag = br.ReadBit();
   br.ReadBits(2); // reserved_zero_2bits
   aDest.level_idc = br.ReadBits(8);
-  aDest.seq_parameter_set_id = br.ReadUE();
-  if (aDest.seq_parameter_set_id >= MAX_SPS_COUNT) {
-    return false;
-  }
+  READUE(seq_parameter_set_id, MAX_SPS_COUNT - 1);
 
   if (aDest.profile_idc == 100 || aDest.profile_idc == 110 ||
       aDest.profile_idc == 122 || aDest.profile_idc == 244 ||
       aDest.profile_idc == 44 || aDest.profile_idc == 83 ||
       aDest.profile_idc == 86 || aDest.profile_idc == 118 ||
       aDest.profile_idc == 128 || aDest.profile_idc == 138 ||
       aDest.profile_idc == 139 || aDest.profile_idc == 134) {
-    if ((aDest.chroma_format_idc = br.ReadUE()) == 3) {
+    READUE(chroma_format_idc, 3);
+    if (aDest.chroma_format_idc == 3) {
       aDest.separate_colour_plane_flag = br.ReadBit();
     }
-    aDest.bit_depth_luma_minus8 = br.ReadUE();
-    aDest.bit_depth_chroma_minus8 = br.ReadUE();
+    READUE(bit_depth_luma_minus8, 6);
+    READUE(bit_depth_chroma_minus8, 6);
     br.ReadBit();       // qpprime_y_zero_transform_bypass_flag
     aDest.seq_scaling_matrix_present_flag = br.ReadBit();
     if (aDest.seq_scaling_matrix_present_flag) {
       scaling_list(br, aDest.scaling_matrix4x4[0], Default_4x4_Intra,
                    Default_4x4_Intra);
       scaling_list(br, aDest.scaling_matrix4x4[1], Default_4x4_Intra,
                    aDest.scaling_matrix4x4[0]);
       scaling_list(br, aDest.scaling_matrix4x4[2], Default_4x4_Intra,
@@ -260,24 +322,26 @@ H264::DecodeSPS(const mozilla::MediaByte
       }
     }
   } else if (aDest.profile_idc == 183) {
     aDest.chroma_format_idc = 0;
   } else {
     // default value if chroma_format_idc isn't set.
     aDest.chroma_format_idc = 1;
   }
-  aDest.log2_max_frame_num = br.ReadUE() + 4;
-  aDest.pic_order_cnt_type = br.ReadUE();
+  READUE(log2_max_frame_num, 12);
+  aDest.log2_max_frame_num += 4;
+  READUE(pic_order_cnt_type, 2);
   if (aDest.pic_order_cnt_type == 0) {
-    aDest.log2_max_pic_order_cnt_lsb = br.ReadUE() + 4;
+    READUE(log2_max_pic_order_cnt_lsb, 12);
+    aDest.log2_max_pic_order_cnt_lsb += 4;
   } else if (aDest.pic_order_cnt_type == 1) {
     aDest.delta_pic_order_always_zero_flag = br.ReadBit();
-    aDest.offset_for_non_ref_pic = br.ReadSE();
-    aDest.offset_for_top_to_bottom_field = br.ReadSE();
+    READSE(offset_for_non_ref_pic, -231, 230);
+    READSE(offset_for_top_to_bottom_field, -231, 230);
     uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ReadUE();
     for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
       br.ReadSE(); // offset_for_ref_frame[i]
     }
   }
   aDest.max_num_ref_frames = br.ReadUE();
   aDest.gaps_in_frame_num_allowed_flag = br.ReadBit();
   aDest.pic_width_in_mbs = br.ReadUE() + 1;
@@ -294,17 +358,19 @@ H264::DecodeSPS(const mozilla::MediaByte
     aDest.frame_crop_right_offset = br.ReadUE();
     aDest.frame_crop_top_offset = br.ReadUE();
     aDest.frame_crop_bottom_offset = br.ReadUE();
   }
 
   aDest.sample_ratio = 1.0f;
   aDest.vui_parameters_present_flag = br.ReadBit();
   if (aDest.vui_parameters_present_flag) {
-    vui_parameters(br, aDest);
+    if (!vui_parameters(br, aDest)) {
+      return false;
+    }
   }
 
   // Calculate common values.
 
   uint8_t ChromaArrayType =
     aDest.separate_colour_plane_flag ? 0 : aDest.chroma_format_idc;
   // Calculate width.
   uint32_t CropUnitX = 1;
@@ -353,17 +419,17 @@ H264::DecodeSPS(const mozilla::MediaByte
     aDest.display_width = aDest.pic_width;
     aDest.display_height =
       ConditionDimension(aDest.pic_height / aDest.sample_ratio);
   }
 
   return true;
 }
 
-/* static */ void
+/* static */ bool
 H264::vui_parameters(BitReader& aBr, SPSData& aDest)
 {
   aDest.aspect_ratio_info_present_flag = aBr.ReadBit();
   if (aDest.aspect_ratio_info_present_flag) {
     aDest.aspect_ratio_idc = aBr.ReadBits(8);
     aDest.sar_width = aDest.sar_height = 0;
 
     // From E.2.1 VUI parameters semantics (ITU-T H.264 02/2014)
@@ -518,26 +584,28 @@ H264::vui_parameters(BitReader& aBr, SPS
       aDest.colour_primaries = aBr.ReadBits(8);
       aDest.transfer_characteristics = aBr.ReadBits(8);
       aDest.matrix_coefficients = aBr.ReadBits(8);
     }
   }
 
   aDest.chroma_loc_info_present_flag = aBr.ReadBit();
   if (aDest.chroma_loc_info_present_flag) {
-    aDest.chroma_sample_loc_type_top_field = aBr.ReadUE();
-    aDest.chroma_sample_loc_type_bottom_field = aBr.ReadUE();
+    BitReader& br = aBr; // so that macro READUE works
+    READUE(chroma_sample_loc_type_top_field, 5);
+    READUE(chroma_sample_loc_type_bottom_field, 5);
   }
 
   aDest.timing_info_present_flag = aBr.ReadBit();
   if (aDest.timing_info_present_flag) {
     aDest.num_units_in_tick = aBr.ReadBits(32);
     aDest.time_scale = aBr.ReadBits(32);
     aDest.fixed_frame_rate_flag = aBr.ReadBit();
   }
+  return true;
 }
 
 /* static */ bool
 H264::DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData,
                              SPSData& aDest)
 {
   H264ParametersSet ps;
   if (!DecodeSPSDataSetFromExtraData(aExtraData, ps.SPSes)) {
@@ -702,38 +770,33 @@ H264::DecodePPS(const mozilla::MediaByte
   if (!aPPS) {
     return false;
   }
 
   if (aSPSes.IsEmpty()) {
     return false;
   }
 
-  BitReader br(aPPS);
-
-  aDest.pic_parameter_set_id = br.ReadUE();
-  aDest.seq_parameter_set_id = br.ReadUE();
+  BitReader br(aPPS, GetBitLength(aPPS));
 
-  if (aDest.pic_parameter_set_id >= MAX_PPS_COUNT ||
-      aDest.seq_parameter_set_id >= MAX_SPS_COUNT) {
-    return false;
-  }
+  READUE(pic_parameter_set_id, MAX_PPS_COUNT - 1);
+  READUE(seq_parameter_set_id, MAX_SPS_COUNT - 1);
 
   const SPSData& sps = aSPSes[aDest.seq_parameter_set_id];
 
   memcpy(aDest.scaling_matrix4x4, sps.scaling_matrix4x4,
          sizeof(aDest.scaling_matrix4x4));
   memcpy(aDest.scaling_matrix8x8, sps.scaling_matrix8x8,
          sizeof(aDest.scaling_matrix8x8));
 
   aDest.entropy_coding_mode_flag = br.ReadBit();
   aDest.bottom_field_pic_order_in_frame_present_flag = br.ReadBit();
-  aDest.num_slice_groups_minus1 = br.ReadUE();
+  READUE(num_slice_groups_minus1, 7);
   if (aDest.num_slice_groups_minus1 > 0) {
-    aDest.slice_group_map_type = br.ReadUE();
+    READUE(slice_group_map_type, 6);
     switch (aDest.slice_group_map_type) {
       case 0:
         for (uint8_t iGroup = 0; iGroup <= aDest.num_slice_groups_minus1;
              iGroup++) {
           aDest.run_length_minus1[iGroup] = br.ReadUE();
         }
         break;
       case 2:
@@ -759,28 +822,23 @@ H264::DecodePPS(const mozilla::MediaByte
             inclusive. */
           br.ReadBits(std::ceil(std::log2(aDest.num_slice_groups_minus1 + 1)));
         }
         break;
       default:
         return false;
     }
   }
-  aDest.num_ref_idx_l0_default_active_minus1 = br.ReadUE();
-  aDest.num_ref_idx_l1_default_active_minus1 = br.ReadUE();
-  if (aDest.num_ref_idx_l0_default_active_minus1 > 32 ||
-      aDest.num_ref_idx_l1_default_active_minus1 > 32) {
-    // reference overflow.
-    return false;
-  }
+  READUE(num_ref_idx_l0_default_active_minus1, 31);
+  READUE(num_ref_idx_l1_default_active_minus1, 31);
   aDest.weighted_pred_flag = br.ReadBit();
   aDest.weighted_bipred_idc = br.ReadBits(2);
-  aDest.pic_init_qp_minus26 = br.ReadSE();
-  aDest.pic_init_qs_minus26 = br.ReadSE();
-  aDest.chroma_qp_index_offset = br.ReadSE();
+  READSE(pic_init_qp_minus26, -(26 + 6 * sps.bit_depth_luma_minus8), 25);
+  READSE(pic_init_qs_minus26, -26, 26);
+  READSE(chroma_qp_index_offset, -12, 12);
   aDest.deblocking_filter_control_present_flag = br.ReadBit();
   aDest.constrained_intra_pred_flag = br.ReadBit();
   aDest.redundant_pic_cnt_present_flag = br.ReadBit();
   if (br.BitsLeft()) {
     aDest.transform_8x8_mode_flag = br.ReadBit();
     if (br.ReadBit()) { // pic_scaling_matrix_present_flag
       if (sps.seq_scaling_matrix_present_flag) {
         scaling_list(br, aDest.scaling_matrix4x4[0], Default_4x4_Intra);
@@ -820,17 +878,17 @@ H264::DecodePPS(const mozilla::MediaByte
                        aDest.scaling_matrix8x8[1]);
           scaling_list(br, aDest.scaling_matrix8x8[4], Default_8x8_Intra,
                        aDest.scaling_matrix8x8[2]);
           scaling_list(br, aDest.scaling_matrix8x8[5], Default_8x8_Inter,
                        aDest.scaling_matrix8x8[3]);
         }
       }
     }
-    aDest.second_chroma_qp_index_offset = br.ReadSE();
+    READSE(second_chroma_qp_index_offset, -12, 12);
   }
   return true;
 }
 
 /* static */ bool
 H264::DecodeParametersSet(const mozilla::MediaByteBuffer* aExtraData,
                          H264ParametersSet& aDest)
 {
@@ -887,9 +945,12 @@ H264::GetFrameType(const mozilla::MediaR
       // IDR NAL.
       return FrameType::I_FRAME;
     }
   }
 
   return FrameType::OTHER;
 }
 
+#undef READUE
+#undef READSE
+
 } // namespace mp4_demuxer
--- a/media/libstagefright/binding/include/mp4_demuxer/BitReader.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/BitReader.h
@@ -1,28 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef BIT_READER_H_
 #define BIT_READER_H_
 
-#include "nsAutoPtr.h"
 #include "MediaData.h"
 
-namespace stagefright { class ABitReader; }
-
 namespace mp4_demuxer
 {
 
 class BitReader
 {
 public:
   explicit BitReader(const mozilla::MediaByteBuffer* aBuffer);
-  BitReader(const uint8_t* aBuffer, size_t aLength);
+  BitReader(const mozilla::MediaByteBuffer* aBuffer, size_t aBits);
+  BitReader(const uint8_t* aBuffer, size_t aBits);
   ~BitReader();
   uint32_t ReadBits(size_t aNum);
   uint32_t ReadBit() { return ReadBits(1); }
   uint32_t ReadU32() { return ReadBits(32); }
   uint64_t ReadU64();
 
   // Read the UTF-8 sequence and convert it to its 64-bit UCS-4 encoded form.
   // Return 0xfffffffffffffff if sequence was invalid.
@@ -33,15 +31,20 @@ public:
   int32_t ReadSE();
 
   // Return the number of bits parsed so far;
   size_t BitCount() const;
   // Return the number of bits left.
   size_t BitsLeft() const;
 
 private:
-  nsAutoPtr<stagefright::ABitReader> mBitReader;
-  const size_t mSize;
+  void FillReservoir();
+  const uint8_t* mData;
+  const size_t mOriginalBitSize;
+  size_t mTotalBitsLeft;
+  size_t mSize;           // Size left in bytes
+  uint32_t mReservoir;    // Left-aligned bits
+  size_t mNumBitsLeft;    // Number of bits left in reservoir.
 };
 
 } // namespace mp4_demuxer
 
 #endif // BIT_READER_H_
\ No newline at end of file
--- a/media/libstagefright/binding/include/mp4_demuxer/H264.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/H264.h
@@ -209,17 +209,17 @@ struct SPSData
 
   /*
     max_num_ref_frames specifies the maximum number of short-term and
     long-term reference frames, complementary reference field pairs,
     and non-paired reference fields that may be used by the decoding
     process for inter prediction of any picture in the
     sequence. max_num_ref_frames also determines the size of the sliding
     window operation as specified in subclause 8.2.5.3. The value of
-    max_num_ref_frames shall be in the range of 0 to MaxDpbSize (as
+    max_num_ref_frames shall be in the range of 0 to MaxDpbFrames (as
     specified in subclause A.3.1 or A.3.2), inclusive.
    */
   uint32_t max_num_ref_frames;
 
   /*
     gaps_in_frame_num_value_allowed_flag specifies the allowed
     values of frame_num as specified in subclause 7.4.3 and the
     decoding process in case of an inferred gap between values of
@@ -379,18 +379,23 @@ struct SPSData
     of transfer_characteristics shall be inferred to be equal to 2
     (the transfer characteristics are unspecified or are determined by the
     application).
    */
   uint8_t transfer_characteristics;
 
   uint8_t matrix_coefficients;
   bool chroma_loc_info_present_flag;
-  uint32_t chroma_sample_loc_type_top_field;
-  uint32_t chroma_sample_loc_type_bottom_field;
+  /*
+    The value of chroma_sample_loc_type_top_field and
+    chroma_sample_loc_type_bottom_field shall be in the range of 0 to 5,
+    inclusive
+  */
+  uint8_t chroma_sample_loc_type_top_field;
+  uint8_t chroma_sample_loc_type_bottom_field;
   bool timing_info_present_flag;
   uint32_t num_units_in_tick;
   uint32_t time_scale;
   bool fixed_frame_rate_flag;
 
   bool scaling_matrix_present;
   uint8_t scaling_matrix4x4[6][16];
   uint8_t scaling_matrix8x8[6][64];
@@ -646,16 +651,16 @@ private:
                                             const SPSDataSet& aPS,
                                             PPSDataSet& aDest);
 
   /* Decode SPS NAL RBSP and fill SPSData structure */
   static bool DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest);
   /* Decode PPS NAL RBSP and fill PPSData structure */
   static bool DecodePPS(const mozilla::MediaByteBuffer* aPPS,
                         const SPSDataSet& aSPSs, PPSData& aDest);
-  static void vui_parameters(BitReader& aBr, SPSData& aDest);
+  static bool vui_parameters(BitReader& aBr, SPSData& aDest);
   // Read HRD parameters, all data is ignored.
   static void hrd_parameters(BitReader& aBr);
 };
 
 } // namespace mp4_demuxer
 
 #endif // MP4_DEMUXER_H264_H_
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -21,16 +21,17 @@
 #pragma warning( push )
 #pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h
 #include <map>
 #pragma warning( pop )
 
 #include "nsAutoPtr.h"
 
 #include "nsWindowsDllInterceptor.h"
+#include "mozilla/Sprintf.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsWindowsHelpers.h"
 #include "WindowsDllBlocklist.h"
 
 using namespace mozilla;
 
 #define ALL_VERSIONS   ((unsigned long long)-1LL)
@@ -255,18 +256,17 @@ static bool sUser32BeforeBlocklist;
 // Duplicated from xpcom glue. Ideally this should be shared.
 void
 printf_stderr(const char *fmt, ...)
 {
   if (IsDebuggerPresent()) {
     char buf[2048];
     va_list args;
     va_start(args, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, args);
-    buf[sizeof(buf) - 1] = '\0';
+    VsprintfLiteral(buf, fmt, args);
     va_end(args);
     OutputDebugStringA(buf);
   }
 
   FILE *fp = _fdopen(_dup(2), "a");
   if (!fp)
       return;
 
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -1144,9 +1144,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 471;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1490105689913000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1490193107372000);
--- a/security/manager/ssl/nsSTSPreloadList.errors
+++ b/security/manager/ssl/nsSTSPreloadList.errors
@@ -2,16 +2,17 @@ 020wifi.nl: [Exception... "Component ret
 0p.no: did not receive HSTS header
 0x.sk: could not connect to host
 0x1337.eu: could not connect to host
 0x44.net: did not receive HSTS header
 0xb612.org: could not connect to host
 1018hosting.nl: did not receive HSTS header
 1022996493.rsc.cdn77.org: could not connect to host
 10seos.com: did not receive HSTS header
+10tacle.io: could not connect to host
 123plons.nl: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 12vpnchina.com: could not connect to host
 18f.gsa.gov: did not receive HSTS header
 1a-jva.de: did not receive HSTS header
 1p.ro: could not connect to host
 1password.com: did not receive HSTS header
 1xcess.com: could not connect to host
 206rc.net: max-age too low: 2592000
@@ -28,16 +29,17 @@ 3chit.cf: could not connect to host
 404.sh: max-age too low: 0
 420dongstorm.com: could not connect to host
 42ms.org: could not connect to host
 4455software.com: did not receive HSTS header
 4679.space: could not connect to host
 47ronin.com: did not receive HSTS header
 491mhz.net: could not connect to host
 4elements.com: did not receive HSTS header
+4miners.net: could not connect to host
 4mm.org: could not connect to host
 4sqsu.eu: could not connect to host
 50millionablaze.org: did not receive HSTS header
 56ct.com: could not connect to host
 60ych.net: did not receive HSTS header
 7kovrikov.ru: did not receive HSTS header
 808.lv: could not connect to host
 83i.net: could not connect to host
@@ -55,16 +57,17 @@ aapp.space: could not connect to host
 abearofsoap.com: did not receive HSTS header
 abecodes.net: did not receive HSTS header
 abeestrada.com: did not receive HSTS header
 abilitylist.org: did not receive HSTS header
 abioniere.de: could not connect to host
 about.ge: could not connect to host
 aboutmyip.info: did not receive HSTS header
 aboutyou-deals.de: did not receive HSTS header
+abthorpe.org: could not connect to host
 abtom.de: did not receive HSTS header
 abury.fr: did not receive HSTS header
 abury.me: did not receive HSTS header
 accelerole.com: did not receive HSTS header
 accountradar.com: could not connect to host
 accuenergy.com: max-age too low: 0
 acorns.com: did not receive HSTS header
 acr.im: could not connect to host
@@ -113,17 +116,16 @@ agilebits.net: could not connect to host
 agrimap.com: did not receive HSTS header
 agrios.de: did not receive HSTS header
 agro-id.gov.ua: could not connect to host
 ahabingo.com: did not receive HSTS header
 ahoynetwork.com: could not connect to host
 ahri.ovh: could not connect to host
 aidanwoods.com: did not receive HSTS header
 aids.gov: did not receive HSTS header
-aify.eu: could not connect to host
 airbnb.com: did not receive HSTS header
 aircomms.com: did not receive HSTS header
 airproto.com: did not receive HSTS header
 aishnair.com: could not connect to host
 aiticon.de: did not receive HSTS header
 aiw-thkoeln.online: could not connect to host
 ajmahal.com: did not receive HSTS header
 akay.me: could not connect to host
@@ -153,21 +155,22 @@ alkami.com: did not receive HSTS header
 all-subtitles.com: did not receive HSTS header
 all.tf: could not connect to host
 alldaymonitoring.com: could not connect to host
 allforyou.at: could not connect to host
 allinnote.com: could not connect to host
 allstarswithus.com: could not connect to host
 alpha.irccloud.com: could not connect to host
 alphabit-secure.com: could not connect to host
-alphabuild.io: did not receive HSTS header
+alphabuild.io: could not connect to host
 alphalabs.xyz: could not connect to host
 alterbaum.net: did not receive HSTS header
 altfire.ca: could not connect to host
 altmv.com: max-age too low: 7776000
+alwaysmine.fi: could not connect to host
 amaforums.org: could not connect to host
 american-truck-simulator.de: could not connect to host
 american-truck-simulator.net: could not connect to host
 americanworkwear.nl: did not receive HSTS header
 amigogeek.net: could not connect to host
 amilx.com: could not connect to host
 amilx.org: could not connect to host
 amitube.com: could not connect to host
@@ -240,35 +243,36 @@ appseccalifornia.org: did not receive HS
 appson.co.uk: did not receive HSTS header
 arabdigitalexpression.org: did not receive HSTS header
 aradulconteaza.ro: could not connect to host
 aran.me.uk: did not receive HSTS header
 arboineuropa.nl: did not receive HSTS header
 arbu.eu: max-age too low: 2419200
 argh.io: could not connect to host
 arlen.se: could not connect to host
-arlet.click: could not connect to host
 armingrodon.de: did not receive HSTS header
 armory.consulting: could not connect to host
 armory.supplies: could not connect to host
 armytricka.cz: did not receive HSTS header
 arrayify.com: could not connect to host
 ars-design.net: could not connect to host
 ars.toscana.it: max-age too low: 0
 artegusto.ru: could not connect to host
 artistnetwork.nl: did not receive HSTS header
 arvamus.eu: could not connect to host
 as.se: could not connect to host
 as9178.net: could not connect to host
 asasuou.pw: could not connect to host
 asc16.com: could not connect to host
 ascii.moe: could not connect to host
 asdpress.cn: could not connect to host
+ashleymedway.com: could not connect to host
 asianodor.com: could not connect to host
 askfit.cz: did not receive HSTS header
+asm-x.com: did not receive HSTS header
 asmui.ga: could not connect to host
 asmui.ml: could not connect to host
 asrob.eu: could not connect to host
 ass.org.au: did not receive HSTS header
 assdecoeur.org: could not connect to host
 asset-alive.com: did not receive HSTS header
 asset-alive.net: did not receive HSTS header
 astrath.net: could not connect to host
@@ -307,17 +311,16 @@ auverbox.ovh: did not receive HSTS heade
 av.de: did not receive HSTS header
 avec-ou-sans-ordonnance.fr: could not connect to host
 avinet.com: max-age too low: 0
 awg-mode.de: did not receive HSTS header
 axado.com.br: did not receive HSTS header
 axeny.com: did not receive HSTS header
 az.search.yahoo.com: did not receive HSTS header
 azprep.us: could not connect to host
-azuxul.fr: could not connect to host
 b3orion.com: max-age too low: 0
 baby-click.de: did not receive HSTS header
 babybic.hu: did not receive HSTS header
 babyhouse.xyz: could not connect to host
 babysaying.me: could not connect to host
 back-bone.nl: did not receive HSTS header
 badcronjob.com: could not connect to host
 badkamergigant.com: could not connect to host
@@ -331,24 +334,23 @@ bananabandy.com: could not connect to ho
 bandb.xyz: could not connect to host
 bandgap.io: could not connect to host
 bandrcrafts.com: could not connect to host
 bangzafran.com: did not receive HSTS header
 bannisbierblog.de: could not connect to host
 banqingdiao.com: could not connect to host
 barbaros.info: could not connect to host
 barely.sexy: did not receive HSTS header
-barunisystems.com: did not receive HSTS header
 bashcode.ninja: could not connect to host
 basicsolutionsus.com: did not receive HSTS header
 basilisk.io: could not connect to host
 basnieuwenhuizen.nl: could not connect to host
 bassh.net: did not receive HSTS header
 baumstark.ca: did not receive HSTS header
-bazarstupava.sk: did not receive HSTS header
+bazarstupava.sk: could not connect to host
 bcbsmagentprofile.com: could not connect to host
 bccx.com: could not connect to host
 bckp.de: could not connect to host
 bcm.com.au: max-age too low: 0
 bcnx.de: max-age too low: 0
 bcsytv.com: could not connect to host
 be.search.yahoo.com: did not receive HSTS header
 beach-inspector.com: did not receive HSTS header
@@ -359,17 +361,16 @@ beastowner.com: did not receive HSTS hea
 beavers.io: could not connect to host
 bebesurdoue.com: could not connect to host
 bedabox.com: max-age too low: 0
 bedeta.de: could not connect to host
 bedreid.dk: did not receive HSTS header
 bedrijvenadministratie.nl: did not receive HSTS header
 beholdthehurricane.com: could not connect to host
 beier.io: could not connect to host
-beijinglug.club: could not connect to host
 belairsewvac.com: did not receive HSTS header
 belics.com: did not receive HSTS header
 belliash.eu.org: could not connect to host
 belltower.io: could not connect to host
 beneffy.com: did not receive HSTS header
 benk.press: could not connect to host
 benny003.de: did not receive HSTS header
 benzkosmetik.de: could not connect to host
@@ -432,34 +433,35 @@ bizon.sk: did not receive HSTS header
 bl4ckb0x.com: did not receive HSTS header
 bl4ckb0x.de: did not receive HSTS header
 bl4ckb0x.eu: did not receive HSTS header
 bl4ckb0x.info: did not receive HSTS header
 bl4ckb0x.net: did not receive HSTS header
 bl4ckb0x.org: did not receive HSTS header
 black-armada.com.pl: could not connect to host
 black-armada.pl: could not connect to host
+black-khat.com: could not connect to host
 blackburn.link: did not receive HSTS header
 blacklane.com: did not receive HSTS header
 blackly.uk: could not connect to host
-blacknova.io: could not connect to host
 blackpayment.ru: could not connect to host
 blackunicorn.wtf: could not connect to host
 blantik.net: could not connect to host
 blenheimchalcot.com: did not receive HSTS header
 blha303.com.au: could not connect to host
 blindsexdate.nl: could not connect to host
 blocksatz-medien.de: did not receive HSTS header
 blog.cyveillance.com: did not receive HSTS header
 blog.lookout.com: did not receive HSTS header
 blubbablasen.de: could not connect to host
 blucas.org: did not receive HSTS header
 blueliv.com: did not receive HSTS header
 bluescloud.xyz: could not connect to host
 bluetenmeer.com: did not receive HSTS header
+bluserv.net: could not connect to host
 bm-trading.nl: did not receive HSTS header
 bngsecure.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 bobiji.com: did not receive HSTS header
 bodyblog.nl: did not receive HSTS header
 bodybuilding-legends.com: could not connect to host
 bodyweightsolution.com: did not receive HSTS header
 boensou.com: did not receive HSTS header
 bogosity.se: could not connect to host
@@ -566,17 +568,16 @@ candylion.rocks: could not connect to ho
 cannyfoxx.me: could not connect to host
 canyonshoa.com: did not receive HSTS header
 capecycles.co.za: did not receive HSTS header
 captchatheprize.com: could not connect to host
 capturethepen.co.uk: could not connect to host
 car-navi.ph: did not receive HSTS header
 carano-service.de: did not receive HSTS header
 caraudio69.cz: could not connect to host
-carck.co.uk: could not connect to host
 cardoni.net: did not receive HSTS header
 cardstream.com: did not receive HSTS header
 cardurl.com: did not receive HSTS header
 cargobay.net: could not connect to host
 carlandfaith.com: could not connect to host
 carlolly.co.uk: could not connect to host
 carlosalves.info: could not connect to host
 carsforbackpackers.com: could not connect to host
@@ -612,37 +613,36 @@ certmgr.org: could not connect to host
 cesal.net: could not connect to host
 cesidianroot.eu: could not connect to host
 cevrimici.com: could not connect to host
 cfcproperties.com: did not receive HSTS header
 cfetengineering.com: could not connect to host
 cg.search.yahoo.com: did not receive HSTS header
 chainmonitor.com: could not connect to host
 championsofregnum.com: did not receive HSTS header
-champserver.net: did not receive HSTS header
 chandlerredding.com: did not receive HSTS header
 changelab.cc: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
-chaos.fail: could not connect to host
+chaos.fail: did not receive HSTS header
 chaoslab.org: could not connect to host
 chargejuice.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 charnleyhouse.co.uk: max-age too low: 604800
 chartpen.com: did not receive HSTS header
 chartstoffarm.de: max-age too low: 10
 chatbot.me: did not receive HSTS header
 chateau-belvoir.com: did not receive HSTS header
 chateauconstellation.ch: did not receive HSTS header
 chatup.cf: could not connect to host
 chaulootz.com: could not connect to host
-chaz6.com: could not connect to host
 chcemvediet.sk: max-age too low: 1555200
 chebedara.com: could not connect to host
 checkout.google.com: did not receive HSTS header (error ignored - included regardless)
 cheerflow.com: could not connect to host
 cheesetart.my: could not connect to host
 chejianer.cn: did not receive HSTS header
+chepaofen.com: could not connect to host
 cherysunzhang.com: max-age too low: 7776000
 chic-leather.com: could not connect to host
 chihiro.xyz: could not connect to host
 chijiokeindustries.co.uk: did not receive HSTS header
 childcaresolutionscny.org: did not receive HSTS header
 chinawhale.com: did not receive HSTS header
 chirgui.eu: could not connect to host
 chm.vn: did not receive HSTS header
@@ -657,16 +657,17 @@ christophheich.me: could not connect to 
 chrisupjohn.com: could not connect to host
 chrome-devtools-frontend.appspot.com: did not receive HSTS header (error ignored - included regardless)
 chrome.google.com: did not receive HSTS header (error ignored - included regardless)
 chroniclesofgeorge.com: did not receive HSTS header
 chua.cf: could not connect to host
 cidr.ml: could not connect to host
 cigarblogs.net: could not connect to host
 cigi.site: could not connect to host
+cim2b.de: could not connect to host
 cip.md: did not receive HSTS header
 ciplanutrition.com: did not receive HSTS header
 citiagent.cz: could not connect to host
 cityoflaurel.org: did not receive HSTS header
 clara-baumert.de: could not connect to host
 classicsandexotics.com: did not receive HSTS header
 classicspublishing.com: could not connect to host
 clcleaningco.com: could not connect to host
@@ -706,16 +707,17 @@ co50.com: did not receive HSTS header
 coam.co: could not connect to host
 cocaine-import.agency: could not connect to host
 codabix.com: did not receive HSTS header
 codabix.de: could not connect to host
 codabix.net: could not connect to host
 code.google.com: did not receive HSTS header (error ignored - included regardless)
 codeco.pw: could not connect to host
 codeforce.io: did not receive HSTS header
+codepult.com: could not connect to host
 codepx.com: did not receive HSTS header
 codiva.io: max-age too low: 2592000
 coffeeetc.co.uk: did not receive HSTS header
 coffeestrategies.com: max-age too low: 2592000
 coiffeurschnittstelle.ch: did not receive HSTS header
 coindam.com: could not connect to host
 coldlostsick.net: could not connect to host
 colisfrais.com: did not receive HSTS header
@@ -753,17 +755,16 @@ corenetworking.de: could not connect to 
 cormactagging.ie: could not connect to host
 cormilu.com.br: did not receive HSTS header
 cornodo.com: could not connect to host
 correctpaardbatterijnietje.nl: did not receive HSTS header
 corruption-mc.net: could not connect to host
 corruption-rsps.net: could not connect to host
 corruption-server.net: could not connect to host
 count.sh: could not connect to host
-couragefound.org: could not connect to host
 couragewhispers.ca: did not receive HSTS header
 coursdeprogrammation.com: could not connect to host
 coursella.com: did not receive HSTS header
 covenantbank.net: could not connect to host
 coverduck.ru: could not connect to host
 cr.search.yahoo.com: did not receive HSTS header
 cracking.org: did not receive HSTS header
 craftbeerbarn.co.uk: could not connect to host
@@ -772,38 +773,37 @@ craftmine.cz: did not receive HSTS heade
 crate.io: did not receive HSTS header
 cravelyrics.com: did not receive HSTS header
 crazycen.com: did not receive HSTS header
 crazyhotseeds.com: did not receive HSTS header
 create-test-publish.co.uk: could not connect to host
 creativephysics.ml: could not connect to host
 creativeplayuk.com: did not receive HSTS header
 crendontech.com: could not connect to host
-crestasantos.com: could not connect to host
 crestoncottage.com: could not connect to host
 criena.net: could not connect to host
 critical.today: could not connect to host
 criticalaim.com: could not connect to host
 crizk.com: could not connect to host
 crosssec.com: did not receive HSTS header
 crowd.supply: did not receive HSTS header
 crowdcurity.com: did not receive HSTS header
 crowdjuris.com: could not connect to host
 crtvmgmt.com: could not connect to host
 crudysql.com: could not connect to host
 cruzr.xyz: could not connect to host
 crypt.guru: could not connect to host
-cryptify.eu: did not receive HSTS header
+cryptify.eu: could not connect to host
 cryptobin.org: could not connect to host
-cryptojar.io: did not receive HSTS header
+cryptojar.io: could not connect to host
 cryptoki.fr: max-age too low: 7776000
 cryptopush.com: did not receive HSTS header
 crysadm.com: max-age too low: 1
 crystalclassics.co.uk: did not receive HSTS header
-csapak.com: max-age too low: 0
+csapak.com: could not connect to host
 csawctf.poly.edu: could not connect to host
 csfs.org.uk: could not connect to host
 csgodicegame.com: did not receive HSTS header
 csgoelemental.com: could not connect to host
 csgokings.eu: could not connect to host
 csohack.tk: could not connect to host
 cspbuilder.info: could not connect to host
 ct.search.yahoo.com: did not receive HSTS header
@@ -811,32 +811,32 @@ cthulhuden.com: could not connect to hos
 cubeserver.eu: could not connect to host
 cubewano.com: could not connect to host
 cujanovic.com: did not receive HSTS header
 cumshots-video.ru: could not connect to host
 cupidmentor.com: did not receive HSTS header
 curroapp.com: could not connect to host
 custe.rs: could not connect to host
 cuvva.insure: did not receive HSTS header
+cvjm-memmingen.de: did not receive HSTS header
 cyanogenmod.xxx: could not connect to host
 cybershambles.com: could not connect to host
 cycleluxembourg.lu: did not receive HSTS header
 cydia-search.io: could not connect to host
 cyphertite.com: could not connect to host
 dad256.tk: could not connect to host
 dah5.com: did not receive HSTS header
 dailystormerpodcasts.com: did not receive HSTS header
 dakrib.net: could not connect to host
 dalingk.co: could not connect to host
 dango.in: did not receive HSTS header
 daniel-steuer.de: did not receive HSTS header
 danieldk.eu: did not receive HSTS header
 danielworthy.com: did not receive HSTS header
 danijobs.com: could not connect to host
-dannyrohde.de: could not connect to host
 danpiel.net: could not connect to host
 danrl.de: did not receive HSTS header
 daolerp.xyz: could not connect to host
 dargasia.is: could not connect to host
 dario.im: could not connect to host
 dark-x.cf: could not connect to host
 darkengine.io: could not connect to host
 darkhole.cn: could not connect to host
@@ -862,17 +862,17 @@ datenreiter.tk: could not connect to hos
 datewon.net: did not receive HSTS header
 davidglidden.eu: could not connect to host
 davidhunter.scot: did not receive HSTS header
 davidmessenger.co.uk: could not connect to host
 davidnoren.com: did not receive HSTS header
 davidreinhardt.de: could not connect to host
 davidscherzer.at: could not connect to host
 daylightcompany.com: did not receive HSTS header
-db.gy: could not connect to host
+db.gy: did not receive HSTS header
 dbx.ovh: did not receive HSTS header
 dccode.gov: could not connect to host
 dcurt.is: did not receive HSTS header
 ddatsh.com: did not receive HSTS header
 dden.ca: could not connect to host
 dden.website: could not connect to host
 dden.xyz: could not connect to host
 debank.tv: did not receive HSTS header
@@ -912,16 +912,17 @@ detest.org: could not connect to host
 detutorial.com: did not receive HSTS header
 devcu.net: did not receive HSTS header
 devh.de: did not receive HSTS header
 devincrow.me: could not connect to host
 devinfo.net: did not receive HSTS header
 devmsg.com: did not receive HSTS header
 devtub.com: did not receive HSTS header
 devuan.org: did not receive HSTS header
+dfranke.com: did not receive HSTS header
 diablotine.rocks: could not connect to host
 diarbag.us: did not receive HSTS header
 diasp.cz: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 diedrich.co: could not connect to host
 digidroom.be: did not receive HSTS header
 digitalbank.kz: could not connect to host
 digitaldaddy.net: could not connect to host
 digitalriver.tk: could not connect to host
@@ -978,68 +979,72 @@ dragonisles.net: could not connect to ho
 dragons-of-highlands.cz: could not connect to host
 dragontrainingmobilezoo.com.au: max-age too low: 0
 draw.uy: could not connect to host
 drawingcode.net: could not connect to host
 drbethanybarnes.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 drdevil.ru: could not connect to host
 drdim.ru: could not connect to host
 dreadbyte.com: could not connect to host
+dreaming.solutions: could not connect to host
 dreamlinehost.com: did not receive HSTS header
 drishti.guru: could not connect to host
 drive.google.com: did not receive HSTS header (error ignored - included regardless)
 driving-lessons.co.uk: did not receive HSTS header
 droidboss.com: could not connect to host
 dropcam.com: did not receive HSTS header
 drtroyhendrickson.com: could not connect to host
 drumbandesperanto.nl: could not connect to host
 ds-christiansen.de: did not receive HSTS header
 dshiv.io: could not connect to host
-dubrovskiy.pro: did not receive HSTS header
+dubrovskiy.net: could not connect to host
+dubrovskiy.pro: could not connect to host
 duch.cloud: could not connect to host
 duesee.org: could not connect to host
 dullsir.com: did not receive HSTS header
 duria.de: max-age too low: 3600
 dwhd.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
+dwnld.me: could not connect to host
 dworzak.ch: could not connect to host
 dxa.io: could not connect to host
 dycontrol.de: could not connect to host
 dylanscott.com.au: did not receive HSTS header
 dymersion.com: did not receive HSTS header
 dzimejl.sk: did not receive HSTS header
 dzlibs.io: could not connect to host
 e-aut.net: could not connect to host
 e-deca2.org: did not receive HSTS header
 earga.sm: could not connect to host
 earlybirdsnacks.com: could not connect to host
+earthrise16.com: could not connect to host
 easez.net: did not receive HSTS header
 easychiller.org: could not connect to host
 easyhaul.com: did not receive HSTS header
 eatlowcarb.de: did not receive HSTS header
 eauclairecommerce.com: could not connect to host
 ebecs.com: did not receive HSTS header
 ebermannstadt.de: max-age too low: 0
 ebp2p.com: did not receive HSTS header
 ebpglobal.com: did not receive HSTS header
 ecake.in: could not connect to host
 ecdn.cz: could not connect to host
 ecfs.link: could not connect to host
 ecg.fr: could not connect to host
 echosystem.fr: did not receive HSTS header
 ecole-en-danger.fr: could not connect to host
+ecomparemo.com: did not receive HSTS header
 ecorus.eu: did not receive HSTS header
 edcphenix.tk: could not connect to host
 edelsteincosmetic.com: did not receive HSTS header
 edissecurity.sk: did not receive HSTS header
 edix.ru: could not connect to host
 edk.com.tr: did not receive HSTS header
 edmodo.com: did not receive HSTS header
 edp-collaborative.com: max-age too low: 2500
 eduvance.in: did not receive HSTS header
-edwardsnowden.com: could not connect to host
 eenhoorn.ga: did not receive HSTS header
 eeqj.com: could not connect to host
 efficienthealth.com: did not receive HSTS header
 effortlesshr.com: did not receive HSTS header
 egg-ortho.ch: did not receive HSTS header
 egit.co: could not connect to host
 eglek.com: did not receive HSTS header
 ego-world.org: could not connect to host
@@ -1056,52 +1061,52 @@ electricianforum.co.uk: could not connec
 electromc.com: could not connect to host
 elemprendedor.com.ve: could not connect to host
 elenag.ga: could not connect to host
 elenoon.ir: did not receive HSTS header
 elgacien.de: could not connect to host
 elimdengelen.com: did not receive HSTS header
 elisabeth-kostecki.de: did not receive HSTS header
 elisabethkostecki.de: did not receive HSTS header
+elite-porno.ru: could not connect to host
 elitefishtank.com: could not connect to host
 elmermx.ch: could not connect to host
 elnutricionista.es: did not receive HSTS header
 elpo.xyz: could not connect to host
 elsamakhin.com: could not connect to host
 elsitar.com: did not receive HSTS header
 email.lookout.com: could not connect to host
 emanatepixels.com: could not connect to host
 emeldi-commerce.com: max-age too low: 0
+emilyhorsman.com: could not connect to host
 emjainteractive.com: did not receive HSTS header
 emmable.com: could not connect to host
 emnitech.com: could not connect to host
 empleostampico.com: did not receive HSTS header
 enargia.jp: max-age too low: 0
 encode.space: did not receive HSTS header
 encoder.pw: could not connect to host
 encrypted.google.com: did not receive HSTS header (error ignored - included regardless)
 endzeit-architekten.com: did not receive HSTS header
-engaugetools.com: could not connect to host
 engelwerbung.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 enigmacpt.com: did not receive HSTS header
 enigmail.net: did not receive HSTS header
 enteente.club: could not connect to host
 enteente.space: could not connect to host
 enteente.xyz: could not connect to host
 enterdev.co: did not receive HSTS header
 enterprise-threat-monitor.com: max-age too low: 0
 entersynapse.com: could not connect to host
 entrepreneur.or.id: did not receive HSTS header
 enumify.com: could not connect to host
 envygeeks.com: did not receive HSTS header
 envygeeks.io: did not receive HSTS header
 eol34.com: did not receive HSTS header
 epanurse.com: could not connect to host
 ephry.com: could not connect to host
-epicenter.works: did not receive HSTS header
 epoxate.com: did not receive HSTS header
 eq8.net.au: could not connect to host
 equate.net.au: max-age too low: 3600
 equatetechnologies.com.au: max-age too low: 3600
 equilibre-yoga-jennifer-will.com: could not connect to host
 erawanarifnugroho.com: did not receive HSTS header
 eressea.xyz: could not connect to host
 ericyl.com: did not receive HSTS header
@@ -1111,17 +1116,19 @@ erotalia.es: could not connect to host
 eroticen.com: did not receive HSTS header
 erotische-aanbiedingen.nl: could not connect to host
 errlytics.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 errolz.com: could not connect to host
 errors.zenpayroll.com: could not connect to host
 ersindemirtas.com: did not receive HSTS header
 esclear.de: did not receive HSTS header
 escotour.com: did not receive HSTS header
+escritoriodearte.com: did not receive HSTS header
 esec.rs: did not receive HSTS header
+esln.org: could not connect to host
 espra.com: could not connect to host
 esquonic.com: could not connect to host
 essexcosmeticdentists.co.uk: did not receive HSTS header
 essexghosthunters.co.uk: did not receive HSTS header
 estilosapeca.com: could not connect to host
 etdonline.co.uk: could not connect to host
 eternitylove.us: could not connect to host
 ethack.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
@@ -1190,16 +1197,17 @@ fayolle.info: [Exception... "Component r
 fbox.li: could not connect to host
 fdj.im: could not connect to host
 feard.space: could not connect to host
 feastr.de: did not receive HSTS header
 fedux.com.ar: could not connect to host
 feezmodo.com: max-age too low: 0
 felisslovakia.sk: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 feliwyn.fr: did not receive HSTS header
+felixsanz.com: could not connect to host
 feminists.co: could not connect to host
 fenteo.com: could not connect to host
 feragon.net: max-age too low: 84600
 feriahuamantla.com: could not connect to host
 festember.com: did not receive HSTS header
 fexmen.com: could not connect to host
 ffmradio.de: did not receive HSTS header
 fhdhelp.de: could not connect to host
@@ -1221,31 +1229,30 @@ firebaseio-demo.com: could not connect t
 firebaseio.com: could not connect to host (error ignored - included regardless)
 firefall.rocks: could not connect to host
 firemail.io: could not connect to host
 fireorbit.de: did not receive HSTS header
 first-time-offender.com: could not connect to host
 firstforex.co.uk: did not receive HSTS header
 fish2.me: did not receive HSTS header
 fit4medien.de: did not receive HSTS header
-fitiapp.com: could not connect to host
+fitiapp.com: did not receive HSTS header
 fitnesswerk.de: could not connect to host
 five.vn: did not receive HSTS header
 fivestarsitters.com: did not receive HSTS header
 fixatom.com: did not receive HSTS header
 fixingdns.com: did not receive HSTS header
 fj.search.yahoo.com: did not receive HSTS header
 fjruiz.es: could not connect to host
 flags.ninja: could not connect to host
 flamewall.net: could not connect to host
 flamingkeys.com: did not receive HSTS header
 flamingkeys.com.au: could not connect to host
 flana.com: could not connect to host
 flawcheck.com: did not receive HSTS header
-fleximus.org: could not connect to host
 fliexer.com: did not receive HSTS header
 floless.co.uk: did not receive HSTS header
 florian-lillpopp.de: max-age too low: 10
 florianlillpopp.de: max-age too low: 10
 floridaescapes.co.uk: did not receive HSTS header
 flouartistique.ch: could not connect to host
 flow.pe: could not connect to host
 flow.su: could not connect to host
@@ -1304,17 +1311,17 @@ fruitusers.com: could not connect to hos
 frusky.net: could not connect to host
 ftctele.com: did not receive HSTS header
 fuckgfw233.org: could not connect to host
 fugle.de: could not connect to host
 fukushima-web.com: did not receive HSTS header
 funkyweddingideas.com.au: could not connect to host
 funrun.com: did not receive HSTS header
 furiffic.com: did not receive HSTS header
-furry.be: could not connect to host
+furry.be: max-age too low: 86400
 fusionmate.com: could not connect to host
 futbol11.com: did not receive HSTS header
 futuretechnologi.es: could not connect to host
 futureyouhealth.com: did not receive HSTS header
 fx-rk.com: did not receive HSTS header
 fyfywka.com: could not connect to host
 fysiohaenraets.nl: did not receive HSTS header
 fzn.io: could not connect to host
@@ -1398,16 +1405,17 @@ glass.google.com: did not receive HSTS h
 glentakahashi.com: max-age too low: 0
 glitzmirror.com: could not connect to host
 globalexpert.co.nz: could not connect to host
 globalittech.com: could not connect to host
 globalmusic.ga: could not connect to host
 gloomyvancouver.com: could not connect to host
 glopoi.com: did not receive HSTS header
 glws.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
+glyph.ws: could not connect to host
 gm.search.yahoo.com: did not receive HSTS header
 gmail.com: did not receive HSTS header (error ignored - included regardless)
 gmantra.org: could not connect to host
 gmoes.at: max-age too low: 600000
 go.ax: did not receive HSTS header
 goabonga.com: could not connect to host
 goaltree.ch: did not receive HSTS header
 goarmy.eu: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
@@ -1426,34 +1434,31 @@ goldminer.ga: could not connect to host
 golocal-media.de: did not receive HSTS header
 gonzalosanchez.mx: did not receive HSTS header
 goodenough.nz: did not receive HSTS header
 goodwin43.ru: did not receive HSTS header
 google: could not connect to host (error ignored - included regardless)
 googlemail.com: did not receive HSTS header (error ignored - included regardless)
 googleplex.com: did not receive HSTS header (error ignored - included regardless)
 goolok.com: did not receive HSTS header
-gordonobrecht.com: did not receive HSTS header
 gorf.chat: could not connect to host
 gorilla-gym.site: could not connect to host
 gotech.com.eg: did not receive HSTS header
 goto.google.com: did not receive HSTS header (error ignored - included regardless)
 gottcode.org: did not receive HSTS header
 govillemo.ca: did not receive HSTS header
 gowe.wang: could not connect to host
 gparent.org: did not receive HSTS header
 gpsfix.cz: could not connect to host
 gpstuner.com: did not receive HSTS header
 gracesofgrief.com: max-age too low: 86400
 grandmascookieblog.com: did not receive HSTS header
 graph.no: did not receive HSTS header
 gravity-net.de: could not connect to host
 grazetech.com: could not connect to host
-greboid.co.uk: could not connect to host
-greboid.com: could not connect to host
 greenhillantiques.co.uk: did not receive HSTS header
 greenvines.com.tw: did not receive HSTS header
 gregorytlee.me: could not connect to host
 gremots.com: did not receive HSTS header
 greplin.com: could not connect to host
 gribani.com: could not connect to host
 grigalanzsoftware.com: could not connect to host
 grossmann.gr: could not connect to host
@@ -1478,32 +1483,32 @@ gxlrx.net: could not connect to host
 gyboche.com: could not connect to host
 gyboche.science: could not connect to host
 gycis.me: could not connect to host
 gypthecat.com: max-age too low: 604800
 gyz.io: could not connect to host
 h2check.org: could not connect to host
 haarkliniek.com: did not receive HSTS header
 habanaavenue.com: did not receive HSTS header
+habbo.life: could not connect to host
 hablemosdetecnologia.com.ve: could not connect to host
 hack.cz: could not connect to host
 hack.li: did not receive HSTS header
 hacker.one: could not connect to host
 hackerforever.com: did not receive HSTS header
 hackerone-ext-adroll.com: could not connect to host
 hackest.org: did not receive HSTS header
 hackit.im: could not connect to host
 hadzic.co: could not connect to host
 haeckdesign.com: did not receive HSTS header
 hahayidu.org: could not connect to host
 haitschi.com: could not connect to host
 haitschi.de: could not connect to host
 haitschi.net: could not connect to host
 haitschi.org: could not connect to host
-haktec.de: did not receive HSTS header
 haku.moe: could not connect to host
 hakugin.org: could not connect to host
 halo.red: could not connect to host
 hancc.net: did not receive HSTS header
 hanfu.la: could not connect to host
 hannover-banditen.de: did not receive HSTS header
 hao2taiwan.com: max-age too low: 0
 haozi.me: did not receive HSTS header
@@ -1544,21 +1549,19 @@ heartlandrentals.com: did not receive HS
 heftkaufen.de: did not receive HSTS header
 helloworldhost.com: did not receive HSTS header
 helpadmin.net: could not connect to host
 helpium.de: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 helpmebuild.com: did not receive HSTS header
 hemdal.se: could not connect to host
 hencagon.com: could not connect to host
 henriknoerr.com: could not connect to host
-heppler.net: could not connect to host
 hermes-net.de: did not receive HSTS header
 herpaderp.net: did not receive HSTS header
 herzbotschaft.de: did not receive HSTS header
-heyguevara.com: could not connect to host
 hibilog.com: could not connect to host
 hicn.gq: could not connect to host
 hiddendepth.ie: max-age too low: 0
 hiddenmail.xyz: could not connect to host
 highseer.com: did not receive HSTS header
 highsurf-miyazaki.com: did not receive HSTS header
 hiitcentre.com: did not receive HSTS header
 hikariempire.com: could not connect to host
@@ -1574,16 +1577,17 @@ hmm.nyc: could not connect to host
 hn.search.yahoo.com: did not receive HSTS header
 hoerbuecher-und-hoerspiele.de: could not connect to host
 hogar123.es: could not connect to host
 hohm.in: could not connect to host
 holifestival-freyung.de: could not connect to host
 holymoly.lu: did not receive HSTS header
 homa.website: could not connect to host
 homads.com: could not connect to host
+homeprivate.de: could not connect to host
 honeytracks.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 hongzhaxiaofendui.com: could not connect to host
 honoo.com: could not connect to host
 hookandloom.com: did not receive HSTS header
 hooray.beer: could not connect to host
 horosho.in: could not connect to host
 horseboners.xxx: did not receive HSTS header
 hortifarm.ro: did not receive HSTS header
@@ -1608,17 +1612,19 @@ http418.xyz: could not connect to host
 httpstatuscode418.xyz: could not connect to host
 hu.search.yahoo.com: did not receive HSTS header
 huarongdao.com: did not receive HSTS header
 hugosleep.com.au: did not receive HSTS header
 humblefinances.com: could not connect to host
 humeurs.net: could not connect to host
 humpteedumptee.in: did not receive HSTS header
 huntshomeinspections.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
+hup.blue: could not connect to host
 hurricanelabs.com: did not receive HSTS header
+huskybutt.dog: could not connect to host
 hydra.ws: could not connect to host
 hyper69.com: did not receive HSTS header
 hzsh.xyz: did not receive HSTS header
 i-jp.net: could not connect to host
 i-partners.sk: did not receive HSTS header
 iamokay.nl: did not receive HSTS header
 iamveto.com: could not connect to host
 iapws.com: did not receive HSTS header
@@ -1722,17 +1728,17 @@ iosmods.com: could not connect to host
 iostips.ru: could not connect to host
 iotsms.io: could not connect to host
 ip-life.net: could not connect to host
 ip6.im: did not receive HSTS header
 ipmimagazine.com: did not receive HSTS header
 iptel.by: max-age too low: 0
 iptel.ro: could not connect to host
 ipv6cloud.club: could not connect to host
-iqcn.co: max-age too low: 2592000
+iqcn.co: did not receive HSTS header
 iqualtech.com: did not receive HSTS header
 iranianlawschool.com: could not connect to host
 iraqidinar.org: did not receive HSTS header
 irazimina.ru: did not receive HSTS header
 irccloud.com: did not receive HSTS header
 ircmett.de: did not receive HSTS header
 irelandesign.com: did not receive HSTS header
 ischool.co.jp: did not receive HSTS header
@@ -1751,16 +1757,17 @@ itsadog.co.uk: did not receive HSTS head
 itsamurai.ru: max-age too low: 2592000
 itsecurityassurance.pw: did not receive HSTS header
 itshost.ru: could not connect to host
 ivi-fertility.com: max-age too low: 0
 ivi.es: max-age too low: 0
 ivk.website: could not connect to host
 iww.mx: could not connect to host
 izdiwho.com: could not connect to host
+izevg.ru: could not connect to host
 izoox.com: did not receive HSTS header
 izzzorgconcerten.nl: could not connect to host
 ja-publications.com: did not receive HSTS header
 jabbari.io: did not receive HSTS header
 jackalworks.com: could not connect to host
 jacobparry.ca: did not receive HSTS header
 jahliveradio.com: could not connect to host
 jakenbake.com: did not receive HSTS header
@@ -1771,17 +1778,17 @@ jamesburton.london: could not connect to
 jamesbywater.me: could not connect to host
 jamesbywater.me.uk: could not connect to host
 jamesconroyfinn.com: did not receive HSTS header
 jamesdoell.com: could not connect to host
 jamesdoylephoto.com: did not receive HSTS header
 jamesf.xyz: could not connect to host
 jamesmorrison.me: did not receive HSTS header
 jamourtney.com: could not connect to host
-jan27.org: could not connect to host
+jan27.org: did not receive HSTS header
 janario.me: could not connect to host
 janbrodda.de: max-age too low: 2592000
 jani.media: could not connect to host
 jannyrijneveld.nl: did not receive HSTS header
 janus-engineering.de: did not receive HSTS header
 japlex.com: could not connect to host
 jaqen.ch: could not connect to host
 jaredeberle.org: did not receive HSTS header
@@ -1821,17 +1828,16 @@ jfmel.com: did not receive HSTS header
 jfx.space: did not receive HSTS header
 jh-media.eu: could not connect to host
 jhburton.uk: could not connect to host
 jhejderup.me: could not connect to host
 jia1hao.com: could not connect to host
 jikken.de: could not connect to host
 jimas.eu: did not receive HSTS header
 jimmycai.org: could not connect to host
-jinbo123.com: did not receive HSTS header
 jkb.pics: could not connect to host
 jkbuster.com: could not connect to host
 jmdekker.it: could not connect to host
 joakimalgroy.com: could not connect to host
 jobmedic.com: did not receive HSTS header
 joedavison.me: could not connect to host
 jogi-server.de: did not receive HSTS header
 johners.me: could not connect to host
@@ -1839,17 +1845,16 @@ johners.tech: did not receive HSTS heade
 johnhgaunt.com: did not receive HSTS header
 johnrom.com: did not receive HSTS header
 jokewignand.nl: did not receive HSTS header
 jonas-keidel.de: did not receive HSTS header
 jonasgroth.se: max-age too low: 2592000
 jonathan.ir: could not connect to host
 jonathancarter.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 jonn.me: could not connect to host
-jonnybarnes.uk: could not connect to host
 joostbovee.nl: did not receive HSTS header
 jordanhamilton.me: could not connect to host
 joretapo.fr: could not connect to host
 josahrens.me: could not connect to host
 joshstroup.me: could not connect to host
 jottit.com: could not connect to host
 jpbike.cz: could not connect to host
 jrc9.ca: did not receive HSTS header
@@ -1861,17 +1866,16 @@ jualssh.com: could not connect to host
 juliamweber.de: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 julian-kipka.de: did not receive HSTS header
 jumbox.xyz: could not connect to host
 junaos.xyz: did not receive HSTS header
 junge-selbsthilfe.info: could not connect to host
 juniwalk.cz: could not connect to host
 junqtion.com: could not connect to host
 jupp0r.de: did not receive HSTS header
-justice4assange.com: could not connect to host
 justlikethat.hosting: did not receive HSTS header
 justnaw.co.uk: could not connect to host
 justudin.com: did not receive HSTS header
 juwairen.cn: could not connect to host
 jvoice.net: could not connect to host
 jwilsson.me: could not connect to host
 jxm.in: could not connect to host
 k-dev.de: could not connect to host
@@ -1913,17 +1917,16 @@ kernl.us: did not receive HSTS header
 kevinapease.com: did not receive HSTS header
 keymaster.lookout.com: did not receive HSTS header
 kg-rating.com: did not receive HSTS header
 kgxtech.com: max-age too low: 2592000
 kickass.al: could not connect to host
 kid-dachau.de: did not receive HSTS header
 kiel-media.de: did not receive HSTS header
 kienlen.org: could not connect to host
-kiffmarks.com: could not connect to host
 kimberg.co.uk: could not connect to host
 kimpost.org: could not connect to host
 kinderly.co.uk: could not connect to host
 kinderwagen-test24.de: could not connect to host
 kingmanhall.org: could not connect to host
 kinnon.enterprises: could not connect to host
 kinogb.net: max-age too low: 0
 kionetworks.com: did not receive HSTS header
@@ -1938,16 +1941,17 @@ kissflow.com: did not receive HSTS heade
 kisun.co.jp: could not connect to host
 kitakemon.com: could not connect to host
 kitchenpunx.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 kitk.at: could not connect to host
 kitsta.com: could not connect to host
 kiwiirc.com: max-age too low: 5256000
 kizil.net: could not connect to host
 kjaermaxi.me: did not receive HSTS header
+kkaufmann.de: did not receive HSTS header
 klasfauseweh.de: could not connect to host
 klauwd.com: did not receive HSTS header
 klaxn.org: could not connect to host
 kleertjesvoordelig.nl: could not connect to host
 kleinblogje.nl: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 kleppe.co: could not connect to host
 kletterkater.com: did not receive HSTS header
 klicktojob.de: could not connect to host
@@ -1966,37 +1970,39 @@ koop-bremen.de: [Exception... "Component
 koophetlokaal.nl: max-age too low: 300
 koordinate.net: could not connect to host
 korni22.org: did not receive HSTS header
 korsanparti.org: could not connect to host
 kotonehoko.net: could not connect to host
 kotovstyle.ru: could not connect to host
 kr.search.yahoo.com: did not receive HSTS header
 kredite.sale: could not connect to host
+kriechel.de: could not connect to host
 kriegt.es: could not connect to host
 krmela.com: could not connect to host
 kroetenfuchs.de: could not connect to host
 kropkait.pl: could not connect to host
 krouzkyliduska.cz: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
 krunut.com: did not receive HSTS header
 krypteia.org: could not connect to host
 ksfh-mail.de: could not connect to host
 kstan.me: could not connect to host
 kucom.it: did not receive HSTS header
 kueulangtahunanak.net: could not connect to host
 kummerlaender.eu: did not receive HSTS header
-kupelne-ptacek.sk: could not connect to host
+kupelne-ptacek.sk: did not receive HSTS header
 kuppingercole.com: did not receive HSTS header
 kura.io: could not connect to host
 kurehun.org: could not connect to host
 kurz.pw: did not receive HSTS header
 kusaka-abacus.jp: max-age too low: 0
 kweddingplanning.com: did not receive HSTS header
 kyanite.co: could not connect to host
 kylinj.com: could not connect to host
+kyochon.fr: could not connect to host
 kz.search.yahoo.com: did not receive HSTS header
 kzjnet.com: could not connect to host
 labaia.info: could not connect to host
 labina.com.tr: did not receive HSTS header
 laboiteapc.fr: did not receive HSTS header
 labordata.io: could not connect to host
 labrador-retrievers.com.au: did not receive HSTS header
 labs.moscow: did not receive HSTS header
@@ -2019,38 +2025,38 @@ lask.in: did not receive HSTS header
 latus.xyz: could not connect to host
 lavabit.no: could not connect to host
 lavval.com: could not connect to host
 lawformt.com: did not receive HSTS header
 laxatus.com: did not receive HSTS header
 laxiongames.es: could not connect to host
 lbrt.xyz: could not connect to host
 ldarby.me.uk: could not connect to host
-le-hosting.de: could not connect to host
 leadership9.com: could not connect to host
 leardev.de: did not receive HSTS header
 learnfrenchfluently.com: did not receive HSTS header
 learningorder.com: could not connect to host
 ledgerscope.net: could not connect to host
 leermotorrijden.nl: max-age too low: 300
 legarage.org: did not receive HSTS header
-leinir.dk: could not connect to host
+leinir.dk: max-age too low: 86400
 leitner.com.au: did not receive HSTS header
 leiyun.me: did not receive HSTS header
 lellyboi.ml: could not connect to host
 lelongbank.com: did not receive HSTS header
 lemp.io: did not receive HSTS header
 lenovogaming.com: did not receive HSTS header
 lentri.com: did not receive HSTS header
 leolana.com: could not connect to host
 leon-jaekel.com: could not connect to host
 leopold.email: could not connect to host
 leopoldina.net: did not receive HSTS header
 leopotamgroup.com: could not connect to host
 leovanna.co.uk: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 121"  data: no]
+lepont.pl: could not connect to host
 lerner.moscow: did not receive HSTS header
 les-corsaires.net: could not connect to host
 lesdouceursdeliyana.com: could not connect to host
 letras.mus.br: did not receive HSTS header
 letsmultiplayerplay.com: did not receive HSTS header
 letustravel.tk: could not connect to host
 lfullerdesign.com: did not receive HSTS header
 lgiswa.com.au: did not receive HSTS header
@@ -2093,16 +2099,17 @@ linuxeyecandy.com: could not connect to 
 linuxfixed.it: could not connect to host
 linuxforyou.com: could not connect to host
 linuxgeek.ro: could not connect to host
 linuxwebservertips.in: could not connect to host
 liquorsanthe.in: could not connect to host
 listafirmelor.com: could not connect to host
 litespeed.io: could not connect to host
 livedemo.io: could not connect to host
+livedesign.at: could not connect to host
 livej.am: could not connect to host
 livi.co: did not receive HSTS header
 loadingdeck.com: did not receive HSTS header
 loafbox.com: could not connect to host
 locktheirphone.com: could not connect to host
 locomotive.ca: did not receive HSTS header
 login.corp.google.com: max-age too low: 7776000 (error ignored - included regardless)
 loginseite.com: could not connect to host
@@ -2155,26 +2162,24 @@ lusis.net: did not receive HSTS header
 lustrumxi.nl: did not receive HSTS header
 luxus-russen.de: did not receive HSTS header
 luxwatch.com: could not connect to host
 lv.search.yahoo.com: did not receive HSTS header
 lzkill.com: could not connect to host
 m-ali.xyz: did not receive HSTS header
 m.gparent.org: could not connect to host
 m.nu: did not receive HSTS header
-m2tc.fr: could not connect to host
 m3-gmbh.de: did not receive HSTS header
 m82labs.com: did not receive HSTS header
 maarten.nyc: did not receive HSTS header
 maartenvandekamp.nl: did not receive HSTS header
 macbolo.com: could not connect to host
 macchaberrycream.com: could not connect to host
 macgeneral.de: did not receive HSTS header
 machon.biz: could not connect to host
-macker.io: could not connect to host
 madars.org: did not receive HSTS header
 maddin.ga: could not connect to host
 madebymagnitude.com: did not receive HSTS header
 maderwin.com: did not receive HSTS header
 madusecurity.com: could not connect to host
 mafamane.com: could not connect to host
 mafiareturns.com: max-age too low: 2592000
 magenx.com: did not receive HSTS header
@@ -2195,53 +2200,53 @@ managemynetsuite.com: could not connect 
 mannsolutions.co.uk: did not receive HSTS header
 mansion-note.com: could not connect to host
 marchagen.nl: did not receive HSTS header
 marcontrol.com: did not receive HSTS header
 marcuskoh.com: could not connect to host
 mariannematthew.com: could not connect to host
 marie-curie.fr: could not connect to host
 marie-elisabeth.dk: did not receive HSTS header
+marie.club: could not connect to host
 markaconnor.com: could not connect to host
 markayapilandirma.com: did not receive HSTS header
 market.android.com: did not receive HSTS header (error ignored - included regardless)
 markrego.com: could not connect to host
 markus-dev.com: did not receive HSTS header
 markusweimar.de: did not receive HSTS header
 marleyresort.com: did not receive HSTS header
 marshut.net: could not connect to host
 martiert.com: could not connect to host
 martijnvhoof.nl: could not connect to host
 martinsfamilyappliance.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  l